Как я могу правильно реорганизовать этот скриптовый конвейер Jenkins для хранения общего кода в одном файле?

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

Я просмотрел здесь много вопросов о Jenkinsfiles, groovy и include, и ни один из них, похоже, не решает мою проблему. Я хочу максимально упростить работу остальной части моей команды, позволив им указать в одной простой структуре данных все задания, которые они хотят выполнять в этом каталоге, например:

def targets = [
  "Build number 1" : "make build",
  "Some other run command" : "custom_script"
]

Моя идея заключалась в том, чтобы создать для них шаблон Jenkinsfile, который выглядел бы примерно так:

def targets = [.....]
node {
   scm......

   load "common/Jenkinsfile"
}

а затем common/Jenkinsfile, чтобы включить код для превращения этих целей в этапы:

    try {
    targets.each {entry ->
        stage (entry.key) {
           sh "${complicated_command_prefix} $entry.value  ${complicated_command_suffix}"
        }
      }
    }
    catch (e) {
    emailext (
     // yadda yadda
            )
       throw e  
   }

Это работает, если я помещаю этот код непосредственно в свой Jenkinsfile вместо команды загрузки. Но очевидно, что load делает что-то более сложное, чем #include препроцессора C, потому что код не работает с load.

Сначала я получаю сообщение об ошибке, связанное с тем, что цели не определены (поскольку предположительно это выходит за рамки загруженного файла). Кто-то предложил добавить «env». в качестве префикса перед целями, и это устранило синтаксическую ошибку, но на самом деле этапы не выполняются.

Итак... как я могу правильно передать «цели» в загруженный файл, чтобы сгенерированные там этапы правильно обрабатывались? В качестве альтернативы, есть ли другие варианты, чтобы сделать эту работу лучше?

Изменить: я попытался вставить свои «цели определения» непосредственно в загруженный общий файл, и этапы обрабатываются правильно. Таким образом, проблема не в том, что загруженный файл не делает то, что должен, а просто в том, что он не получает значение «targets» из родительского Jenkinsfile.


person Matt    schedule 27.02.2020    source источник


Ответы (2)


Вот возможный способ рефакторинга, превращающий неявную зависимость от target в явную, что упрощает понимание и поддержку кода.

Шаблон Jenkinsfile:

def targets = [.....]
node {
   scm......

   processTargets = load "common/Jenkinsfile"
   processTargets( targets )
}

общий/Jenkinsfile:

void call( Map targets ) {
    try {
      targets.each {entry ->
        stage (entry.key) {
           sh "${complicated_command_prefix} $entry.value  ${complicated_command_suffix}"
        }
      }
    }
    catch (e) {
      emailext (
        // yadda yadda
      )
      throw e  
   }
}

// very important: return instance of the script class, which load() will return
return this  

Определив стандартную функцию call, объект, возвращаемый load, можно будет вызывать как функцию: processTargets( targets ). Если вы определяете функции с другими именами, вы можете вызывать их следующим образом:

common = load "common/Jenkinsfile"
common.myFunction1( foo )
common.myFunction2( bar )
person zett42    schedule 28.02.2020
comment
Это выглядит очень полезно, но мой ответ работает быстро, и хотя он грязный и плохой, он достаточно хорош для моих нужд. - person Matt; 09.09.2020

Догадаться! Я нашел это странное поведение области действия переменной в Jenkinsfile, которое объясняло, что если я пропущу def, переменная будет видна извне. И это. Так что теперь я определяю цели без def. Успех!

person Matt    schedule 27.02.2020
comment
Удалив def, вы создадите уродливую глобальную переменную. Я бы определил функцию в common/jenkinsfile и передал targets в качестве явного аргумента, чтобы прояснить зависимости. - person zett42; 28.02.2020
comment
Упс никогда не принимал ответ. Ваш ответ, вероятно, лучше, но в этом случае мне нужна быстрая и грязная версия, и моя сработала. - person Matt; 09.09.2020