仅当特定文件集发生更改时如何触发构建

发布于 2024-10-20 18:20:16 字数 49 浏览 2 评论 0原文

如何告诉 Jenkins/Hudson 仅针对 Git 树中特定项目的更改触发构建?

How do I tell Jenkins/Hudson to trigger a build only for changes on a particular project in my Git tree?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

蓝海 2024-10-27 18:20:17

为此,您可以使用 通用 Webhook 触发器插件

使用 changed_files 之类的变量和表达式 $.commits[*].['modified','added','removed'][*]

您可以使用类似 $changed_files 的过滤器文本和类似 "folder/subfolder/[^"]+?" if folder/subfolder 的过滤器正则表达式是应该触发构建的文件夹。

You can use Generic Webhook Trigger Plugin for this.

With a variable like changed_files and expression $.commits[*].['modified','added','removed'][*].

You can have a filter text like $changed_files and filter regexp like "folder/subfolder/[^"]+?" if folder/subfolder is the folder that should trigger builds.

苏璃陌 2024-10-27 18:20:17

我在另一篇文章中回答了这个问题:

如何获取自上次在 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 的顶部,它会< code>exit 0 如果没有检测到任何更改...因此,您始终可以轮询顶层以进行签入以触发构建。

I answered this question in another post:

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;

You can add the check directly to the top of the job's exec shell, and it will exit 0 if no changes are detected... Hence, you can always poll the top level for check-in's to trigger a build.

谷夏 2024-10-27 18:20:16

如果您使用Jenkinsfile的声明性语法来描述您的构建管道,则可以使用changeset条件将阶段执行限制为以下情况:特定文件已更改。现在,这是 Jenkins 的标准功能,不需要任何额外的配置/软件。

stages {
    stage('Nginx') {
        when { changeset "nginx/*"}
        steps {
            sh "make build-nginx"
            sh "make start-nginx"
        }
    }
}

您可以使用 anyOfallOf 关键字组合多个条件,以实现 ORAND 相应行为:

when {
    anyOf {
        changeset "nginx/**"
        changeset "fluent-bit/**"
    }
}
steps {
    sh "make build-nginx"
    sh "make start-nginx"
}

If you are using a declarative syntax of Jenkinsfile to describe your building pipeline, you can use changeset condition to limit stage execution only to the case when specific files are changed. This is now a standard feature of Jenkins and does not require any additional configruation/software.

stages {
    stage('Nginx') {
        when { changeset "nginx/*"}
        steps {
            sh "make build-nginx"
            sh "make start-nginx"
        }
    }
}

You can combine multiple conditions using anyOf or allOf keywords for OR or AND behaviour accordingly:

when {
    anyOf {
        changeset "nginx/**"
        changeset "fluent-bit/**"
    }
}
steps {
    sh "make build-nginx"
    sh "make start-nginx"
}
苦笑流年记忆 2024-10-27 18:20:16

Git 插件有一个选项(排除区域),可以使用正则表达式根据提交中的文件是否与排除区域正则表达式匹配来确定是否跳过构建。

不幸的是,现有的 Git 插件目前没有“包含区域”功能(1.15)。然而,有人在 GitHub 上发布了适用于 Jenkins 和 Hudson 的补丁,可以实现您想要的功能。

构建它的工作量很小,但它的工作原理正如宣传的那样,并且非常有用,因为我的一个 Git 树有多个独立的项目。

https://github.com/jenkinsci/git-plugin/pull/49

更新:Git 插件 (1.16) 现在具有“包含”区域功能。

The Git plugin has an option (excluded region) to use regexes to determine whether to skip building based on whether files in the commit match the excluded region regex.

Unfortunately, the stock Git plugin does not have a "included region" feature at this time (1.15). However, someone posted patches on GitHub that work on Jenkins and Hudson that implement the feature you want.

It is a little work to build, but it works as advertised and has been extremely useful since one of my Git trees has multiple independent projects.

https://github.com/jenkinsci/git-plugin/pull/49

