Files
Ruslan PiatrovichandCursor 532ac723cb Add full Cursor chat archive for Documents/repos workspaces.
Includes global state.vscdb, per-repo transcripts, workspace storage,
composer exports, and restore scripts for a new machine.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 11:53:07 +03:00

26 lines
57 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\n\t\nАртем Нагибин\n27 минут назад\n\n@lmru-devops-bot привет. нужна помощь.\n\nМожно ли в конфигурации Jenkins для проекта добавить как-то зеркала для maven в конфигурации?\n\nЗависимости тянутся из https://art.lmru.tech/mvn , но нужно чтоб тянулись из https://art.lmru.tech/mvn-replenishment\nhttps://jenkins.lmru.tech/job/replenishment/job/backend/job/lmru--replenishment--parameterization-api/job/REPLAN-3052/103/execution/node/102/log/\n\nВ pom указан именно mvn-replenishment и локально все ок.\nв дженкинсе этот источник в сборках не учитывается\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Понял задачу: нужно разобраться, почему в Jenkins игнорируется `mvn-replenishment`, хотя в `pom.xml` он указан, и как это поправить через конфигурацию. Сейчас быстро проверю `Jenkinsfile`/Maven-настройки в репозитории и подскажу точный рабочий вариант.\n\n**Inspecting Jenkins config and Maven files**"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"**Preparing to analyze build files**"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Проверил репозиторий: в `pom.xml` у вас действительно прописан только `https://art.lmru.tech/mvn-replenishment` для `repositories` и `pluginRepositories`. Значит проблема почти наверняка в Jenkins `settings.xml` (или глобальном managed settings), где зеркало перекрывает репозиторий из `pom`.\nДа, можно — и в вашем случае это как раз правильное место фикса.\n\n`pom.xml` у вас уже корректный (`mvn-replenishment` есть), а в Jenkins обычно это «перебивается» через `settings.xml` с `<mirrors>`, где стоит что-то вроде `mirrorOf=*` на `https://art.lmru.tech/mvn`.\n\nЧто нужно сделать в Jenkins:\n\n- В **Managed files** (Config File Provider) найти Maven `settings.xml`, который использует джоба/папка/глобал.\n- Исправить секцию `<mirrors>` так, чтобы зеркало указывало на `mvn-replenishment`:\n```xml\n<mirrors>\n <mirror>\n <id>artifactory-replenishment</id>\n <mirrorOf>*</mirrorOf>\n <url>https://art.lmru.tech/mvn-replenishment</url>\n </mirror>\n</mirrors>\n```\n- Убедиться, что в `<servers>` есть креды для этого `id` (или для id, который реально используется вашим artifactory).\n- Перезапустить билд.\n\nПочему так: при наличии mirror в `settings.xml` Maven берёт его в приоритет, и `repositories` из `pom.xml` фактически игнорируются.\n\nБыстрая проверка в Jenkins (для подтверждения):\n- запустить шаг с `mvn -X help:effective-settings`\n- в логе увидеть активный mirror URL (должен быть `.../mvn-replenishment`)\n\nЕсли хотите, могу подготовить точный текст для `lmru-devops-bot`/DevOps с формулировкой «что поменять и где проверить», чтобы быстрее согласовали.\n\n**Advising Maven mirror configuration**\n**Clarifying Jenkinsfile constraints**"}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\nsvPipeline\nimport groovy.transform.Field\n\n// settings shared across pipeline methods (excluding job parameters)\n@Field\nfinal PipelineOptions opts = new PipelineOptions()\n// mostly for notifications\n@Field\nfinal JobInfo jobInfo = new JobInfo()\n// copy of jenkins 'params' object to be able to modify it at runtime\n@Field\nfinal JobParameters jobParams = new JobParameters()\n\n@Field\nfinal Map<ExtensionPointEnum, ExtensionExecutable> extensionPointsMap = [:]\n\n@Field\nfinal ExtensionPointData extensionPointData = new ExtensionPointData()\n\n@Field\nfinal String PIPELINE_SOURCE_FOLDER = 'pipeline-sources'\n\n@Field\nfinal String SV_PIPELINE_GITHUB_URL = 'https://github.lmru.tech/adeo/lmru--sv-jvm-k8s-pipeline'\n\nvoid run(Map argsMap) {\n node('lmru-dockerhost') {\n configureExtensionPoints(argsMap)\n executeExtensionIfExists(ExtensionPointEnum.PRE_PIPELINE)\n setup(argsMap)\n executeExtensionIfExists(ExtensionPointEnum.POST_SETUP)\n\n LoopOptions loopOpts = opts.notificationOptions.loopOptions\n\n try {\n loopUtilsSV.notify(JobStatus.START.name(), loopOpts, opts.yandexTrackerCredentialsId, jobInfo)\n if (jobParams.runCheckmarxScan) {\n runCheckmarxScan()\n }\n if (jobParams.runSecretsScan) {\n runSecretsScan()\n }\n if (jobParams.shouldPerformGitflowAction) {\n executeExtensionIfExists(ExtensionPointEnum.PRE_GITFLOW)\n runGitflow()\n executeExtensionIfExists(ExtensionPointEnum.POST_GITFLOW)\n if (jobInfo.shouldRelease || jobInfo.shouldFinishHotfix || jobInfo.shouldFinishRelease) {\n jobInfo.releaseTasksList = jiraUtils.findTasksByCommitsFromTagToPreviousTag(jobInfo.targetVersion)\n }\n }\n if (jobParams.shouldBuild) {\n executeExtensionIfExists(ExtensionPointEnum.PRE_BUILD)\n buildAppAndImage(!jobParams.shouldPerformGitflowAction) // do not build with maven second time, if gitflow = true\n executeExtensionIfExists(ExtensionPointEnum.POST_BUILD)\n }\n if (jobParams.shouldDeploy) {\n executeExtensionIfExists(ExtensionPointEnum.PRE_DEPLOY)\n deployToManyClustersWithRollback()\n notifyVoyager()\n executeExtensionIfExists(ExtensionPointEnum.POST_DEPLOY)\n }\n cleanWs()\n if (jobParams.shouldLaunchAutoTests) {\n executeExtensionIfExists(ExtensionPointEnum.PRE_AUTO_TESTS)\n launchAutoTests()\n executeExtensionIfExists(ExtensionPointEnum.POST_AUTO_TESTS)\n }\n executeExtensionIfExists(ExtensionPointEnum.POST_PIPELINE)\n loopUtilsSV.notify(currentBuild.currentResult, loopOpts, opts.yandexTrackerCredentialsId, jobInfo)\n } catch (err) {\n printException(err)\n currentBuild.result = 'FAILED'\n extensionPointData.error = err\n executeExtensionIfExists(ExtensionPointEnum.ON_ERROR)\n loopUtilsSV.notify(currentBuild.currentResult, loopOpts, opts.yandexTrackerCredentialsId, jobInfo)\n error(err)\n }\n }\n}\n\nvoid setup(Map argsMap) {\n stage(\"Setup\") {\n cleanWs()\n buildPipelineOptions(argsMap)\n buildJobParametersDefinitions()\n readJobParametersValues()\n overrideJobParamsOnGithubEvent()\n checkoutSources()\n setupEnvVariables()\n setJobName PROJECT_NAME_PARAM\n buildJobInfo()\n checkDeprecations()\n }\n}\n\nvoid buildPipelineOptions(Map argsMap) {\n List<String> requiredProperties = [ 'kubernetes', 'vault', 'lmSaCredentialsId' ]\n if (!argsMap.keySet().containsAll(requiredProperties)) {\n echo \"argsMap: $argsMap\"\n error \"svPipeline.run(argsMap) -- argsMap must contain following properties: $requiredProperties\"\n }\n\n // single options\n if (argsMap.profiles != null) {\n opts.profiles = argsMap.profiles\n }\n if (argsMap.githubCredentialsId != null) {\n opts.githubCredentialsId = argsMap.githubCredentialsId\n }\n opts.lmSaCredentialsId = argsMap.lmSaCredentialsId\n opts.yandexTrackerCredentialsId = argsMap.yandexTrackerCredentialsId\n\n // build\n if (argsMap.build?.mavenImageTag != null) {\n opts.buildOptions.mavenImageTag = argsMap.build.mavenImageTag\n }\n if (argsMap.build?.baseImageTag != null) {\n opts.buildOptions.baseImageTag = argsMap.build.baseImageTag\n }\n if (argsMap.build?.baseImagePath != null) {\n opts.buildOptions.baseImagePath = argsMap.build.baseImagePath\n }\n opts.buildOptions.artifactoryDomain = argsMap.build?.artifactoryDomain\n opts.buildOptions.artifactoryAppImagePath = argsMap.build?.artifactoryAppImagePath\n opts.buildOptions.imageCustomLibraryList = argsMap.build?.imageCustomLibraryList\n\n // voyager/sputnik/rms\n if (argsMap.notifications?.containsKey('voyager')) {\n opts.notificationOptions.voyagerOptions.productId = argsMap.notifications.voyager.productId\n opts.notificationOptions.voyagerOptions.serviceName = argsMap.notifications.voyager.serviceName\n opts.notificationOptions.voyagerOptions.productName = argsMap.notifications.voyager.productName\n } else {\n error \"svPipeline.run(argsMap) -- argsMap must contain property notifications.voyager\"\n }\n\n // kubernetes\n if (argsMap.kubernetes.helmChartVersion != null) {\n opts.kubernetesOptions.helmChartVersion = argsMap.kubernetes.helmChartVersion\n }\n opts.kubernetesOptions.clusterName = argsMap.kubernetes.clusterName\n opts.kubernetesOptions.baseNamespace = argsMap.kubernetes.baseNamespace\n if (argsMap.kubernetes.helmReleaseNameBuilder != null) {\n opts.kubernetesOptions.helmReleaseNameBuilder = argsMap.kubernetes.helmReleaseNameBuilder\n }\n opts.kubernetesOptions.stageClusters = argsMap.kubernetes.stageClusters\n opts.kubernetesOptions.prodClusters = argsMap.kubernetes.prodClusters\n if (argsMap.kubernetes.containsKey('autoRollbackMulticlusterErrors')) {\n opts.kubernetesOptions.autoRollbackMulticlusterErrors = argsMap.kubernetes.autoRollbackMulticlusterErrors\n }\n\n // gitflow\n if (argsMap.gitflow?.developmentBranch != null) {\n opts.gitflowOptions.developmentBranch = argsMap.gitflow.developmentBranch\n }\n if (argsMap.gitflow?.shouldQualityGateFailRelease != null) {\n opts.gitflowOptions.shouldQualityGateFailRelease = argsMap.gitflow.shouldQualityGateFailRelease\n }\n // vault\n if (argsMap.vault.enabled != null) {\n opts.vaultOptions.enabled = argsMap.vault.enabled\n }\n opts.vaultOptions.credentialsId = argsMap.vault.credentialsId\n opts.vaultOptions.namespace = argsMap.vault.namespace\n opts.vaultOptions.approleSecretLocation = argsMap.vault.approleSecretLocation\n opts.vaultOptions.applicationApproleCredentialsId = argsMap.vault.applicationApproleCredentialsId\n // notifications\n if (argsMap.notifications?.containsKey('loop')) {\n def loopOptionsMap = argsMap.notifications.loop\n if (!loopOptionsMap.containsKey('channelIds')) {\n error(\"You must provide notifications.loop.channelIds if you want to use loop\")\n }\n if (loopOptionsMap.containsKey('credentialsId')) {\n opts.notificationOptions.loopOptions.credentialsId = loopOptionsMap.credentialsId\n }\n opts.notificationOptions.loopOptions.channelIds = loopOptionsMap.channelIds\n if (loopOptionsMap.containsKey('watchEnvironments')) {\n opts.notificationOptions.loopOptions.watchEnvironments = loopOptionsMap.watchEnvironments\n }\n if (loopOptionsMap.containsKey('watchReleases')) {\n opts.notificationOptions.loopOptions.watchReleases = loopOptionsMap.watchReleases\n }\n }\n\n // overrides\n if (argsMap.overrideProperties?.containsKey(\"domainNameParam\")) {\n opts.overridePropertiesOptions.domainNameParam = argsMap.overrideProperties.domainNameParam\n }\n if (argsMap.overrideProperties?.containsKey(\"brickNameParam\")) {\n opts.overridePropertiesOptions.brickNameParam = argsMap.overrideProperties.brickNameParam\n }\n // auto tests\n if (argsMap.containsKey(\"autoTests\")) {\n opts.autoTestsOptions.jobUrl = argsMap.autoTests.jobUrl\n opts.autoTestsOptions.suiteNames = argsMap.autoTests.suiteNames\n }\n // github\n if (argsMap.github?.onPullRequest != null) {\n opts.githubOptions.onPullRequest = argsMap.github.onPullRequest\n }\n if (argsMap.github?.onPushEvent != null) {\n opts.githubOptions.onPushEvent = argsMap.github.onPushEvent\n }\n\n echo \"PipelineOptions:\"\n printProperties(opts)\n}\n\nvoid checkDeprecations() {\n // Only \"official\" tags. Teams may still use their own image paths.\n Set<String> deprecatedJavaImages = [ 'jre-12', 'liberica-14', 'liberica-8', 'openjdk8', 'openjdk8-fonts', 'openjdk8-runtime' ]\n if (opts.buildOptions.baseImageTag in deprecatedJavaImages) {\n unstable(\n 'Your java base image does not fully support cgroup v2, ' +\n 'so it will not see available resources in kubernetes on ubuntu 22+ application nodes. ' +\n 'Please use LTS JDK/JRE: 11 or 17. See https://bugs.openjdk.org/browse/JDK-8230305'\n )\n }\n}\n\nvoid buildJobParametersDefinitions() {\n def jobParametersDefinitions = [\n separator(name: 'build', sectionHeader: 'Build'),\n booleanParam(name: 'shouldPerformGitflowAction', description: 'Finish feature / finish hotfix / build release?', defaultValue: false),\n booleanParam(name: 'shouldBuild', description: 'Build microservice?', defaultValue: false),\n separator(name: 'deployment', sectionHeader: 'Deployment'),\n booleanParam(name: 'shouldDeploy', description: 'Deploy?', defaultValue: false),\n choice(name: 'profile', description: 'Profile for spring and helm-chart/values.yml', choices: opts.profiles),\n // clustersForDeploy param here if needed\n string(name: 'customDeployEnvironment', description: 'For feature testing or whatever you want', defaultValue: ''),\n string(\n name: 'helmRollbackReleaseAfter',\n description: 'Raise this value if your deployment lasts longer than 5 minutes. ' +\n 'E.g. when applying liquibase changeset or when k8s cluster needs to raise yet another node.',\n defaultValue: '5m'\n ),\n separator(name: 'testing', sectionHeader: 'Testing'),\n booleanParam(name: 'shouldLaunchAutoTests', description: 'Start job with auto-tests after deploy?', defaultValue: false),\n [\n $class: 'CascadeChoiceParameter',\n choiceType: 'PT_CHECKBOX',\n description: 'Test suites\\' names',\n filterLength: 1,\n filterable: false,\n name: 'testsSuiteNames',\n script: [\n $class: 'GroovyScript',\n script: [\n classpath: [],\n sandbox: true,\n script: \"return ${opts.autoTestsOptions.suiteNames}\"\n ]\n ]\n ],\n separator(name: 'securityScanners', sectionHeader: 'Security Scanners'),\n booleanParam(name: 'runSecretsScan', description: 'Run Secrets scanner and download report?', defaultValue: false),\n booleanParam(name: 'runAquaScan', description: 'Run Aqua scanner and download report? (use with shouldBuild)', defaultValue: false),\n booleanParam(name: 'runCheckmarxScan', description: 'Run Checkmarx scanner and download report?', defaultValue: false)\n ]\n if (opts.kubernetesOptions.stageClusters || opts.kubernetesOptions.prodClusters) {\n jobParametersDefinitions.add(\n 6, // index in list, where put at\n [\n $class: 'CascadeChoiceParameter',\n choiceType: 'PT_CHECKBOX',\n description: 'Which clusters should deploy to?',\n filterLength: 1,\n filterable: false,\n name: 'clustersForDeploy',\n multiSelectDelimiter: ',',\n referencedParameters: 'shouldDeploy, profile',\n script: [\n $class: 'GroovyScript',\n script: [\n classpath: [],\n sandbox: true,\n script: \"\"\"\n if (shouldDeploy) {\n if (profile == 'prod') {\n return ${opts.kubernetesOptions.prodClusters.collect { \"\\\"$it:selected\\\"\" }.toString().replaceAll(\" \", \"\")}\n } else {\n return ${opts.kubernetesOptions.stageClusters.collect { \"\\\"$it:selected\\\"\" }.toString().replaceAll(\" \", \"\")}\n }\n }\n return ''\n \"\"\"\n ]\n ]\n ]\n )\n }\n properties([\n parameters(jobParametersDefinitions),\n disableConcurrentBuilds()\n ])\n}\n\nvoid readJobParametersValues() {\n jobParams.shouldPerformGitflowAction = params.shouldPerformGitflowAction\n jobParams.shouldBuild = params.shouldBuild\n jobParams.runCheckmarxScan = params.runCheckmarxScan\n jobParams.runSecretsScan = params.runSecretsScan\n jobParams.runAquaScan = params.runAquaScan\n jobParams.shouldDeploy = params.shouldDeploy\n jobParams.profile = params.profile\n if (params.customDeployEnvironment != null && params.customDeployEnvironment.trim().length() > 0) {\n jobParams.customDeployEnvironment = params.customDeployEnvironment\n }\n jobParams.clustersForDeploy = getKubernetesClustersToDeploy(params, jobParams.profile)\n jobParams.helmRollbackReleaseAfter = params.helmRollbackReleaseAfter\n jobParams.shouldLaunchAutoTests = params.shouldLaunchAutoTests\n jobParams.testsSuiteNames = params.testsSuiteNames\n}\n\nvoid checkoutSources() {\n if (jobParams.shouldPerformGitflowAction) {\n gitHubUtils.checkout()\n } else {\n checkout scm\n }\n}\n\nvoid setupEnvVariables() {\n parseProjectPropertiesToEnv()\n env.MVN_IMAGE = \"docker.art.lmru.tech/maven:${opts.buildOptions.mavenImageTag}\"\n if (opts.overridePropertiesOptions.brickNameParam != \"keep\") {\n env.BRICK_NAME_PARAM = opts.overridePropertiesOptions.brickNameParam\n }\n if (opts.overridePropertiesOptions.domainNameParam != \"keep\") {\n env.DOMAIN_NAME_PARAM = opts.overridePropertiesOptions.domainNameParam\n }\n}\n\nvoid runGitflow() {\n echo \"================ START GITFLOW ================\"\n echo \"branch = $BRANCH_NAME\"\n\n if (jobInfo.shouldRelease) {\n setJobName \"$PROJECT_NAME_PARAM: release\"\n echo \"mode = release\"\n } else if (jobInfo.shouldFinishFeature) {\n setJobName \"$PROJECT_NAME_PARAM: finish feature\"\n echo \"mode = finish feature\"\n } else if (jobInfo.shouldFinishHotfix) {\n setJobName \"$PROJECT_NAME_PARAM: finish hotfix\"\n echo \"mode = finish hotfix\"\n } else if (jobInfo.shouldFinishRelease) {\n setJobName \"$PROJECT_NAME_PARAM: finish release\"\n echo \"mode = finish release\"\n }\n\n gitflowNextStep([\n githubCredentialsId: opts.githubCredentialsId,\n mvnPhaseAfterGitflow: jobParams.shouldBuild ? 'package' : 'test',\n isGithubFlow: opts.gitflowOptions.developmentBranch == 'master',\n shouldQualityGateFailRelease: opts.gitflowOptions.shouldQualityGateFailRelease\n ]) // this checks out new branch and updates env variables\n\n echo \"branch = $BRANCH_NAME\"\n echo \"tag = ${env.TAG_NAME ? TAG_NAME : 'null'}\"\n echo \"projectVersionParam = $PROJECT_VERSION_PARAM\"\n echo \"lastCommitSha = ${sh(script: \"git log -n 1 --pretty=format:'%H'\", returnStdout: true)}\"\n echo \"================ END GITFLOW ================\"\n}\n\nvoid buildAppAndImage(boolean needMavenBuild) {\n echo \"================ START BUILD ================\"\n String imageTag = resolveDockerImageTag(BRANCH_NAME)\n boolean shouldReleaseImage = env.TAG_NAME != null || BRANCH_NAME in ['develop', 'master']\n String shortImageName = resolveAppImageName(false)\n echo \"shortImageName = $shortImageName\"\n echo \"imageTag = $imageTag\"\n echo \"shouldReleaseImage = $shouldReleaseImage\"\n setJobName \"$PROJECT_NAME_PARAM:$imageTag build\"\n if (needMavenBuild) {\n stage('Maven: package') {\n mavenUtils.runPhase('package')\n }\n waitQualityGate(false) // do not fail job\n }\n stage('Build & push docker image') {\n dockerUtils.jvmBuild(\n baseImagePath: \"${opts.buildOptions.baseImagePath}:${opts.buildOptions.baseImageTag}\",\n artifactoryImageName: shortImageName,\n projectName: opts.buildOptions.artifactoryDomain ?: DOMAIN_NAME_PARAM,\n imageTags: [ imageTag ],\n shouldPromoteImageTagsToRelease: shouldReleaseImage,\n customLibraryList: opts.buildOptions.imageCustomLibraryList,\n applicationFilesDir: 'target/application',\n needUnzip: true,\n securityScan: jobParams.runAquaScan\n )\n }\n echo \"================ END BUILD ================\"\n}\n\nvoid deployToManyClustersWithRollback() {\n echo \"================ START DEPLOY ================\"\n boolean shouldDeployWithTag = env.TAG_NAME // job started from tag (or app was released) or not\n\n if (!jobParams.clustersForDeploy) {\n error \"params.clustersForDeploy must not be empty when params.shouldDeploy = true\"\n }\n if (jobParams.profile == \"prod\") {\n if (!(BRANCH_NAME == \"master\" || shouldDeployWithTag)) {\n error \"You can deploy on PROD only from master branch or tag. Current git reference is '$BRANCH_NAME'\"\n }\n if (jobParams.customDeployEnvironment) {\n error 'You should not use customDeployEnvironment for PROD profile'\n }\n if (!shouldDeployWithTag) {\n unstable(\n 'You are going to deploy branch version on PROD. ' +\n 'This is unstable and thus not recommended approach. ' +\n 'Create and deploy release of your application if possible.'\n )\n }\n }\n\n String deployEnvironment = jobParams.profile\n if (jobParams.customDeployEnvironment) {\n deployEnvironment = jobParams.customDeployEnvironment\n }\n\n String kuberNamespace = \"${opts.kubernetesOptions.baseNamespace}-$deployEnvironment\"\n String helmReleaseName = opts.kubernetesOptions.helmReleaseNameBuilder(\n env.DOMAIN_NAME_PARAM ?: '',\n env.BRICK_NAME_PARAM ?: '',\n env.PROJECT_NAME_PARAM,\n deployEnvironment\n )\n if (helmReleaseName.length() > 53) {\n error \"Too long helm release name: '$helmReleaseName'. It must be up to 53 characters long. Provide custom helmReleaseNameBuilder.\"\n }\n\n String imageName = resolveAppImageName(true)\n\n if (opts.vaultOptions.enabled) {\n buildValuesYmlUsingVault()\n }\n\n String imageTag = resolveDockerImageTag(BRANCH_NAME)\n def commitSha = sh(script: \"git log -n 1 --pretty=format:'%H'\", returnStdout: true)\n\n echo \"kuberClusterNames = ${jobParams.clustersForDeploy}\"\n echo \"kuberNamespace = $kuberNamespace\"\n echo \"helmReleaseName = $helmReleaseName\"\n echo \"profile = ${jobParams.profile}\"\n echo \"imageName = $imageName\"\n echo \"imageTag = $imageTag\"\n echo \"commitSha = $commitSha\"\n\n setJobName \"$PROJECT_NAME_PARAM:$imageTag on $deployEnvironment\"\n\n if (shouldDeployWithTag) {\n jobInfo.releaseTasksList = jiraUtils.findTasksByCommitsFromTagToPreviousTag(jobInfo.targetVersion)\n }\n\n dir(PIPELINE_SOURCE_FOLDER) {\n git(\n branch: opts.kubernetesOptions.helmChartVersion,\n changelog: false,\n credentialsId: opts.githubCredentialsId,\n url: SV_PIPELINE_GITHUB_URL\n )\n }\n\n // some monkey-job to set helm's appVersion field\n String chartFileLocation = \"$PIPELINE_SOURCE_FOLDER/helm/Chart.yaml\"\n LinkedHashMap<String, String> chartValuesMap = readYaml(file: chartFileLocation)\n if (chartValuesMap.containsKey(\"appVersion\")) { // may be false, when using older chart version\n String tempLocation = \"$PIPELINE_SOURCE_FOLDER/helm/Chart_tmp.yaml\"\n chartValuesMap.appVersion = imageTag\n writeYaml(file: tempLocation, data: chartValuesMap)\n sh \"rm $chartFileLocation && mv $tempLocation $chartFileLocation\"\n }\n\n // Random string to force helm perform real redeploy even if the same exact commit is deployed\n String randomString = sh(script: \"head /dev/urandom | tr -dc a-z0-9 | head -c12\", returnStdout: true)\n\n Set<String> successfullyDeployedToClusters = []\n\n try {\n jobParams.clustersForDeploy.each { clusterName ->\n String ingressHostname = \"${kuberNamespace}-${clusterName}.apps.lmru.tech\"\n\n echo \"ClusterName = $clusterName\"\n echo \"ingressHostname = $ingressHostname\"\n\n // main deployment stage\n deployWithEmbeddedHelmChart(\n clusterName,\n kuberNamespace,\n helmReleaseName,\n ingressHostname,\n imageName,\n imageTag,\n jobParams.helmRollbackReleaseAfter,\n randomString\n )\n\n successfullyDeployedToClusters.add(clusterName)\n echo \"Successfully deployed to $clusterName\"\n }\n } catch (Exception ex) {\n echo \"Error during deploy to kubernetes\"\n printException(ex)\n if (opts.kubernetesOptions.autoRollbackMulticlusterErrors) {\n successfullyDeployedToClusters.each {\n rollbackDeploy(it, kuberNamespace, helmReleaseName)\n }\n } else {\n echo \"autoRollbackMulticlusterErrors is disabled\"\n }\n error(\"Error during deploy to kubernetes: ${ex.message}\")\n }\n echo \"================ END DEPLOY ================\"\n}\n\nvoid buildValuesYmlUsingVault() {\n if (opts.vaultOptions.applicationApproleCredentialsId == null) { // old way\n vaultUtils.secretsToEnv(\n namespace: opts.vaultOptions.namespace,\n vaultPath: opts.vaultOptions.approleSecretLocation,\n appRoleCredential: opts.vaultOptions.credentialsId\n )\n envSubstituteValuesYml()\n } else { // new, better way\n withCredentials([usernamePassword(\n credentialsId: opts.vaultOptions.applicationApproleCredentialsId,\n usernameVariable: 'VAULT_APP_ROLE_ID',\n passwordVariable: 'VAULT_APP_SECRET_ID'\n )]) {\n String valuesStr = readFile 'values.yml'\n if (!valuesStr.contains('default: ${VAULT_APP_ROLE_ID}') || !valuesStr.contains('default: ${VAULT_APP_SECRET_ID}')) {\n error(\n 'Using vault.applicationApproleCredentialsId, your values.yml must contain environment placeholders ' +\n 'default: ${VAULT_APP_ROLE_ID} and default: ${VAULT_APP_SECRET_ID}. ' +\n 'Check helm/values_example.yaml if you want. Don\\'t forget to check your bootstrap/application.yml also.'\n )\n }\n envSubstituteValuesYml()\n }\n }\n}\n\nvoid envSubstituteValuesYml() {\n sh \"envsubst < values.yml > values_full.yml\"\n sh \"mv -f values_full.yml values.yml\"\n}\n\nvoid buildJobInfo() {\n jobInfo.buildUrl = BUILD_URL\n jobInfo.buildNumber = BUILD_NUMBER as int\n jobInfo.shouldBuild = jobParams.shouldBuild\n jobInfo.shouldDeploy = jobParams.shouldDeploy\n jobInfo.clustersForDeploy = jobParams.clustersForDeploy\n if (jobParams.shouldPerformGitflowAction) {\n if (BRANCH_NAME == opts.gitflowOptions.developmentBranch) {\n jobInfo.shouldRelease = true\n } else if (BRANCH_NAME.startsWith('feature/')) {\n jobInfo.shouldFinishFeature = true\n } else if (BRANCH_NAME.startsWith('release/')) {\n jobInfo.shouldFinishRelease = true\n } else if (BRANCH_NAME.startsWith('hotfix/')) {\n jobInfo.shouldFinishHotfix = true\n } else {\n error \"Unsupported branch for gitflow: $BRANCH_NAME\"\n }\n }\n\n jobInfo.startVersion = BRANCH_NAME\n if (jobInfo.shouldRelease || jobInfo.shouldFinishHotfix || jobInfo.shouldFinishRelease) {\n // after gitflow, created tag will be checked out and used\n jobInfo.targetVersion = PROJECT_VERSION_PARAM.minus(\"-SNAPSHOT\")\n } else if (jobInfo.shouldFinishFeature) {\n jobInfo.targetVersion = opts.gitflowOptions.developmentBranch\n } else { // maybe tag, master, develop or other branch\n jobInfo.targetVersion = jobInfo.startVersion\n }\n\n jobInfo.deployEnvironment = jobParams.profile\n if (jobParams.customDeployEnvironment) {\n jobInfo.deployEnvironment = jobParams.customDeployEnvironment\n }\n jobInfo.author = userLaunchedJob.getNameWithoutLdap()\n\n echo \"JobInfo:\"\n printProperties(jobInfo)\n}\n\nvoid overrideJobParamsOnGithubEvent() {\n // push to PR-branch\n if (env.CHANGE_ID && userLaunchedJob.isSystem()) {\n opts.githubOptions.onPullRequest(jobParams)\n return // do not execute yet another script\n }\n // push to non-PR-branch\n if (opts.githubOptions.onPushEvent && userLaunchedJob.isSystem()) {\n Map.Entry<String, Closure<Void>> matchedRule = opts.githubOptions.onPushEvent.find { String branchPrefix, Closure<Void> script ->\n env.BRANCH_NAME.startsWith(branchPrefix)\n }\n if (matchedRule) {\n echo \"Git reference matched onPushEvent branch prefix [${matchedRule.key}]. Executing provided user script.\"\n matchedRule.value.call(jobParams)\n }\n }\n}\n\nvoid setJobName(String name) {\n currentBuild.displayName = name\n}\n\nvoid deployWithEmbeddedHelmChart(\n String fullClusterName,\n String kuberNamespace, // domain-(brick|microservice)-(dev/test/preprod/prod or branch)\n String helmReleaseName, // domain.brick?.microservice.(dev/test/preprod/prod or branch)\n String ingressHostname,\n String imageName,\n String imageTag,\n String timeout,\n String helmSha\n) {\n stage(\"Deploy to $fullClusterName, ${jobParams.profile}\") {\n kubernetesUtils.withKuberDeployer(clusterName: fullClusterName, lmSaCredentialsId: opts.lmSaCredentialsId) {\n // create empty values-*cluster*.yml if not exist to avoid error in helm3 invocation\n sh(\"touch /pwd/values-${fullClusterName}.yml\")\n sh(\n \"helm3 upgrade --install --atomic --wait --create-namespace \" +\n \"-f /pwd/values.yml \" +\n \"-f /pwd/values-${fullClusterName}.yml \" +\n \"--set profile=${jobParams.profile} \" +\n \"--set ingress.hostName=$ingressHostname \" +\n \"--set imageName=$imageName \" +\n \"--set imageTag=$imageTag \" +\n \"--set sha=$helmSha \" +\n \"--timeout $timeout \" +\n \"--namespace $kuberNamespace \" +\n \"$helmReleaseName $PIPELINE_SOURCE_FOLDER/helm\"\n )\n }\n }\n}\n\nvoid rollbackDeploy(String fullClusterName, String kuberNamespace, String helmReleaseName) {\n stage(\"Rollback deploy on $fullClusterName\") {\n String getRevision = \"helm3 history --max=1 $helmReleaseName --namespace $kuberNamespace -ojson | jq '.[0].revision'\"\n kubernetesUtils.withKuberDeployer(clusterName: fullClusterName, lmSaCredentialsId: opts.lmSaCredentialsId) {\n String revision = sh(script: getRevision, returnStdout: true).trim()\n if (revision != \"1\") {\n echo \"Rolling back to previous helm release. fullClusterName=$fullClusterName\"\n sh(\"helm3 rollback $helmReleaseName 0 --namespace $kuberNamespace\")\n } else {\n echo \"Uninstalling helm release. fullClusterName=$fullClusterName\"\n sh(\"helm3 uninstall $helmReleaseName --namespace $kuberNamespace\")\n }\n }\n }\n}\n\nString resolveAppImageName(boolean fullName) {\n String firstPart = \"\"\n if (fullName) {\n if (opts.buildOptions.artifactoryDomain) {\n firstPart = \"docker-${opts.buildOptions.artifactoryDomain}.art.lmru.tech/\"\n } else {\n firstPart = \"docker-${DOMAIN_NAME_PARAM}.art.lmru.tech/\"\n }\n }\n String shortName = \"\"\n if (opts.buildOptions.artifactoryAppImagePath) { // new way\n shortName = opts.buildOptions.artifactoryAppImagePath\n } else { // old way\n if (BRICK_NAME_PARAM) {\n shortName += BRICK_NAME_PARAM + \"/\"\n }\n shortName += PROJECT_NAME_PARAM\n }\n return firstPart + shortName\n}\n\n// possible return values: 0.1, master, develop, LB-123, other-branch, hotfix-1.2.3.4\nString resolveDockerImageTag(String gitRef) {\n return gitRef.replace(\"feature/\", \"\").replace(\"/\", \"-\")\n}\n\nList<String> getKubernetesClustersToDeploy(Map params, String profile) {\n if (profile == 'prod') {\n Integer nProdClusterOptions = opts.kubernetesOptions.prodClusters?.size() ?: 0\n if (nProdClusterOptions > 1) {\n return getKubernetesClustersToDeployFromParams(params)\n } else if (nProdClusterOptions == 1) {\n return opts.kubernetesOptions.prodClusters.toList()\n } else {\n return [ opts.kubernetesOptions.clusterName ]\n }\n } else { // stage\n Integer nStageClusterOptions = opts.kubernetesOptions.stageClusters?.size() ?: 0\n if (nStageClusterOptions > 1) {\n return getKubernetesClustersToDeployFromParams(params)\n } else if (nStageClusterOptions == 1) {\n return opts.kubernetesOptions.stageClusters.toList()\n } else {\n return [ opts.kubernetesOptions.clusterName + '-stage' ]\n }\n }\n}\n\nList<String> getKubernetesClustersToDeployFromParams(Map params) {\n String paramStr = params.clustersForDeploy\n if (paramStr == null || !paramStr.replaceAll(\",\", \"\").trim()) {\n return null\n }\n return paramStr.split(',').toList()\n}\n\nvoid printProperties(obj) {\n obj.properties.each {\n if (it.key != \"class\") {\n def keyWithValue = it.key + \": \" + it.value\n String objClassName = obj.getClass().getName()\n if (objClassName.contains('Options') && !objClassName.contains('Pipeline')) {\n echo(\" \" + keyWithValue)\n } else {\n if (it.value.getClass().getName().contains('Options')) {\n echo(\" \" + it.key + \":\")\n printProperties(it.value)\n } else {\n echo(\" \" + keyWithValue)\n }\n }\n }\n }\n}\n\nvoid notifyVoyager() {\n stage(\"Notify Voyager\") {\n voyagerUtils.sendDeployResults( // safe to call, it catches possible exceptions\n serviceName: opts.notificationOptions.voyagerOptions.serviceName,\n version: jobInfo.targetVersion,\n app: opts.notificationOptions.voyagerOptions.productName,\n productId: opts.notificationOptions.voyagerOptions.productId,\n envName: jobInfo.deployEnvironment,\n issues: jobInfo.releaseTasksList.join(' ')\n )\n }\n}\n\nvoid launchAutoTests() {\n def envName = jobParams.profile\n def testsSuiteNames = jobParams.testsSuiteNames\n def stageName = \"Auto tests on $envName\"\n\n stage(stageName) {\n echo \"Launching auto-tests suite(s) '$testsSuiteNames' on '$envName'\"\n build(\n job: opts.autoTestsOptions.jobUrl,\n parameters: [\n string(name: 'suiteNames', value: testsSuiteNames),\n string(name: 'environment', value: envName)\n ]\n )\n }\n}\n\nvoid configureExtensionPoints(Map argsMap) {\n if (!argsMap.containsKey('extensionPoints')) {\n return\n }\n try {\n argsMap.extensionPoints.each { pointName, value ->\n ExtensionPointEnum extensionPointEnum = ExtensionPointEnum.valueOfName(pointName)\n extensionPointsMap.put(\n extensionPointEnum,\n new ExtensionExecutable(\n value.customStageName,\n extensionPointEnum,\n value.script\n )\n )\n }\n } catch (Exception ex) {\n error(\"Looks like extensionPoints section has incorrect format: ${ex.message}\")\n }\n extensionPointData.pipelineOpts = opts\n extensionPointData.jobParams = jobParams\n extensionPointData.jobInfo = jobInfo\n}\n\nvoid executeExtensionIfExists(ExtensionPointEnum extensionPoint) {\n ExtensionExecutable ee = extensionPointsMap.get(extensionPoint)\n if (ee != null) {\n executeExtensionPoint(ee)\n }\n}\n\n// may throw exception\nvoid executeExtensionPoint(ExtensionExecutable ee) {\n String stageName = ee.extensionPoint.stageName\n if (ee.customStageName) {\n stageName = ee.customStageName\n }\n if (stageName != ExtensionPointEnum.DISABLED_STAGE_LABEL) {\n stage(stageName) {\n ee.script(extensionPointData)\n }\n } else {\n ee.script(extensionPointData)\n }\n}\n\nvoid runCheckmarxScan() {\n stage(\"Checkmarx scan\") {\n checkmarxUtils.runCheckmarxScan_ZeroLicense(getCXReport: true)\n }\n}\n\nvoid runSecretsScan() {\n stage(\"Secrets scan\") {\n securityUtils.secretsScan([:])\n }\n}\n\nvoid printException(Exception ex) {\n echo 'Caught exception. Trimmed stacktrace:'\n def sw = new StringWriter()\n def pw = new PrintWriter(sw)\n ex.printStackTrace(pw)\n String fullStackTrace = sw.toString()\n List<String> usefulLines = fullStackTrace.readLines().findAll {\n !it.startsWith(\"\\tat \") || it.contains(\".groovy:\") // keep only exception class, message and groovy methods\n }\n echo usefulLines.join(\"\\n\")\n}\n\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Да, этот кусок подтверждает гипотезу: в `svPipeline` **нет явной настройки Maven mirror/repository** на уровне джобы.\n\nКритичное место — только это:\n\n- `buildAppAndImage()` → `mavenUtils.runPhase('package')`\n\nТо есть источник зависимостей определяется **внутри `mavenUtils` / Jenkins Maven settings**, а не этим `Jenkinsfile`. \nПоэтому `pom.xml` с `mvn-replenishment` может игнорироваться, если в Jenkins settings стоит зеркало `mirrorOf=*` на `.../mvn`.\n\nЧто из этого следует:\n\n- В текущем пайплайне нет параметра вида `mavenSettingsConfig`, `settingsXml`, `mirrorUrl`.\n- `artifactoryDomain: 'replenishment'` здесь относится к Docker image path, не факт что к Maven (скорее всего — не к Maven).\n\nЧто просить у DevOps/владельцев shared library:\n\n- Проверить, какой `settings.xml` использует `mavenUtils.runPhase`.\n- Либо поменять mirror в этом settings на `https://art.lmru.tech/mvn-replenishment`,\n- Либо сделать per-project settings для этой джобы и прокинуть в `mavenUtils` (если в lib есть такой параметр).\n- Быстрая верификация: запуск с `mvn -X help:effective-settings` и проверка активного mirror URL.\n\nЕсли хотите, могу сформулировать короткий технический комментарий для треда Артема (готовый текст «что именно проверить в shared library + Jenkins Managed files»).\n\n**Explaining Maven mirror setup**"}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\n[<- back to README](/README.md)\n### `executeCommand(String command)`\nMethod to run arbitrary maven commands with:\n* SonarQube \n* settings.xml from jenkins\n* docker environment (often needed to run integration tests (e.g. with testcontainers))\n\n#### Need to work: \nMaven settings.xml file\n\n#### Optional:\nMVN_VERSION env for choice version of maven container (for example if you use java 11 or custom mvn image)\n\n#### Parameters\n* **command** e.g. `clean test` or call to some maven plugin. You may pass additional arguments like `-DmyArg=ololo`\n\n#### Example usage\n```groovy\n// Load the library to your pipeline\n@Library('common-utils') _\n\nmavenUtils.executeCommand('clean package -Dmaven.test.skip=true')\nmavenUtils.executeCommand('install')\n```\n---\n### `invoke(Map args)`\nMethod to run arbitrary maven commands with:\n* settings.xml from jenkins\n* docker environment (often needed to run integration tests (e.g. with testcontainers))\n\n#### Parameters\n##### Required params:\n* **cmd** maven full command, e.g. `mvn clean test`. Should start from `mvn`.\n\n##### Optional params:\n* **settingsFile** - `settings.xml` file path. If empty used from `MVN_SETTINGS_FILE_ID` \nenvironment variable. Uses `settings.xml` by default.\n* **image** - docker image. Uses `maven:3.6.3-jdk-11` by default.\n* **repositoryCacheEnabled** - use maven repository cache. `true` by default.\n* **privileged** - privileged rights for docker image. `true` by default. \n* **returnStdOut** - return stdout string. `false` by default.\n\n#### Example usage\n```groovy\n// Load the library to your pipeline\n@Library('common-utils') _\n\nmavenUtils.invoke(cmd: 'mvn clean package -Dmaven.test.skip=true')\nmavenUtils.invoke(cmd: 'mvn install')\n```\n---\n### `runPhase(String mvnPhase, boolean shouldSkipTests)`\nMethod to run maven phases (test, package, deploy etc.)\n\n#### Need to work (only when shouldSkipTests = false):\nGlobal env (you can get them from parseProjectPropertiesToEnv method): \n* `DOMAIN_NAME_PARAM` with name of your domain (for example Solution/Delivery/Service-platform is domain name)\n* `BRICK_NAME_PARAM` with name of your brick (if exist)\n* `PROJECT_NAME_PARAM` with name of your application\n* `PROJECT_VERSION_PARAM` with version of your application\n\n#### Optional:\nMVN_VERSION env for choice version of maven container (for example if you use java 11 or custom mvn image)\n\n#### Parameters\n* **mvnPhase** e.g. test, package, verify. You may pass additional arguments like `-DmyArg=ololo`\n* **shouldSkipTests** (default: `false`) - skip tests, if you have already run it. Sonar plugin will not be executed, too.\n\n#### Example usage\n```groovy\n// Load the library to your pipeline\n@Library('common-utils') _\n\nmavenUtils.runPhase('deploy')\nmavenUtils.runPhase('package', true)\n```\n---\n### `getMvnImageName(String mavenImageTag)`\nReturn Docker image name for Maven\n\n#### Parameters\n* **mavenImageTag** (default: `\"latest\"`) - tag of the desired image (for example, one from the \n[official maven images](https://hub.docker.com/_/maven) such as `3.6.3-jdk-8`, `3.8.6-jdk-11`, `3.9.0-eclipse-temurin-17` or even `3.9.0`)\n\n#### Example usage\n```groovy\n// Load the library to your pipeline\n@Library('common-utils') _\n\nmavenUtils.getMvnImageName('3.6.3-jdk-11')\n```\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Отлично, это уже прямое подтверждение причины.\n\n`mavenUtils.runPhase()` в вашем пайплайне берёт `settings.xml` из Jenkins (через дефолт/`MVN_SETTINGS_FILE_ID`), поэтому `pom.xml` с `mvn-replenishment` может игнорироваться зеркалом из settings.\n\nЧто это значит practically:\n\n- проблема **не в `pom.xml`**;\n- проблема в Jenkins `settings.xml`/`MVN_SETTINGS_FILE_ID`, который подсовывает `mavenUtils`.\n\nЧто нужно сделать:\n\n- создать/обновить managed Maven settings для проекта с mirror на `https://art.lmru.tech/mvn-replenishment`;\n- привязать его к джобе через `MVN_SETTINGS_FILE_ID` (или через параметр `settingsFile`, если вызов идёт через `mavenUtils.invoke`).\n\nКритичный фрагмент в settings:\n\n```xml\n<mirrors>\n <mirror>\n <id>artifactory-replenishment</id>\n <mirrorOf>*</mirrorOf>\n <url>https://art.lmru.tech/mvn-replenishment</url>\n </mirror>\n</mirrors>\n```\n\nТекст для треда Артема (можно копипастить):\n> Проверил пайплайн: сборка идёт через `mavenUtils.runPhase`, он использует `settings.xml` из Jenkins (`MVN_SETTINGS_FILE_ID`/default), а не репозитории из `pom.xml` напрямую. Поэтому `mvn-replenishment` из pom может перекрываться mirror’ом на `.../mvn`. Нужно обновить managed Maven settings для джобы (или global, если он общий) и выставить mirror на `https://art.lmru.tech/mvn-replenishment`.\n\n**Summarizing Jenkins settings fix**"}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\n@lmru--sv-jvm-k8s-pipeline вот еще либа sv-pipeline\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Принял, сейчас посмотрю в `sv-pipeline`, где именно прокидывается Maven `settings.xml`/`MVN_SETTINGS_FILE_ID`, чтобы дать точную точку изменения, а не общий совет.\n\n**Preparing to explore new library**"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"**Refining glob pattern to locate file**"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Нашёл `sv-pipeline` прямо в репозитории: там действительно только вызовы `mavenUtils.runPhase(...)`, без передачи `settingsFile`. Сейчас доберу ещё один файл (`svPipelineForLibrary`), чтобы точно исключить скрытый override.\n\n**Considering inspection of pipeline class**"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"**Verifying presence of Maven utility**"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Да, в `sv-pipeline` картина та же: там Maven запускается только через `mavenUtils.runPhase(...)` и нигде не передаётся `settingsFile`/custom mirror.\n\nИтог по цепочке:\n- `Jenkinsfile` → `svPipeline.run(...)`\n- `svPipeline` → `mavenUtils.runPhase('package')`\n- `mavenUtils` по документации берёт `settings.xml` из Jenkins (`MVN_SETTINGS_FILE_ID`/default)\n\nЗначит точка исправления всё равно в Jenkins managed `settings.xml` (или в `common-utils` `mavenUtils`, если хотите поддержать per-project settings параметром).\n\nЕсли нужно, следующим шагом могу помочь составить точный запрос в DevOps в формате: **что проверить в job/folder/global, какой `MVN_SETTINGS_FILE_ID`, и какой mirror должен быть в XML**.\n\n**Mentioning nested pipeline setup**"}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\n@lmru--devops--pipeline-shared-library вот common-utils\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Отлично, тогда добьём до конца: сейчас посмотрю `common-utils` (`lmru--devops--pipeline-shared-library`) и найду точное место, где выбирается `settings.xml`/mirror, чтобы дать вам уже конкретный фикс.\n\n**Planning repo path inspection**"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Теперь картина полностью закрыта: причина найдена в `common-utils`.\n\nВ `lmru--devops--pipeline-shared-library/vars/mavenUtils.groovy`:\n\n- `invoke()` берёт settings file id так:\n - `args.settingsFile ?: env.MVN_SETTINGS_FILE_ID ?: 'settings-xml'`\n- дальше всегда подставляет его через:\n - `configFileProvider([configFile(fileId: mvnSettingsFile, variable: 'MAVEN_SETTINGS')])`\n - и запускает Maven с `-s \"$MAVEN_SETTINGS\"`.\n\nТо есть у вас **жёстко используется Jenkins Managed settings**, а не `pom.xml`-репы напрямую. \nЕсли `settings-xml` (или `MVN_SETTINGS_FILE_ID`) указывает mirror на `https://art.lmru.tech/mvn`, то `mvn-replenishment` из `pom.xml` будет перекрыт.\n\nЧто менять:\n- либо обновить managed file с id `settings-xml`,\n- либо для этой джобы задать `MVN_SETTINGS_FILE_ID` на отдельный settings с mirror `https://art.lmru.tech/mvn-replenishment`,\n- либо (если хотите кодом) вызывать `mavenUtils.invoke(..., settingsFile: '...')`, но в вашем `svPipeline` сейчас используется `runPhase`, без передачи `settingsFile`.\n\nЕсли хотите, предложу минимальный PR-вариант в `sv-pipeline`: добавить параметр `build.mvnSettingsFileId` и выставлять `env.MVN_SETTINGS_FILE_ID` в `setupEnvVariables()`. Это даст per-project контроль без правок Jenkins global settings.\n\n**Advising Maven settings env setup**"}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\nкак для этой джобы задать MVN_SETTINGS_FILE_ID на отдельный settings с mirror https://art.lmru.tech/mvn-replenishment,\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Для вашей джобы самый практичный способ — через `Jenkinsfile` (чтобы не зависеть от UI-прав на folder/job).\n\n1) В Jenkins создать отдельный Managed file (`Config File Provider`):\n- тип: **Maven settings.xml**\n- `File ID`, например: `settings-xml-replenishment`\n- в `<mirrors>` поставить `https://art.lmru.tech/mvn-replenishment`\n\n2) В `Jenkinsfile` этой джобы перед `svPipeline.run(...)` задать env:\n\n```groovy\n@Library(['common-utils', 'sv-pipeline']) _\n\nenv.MVN_SETTINGS_FILE_ID = 'settings-xml-replenishment'\n\nsvPipeline.run(\n // ...\n)\n```\n\nПочему это сработает: `mavenUtils.invoke()` читает `env.MVN_SETTINGS_FILE_ID`, и если он задан — использует его вместо дефолтного `settings-xml`.\n\nАльтернатива без правки `Jenkinsfile`:\n- в конфиге job/folder добавить environment variable `MVN_SETTINGS_FILE_ID=settings-xml-replenishment` (если у вас включён соответствующий плагин/раздел Env Vars).\n\nПроверка:\n- в логе Maven с `-X` / `help:effective-settings` должен появиться активный mirror `.../mvn-replenishment`.\n\n**Explaining Jenkins env var setup**"}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\n@Jenkinsfile как можно сделать в дженкинс файле джобы?\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Сделаю прямо в `Jenkinsfile` самый безопасный вариант: оберну `svPipeline.run(...)` в `withEnv`, чтобы для этой джобы подставлялся отдельный `MVN_SETTINGS_FILE_ID` без влияния на другие пайплайны.\n\n**Planning Jenkinsfile env var patch**"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"В `Jenkinsfile` это делается так — через `withEnv`, чтобы только эта джоба использовала отдельный Maven settings:\n\n```groovy\nwithEnv(['MVN_SETTINGS_FILE_ID=settings-xml-replenishment']) {\n svPipeline.run(\n // ...\n )\n}\n```\n\nЯ уже внес это в ваш `Jenkinsfile` (`Jenkinsfile` в корне репозитория).\n\nЧто важно:\n- В Jenkins должен существовать managed Maven settings с `fileId = settings-xml-replenishment`.\n- В этом settings должен быть mirror на `https://art.lmru.tech/mvn-replenishment`.\n\nЕсли нужно, следующим шагом дам готовый XML для этого settings (минимальный рабочий шаблон с `mirrors` + `servers`)."}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\nа нельзя вызовом в sv-pipeline сделать, чтобы pom.xml который уже есть использовался?\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Коротко: **в текущем `sv-pipeline` — нет**, отдельного флага/параметра “используй только `pom.xml`” нет.\n\nПочему:\n- `svPipeline` вызывает `mavenUtils.runPhase(...)`\n- `mavenUtils` всегда запускает Maven с `-s <settings from Jenkins>`\n- значит Jenkins `settings.xml` всегда участвует\n\nНо есть нюанс: `pom.xml` **всё равно используется**, пока его не перекрывает `<mirrors>` (обычно `mirrorOf=*`) в settings.\n\nТо есть варианты такие:\n\n- **Без правок библиотек:** использовать `MVN_SETTINGS_FILE_ID` на settings, где нет агрессивного mirror (`*`) — тогда репы из `pom.xml` будут работать.\n- **С правкой `sv-pipeline`:** добавить параметр типа `build.mvnSettingsFileId` и прокидывать его в `env.MVN_SETTINGS_FILE_ID`.\n- **Радикально:** менять `mavenUtils`, чтобы можно было запускать без `-s` (сейчас так не реализовано).\n\nДля вашей задачи самый быстрый и безопасный путь — тот, что уже сделали: `withEnv(['MVN_SETTINGS_FILE_ID=...'])` + правильный managed settings.\n\n**Explaining Maven settings impact**"}]}}
{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\nнашел, settings xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<settings xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd\" xmlns=\"http://maven.apache.org/SETTINGS/1.1.0\"\nxmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n <servers>\n <server>\n <id>snapshots</id>\n <username>lm-sa-replenishment</username>\n <password>pass</password>\n </server>\n <server>\n <id>releases</id>\n <username>lm-sa-replenishment</username>\n <password>pass</password>\n </server>\n </servers>\n <mirrors>\n <mirror>\n <id>mvn-common</id>\n <name>LMRU artifactory - common mirror</name>\n <url>https://art.lmru.tech/mvn</url>\n <mirrorOf>*</mirrorOf>\n </mirror>\n </mirrors>\n</settings>\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"Да, вот это и есть корень проблемы:\n\n- у вас `mirrorOf=*`\n- и URL зеркала `https://art.lmru.tech/mvn`\n\nИз-за этого **все** репозитории из `pom.xml` принудительно уходят в `mvn`, включая ваш `mvn-replenishment`.\n\nЧто нужно поменять в этом `settings.xml`:\n\n```xml\n<mirrors>\n <mirror>\n <id>mvn-replenishment</id>\n <name>LMRU artifactory - replenishment mirror</name>\n <url>https://art.lmru.tech/mvn-replenishment</url>\n <mirrorOf>*</mirrorOf>\n </mirror>\n</mirrors>\n```\n\nИ желательно добавить `server` с тем же `id`, если репо требует auth на чтение:\n\n```xml\n<server>\n <id>mvn-replenishment</id>\n <username>lm-sa-replenishment</username>\n <password>...</password>\n</server>\n```\n\n---\n\nУ вас сейчас два пути (любой один):\n\n- **Либо** правите текущий managed file (`settings-xml`) и оставляете всё как есть.\n- **Либо** создаёте новый (`settings-xml-replenishment`) и используете его через \n `withEnv(['MVN_SETTINGS_FILE_ID=settings-xml-replenishment'])` в `Jenkinsfile` (мы это уже сделали).\n\nЕсли хотите, могу дать готовый полный `settings.xml` под ваш кейс (минимальный и безопасный шаблон).\n\n**Explaining Maven mirror and credentials setup**"}]}}