Jenkins + Git:仅当 PR 在子目录中引入更改时才构建 [英] Jenkins + Git: Only build if PR introduced changes in subdirectory

查看:11
本文介绍了Jenkins + Git:仅当 PR 在子目录中引入更改时才构建的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个大型 monorepo,其中包含多个项目(A 和 B).我目前将 Jenkins 设置为一个多分支流水线项目,用于监视 PR 的 monorepo.如果创建了 PR,Jenkins 会同时构建 A 和 B.

We have a large monorepo with multiple projects (A and B) inside of it. I currently have Jenkins setup as a Multibranch Pipelines project that watches the monorepo for PRs. If a PR is created, Jenkins builds both A and B.

现在,我希望 Jenkins 更聪明,并且仅在 PR 中的任何更改引入了 A/ 目录的更改时才构建项目 A.事实证明这非常困难.

Now, I want Jenkins to be smarter and only build project A if any change in the PR introduced a change in the A/ directory. This is proving very difficult.

when { changeset "A/" } 仅用于检查 last commit 是否更改了 A/ 中的文件,而不是PR 更改了 A/ 中的文件.

when { changeset "A/" } only appears to check if the last commit changed a file in A/, not if the PR changed a file in A/.

所以我使用 https://issues.jenkins-ci.org/让它变得更智能浏览/JENKINS-54285 并做了:

So I made it smarter using https://issues.jenkins-ci.org/browse/JENKINS-54285 and did:

when { 表达式 { return sourceChanged("A/") } }

sourceChanged 定义为:

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 ' ' '
' | grep -v ${MASTER} | head -n 1").trim()

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

但是,无论我尝试什么,我都无法获得 CHANGE_TARGET 的提交哈希.我总是遇到以下错误:

However, no matter what I try, I can't get a commit hash for the CHANGE_TARGET. I always get something along the following error:

git rev-parse origin/master
fatal: ambiguous argument 'origin/master': unknown revision or path not in the working tree.

为什么 Git 找不到 masterorigin/masterrefs/head/master 等(我都试过了)?有没有更简单的方法来完成我想做的事情?

Why isn't Git able to find master, origin/master, refs/head/master, etc (I tried them all)? Is there an easier way to accomplish what I am trying to do?

我正在使用来自 docker hub 的 jenkins/jenkins:lts 以及 BitBucket Branch Source 插件.

I'm using jenkins/jenkins:lts from docker hub as well as the BitBucket Branch Source plugin.

如果有帮助,这里是相关的 Jenkins 日志序列:

Here is the relevant Jenkins log sequence, if it helps:

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.

我只想访问 Jenkinsfile 中的两个相关提交 ID:7ef64efeb0fb19d8931a684f147666ae681b4ddf47600816c0dca3e5555e417085ab2052453a39b2

All I want is access to the two relevant commit ids in the Jenkinsfile: 7ef64efeb0fb19d8931a684f147666ae681b4ddf and 47600816c0dca3e5555e417085ab2052453a39b2!

推荐答案

好的,我终于解决了.

看来(如果我的术语不正确,请纠正我)Jenkins 做了所谓的 bare 克隆,这意味着除非您专门获取它们,否则您将无法访问任何参考.因此,您将无法访问本地或远程分支名称.

It appears (please correct me if I have the terminology incorrect) that Jenkins does what's called a bare clone which means you won't have access to any refs unless you specifically fetch them. Thus, you will not have access to your branch names, local or remote.

关键在日志的这两行:

> 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

以下是上述两个命令的简短注释版本:

Here is a shortened, annotated version of those above two commands:

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

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

Thus, the two commits of interest are stored in origin/PR-9 and upstream/master.

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

Conveniently, the Jenkins environment variables BRANCH_NAME and CHANGE_TARGET contain PR-9 and master respectively.

因此,Jenkinsfile 应该使用以下内容:

Thus, the Jenkinsfile should use the following:

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

检查多个目录中的差异可以这样完成:

Checking for diffs in multiple directories would be accomplished as such:

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

这篇关于Jenkins + Git:仅当 PR 在子目录中引入更改时才构建的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