<user_query>
#!groovy

properties([
    buildDiscarder (logRotator (artifactDaysToKeepStr: '', artifactNumToKeepStr: '10', daysToKeepStr: '', numToKeepStr: '10')),
    disableConcurrentBuilds (),
    disableResume (),
]);

import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
@Library('common-utils') _

node ('dockerhost') {
    timestamps {
        ansiColor('xterm') {
            cleanWs()

            stage('checkout') {
                def branchName = env.CHANGE_BRANCH ?: env.BRANCH_NAME ?: 'master'

                checkout([
                    $class: 'GitSCM',
                    userRemoteConfigs: [[
                        url: 'git@github.lmru.tech:adeo/dp--deploy-reviseur-orch.git',
                        credentialsId: 'id_rsa_svc-lmru-dataplatform'
                    ]],
                    branches: [[name: "*/${branchName}"]],
                    doGenerateSubmoduleConfigurations: false,
                    extensions: [
                        [$class: 'CleanBeforeCheckout'],
                        [$class: 'CloneOption', noTags: true, shallow: false, timeout: 20]
                    ],
                    submoduleCfg: []
                    ])

                    sh '''
                    set -eu
                    git submodule sync --recursive
                    git -c submodule.active=. submodule update --init --recursive
                    git submodule status --recursive
                    '''
            }

            stage('prepare'){
                // def response = httpRequest "http://amapi.apps.dev.data.lmru.tech/api/v1/getdeployinfo?repo_url=adeo/dp--batch-proc--ff-dom&module_env=test"
                withFolderProperties{
                    AIRFLOW_CLUSTER = env.airflow_cluster
                    PRODUCT_TITLE = env.product_title
                    VAULT_APPROLE = env.vault_approle
                }
                if (!AIRFLOW_CLUSTER){
                    echo "'airflow_cluster' variable doesn't set"
                    sh 'exit 1'
                }
                if (!PRODUCT_TITLE){
                    echo "'product_title' variable doesn't set"
                    sh 'exit 1'
                }
                if (!VAULT_APPROLE){
                    echo "'vault_approle' variable doesn't set"
                    sh 'exit 1'
                }
                PROJECT_CONFIG = vaultUtils.getSecrets([namespace:PRODUCT_TITLE, vaultPath:"airflow/${AIRFLOW_CLUSTER}/project_config", appRoleCredential: VAULT_APPROLE])
                imageName = "${PROJECT_CONFIG.docker_registry_url}/airflow/${PROJECT_CONFIG.deploy_image}"
                imageTag = getImageTag()
                toPush = toPush()
            }

            stage('build'){
                image = docker.build("${imageName}:${imageTag}", ". --pull")
            }

            stage('publish'){
                if (toPush){
                    docker.withRegistry("https://${PROJECT_CONFIG.docker_registry_url}", PROJECT_CONFIG.docker_registry_crentials_id) {
                    image.push("${imageTag}")
                }
                } else {
                    Utils.markStageSkippedForConditional('publish')
                }
            }

            stage('Security scans') {
                parallel (
                    'CheckmarxScan': {
                        checkmarxUtils.runCheckmarxScan_ZeroLicense(
                            failBuildOnError: "false"
                        )
                    },
                    'SecretsScan': {
                        securityUtils.secretsScan[:]
                    },
                    'IacScan': {
                        securityUtils.iacScan(reportFormat: 'html')
                    },
                    'ImageScan': {
                        sh "docker pull ${imageName}:${imageTag}"
                        securityUtils.scanImage(imageName: "${imageName}", imageTag: "${imageTag}")
                    }
                )
            }

        }
    }
}

def getImageTag() {
    def tag = "pipeline${env.BUILD_NUMBER}"
    if (env.BRANCH_NAME == "main" || env.BRANCH_NAME == "master") {
        tag = "latest"
    } else if (env.BRANCH_NAME == 'dev') {
        tag = "dev"
    }
    return tag
}

// Returns deployment env ID based on build env branch/tags.
def getDeployEnv() {
    if (env.BRANCH_NAME == "main" || env.BRANCH_NAME == "master") {
        return "dev"
    } else if (env.BRANCH_NAME == 'test') {
        return "test"
    } else if (env.TAG_NAME) {
        return "prod"
    }
    return null
}

def toPush(){
    if (env.BRANCH_NAME == "main" || env.BRANCH_NAME == "master" || env.BRANCH_NAME == "dev"){
        return true
    }
    return false
}

10:51:25  Started by user Руслан Петрович (60231159)

10:51:25  Replayed #17