Update: The Git plugin (1.16) now has the 'included' region feature.

羞稚 2024-10-27 18:20:16

基本上,你需要两份工作。一项用于检查文件是否更改,一项用于执行实际构建:

作业 #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 ,如下所示,以确保您正在构建的版本与第一个作业选择构建的版本完全相同。

参数化构建字符串参数
参数化构建 Git checkout

Basically, you need two jobs. One to check whether files changed and one to do the actual build:

Job #1

This should be triggered on changes in your Git repository. It then tests whether the path you specify ("src" here) has changes and then uses Jenkins' CLI to trigger a second job.

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

Job #2

Configure this job to take a parameter GIT_REVISION like so, to make sure you're building exactly the revision the first job chose to build.

Parameterized build string parameter
Parameterized build Git checkout

迟到的我 2024-10-27 18:20:16

虽然这不会影响单个作业,但如果最新提交不包含任何更改,您可以使用此脚本忽略某些步骤:

/*
 * 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
}

While this doesn't affect single jobs, you can use this script to ignore certain steps if the latest commit did not contain any changes:

/*
 * 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
}
孤寂小茶 2024-10-27 18:20:16

对于 Bitbucket 存储库用户(以及使用源代码控制管理主机的其他人,其 Webhook 负载似乎不指示文件更改)。

无论我做什么,Git 插件“包含区域”似乎都会失败,并且总是触发该作业。我的设置是 Jenkins 2.268,在 Docker 容器中运行,找到一种正确的方法来根据文件更改来实现构建作业简直就是炼狱,但下面是一个。

所需的 Jenkins 插件:

  • Groovy
  • Bitbucket(或者,如果您在另一台 SCM 主机上:可以在该主机的 Webhooks 上触发构建的插件)

创建一个新的 Freestyle 名为“Switch”的作业:

  1. 源代码管理:指示您的 SCM 信息(确保“要构建的分支”是正确的。
  2. 构建触发器 > >) 将更改推送到 Bitbucket 时进行构建:选中
  3. 构建步骤 > 执行系统 ​​Groovy 脚本(不仅仅是执行 Groovy 脚本!),留下 < em>未选中使用 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)
}

我相信此方法可以处理自上次构建以来的多次提交,因此它似乎可以满足任何增强建议。

For Bitbucket Repository users (and other people using Source-Control Management hosts which webhook payload doesn't seem to indicate file changes).

It seems that Git plugin 'included regions' fail whatever I do, and always trigger the job. My setup is Jenkins 2.268, run in a Docker container, and it was purgatory to find a correct way to achieve building jobs depending on file changes, but here's one below.

Required Jenkins plugins:

  • Groovy
  • Bitbucket (or, if you're on another SCM host: a plugin which can trigger builds on this host's webhooks)

Create a new Freestyle job called 'Switch':

  1. Source Code Management: indicate your SCM information (make sure the 'Branches to build' are the right ones.
  2. Build triggers > Build when a change is pushed to Bitbucket: checked
  3. Build step > Execute system Groovy script (not just Execute Groovy script!), leaving Use Groovy sandbox unchecked.

The script:

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)
}

I believe this method can handle multiple commits since the last build, so it seems to answer the need. Any suggestion of enhancement welcomed.

怎会甘心 2024-10-27 18:20:16

我编写此脚本是为了在发生更改时跳过或执行测试:

#!/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 cmdvendor go.mod go.sum Fixtures/tools/ - - 去测试

I wrote this script to skip or execute tests if there are changes:

#!/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 "$@"

So you can do something like:

./scripts/git-run-if-changed.sh cmd vendor go.mod go.sum fixtures/ tools/ -- go test

玉环 2024-10-27 18:20:16

如果选择文件的逻辑并不简单,我会在每次更改时触发脚本执行,然后编写一个脚本来检查是否确实需要构建,如果需要则触发构建。

If the logic for choosing the files is not trivial, I would trigger script execution on each change and then write a script to check if indeed a build is required, then triggering a build if it is.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文