Создать повторно используемый сценарий конвейера jenkins

Я недавно решил использовать сценарий конвейера Jenkins. Один из вопросов заключается в том, что я не могу придумать, как правильно создавать внутренний код повторного использования, представьте, у меня есть общая функция. helloworld который будет использоваться многими конвейерными заданиями, поэтому я надеюсь создать utils.jar может ввести его в задание classpath.

Я заметил, что у Дженкинса похожая концепция с глобальной библиотекой, но моя проблема в отношении этого плагина:

Поскольку это плагин, поэтому нам нужно установить / обновить его через менеджер плагинов jenkins, тогда для применения изменений может потребоваться перезагрузка, это не то, что я хочу видеть, так как утилиты могут меняться, добавлять всегда, мы надеемся, что это может быть доступно немедленно.

Во-вторых, это официальная библиотека jenkins shared lib, я не хочу (или они не будут применять нас) помещать закрытый код в репозиторий jenkins.

Любая хорошая идея?

3 ответа

Решение

Общие библиотеки ( документы) позволяют сделать ваш код доступным для всех ваших конвейерных сценариев. Вам не нужно создавать плагин для этого, и вам не нужно перезапускать Jenkins.

Например, это моя библиотека, а это Jenkinsfile, который вызывает эту общую функцию.


РЕДАКТИРОВАТЬ (февраль 2017 г.): доступ к библиотеке можно получить через внутренний Git-сервер Jenkins или развернуть с помощью других средств (например, через Chef) на workflow-lib/ каталог в домашнем каталоге пользователя jenkins. (все еще возможно, но очень неудобно).

Глобальная библиотека может быть настроена с помощью следующих средств:

  • @Library('github.com/...') аннотация в Jenkinsfile указывая на URL-адрес общей библиотеки репо.
  • настроен на уровне папок заданий Jenkins.
  • настроен в конфигурации Jenkins как глобальная библиотека с тем преимуществом, что код является доверенным, т.е. не подлежит защите скриптов.

Смесь первого и последнего метода будет представлять собой не загруженную явно разделяемую библиотеку, которая затем запрашивается только с использованием ее имени в Jenkinsfile: @Library('mysharedlib'),

В зависимости от того, как часто вы планируете использовать код повторно, вы также можете загрузить функцию (или набор функций) как часть другого конвейера.

{
  // ...your pipeline code...

  git 'http://urlToYourGit/projectContainingYourScript'
  pipeline = load 'global-functions.groovy'
  pipeline.helloworld()    // Call one of your defined function

  // ...some other pipeline code...
}

Это решение может показаться немного громоздким по сравнению с решением StephenKing, но что мне нравится в этом решении, так это то, что все мои глобальные функции переданы в Git, и любой может легко изменить их без (почти) какого-либо знания Дженкинса, только основы Groovy.

В скрипте Groovy вы находитесь loadИнг, обязательно добавьте return this в самом конце. Это позволит вам звонить позже. В противном случае, когда вы установите pipeline = load global-functions.groovyпеременная будет установлена ​​в null,

Вот решение, которое мы используем в настоящее время для повторного использования кода Jenkinsfile:

node {

  curl_cmd = "curl -H 'Accept: application/vnd.github.v3.raw' -H 'Authorization: token ${env.GITHUB_TOKEN}' https://raw.githubusercontent.com/example/foobar/master/shared/Jenkinsfile > Jenkinsfile.t
  sh "${curl_cmd}"
  load 'Jenkinsfile.tmp'

}

Я мог бы быть немного уродливым, но он работает реально и в дополнение к этому он также позволяет нам вставлять некоторый специфичный для репозитория код до или после общего кода.

Я предпочитаю создавать buildRepo()метод, который я вызываю из репозиториев. Его подпись def call(givenConfig = [:]) так что он также может быть вызван с параметрами, например:

buildRepo([
  "npm": [
    "cypress": false
  ]
])

Я использую минимальные параметры и стараюсь полагаться на соглашения, а не на конфигурацию.

Я предоставляю конфигурацию по умолчанию, в которую также помещаю документацию:

  def defaultConfig = [
    /**
      * The Jenkins node, or label, that will be allocated for this build.
      */
    "jenkinsNode": "BUILD",
    /**
      * All config specific to NPM repo type.
      */
    "npm": [
      /**
        * Whether or not to run Cypress tests, if there are any.
        */
      "cypress": true
    ]
  ]
  def effectiveConfig merge(defaultConfig, givenConfig)
  println "Configuration is documented here: https://whereverYouHos/getConfig.groovy"
  println "Default config: " + defaultConfig
  println "Given config: " + givenConfig
  println "Effective config: " + effectiveConfig

Используя эффективную конфигурацию и содержимое репозитория, я создаю план сборки.

...
derivedBuildPlan.npm.cypress = effectiveConfig.npm.cypress && packageJSON.devDependencies.cypress
...

План сборки - это входные данные для некоторых методов сборки, например:

  node(buildPlan.jenkinsNode) {
    stage("Install") {
      sh "npm install"
    }
    stage("Build") {
      sh "npm run build"
    }
    if (buildPlan.npm.tslint) {
      stage("TSlint") {
        sh "npm run tslint"
      }
    }
    if (buildPlan.npm.eslint) {
      stage("ESlint") {
        sh "npm run eslint"
      }
    }
    if (buildPlan.npm.cypress) {
      stage("Cypress") {
        sh "npm run e2e:cypress"
      }
    }
  }

Я написал об этом сообщение в блоге на Jenkins.io:https://www.jenkins.io/blog/2020/10/21/a-sustainable-pattern-with-shared-library/

Другие вопросы по тегам