如何告诉 Jenkins/Hudson 仅针对我的 Git 树中特定项目的更改触发构建?
9 回答
Git 插件有一个选项(排除区域)来使用正则表达式来确定是否根据提交中的文件是否与排除区域正则表达式匹配来跳过构建。
不幸的是,股票 Git 插件目前(1.15)没有“包含区域”功能。但是,有人在 GitHub 上发布了适用于 Jenkins 和 Hudson 的补丁,这些补丁实现了您想要的功能。
构建起来需要一点点工作,但它可以像宣传的那样工作,并且非常有用,因为我的一个 Git 树有多个独立的项目。
https://github.com/jenkinsci/git-plugin/pull/49
更新:Git 插件 (1.16) 现在具有“包含”区域功能。
如果您使用Jenkinsfile的声明性语法来描述您的构建管道,则可以使用变更集条件将阶段执行限制为仅在特定文件发生更改的情况下执行。这现在是Jenkins的标准功能,不需要任何额外的配置/软件。
stages {
stage('Nginx') {
when { changeset "nginx/*"}
steps {
sh "make build-nginx"
sh "make start-nginx"
}
}
}
anyOf
您可以使用orallOf
关键字来组合多个条件,以相应地实现OR或AND行为:
when {
anyOf {
changeset "nginx/**"
changeset "fluent-bit/**"
}
}
steps {
sh "make build-nginx"
sh "make start-nginx"
}
基本上,你需要两份工作。一种用于检查文件是否更改,另一种用于进行实际构建:
工作#1
这应该在您的 Git 存储库中发生更改时触发。然后它会测试您指定的路径(此处为“src”)是否发生了变化,然后使用Jenkins 的 CLI来触发第二个工作。
export JENKINS_CLI="java -jar /var/run/jenkins/war/WEB-INF/jenkins-cli.jar"
export JENKINS_URL=http://localhost:8080/
export GIT_REVISION=`git rev-parse HEAD`
export STATUSFILE=$WORKSPACE/status_$BUILD_ID.txt
# Figure out, whether "src" has changed in the last commit
git diff-tree --name-only HEAD | grep src
# Exit with success if it didn't
$? || exit 0
# Trigger second job
$JENKINS_CLI build job2 -p GIT_REVISION=$GIT_REVISION -s
工作 #2
配置此作业以获取参数 GIT_REVISION ,以确保您正在构建第一个作业选择构建的修订版。
虽然这不会影响单个作业,但如果最新提交不包含任何更改,您可以使用此脚本忽略某些步骤:
/*
* Check a folder if changed in the latest commit.
* Returns true if changed, or false if no changes.
*/
def checkFolderForDiffs(path) {
try {
// git diff will return 1 for changes (failure) which is caught in catch, or
// 0 meaning no changes
sh "git diff --quiet --exit-code HEAD~1..HEAD ${path}"
return false
} catch (err) {
return true
}
}
if ( checkFolderForDiffs('api/') ) {
//API folder changed, run steps here
}
如果选择文件的逻辑不是微不足道的,我会在每次更改时触发脚本执行,然后编写一个脚本来检查是否确实需要构建,如果是则触发构建。
您可以为此使用通用 Webhook 触发器插件。
使用变量 likechanged_files
和 expression $.commits[*].['modified','added','removed'][*]
。
您可以使用过滤文本$changed_files
和过滤正则表达式,例如"folder/subfolder/[^"]+?"
是否folder/subfolder
应该触发构建的文件夹。
如果有更改,我编写了此脚本以跳过或执行测试:
#!/bin/bash
set -e -o pipefail -u
paths=()
while [ "$1" != "--" ]; do
paths+=( "$1" ); shift
done
shift
if git diff --quiet --exit-code "${BASE_BRANCH:-origin/master}"..HEAD ${paths[@]}; then
echo "No changes in ${paths[@]}, skipping $@..." 1>&2
exit 0
fi
echo "Changes found in ${paths[@]}, running $@..." 1>&2
exec "$@"
因此,您可以执行以下操作:
./scripts/git-run-if-changed.sh cmd vendor go.mod go.sum fixtures/ tools/ -- go test
对于Bitbucket 存储库用户(以及其他使用源代码控制管理主机的人,其 webhook 有效负载似乎没有指示文件更改)。
似乎 Git 插件“包含的区域”无论我做什么都会失败,并且总是触发这项工作。我的设置是 Jenkins 2.268,在 Docker 容器中运行,要找到一种根据文件更改来实现构建作业的正确方法是炼狱,但下面是一个。
所需的 Jenkins 插件:
- 时髦的
- Bitbucket(或者,如果您在另一个 SCM 主机上:可以触发在该主机的 webhook 上构建的插件)
创建一个名为“Switch”的新Freestyle作业:
- 源代码管理:指明您的 SCM 信息(确保“要构建的分支”是正确的。
- 构建触发器>将更改推送到 Bitbucket 时构建:选中
- 构建步骤>执行系统 Groovy 脚本(不仅仅是执行 Groovy 脚本!),未选中使用 Groovy 沙箱。
剧本:
import jenkins.*;
import jenkins.model.*;
// CONFIGURATION
// Links between changed file patterns and job names to build
def jobsByPattern = [
"/my-project/": "my-project-job",
"my-super-project/":"super-job",
]
// Listing changes files since last build
def changeLogSets = build.changeSets
def changedFiles = []
for (int i = 0; i < changeLogSets.size(); i++) {
def entries = changeLogSets[i].items
for (int j = 0; j < entries.length; j++) {
def entry = entries[j]
def files = new ArrayList(entry.affectedFiles)
for (int k = 0; k < files.size(); k++) {
def file = files[k]
changedFiles.add(file.path)
}
}
}
// Listing ad hoc jobs to build
jobsToBuild = [:] // declare an empty map
for(entry in jobsByPattern ) {
def pattern = entry.key
println "Check pattern: $pattern"
for (int i = 0; i < changedFiles.size(); i++) {
def file = changedFiles[i]
println "Check file: $file"
if( file.contains( pattern ) ) {
def jobName = entry.value
jobsToBuild[ jobName ] = true
break
}
}
}
// Building appropriate jobs
jobsToBuild.each{
def jobName = it.key
println "$jobName must be built!"
def job = Jenkins.instance.getJob(jobName)
def cause = new hudson.model.Cause.UpstreamCause(build)
def causeAction = new hudson.model.CauseAction(cause)
Jenkins.instance.queue.schedule(job, 0, causeAction)
}
我相信这种方法可以处理自上次构建以来的多次提交,因此它似乎满足了需求。欢迎任何改进建议。
我在另一篇文章中回答了这个问题:
如何获取自上次在 Jenkins/Hudson 中构建以来更改的文件列表
#!/bin/bash
set -e
job_name="whatever"
JOB_URL="http://myserver:8080/job/${job_name}/"
FILTER_PATH="path/to/folder/to/monitor"
python_func="import json, sys
obj = json.loads(sys.stdin.read())
ch_list = obj['changeSet']['items']
_list = [ j['affectedPaths'] for j in ch_list ]
for outer in _list:
for inner in outer:
print inner
"
_affected_files=`curl --silent ${JOB_URL}${BUILD_NUMBER}'/api/json' | python -c "$python_func"`
if [ -z "`echo \"$_affected_files\" | grep \"${FILTER_PATH}\"`" ]; then
echo "[INFO] no changes detected in ${FILTER_PATH}"
exit 0
else
echo "[INFO] changed files detected: "
for a_file in `echo "$_affected_files" | grep "${FILTER_PATH}"`; do
echo " $a_file"
done;
fi;
您可以将检查直接添加到作业的 exec shell 的顶部,exit 0
如果未检测到任何更改,它会...因此,您始终可以轮询顶层以检查签入以触发构建。