Jenkins + Git:仅当PR在子目录中引入更改后才能构建

时间:2019-10-05 20:21:17

标签: git jenkins jenkins-pipeline multibranch-pipeline

我们有一个大型的monorepo,其中包含多个项目(A和B)。我目前将Jenkins设置为Multibranch Pipelines项目,该项目监视PR的monorepo。如果创建了PR,Jenkins将同时构建A和B。

现在,我希望Jenkins更聪明,并且仅在PR中的任何更改在A/目录中进行了更改的情况下,才构建项目A。事实证明,这非常困难。

when { changeset "A/" }仅显示检查最后一次提交是否更改了A/中的文件,而不检查 PR 是否更改了{{ 1}}。

因此,我使用https://issues.jenkins-ci.org/browse/JENKINS-54285使其变得更聪明,并且做到了:

A/

其中when { expression { return sourceChanged("A/") } }定义为:

sourceChanged

但是,无论我如何尝试,都无法获得CHANGE_TARGET的提交哈希。我总是会遇到以下错误:

def boolean sourceChanged(String module) {
    if (env.CHANGE_TARGET == null)
        return true;

    def MASTER = sh(returnStdout: true, script: "git rev-parse origin/${env.CHANGE_TARGET}").trim()
    def HEAD = sh(returnStdout: true, script: "git show -s --no-abbrev-commit --pretty=format:%P%n%H%n HEAD | tr ' ' '\n' | grep -v ${MASTER} | head -n 1").trim()

    return sh(returnStatus: true, script: "git diff --exit-code --name-only ${MASTER}...${HEAD} {module}") == 1;
}

为什么Git无法找到git rev-parse origin/master fatal: ambiguous argument 'origin/master': unknown revision or path not in the working tree. masterorigin/master等(我都尝试了)?有没有更简单的方法可以完成我想做的事情?


我正在使用来自docker hub的refs/head/master以及BitBucket Branch Source插件。

以下是相关的詹金斯记录顺序,如果有帮助的话:

jenkins/jenkins:lts

我要访问的是Jenkinsfile中的两个相关提交ID:Fetching changes from 2 remote Git repositories > git config remote.origin.url http://bitbucket.ccm.com:7990/scm/JUP/jt.git # timeout=10 Fetching without tags Fetching upstream changes from http://bitbucket.ccm.com:7990/scm/JUP/jt.git > git --version # timeout=10 using GIT_ASKPASS to set credentials > git fetch --no-tags --progress -- http://bitbucket.ccm.com:7990/scm/JUP/jt.git +refs/pull-requests/9/from:refs/remotes/origin/PR-9 > git config remote.upstream.url http://bitbucket.ccm.com:7990/scm/JUP/jt.git # timeout=10 Fetching without tags Fetching upstream changes from http://bitbucket.ccm.com:7990/scm/JUP/jt.git using GIT_ASKPASS to set credentials > git fetch --no-tags --progress -- http://bitbucket.ccm.com:7990/scm/JUP/jt.git +refs/heads/master:refs/remotes/upstream/master Merging remotes/upstream/master commit 7ef64efeb0fb19d8931a684f147666ae681b4ddf into PR head commit 47600816c0dca3e5555e417085ab2052453a39b2 Enabling Git LFS pull > git config core.sparsecheckout # timeout=10 > git checkout -f 47600816c0dca3e5555e417085ab2052453a39b2 > git config --get remote.origin.url # timeout=10 using GIT_ASKPASS to set credentials > git lfs pull origin > git merge 7ef64efeb0fb19d8931a684f147666ae681b4ddf # timeout=10 > git rev-parse HEAD^{commit} # timeout=10 Merge succeeded, producing 47600816c0dca3e5555e417085ab2052453a39b2 Checking out Revision 47600816c0dca3e5555e417085ab2052453a39b2 (PR-9) Enabling Git LFS pull > git config core.sparsecheckout # timeout=10 > git checkout -f 47600816c0dca3e5555e417085ab2052453a39b2 > git config --get remote.origin.url # timeout=10 using GIT_ASKPASS to set credentials > git lfs pull origin Commit message: "l" [Pipeline] withEnv [Pipeline] { [Pipeline] sh + docker inspect -f . registry.ccm.com:7991/jt:1.0 . [Pipeline] withDockerContainer Jenkins seems to be running inside container fdc7e8eec5ea708e59cebe4682651bc5192478b95de803b5981edd222f39af97 $ docker run -t -d -u 1000:979 -v $PWD:/build_env -v $HOME/.ssh:/home/docker_user/.ssh -w /build_env --add-host civm3:10.33.67.183 -e UNIX_USER=jtbuild -w /var/jenkins_home/workspace/jt_PR-9@2 --volumes-from fdc7e8eec5ea708e59cebe4682651bc5192478b95de803b5981edd222f39af97 -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** registry.ccm.com:7991/jt:1.0 cat $ docker top c7bb23bbc91119c2b1875ab2a9186ae34da1754f2b8ae42f758594227ff77137 -eo pid,comm [Pipeline] { [Pipeline] sh + git rev-parse origin/master fatal: ambiguous argument 'origin/master': unknown revision or path not in the working tree. 7ef64efeb0fb19d8931a684f147666ae681b4ddf

