如何告诉Jenkins / Hudson仅针对我的Git树中特定项目的更改触发构建?
答案 0 :(得分:60)
Git插件有一个选项(排除区域),可以使用正则表达式来确定是否根据提交中的文件是否与排除的区域正则表达式匹配来跳过构建。
不幸的是,股票Git插件没有"包含的区域"此时的功能(1.15)。但是,有人在GitHub上发布了一些补丁,这些补丁可以在Jenkins和Hudson上运行,实现你想要的功能。
构建它是一项小工作,但它的工作方式与广告一样,并且非常有用,因为我的一个Git树有多个独立项目。
https://github.com/jenkinsci/git-plugin/pull/49
更新:Git插件(1.16)现在包含'包含'区域特征。
答案 1 :(得分:29)
基本上,你需要两份工作。一个用于检查文件是否已更改,另一个用于执行实际构建:
工作#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,以确保您正在构建第一个作业选择构建的修订版。
答案 2 :(得分:10)
如果您使用 Jenkinsfile 的声明性语法来描述您的构建管道,则可以使用 changeset 条件将阶段执行限制为更改特定文件的情况。现在,这是Jenkins的标准功能,不需要任何其他配置/软件。
stages {
stage('Nginx') {
when { changeset "nginx/*"}
steps {
sh "make build-nginx"
sh "make start-nginx"
}
}
}
您可以使用anyOf
或allOf
关键字组合多个条件,以实现 OR 或 AND 行为:
when {
anyOf {
changeset "nginx/**"
changeset "fluent-bit/**"
}
}
steps {
sh "make build-nginx"
sh "make start-nginx"
}
答案 3 :(得分:4)
虽然这不会影响单个作业,但如果最新提交不包含任何更改,则可以使用此脚本忽略某些步骤:
/*
* 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
}
答案 4 :(得分:2)
如果选择文件的逻辑不是很简单,我会在每次更改时触发脚本执行,然后编写一个脚本来检查是否确实需要构建,然后触发构建(如果是的话)。
答案 5 :(得分:1)
我在另一篇文章中回答了这个问题:
How to get list of changed files since last build in 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
...因此,您始终可以轮询顶级检查 - 用于触发构建。
答案 6 :(得分:1)
您可以使用Generic Webhook Trigger Plugin。
使用changed_files
和表达式$.commits[*].['modified','added','removed'][*]
之类的变量。
如果$changed_files
是应触发构建的文件夹,则可以使用"folder/subfolder/[^"]+?"
等过滤器文本和过滤器regexp,如folder/subfolder
。
答案 7 :(得分:1)
我编写了此脚本以跳过或执行测试(如果有更改):
#!/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
答案 8 :(得分:1)
对于Bitbucket Repository 用户(以及其他使用源代码控制管理主机但 webhook 负载似乎没有表明文件更改的人)。
似乎无论我做什么,Git 插件“包含的区域”都会失败,并且总是触发作业。我的设置是 Jenkins 2.268,在 Docker 容器中运行,根据文件更改找到一种正确的方法来实现构建作业是炼狱,但下面是一个。
所需的 Jenkins 插件:
创建一个名为“Switch”的新Freestyle作业:
脚本:
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)
}
我相信这个方法可以处理自上次构建以来的多次提交,所以它似乎可以满足需求。欢迎提出任何改进建议。