【问题标题】:How to trigger a build only if changes happen on particular set of files仅当特定文件集发生更改时如何触发构建
【发布时间】:2011-07-11 17:52:12
【问题描述】:

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

【问题讨论】:

    标签: git hudson jenkins


    【解决方案1】:

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

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

    构建起来需要一点工作,但它可以像宣传的那样工作,并且非常有用,因为我的一个 Git 树有多个独立的项目。

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

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

    【讨论】:

    • 1.1.16 是包含功能的正确版本号。 (没有1.16)
    • 我无法让它工作,我有一个包含多个模块(域、通用、api、desktop_app,...)的存储库,例如,我想为 desktop_app 触发构建,我把“包含区域”production_app/*,我尝试了几种组合,例如 ./desktop_app 甚至是绝对路径。我总是得到Ignored commit c6e2b1dca0d1885: No paths matched included region whitelist。有什么线索吗?更多细节在这里:stackoverflow.com/questions/47439042/…
    • Bitbucket 用户:我可能错了,但这种方法似乎不起作用。我正在发布一个可行的解决方案。
    【解决方案2】:

    如果您使用 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"
    }
    

    【讨论】:

    • 请记住,它在某些情况下不起作用。详情请参阅issues.jenkins-ci.org/browse/JENKINS-26354
    • 另外请记住,当您在多模块构建(例如 Maven 反应器)中使用此功能时,它不会解析和构建这些模块之间的依赖关系。为了实现这一点,您宁愿需要一个能够理解您的构建和依赖关系的构建插件。
    • 如果你想在两个特定分支之间生成变更集(例如,如果你正在处理一个拉请求,你会有源分支和目标分支),你可以使用 Jenkins 的流水线语法片段生成器,选择名为“签出:从版本控制签出”的示例步骤,然后在“其他行为”下,单击“添加”并添加名为“针对特定分支计算更改日志”的行为。然后,当您填写字段并单击“生成管道脚本”时,将为您配置“ChangelogToBranch”选项(它将相应地填充变更集)。
    【解决方案3】:

    基本上,您需要两份工作。一个检查文件是否更改,一个进行实际构建:

    工作 #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 参数,以确保您构建的正是第一个作业选择构建的修订版。

    【讨论】:

    • 如果自上次构建以来发生了两次或多次提交怎么办?我认为您可能会错过 src 中的更改,因为您只是在检查 HEAD 提交。
    • @AdamMonsen 对。但是您可以轻松地将上述脚本调整为您想要测试的任何情况/条件。例如,不是针对 HEAD,而是针对上次脚本运行时的 HEAD。
    • $? || exit 0 处缺少某些东西...test $? -eq 0 || exit 0 可能吗?
    • jenkins-cli.jar 在我的公司出于安全原因被禁用,我如何在 源代码管理存储库 URL 上指定 GIT-REVISION > 詹金斯的部分?对于 SVN,我们可以在 url 后面加上 @${repo_rev}.
    【解决方案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
    }
    

    【讨论】:

    • @Karl 如果代码适合您,请随时修复代码。这是我在应用此代码时遇到的一个问题(在构建失败时,它不会重试此提交,如果绝对最新的提交也没有更改 api/ 文件夹。)如果你能解决这个问题,我会喜欢建议的更改!
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      您可以为此使用Generic Webhook Trigger Plugin

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

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

      【讨论】:

      • 我正在尝试这样做,但我有点迷路了。如何将更改文件的路径发送给詹金斯?你能解释一下吗?将变量 changed_files 放在哪里?
      • 你需要在你使用的 Git 服务中配置一个 webhook。如果是 GitHub,这里有一个例子:github.com/jenkinsci/generic-webhook-trigger-plugin/blob/master/…
      • 事实上,当我使用 Bitbucket 时,我意识到 Changed_files 条目在 bibucket 中推送事件的有效负载中不可用(参考:confluence.atlassian.com/bitbucket/…)所以我不确定我该怎么做这。我将依赖我认为的提交消息。谢谢
      • @Souad :这里的问题完全相同。我找到了一种方法,我在这里发布了。有帮助吗?
      【解决方案7】:

      如果有变化,我编写了这个脚本来跳过或执行测试:

      #!/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】:

        适用于 Bitbucket 存储库用户(以及其他使用源代码控制管理主机且 webhook 有效负载似乎未指示文件更改的人)。

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

        必需的 Jenkins 插件:

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

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

        1. 源代码管理:指明您的 SCM 信息(确保“要构建的分支”是正确的。
        2. 构建触发器 > 将更改推送到 Bitbucket 时构建:选中
        3. 构建步骤 > 执行系统 ​​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)
        }
        

        我相信这种方法可以处理自上次构建以来的多次提交,因此它似乎满足了需求。欢迎任何改进建议。

        【讨论】:

          【解决方案9】:

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

          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...因此,您始终可以轮询顶层以进行签入以触发构建。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-10-17
            • 1970-01-01
            • 2015-02-07
            • 2010-11-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-02-02
            相关资源
            最近更新 更多