{"role":"user","message":{"content":[{"type":"text","text":"\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"}]}} {"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` с ``, где стоит что-то вроде `mirrorOf=*` на `https://art.lmru.tech/mvn`.\n\nЧто нужно сделать в Jenkins:\n\n- В **Managed files** (Config File Provider) найти Maven `settings.xml`, который использует джоба/папка/глобал.\n- Исправить секцию `` так, чтобы зеркало указывало на `mvn-replenishment`:\n```xml\n\n \n artifactory-replenishment\n *\n https://art.lmru.tech/mvn-replenishment\n \n\n```\n- Убедиться, что в `` есть креды для этого `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":"\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 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 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 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 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 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> matchedRule = opts.githubOptions.onPushEvent.find { String branchPrefix, Closure 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 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 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 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"}]}} {"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":"\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"}]}} {"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\n \n artifactory-replenishment\n *\n https://art.lmru.tech/mvn-replenishment\n \n\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":"\n@lmru--sv-jvm-k8s-pipeline вот еще либа sv-pipeline\n"}]}} {"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":"\n@lmru--devops--pipeline-shared-library вот common-utils\n"}]}} {"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":"\nкак для этой джобы задать MVN_SETTINGS_FILE_ID на отдельный settings с mirror https://art.lmru.tech/mvn-replenishment,\n"}]}} {"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- в `` поставить `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":"\n@Jenkinsfile как можно сделать в дженкинс файле джобы?\n"}]}} {"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":"\nа нельзя вызовом в sv-pipeline сделать, чтобы pom.xml который уже есть использовался?\n"}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"Коротко: **в текущем `sv-pipeline` — нет**, отдельного флага/параметра “используй только `pom.xml`” нет.\n\nПочему:\n- `svPipeline` вызывает `mavenUtils.runPhase(...)`\n- `mavenUtils` всегда запускает Maven с `-s `\n- значит Jenkins `settings.xml` всегда участвует\n\nНо есть нюанс: `pom.xml` **всё равно используется**, пока его не перекрывает `` (обычно `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":"\nнашел, settings xml\n\n\n \n \n snapshots\n lm-sa-replenishment\n pass\n \n \n releases\n lm-sa-replenishment\n pass\n \n \n \n \n mvn-common\n LMRU artifactory - common mirror\n https://art.lmru.tech/mvn\n *\n \n \n\n"}]}} {"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\n \n mvn-replenishment\n LMRU artifactory - replenishment mirror\n https://art.lmru.tech/mvn-replenishment\n *\n \n\n```\n\nИ желательно добавить `server` с тем же `id`, если репо требует auth на чтение:\n\n```xml\n\n mvn-replenishment\n lm-sa-replenishment\n ...\n\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**"}]}}