Создать повторно используемый сценарий конвейера 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/