2 个答案:

答案 0 :(得分:2)

好,我终于解决了。

Jenkins似乎进行了 bare 克隆(如果我使用的术语不正确,请纠正我),这意味着您将无法访问任何引用,除非您特别地获取它们。因此,您将无权访问本地或远程分支机构的名称。

键在日志的这两行中:

> git fetch --no-tags --progress -- http://bitbucket.ccm.com:7990/scm/JUP/jt.git +refs/pull-requests/9/from:refs/remotes/origin/PR-9 
> git fetch --no-tags --progress -- http://bitbucket.ccm.com:7990/scm/JUP/jt.git +refs/heads/master:refs/remotes/upstream/master

以下是上述两个命令的缩写版本:

> git fetch the PR ref, store it as 'origin/PR-9'
> git fetch master ref, store it as 'upstream/master'

因此,这两个感兴趣的提交存储在origin/PR-9upstream/master中。

方便地,Jenkins环境变量BRANCH_NAMECHANGE_TARGET分别包含PR-9master

因此,Jenkins文件应使用以下内容:

def boolean sourceChanged(String module) {
    def target_branch = env.CHANGE_TARGET;
    def pr_ref        = env.BRANCH_NAME;

    if (target_branch == null) {
        echo "No target branch defined...";
        return true;
    }

    def TARGET = sh(returnStdout: true, script: "git rev-parse upstream/${target_branch}").trim()
    def HEAD   = sh(returnStdout: true, script: "git rev-parse origin/${pr_ref}").trim()

    echo "Checking for source changes between ${TARGET} (${target_branch}) and ${HEAD} (${pr_ref})...";
    return sh(returnStatus: true, script: "git diff --exit-code --name-only ${TARGET}...${HEAD} {module}") == 1;
}

结合,即:

when { expression { return sourceChanged("A/") } }

按以下方式检查多个目录中的差异:

def SOURCE_DIRS = [
    "A/",
    "X/"
];
...
when { expression { return sourceChanged(SOURCE_DIRS) } }
...
def sourceChanged(ArrayList<String> source_dirs) {
    def target_branch = env.CHANGE_TARGET;
    def pr_ref        = env.BRANCH_NAME;

    if (target_branch == null) {
        echo "No target branch defined...";
        return true;
    }

    def TARGET = sh(returnStdout: true, script: "git rev-parse upstream/${target_branch}").trim()
    def HEAD   = sh(returnStdout: true, script: "git rev-parse origin/${pr_ref}").trim()

    echo "Checking for source changes between ${TARGET} (${target_branch}) and ${HEAD} (${pr_ref})...";
    for (String dir : source_dirs) {
        def rc = sh(returnStatus: true, script: "git diff --name-only --exit-code ${TARGET}...${HEAD} ${dir}");
        if (rc == 1) {
            echo "Changes detected in ${dir}!";
            return true;
        }
    }

    echo "No changes detected.";
    return false;
}

答案 1 :(得分:0)

您还可以使用 Configuration > Branch Sources > Behaviours 为您的多分支项目指定其他参考规范。 Add > Specify ref specs,这将使您需要的任何分支名称可用。

请参阅此答案以获取屏幕截图:https://stackoverflow.com/a/63267947/64505