10:51:29 10:51:29  Connecting to https://github.lmru.tech/api/v3 using GitHub Enterprise Jenkins integration (svc-lmru-common)
10:51:30  Obtained Jenkinsfile from 02b357830a86a284f9f2d2e7ab0f86b24653db5b
10:51:30  Resume disabled by user, switching to high-performance, low-durability mode.
10:51:30  Loading library common-utils@master
10:51:30  Attempting to resolve master from remote references...
10:51:30   > git --version # timeout=10
10:51:30   > git --version # 'git version 2.47.3'
10:51:30  using GIT_SSH to set credentials SSH key for svc-lmru-devops (GitHub Enterprise)
10:51:30   > git ls-remote -h -t -- git@github.lmru.tech:adeo/lmru--devops--pipeline-shared-library.git # timeout=10
10:51:30  Found match: refs/heads/master revision 631dab72d03353f5a22eced17329b52730434d4a
10:51:30  The recommended git tool is: git
10:51:30  using credential id_rsa_svc-lmru-devops
10:51:30   > git rev-parse --resolve-git-dir /var/jenkins_home/workspace/_dp--deploy-reviseur-orch_master@libs/e02a363bc11db211517c6042e0c2d5f8c30e8205c9bc2507b45d17bfe6949736/.git # timeout=10
10:51:30  Fetching changes from the remote Git repository
10:51:30   > git config remote.origin.url git@github.lmru.tech:adeo/lmru--devops--pipeline-shared-library.git # timeout=10
10:51:30  Fetching without tags
10:51:30  Fetching upstream changes from git@github.lmru.tech:adeo/lmru--devops--pipeline-shared-library.git
10:51:30   > git --version # timeout=10
10:51:30   > git --version # 'git version 2.47.3'
10:51:30  using GIT_SSH to set credentials SSH key for svc-lmru-devops (GitHub Enterprise)
10:51:30   > git fetch --no-tags --force --progress -- git@github.lmru.tech:adeo/lmru--devops--pipeline-shared-library.git +refs/heads/*:refs/remotes/origin/* # timeout=10
10:51:31  Checking out Revision 631dab72d03353f5a22eced17329b52730434d4a (master)
10:51:31   > git config core.sparsecheckout # timeout=10
10:51:31   > git checkout -f 631dab72d03353f5a22eced17329b52730434d4a # timeout=10
10:51:31  Commit message: "Aqua sca fix (#487)"
10:51:31  [Pipeline] Start of Pipeline
10:51:31  [Pipeline] properties
10:51:31  [Pipeline] node
10:51:31  Running on p-jenkins-node-m-02
 in /data/jenkins-root/workspace/_dp--deploy-reviseur-orch_master
10:51:31  [Pipeline] {
10:51:31  [Pipeline] timestamps
10:51:31  The timestamps step is unnecessary when timestamps are enabled for all Pipeline builds.
10:51:31  [Pipeline] {
10:51:31  [Pipeline] ansiColor
10:51:31  [Pipeline] {
10:51:31
10:51:31  [Pipeline] cleanWs
10:51:31  [WS-CLEANUP] Deleting project workspace...
10:51:31  [WS-CLEANUP] Deferred wipeout is used...
10:51:31  [WS-CLEANUP] done
10:51:31  [Pipeline] stage
10:51:31  [Pipeline] { (checkout)
10:51:31  [Pipeline] checkout
10:51:31  The recommended git tool is: NONE
10:51:31  using credential id_rsa_svc-lmru-dataplatform
10:51:31  Cloning the remote Git repository
10:51:31  Avoid fetching tags
10:51:32  Avoid second fetch
10:51:32  Checking out Revision 02b357830a86a284f9f2d2e7ab0f86b24653db5b (refs/remotes/origin/master)
10:51:32  Commit message: "init"
10:51:31  Cloning repository git@github.lmru.tech:adeo/dp--deploy-reviseur-orch.git
10:51:31   > git init /data/jenkins-root/workspace/_dp--deploy-reviseur-orch_master # timeout=10
10:51:31  Fetching upstream changes from git@github.lmru.tech:adeo/dp--deploy-reviseur-orch.git
10:51:31   > git --version # timeout=10
10:51:31   > git --version # 'git version 2.43.0'
10:51:31  using GIT_SSH to set credentials SSH key for svc-lmru-dataplatform (GitHub Enterprise)
10:51:31   > git fetch --no-tags --force --progress -- git@github.lmru.tech:adeo/dp--deploy-reviseur-orch.git +refs/heads/*:refs/remotes/origin/* # timeout=20
10:51:32   > git config remote.origin.url git@github.lmru.tech:adeo/dp--deploy-reviseur-orch.git # timeout=10
10:51:32   > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
10:51:32   > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
10:51:32   > git config core.sparsecheckout # timeout=10
10:51:32   > git checkout -f 02b357830a86a284f9f2d2e7ab0f86b24653db5b # timeout=10
10:51:32   > git rev-list --no-walk 02b357830a86a284f9f2d2e7ab0f86b24653db5b # timeout=10
10:51:32  [Pipeline] sh
10:51:33  + set -eu
10:51:33  + git submodule sync --recursive
10:51:33  + git -c submodule.active=. submodule update --init --recursive
10:51:33  + git submodule status --recursive
10:51:33  [Pipeline] }
10:51:33  [Pipeline] // stage
10:51:33  [Pipeline] stage
10:51:33  [Pipeline] { (prepare)
10:51:33  [Pipeline] withFolderProperties
10:51:33  [Pipeline] {
10:51:33  Did you forget the `def` keyword? WorkflowScript seems to be setting a field named AIRFLOW_CLUSTER (to a value of type String) which could lead to memory leaks or other issues.
10:51:33  Did you forget the `def` keyword? WorkflowScript seems to be setting a field named PRODUCT_TITLE (to a value of type String) which could lead to memory leaks or other issues.
10:51:33  Did you forget the `def` keyword? WorkflowScript seems to be setting a field named VAULT_APPROLE (to a value of type String) which could lead to memory leaks or other issues.
10:51:33  [Pipeline] }
10:51:33  [Pipeline] // withFolderProperties
10:51:33  [Pipeline] httpRequest
10:51:33  [Pipeline] withCredentials
10:51:33  Masking supported pattern matches of $USERNAME or $PASSWORD
10:51:33  [Pipeline] {
10:51:33  [Pipeline] sh
10:51:33  Warning: A secret was passed to "sh" using Groovy String interpolation, which is insecure.
10:51:33  		 Affected argument(s) used the following variable(s): [PASSWORD, USERNAME]
10:51:33  		 See https://jenkins.io/redirect/groovy-string-interpolation for details.
10:51:33  + set +x
10:51:33  [Pipeline] readJSON
10:51:33  [Pipeline] sh
10:51:33  + set +x
10:51:34  [Pipeline] readJSON
10:51:34  [Pipeline] sh
10:51:34  + set +x
10:51:34  [Pipeline] readJSON
10:51:34  [Pipeline] }
10:51:34  [Pipeline] // withCredentials
10:51:34  Did you forget the `def` keyword? WorkflowScript seems to be setting a field named PROJECT_CONFIG (to a value of type LinkedHashMap) which could lead to memory leaks or other issues.
10:51:34  Did you forget the `def` keyword? WorkflowScript seems to be setting a field named imageName (to a value of type GStringImpl) which could lead to memory leaks or other issues.
10:51:34  Did you forget the `def` keyword? WorkflowScript seems to be setting a field named imageTag (to a value of type String) which could lead to memory leaks or other issues.
10:51:34  Did you forget the `def` keyword? WorkflowScript seems to be setting a field named toPush (to a value of type Boolean) which could lead to memory leaks or other issues.
10:51:34  [Pipeline] }
10:51:34  [Pipeline] // stage
10:51:34  [Pipeline] stage
10:51:34  [Pipeline] { (build)
10:51:34  [Pipeline] isUnix
10:51:34  [Pipeline] withEnv
10:51:34  [Pipeline] {
10:51:34  [Pipeline] sh
10:51:34  + docker build -t null/airflow/null:latest . --pull
10:51:34  #0 building with "default" instance using docker driver
10:51:34
10:51:34  #1 [internal] load build definition from Dockerfile
10:51:34  #1 transferring dockerfile: 293B done
10:51:34  #1 WARN: InvalidDefaultArgInFrom: Default value for ARG ${DOCKER_IMAGE} results in empty or invalid base image name (line 2)
10:51:34  #1 DONE 0.0s
10:51:34
10:51:34   1 warning found (use docker --debug to expand):
10:51:34   - InvalidDefaultArgInFrom: Default value for ARG ${DOCKER_IMAGE} results in empty or invalid base image name (line 2)
10:51:34  Dockerfile:2
10:51:34  --------------------
10:51:34     1 |     ARG DOCKER_IMAGE=''
10:51:34     2 | >>> FROM ${DOCKER_IMAGE}
10:51:34     3 |
10:51:34     4 |     FROM docker-local-dataplatform.art.lmru.tech/common-deploy:latest
10:51:34  --------------------
10:51:34  ERROR: failed to build: failed to solve: base name (${DOCKER_IMAGE}) should not be blank
10:51:34  [Pipeline] }
10:51:34  [Pipeline] // withEnv
10:51:34  [Pipeline] }
10:51:34  [Pipeline] // stage
10:51:34  [Pipeline] }
10:51:34
10:51:34  [Pipeline] // ansiColor
10:51:34  [Pipeline] }
10:51:34  [Pipeline] // timestamps
10:51:35  [Pipeline] }
10:51:35  [Pipeline] // node
10:51:35  [Pipeline] End of Pipeline
10:51:35  ERROR: script returned exit code 1
10:51:35
10:51:35  GitHub has been notified of this commit’s build result
10:51:35
10:51:35  Finished: FAILURE

</user_query>


Отлично: checkout и сабмодули уже починились.
Текущий фейл теперь в `prepare/build`, не в Git.

## Что сломалось сейчас

По логу:

- `docker build -t null/airflow/null:latest ...`
- `FROM ${DOCKER_IMAGE}` → `base name ... should not be blank`

Значит в `prepare` у вас получились `null` как минимум для:

- `PROJECT_CONFIG.docker_registry_url`
- `PROJECT_CONFIG.deploy_image`

Именно поэтому тег стал `null/airflow/null:latest`.

## Вероятная причина

`vaultUtils.getSecrets(...)` вернул map без нужных ключей (или с `null`) по пути:

- `airflow/${AIRFLOW_CLUSTER}/project_config`

То есть либо путь/namespace не тот, либо в секрете нет этих полей.

## Что проверить первым делом

- Фактические значения `AIRFLOW_CLUSTER`, `PRODUCT_TITLE`, `VAULT_APPROLE` (без секретов).
- Какие ключи реально пришли в `PROJECT_CONFIG`.
- Совпадает ли имя ключа `docker_registry_crentials_id` (в коде опечатка в слове `credentials` — возможно это историческое имя, но проверить надо).

## Почему есть warning’и про `def`

`Did you forget the def keyword?` — это не причина падения, но лучше поправить.
Вы присваиваете переменные как поля скрипта (CPS warning), а не локальные переменные.

## Минимальная диагностика в `prepare` (что добавить)

- вывести безопасные поля:
  - `echo "AIRFLOW_CLUSTER=${AIRFLOW_CLUSTER}, PRODUCT_TITLE=${PRODUCT_TITLE}"`
  - `echo "PROJECT_CONFIG keys: ${PROJECT_CONFIG.keySet().sort()}"`

- и сделать явную валидацию до `docker.build`:
  - `docker_registry_url`
  - `deploy_image`
  - `docker_registry_crentials_id` (или корректное имя ключа)

Если хотите, дам готовый блок `prepare` с `def` + fail-fast валидацией, чтобы сборка падала с понятной ошибкой “какого ключа не хватает”, а не на Docker этапе.

**Diagnosing null config values**


<user_query>
как ты понял, что починились сабмодули
</user_query>


По вашему последнему логу видно прямые признаки, что сабмодули уже не ломают пайплайн:

- Нет ошибки `SubmoduleOption.onCheckoutCompleted` и `git config --get submodule.playbooks.url` (раньше на этом падало).
- `stage('checkout')` завершился успешно и выполнился ваш `sh`:
  - `git submodule sync --recursive`
  - `git -c submodule.active=. submodule update --init --recursive`
  - `git submodule status --recursive`
- После checkout пайплайн пошел дальше в `stage('prepare')` и упал уже на `docker build` с другой причиной (`null/airflow/null`, пустой `DOCKER_IMAGE`).

То есть переход точки падения с checkout/submodule на prepare/build = сабмодульная проблема устранена.


<user_query>
могу предложить ему такой вариант чекаута, а может ему вообще предложить отказаться от сабмодулей? что тогда написать?
</user_query>


Да, можно написать в двух вариантах: быстрый фикс и стратегическое предложение.

## Что написать сейчас (практично)

Предложи так:

- Проблема была в `SubmoduleOption` Jenkins (`git config --get submodule.playbooks.url`).
- Мы обошли это явным checkout без `checkout scm` и ручным `git submodule update`.
- Это уже отработало: checkout + submodules прошли, дальше упало в другом месте.

Текст для коллеги:

> Привет! Нашли причину падения checkout: ломается Jenkins `SubmoduleOption` на `git config --get submodule.playbooks.url`.
> Временный рабочий вариант — не использовать `checkout scm`, а сделать явный `GitSCM` checkout без submodule extension и затем руками:
> `git submodule sync --recursive && git -c submodule.active=. submodule update --init --recursive`.
> После этого checkout проходит стабильно.

---

## Стоит ли предлагать отказаться от сабмодулей

Да, **как отдельную инициативу** — стоит, но не как срочный фикс.

Когда это уместно:
- частые падения CI из-за сабмодулей/прав/веток;
- много команд и независимый lifecycle `playbooks`/`helm`;
- нужны предсказуемые релизы без “магии” Jenkins.

Что предложить вместо:
- либо отдельные репы + версионирование через теги/релизные артефакты;
- либо монорепа (если команды и релизы жестко связаны);
- либо dependency manager (helm chart dependency, release bundle и т.д.).

Текст “стратегии”:

> Отдельно предлагаю рассмотреть отказ от git submodules для deploy-репо.
> Сейчас сабмодули добавляют нестабильность в Jenkins checkout (особенно при разных версиях git/plugin).
> Более надежный вариант — подтягивать зависимости как версии (тег/релиз) из отдельных репозиториев или перейти на монорепу для тесно связанных компонентов.
> Это снизит число инфраструктурных фейлов в CI.

---

Если хочешь, могу дать готовое короткое сообщение “в чат” и длинное “в тикет” (с рисками/планом миграции).


<user_query>
дай в чат простым языком
</user_query>


Коллеги, нашли причину падения: ломался не сам git checkout, а встроенная обработка сабмодулей в Jenkins (`SubmoduleOption`).

Что сделали:
- убрали зависимость от `checkout scm` для этого шага;
- делаем обычный checkout репозитория;
- сабмодули подтягиваем вручную командами:
`git submodule sync --recursive`
`git -c submodule.active=. submodule update --init --recursive`

После этого checkout проходит, и пайплайн идет дальше.

Отдельно предлагаю подумать, не уйти ли от сабмодулей совсем (они часто дают нестабильность в CI). Но как быстрый фикс текущей проблемы — вариант выше рабочий.


[Image]
<image_files>
The following images were provdied by the user and saved to the workspace for future use:
1. /home/ruslanpi/.cursor/projects/home-ruslanpi-Documents-repos-lmru-devops-argocd-apps/assets/image-00370e3f-c7ac-481c-9c72-4dcc87a04036.png

These images can be copied for use in other locations.
</image_files>
<user_query>

Александр Дядиченко
5 минут назад
@Руслан Петрович привет, этот проект построен с использованием сабмодулей, и в других местах оно без проблем работает
https://jenkins.lmru.tech/job/datalakehouse/job/airflow-core/job/dp--deploy-airflow2-dlh/job/master/
все таки хочется понять причину, почему здесь не работает

вот где он скинул рабочий вариант
#!groovy

properties([
    buildDiscarder (logRotator (artifactDaysToKeepStr: '', artifactNumToKeepStr: '10', daysToKeepStr: '', numToKeepStr: '10')),
    disableConcurrentBuilds (),
    disableResume (),
]);

import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
@Library('common-utils') _

node ('dockerhost') {
    timestamps {
        ansiColor('xterm') {
            cleanWs()

            stage('checkout'){
                checkout scm
            }

            stage('prepare'){
                // def response = httpRequest "http://amapi.apps.dev.data.lmru.tech/api/v1/getdeployinfo?repo_url=adeo/dp--batch-proc--ff-dom&module_env=test"
                withFolderProperties{
                    AIRFLOW_CLUSTER = env.airflow_cluster
                    PRODUCT_TITLE = env.product_title
                    VAULT_APPROLE = env.vault_approle
                }
                if (!AIRFLOW_CLUSTER){
                    echo "'airflow_cluster' variable doesn't set"
                    sh 'exit 1'
                }
                if (!PRODUCT_TITLE){
                    echo "'product_title' variable doesn't set"
                    sh 'exit 1'
                }
                if (!VAULT_APPROLE){
                    echo "'vault_approle' variable doesn't set"
                    sh 'exit 1'
                }
                PROJECT_CONFIG = vaultUtils.getSecrets([namespace:PRODUCT_TITLE, vaultPath:"airflow/${AIRFLOW_CLUSTER}/project_config", appRoleCredential: VAULT_APPROLE])
                imageName = "${PROJECT_CONFIG.docker_registry_url}/airflow/${PROJECT_CONFIG.deploy_image}"
                imageTag = getImageTag()
                toPush = toPush()
            }

            stage('build'){
                image = docker.build("${imageName}:${imageTag}", ". --pull")
            }

            stage('publish'){
                if (toPush){
                    docker.withRegistry("https://${PROJECT_CONFIG.docker_registry_url}", PROJECT_CONFIG.docker_registry_crentials_id) {
                    image.push("${imageTag}")
                }
                } else {
                    Utils.markStageSkippedForConditional('publish')
                }
            }

            stage('Security scans') {
                parallel (
                    'CheckmarxScan': {
                        checkmarxUtils.runCheckmarxScan_ZeroLicense(
                            failBuildOnError: "false"
                        )
                    },
                    'SecretsScan': {
                        securityUtils.secretsScan[:]
                    },
                    'IacScan': {
                        securityUtils.iacScan(reportFormat: 'html')
                    },
                    'ImageScan': {
                        sh "docker pull ${imageName}:${imageTag}"
                        securityUtils.scanImage(imageName: "${imageName}", imageTag: "${imageTag}")
                    }
                )
            }

        }
    }
}

def getImageTag() {
    def tag = "pipeline${env.BUILD_NUMBER}"
    if (env.BRANCH_NAME == "main" || env.BRANCH_NAME == "master") {
        tag = "latest"
    } else if (env.BRANCH_NAME == 'dev') {
        tag = "dev"
    }
    return tag
}

// Returns deployment env ID based on build env branch/tags.
def getDeployEnv() {
    if (env.BRANCH_NAME == "main" || env.BRANCH_NAME == "master") {
        return "dev"
    } else if (env.BRANCH_NAME == 'test') {
        return "test"
    } else if (env.TAG_NAME) {
        return "prod"
    }
    return null
}

def toPush(){
    if (env.BRANCH_NAME == "main" || env.BRANCH_NAME == "master" || env.BRANCH_NAME == "dev"){
        return true
    }
    return false
}

</user_query>


Можно ответить так, простым языком:

> Саша, да, сабмодули как подход рабочие — проблема не в них как в концепции.
> По логам здесь ломается именно Jenkins Git-плагин на шаге `SubmoduleOption`:
> он видит `playbooks` в `.gitmodules`, но не находит `submodule.playbooks.url` в локальном `.git/config` и падает.
>
> То есть это разница окружения/инструментов на конкретных агентах (у нас в логе для этого checkout git `2.43.0` и `recommended git tool: NONE`), а не “проект с сабмодулями неправильный”.
>
> Поэтому временно обошли это явным checkout + ручным `git submodule sync/update` — так стабильно работает.
> Параллельно можно добить первопричину на Jenkins-агентах (git tool/version/global git config), чтобы `checkout scm` тоже вел себя одинаково.

Если нужно, дам еще супер-короткую версию в 2 строки для чата.

**Crafting simple Russian explanation**


<user_query>
а давай попробуем и вправду разобраться, скинул рабочий вариант выше
</user_query>


Отличная идея — тут реально можно докопаться до первопричины, а не жить на workaround.

Самое вероятное сейчас: на “падающих” агентах есть отличие в git-окружении (особенно `submodule.active`/global git config), из-за чего Jenkins `SubmoduleOption` не прописывает `submodule.playbooks.url` в `.git/config`.

## Почему так думаю

- Падает именно на `git config --get submodule.playbooks.url`.
- `.gitmodules` у вас корректный.
- Явный checkout + `git -c submodule.active=. submodule update ...` у вас проходит.
- Значит проблема в том, **как Jenkins plugin инициализирует submodule в конкретном окружении**.

## Как быстро проверить (3 коротких шага)

### 1) Сверить агент/версию git между “рабочим” и “падающим”
В логах уже видно разницу (`2.47.3` vs `2.43.0` в разных местах).
Нужно проверить именно git на node, где падает `checkout scm`.

### 2) Перед `checkout scm` вывести git config на агенте
Добавить временно stage до checkout:

```groovy
stage('diag-git-env') {
  sh '''
    set -eu
    whoami
    hostname
    git --version
    git config --system --list || true
    git config --global --list || true
    git config --list || true
  '''
}
```

Что ищем: `submodule.active`, `url.*.insteadOf`, любые submodule-related override.

### 3) На этом же агенте вручную воспроизвести цепочку plugin
(через `sshagent` с теми же кредами), и проверить момент, когда должен появиться `submodule.playbooks.url`:

```bash
git init .
git remote add origin git@github.lmru.tech:adeo/dp--deploy-reviseur-orch.git
git fetch --no-tags origin +refs/heads/master:refs/remotes/origin/master
git checkout -f <commit>
git config -f .gitmodules --get-regexp '^submodule\.(.+)\.url'
git submodule init
git config --get submodule.playbooks.url
```

Если после `git submodule init` ключа нет — причина точно в git env на агенте, а не в репо.

---

Если хочешь, я соберу для тебя готовый диагностический `stage('diag')` под Jenkinsfile (с `sshagent`), чтобы вставить и получить полный дамп за один прогон.

**Proposing concrete debugging plan**


<user_query>
давай
</user_query>


Супер, вот готовый диагностический блок.
Его лучше поставить **сразу после `cleanWs()` и до `stage('checkout')`**.

```groovy
stage('diag submodule env') {
    sshagent(credentials: ['id_rsa_svc-lmru-dataplatform']) {
        sh '''
            set -eu

            echo "=== NODE / SHELL / GIT ==="
            hostname || true
            whoami || true
            pwd
            git --version
            command -v git || true

            echo "=== GIT CONFIG (system/global/local) ==="
            git config --system --list --show-origin || true
            git config --global --list --show-origin || true

            echo "=== CLEAN TEST REPO ==="
            rm -rf .git .gitmodules playbooks helm || true

            git init .
            git remote add origin git@github.lmru.tech:adeo/dp--deploy-reviseur-orch.git
            git fetch --no-tags --force origin +refs/heads/master:refs/remotes/origin/master
            git checkout -f refs/remotes/origin/master

            echo "=== .gitmodules content ==="
            test -f .gitmodules && cat .gitmodules || echo "NO .gitmodules"

            echo "=== SUBMODULE URLS FROM .gitmodules ==="
            git config -f .gitmodules --get-regexp '^submodule\\.(.+)\\.url' || true

            echo "=== BEFORE init: submodule.* from .git/config ==="
            git config --get-regexp '^submodule\\.' || true

            echo "=== RUN git submodule init ==="
            git submodule init || true

            echo "=== AFTER init: submodule.* from .git/config ==="
            git config --get-regexp '^submodule\\.' || true

            echo "=== DIRECT CHECK (problem key) ==="
            git config --get submodule.playbooks.url || true

            echo "=== TRY WITH submodule.active=. ==="
            git -c submodule.active=. submodule init || true
            git config --get submodule.playbooks.url || true

            echo "=== SUBMODULE STATUS ==="
            git submodule status || true
        '''
    }
}
```

Что смотреть в выводе:

- Есть ли `submodule.playbooks.url` после обычного `git submodule init`.
- Появляется ли он только после `-c submodule.active=.`.
- Есть ли в `--system/--global` что-то вроде `submodule.active=...` (это частый корень проблемы).

Если после этого скинешь кусок лога `diag submodule env`, я помогу точно сформулировать RCA для команды Jenkins.


<user_query>
только что просто нажал билд нау по мастеру и ошибки с сабмодулями не вылезло)
</user_query>


Отлично — это как раз подтверждает гипотезу про контекст запуска, а не про “битые сабмодули” в репо.

Почему так могло быть:

- `Replay/Rebuild` мог тянуть старый `scm`-объект/настройки branch-job.
- После обычного `Build Now` multibranch подхватил актуальную конфигурацию.
- Разные агенты/окружения тоже могли влиять (но сейчас уже не блокирует).

Что лучше сделать сейчас:

- Зафиксировать, что проблема воспроизводилась только в replay/rebuild.
- Оставить короткий комментарий в задаче: “обычный Build Now по master проходит; падение было в checkout scm + SubmoduleOption в старом контексте запуска”.
- На всякий случай прогнать еще 2-3 обычных билда для стабильности.

Если хочешь, дам готовый текст в чат Александру “что было и почему сейчас ок”.

**Explaining stale context potential**


<user_query>
15:04:06  [ssh-agent] Using credentials svc-lmru-dataplatform (SSH key for svc-lmru-dataplatform (GitHub Enterprise))
15:04:06  $ ssh-agent
15:04:06  SSH_AUTH_SOCK=/tmp/ssh-BS6X2Qd9rwTW/agent.4183200
15:04:06  SSH_AGENT_PID=4183203
15:04:06  Running ssh-add (command line suppressed)
15:04:06  Identity added: /data/jenkins-root/workspace/_dp--deploy-reviseur-orch_master@tmp/private_key_7865611287264224738.key (svc-lmru-dataplatform)
15:04:06  [ssh-agent] Started.
15:04:06  [Pipeline] {
15:04:06  [Pipeline] sh
15:04:07  + set -eu
15:04:07  + echo === NODE / SHELL / GIT ===
15:04:07  === NODE / SHELL / GIT ===
15:04:07  + hostname
15:04:07  p-jenkins-node-m-07
15:04:07  + whoami
15:04:07  root
15:04:07  + pwd
15:04:07  /data/jenkins-root/workspace/_dp--deploy-reviseur-orch_master
15:04:07  + git --version
15:04:07  git version 2.43.0
15:04:07  + command -v git
15:04:07  /usr/bin/git
15:04:07  + echo === GIT CONFIG (system/global/local) ===
15:04:07  === GIT CONFIG (system/global/local) ===
15:04:07  + git config --system --list --show-origin
15:04:07  fatal: unable to read config file '/etc/gitconfig': No such file or directory
15:04:07  + true
15:04:07  + git config --global --list --show-origin
15:04:07  file:/root/.gitconfig	user.email=jenkins@lmru.com
15:04:07  file:/root/.gitconfig	user.name=Jenkins LMRU
15:04:07  file:/root/.gitconfig	credential.helper=cache
15:04:07  file:/root/.gitconfig	push.default=simple
15:04:07  file:/root/.gitconfig	safe.directory=/opt/homebrew
15:04:07  + echo === CLEAN TEST REPO ===
15:04:07  === CLEAN TEST REPO ===
15:04:07  + rm -rf .git .gitmodules playbooks helm
15:04:07  + git init .
15:04:07  hint: Using 'master' as the name for the initial branch. This default branch name
15:04:07  hint: is subject to change. To configure the initial branch name to use in all
15:04:07  hint: of your new repositories, which will suppress this warning, call:
15:04:07  hint:
15:04:07  hint: 	git config --global init.defaultBranch <name>
15:04:07  hint:
15:04:07  hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
15:04:07  hint: 'development'. The just-created branch can be renamed via this command:
15:04:07  hint:
15:04:07  hint: 	git branch -m <name>
15:04:07  Initialized empty Git repository in /data/jenkins-root/workspace/_dp--deploy-reviseur-orch_master/.git/
15:04:07  + git remote add origin git@github.lmru.tech:adeo/dp--deploy-reviseur-orch.git
15:04:07  + git fetch --no-tags --force origin +refs/heads/master:refs/remotes/origin/master
15:04:07  From github.lmru.tech:adeo/dp--deploy-reviseur-orch
15:04:07   * [new branch]      master     -> origin/master
15:04:07  + git checkout -f refs/remotes/origin/master
15:04:07  Note: switching to 'refs/remotes/origin/master'.
15:04:07
15:04:07  You are in 'detached HEAD' state. You can look around, make experimental
15:04:07  changes and commit them, and you can discard any commits you make in this
15:04:07  state without impacting any branches by switching back to a branch.
15:04:07
15:04:07  If you want to create a new branch to retain commits you create, you may
15:04:07  do so (now or later) by using -c with the switch command. Example:
15:04:07
15:04:07    git switch -c <new-branch-name>
15:04:07
15:04:07  Or undo this operation with:
15:04:07
15:04:07    git switch -
15:04:07
15:04:07  Turn off this advice by setting config variable advice.detachedHead to false
15:04:07
15:04:07  HEAD is now at 41b400b init
15:04:07  + echo === .gitmodules content ===
15:04:07  === .gitmodules content ===
15:04:07  + test -f .gitmodules
15:04:07  + cat .gitmodules
15:04:07  [submodule "playbooks"]
15:04:07  	path = playbooks
15:04:07  	url = git@github.lmru.tech:adeo/dp--deploy-airflow2-ansible.git
15:04:07  	branch = rel_v0.3.1
15:04:07  [submodule "helm"]
15:04:07  	path = helm
15:04:07  	url = git@github.lmru.tech:adeo/dp--deploy-airflow2-helm.git
15:04:07  	branch = rel_0.3
15:04:07  + echo === SUBMODULE URLS FROM .gitmodules ===
15:04:07  === SUBMODULE URLS FROM .gitmodules ===
15:04:07  + git config -f .gitmodules --get-regexp ^submodule\.(.+)\.url
15:04:07  submodule.playbooks.url git@github.lmru.tech:adeo/dp--deploy-airflow2-ansible.git
15:04:07  submodule.helm.url git@github.lmru.tech:adeo/dp--deploy-airflow2-helm.git
15:04:07  + echo === BEFORE init: submodule.* from .git/config ===
15:04:07  === BEFORE init: submodule.* from .git/config ===
15:04:07  + git config --get-regexp ^submodule\.
15:04:07  + true
15:04:07  + echo === RUN git submodule init ===
15:04:07  === RUN git submodule init ===
15:04:07  + git submodule init
15:04:07  + echo === AFTER init: submodule.* from .git/config ===
15:04:07  === AFTER init: submodule.* from .git/config ===
15:04:07  + git config --get-regexp ^submodule\.
15:04:07  + true
15:04:07  + echo === DIRECT CHECK (problem key) ===
15:04:07  === DIRECT CHECK (problem key) ===
15:04:07  + git config --get submodule.playbooks.url
15:04:07  + true
15:04:07  + echo === TRY WITH submodule.active=. ===
15:04:07  === TRY WITH submodule.active=. ===
15:04:07  + git -c submodule.active=. submodule init
15:04:07  + git config --get submodule.playbooks.url
15:04:07  + true
15:04:07  + echo === SUBMODULE STATUS ===
15:04:07  === SUBMODULE STATUS ===
15:04:07  + git submodule status
15:04:07  [Pipeline] }
15:04:07  $ ssh-agent -k
15:04:07  unset SSH_AUTH_SOCK;
15:04:07  unset SSH_AGENT_PID;
15:04:07  echo Agent pid 4183203 killed;
15:04:07  [ssh-agent] Stopped.
</user_query>


Супер, этот лог дал **четкую первопричину**.

## Что он доказал

- `.gitmodules` есть и в нем `playbooks`/`helm` прописаны.
- Но после `git submodule init` в `.git/config` **ничего не появилось**:
  - `git config --get-regexp ^submodule\.` → пусто
  - `git config --get submodule.playbooks.url` → пусто
- `git submodule status` тоже пустой.

Это обычно значит: в самом коммите нет gitlink-записей сабмодулей (mode `160000`) для `playbooks`/`helm`, то есть есть только `.gitmodules`, а “подмодули как объекты git” не зарегистрированы в дереве коммита.

Из-за этого Jenkins `SubmoduleOption` и падает: он ожидает `submodule.playbooks.url` в `.git/config`, а `git submodule init` его не создает.

---

Можно ответить Александру так:

> Причину нашли: в этом репо в текущем коммите есть `.gitmodules`, но сами сабмодули не инициализируются (`git submodule status` пустой, `git submodule init` не добавляет `submodule.*` в `.git/config`).
> Поэтому Jenkins `SubmoduleOption` падает на `git config --get submodule.playbooks.url`.
> То есть проблема не “сабмодули вообще не работают”, а несоответствие состояния репозитория: `.gitmodules` есть, а gitlink-записей сабмодулей в коммите нет/неактивны.

---

Если хотите быстро добить проверку “на 100%”, попросите выполнить в репо:

- `git ls-tree HEAD playbooks helm`
- у корректного сабмодуля там должен быть тип `commit` и mode `160000`.

Если там пусто (или не `160000`) — RCA окончательно подтверждено.

**Explaining git submodule config issue**


<user_query>
@dp--deploy-reviseur-orch на сам проверь
</user_query>


Проверю сам репозиторий `dp--deploy-reviseur-orch`: посмотрю состояние `.gitmodules`, gitlink-записей для `playbooks/helm` и воспроизведу команды, на которых падал Jenkins. Сейчас сначала найду его локально и сниму факты по `git`.

**Planning repo location check**


Нашел репозиторий локально в `lmru--devops--argocd-apps/dp--deploy-reviseur-orch`. Сейчас проверю его как Git-объект: есть ли gitlink для `playbooks/helm` в `HEAD`, и что дают `submodule init/status` на чистом клоне.

**Inspecting nested git repo**


**Planning safe submodule inspection**


Проверил “наживую”: в `master` реально есть только файл `.gitmodules`, а самих gitlink-сабмодулей нет, поэтому `submodule init` ничего не добавляет. Сейчас посмотрю историю, когда это состояние появилось, чтобы можно было аргументировать коллегам.

**Planning repository history inspection**


**Testing git revision listing strategies**


**Confirming no submodules added**


Проверил сам `dp--deploy-reviseur-orch` локально.

Факты:

- Репо чистое: `master...origin/master`.
- В `HEAD` для сабмодулей нет gitlink-объектов:
  - `git ls-tree HEAD playbooks helm .gitmodules` возвращает только `.gitmodules`.
- `.gitmodules` содержит секции `playbooks` и `helm`, но это только декларация.
- В свежем клоне:
  - `git submodule init` ничего не добавляет в `.git/config`
  - `git config --get submodule.playbooks.url` пусто
  - `git submodule status` пусто
- Коммит `3bf462f` (первое добавление `.gitmodules`) добавил файл `.gitmodules`, но **не добавил** gitlink-записи `playbooks/helm` как сабмодули.

Итог:

- Jenkins падает закономерно: `SubmoduleOption` ожидает `submodule.playbooks.url` после `submodule init`, но его неоткуда взять, потому что в дереве коммита нет сабмодулей как gitlink (`160000`).

Что делать:

- либо оформить `playbooks`/`helm` как реальные сабмодули (чтобы в `ls-tree` были `160000 commit ... playbooks|helm`),
- либо убрать `.gitmodules` и отключить submodule processing для этого репо (если сабмодули фактически не используются).

**Summarizing submodule issue**


<user_query>
что я могу сделать сам чтобы починить?
</user_query>


Ты можешь починить сам двумя путями — зависит от того, нужны ли сабмодули реально.

## Вариант 1 (самый быстрый): сабмодули не нужны
Если `playbooks/helm` не используются как сабмодули в этом репо:

1. В Jenkins job отключи обработку сабмодулей (`Disable submodules processing`).
2. В репо убери `.gitmodules` (чтобы не вводил в заблуждение).
3. Закоммить и запусти билд.

Это самый простой и стабильный путь.

---

## Вариант 2 (правильный, если сабмодули нужны)
Сейчас у тебя есть `.gitmodules`, но **нет gitlink-записей сабмодулей** в коммите. Нужно добавить сабмодули правильно.

В корне `dp--deploy-reviseur-orch`:

```bash
git checkout master
git pull

# если папки playbooks/helm существуют локально как обычные директории — удалить/перенести перед add
git submodule add -b rel_v0.3.1 git@github.lmru.tech:adeo/dp--deploy-airflow2-ansible.git playbooks
git submodule add -b rel_0.3 git@github.lmru.tech:adeo/dp--deploy-airflow2-helm.git helm

git add .gitmodules playbooks helm
git commit -m "fix: register playbooks and helm as real git submodules"
git push
```

Проверка (обязательная):

```bash
git ls-tree HEAD playbooks helm
```

Должно быть что-то вроде:
- `160000 commit ... playbooks`
- `160000 commit ... helm`

Если видишь `160000`, значит Jenkins `checkout scm` с submodule option больше не должен падать.

---

Если хочешь, помогу выбрать между 1 и 2 по вашему реальному процессу релиза (что именно от `playbooks/helm` ожидается в этом пайплайне).

**Предлагая два варианта решения**