在Jenkinsfile中切换到不同的分支是否不可能?

26

我在 BitBucket 上有两个分支:masterdevelop。我还在我的 Jenkins 服务器上配置了一个 BitBucket 团队文件夹作业来构建该代码库。在 develop 分支上有以下的 Jenkinsfile:

node {
    stage('Checkout') {
        checkout scm
    }

    stage('Try different branch') {
        sh "git branch -r"
        sh "git checkout master"
    }
}

当Jenkins运行时,当它尝试检出master时,构建失败:

[Pipeline] stage
[Pipeline] { (Try different branch)
[Pipeline] sh
[e_jenkinsfile-tests_develop-4R65E2H6B73J3LB52BLACQOZLBJGN2QG22IPONX3CV46B764LAXA] Running shell script
+ git branch -r
  origin/develop
[Pipeline] sh
[e_jenkinsfile-tests_develop-4R65E2H6B73J3LB52BLACQOZLBJGN2QG22IPONX3CV46B764LAXA] Running shell script
+ git checkout master
error: pathspec 'master' did not match any file(s) known to git.
[Pipeline] }

我原本预期git branch -r命令将打印出origin/masterorigin/develop,但出于某些原因它只打印了后者。

我查阅了一些资料并尝试了各种方法,例如我尝试安装Jenkins的SSH Agent插件并更改了Jenkinsfile:

node {
    stage('Checkout') {
        checkout scm
    }

    stage('Try different branch') {
        sshagent(['Bitbucket']) {
            sh "git branch -r"
            sh "git checkout master"
        }
    }
}

但它仍然似乎找不到origin/master。 更糟糕的是,在尝试检出master之前,SSH代理似乎已经被杀死:

[Pipeline] { (Try different branch)
[Pipeline] sshagent
[ssh-agent] Using credentials ThomasKasene (Used to communicate with Bitbucket)
[ssh-agent] Looking for ssh-agent implementation...
[ssh-agent]   Exec ssh-agent (binary ssh-agent on a remote machine)
$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-M6pIguCUpAV4/agent.11899
SSH_AGENT_PID=11902
$ ssh-add /var/jenkins_home/workspace/e_jenkinsfile-tests_develop-4R65E2H6B73J3LB52BLACQOZLBJGN2QG22IPONX3CV46B764LAXA@tmp/private_key_2394129657382526146.key
Identity added: /var/jenkins_home/workspace/e_jenkinsfile-tests_develop-4R65E2H6B73J3LB52BLACQOZLBJGN2QG22IPONX3CV46B764LAXA@tmp/private_key_2394129657382526146.key (/var/jenkins_home/workspace/e_jenkinsfile-tests_develop-4R65E2H6B73J3LB52BLACQOZLBJGN2QG22IPONX3CV46B764LAXA@tmp/private_key_2394129657382526146.key)
[ssh-agent] Started.
[Pipeline] {
[Pipeline] sh
[e_jenkinsfile-tests_develop-4R65E2H6B73J3LB52BLACQOZLBJGN2QG22IPONX3CV46B764LAXA] Running shell script
+ git branch -r
  origin/develop
[Pipeline] sh
$ ssh-agent -k
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
echo Agent pid 11902 killed;
[ssh-agent] Stopped.
[e_jenkinsfile-tests_develop-4R65E2H6B73J3LB52BLACQOZLBJGN2QG22IPONX3CV46B764LAXA] Running shell script
+ git checkout master
error: pathspec 'master' did not match any file(s) known to git.
[Pipeline] }

我的最终计划是向 develop 提交一些内容,然后将其合并到 master,但迄今为止我没有太大的运气。有人有可能的解决方案或解决方法吗?

PS:这只是在 Jenkinsfile 中似乎存在问题;我有一个自由样式工作任务与我想要的类似,它可以正常工作。

5个回答

30

经过几个小时的试错,我想出了一个可能的解决方案。它部分地基于Matt的答案,但我不得不修改它才能使它正常工作。

Matt在基本原则上是正确的:checkout scm并不灵活,无法让我做我需要的事情,所以我必须使用GitSCM进行自定义。感兴趣的主要点是:

  • 添加了扩展程序LocalBranch,确保我检出了一个实际的分支,而不仅仅是一个分离的HEAD
  • 添加扩展程序WipeWorkspace,以删除工作区中的所有内容并强制执行完全克隆。我认为这不是解决我的问题的一部分,但仍然很方便。
  • 使用credentialsId属性指定SSH凭据,因为存储库是私有的。

由于某种原因,当执行checkout步骤时,它仅检出分支,但未设置跟踪远程分支。在我找到更优雅的解决方案之前,我不得不手动执行此操作。

完成所有这些后,我可以使用常规的sh "git checkout master"甚至sh "git push",只要我将它们包含在sshagent步骤中。

我在下面添加了一个结果Jenkinsfile的工作示例,但请记住,它不应用于任何接近生产的东西,因为它仍然处于非常初期的阶段;例如硬编码版本号和没有检查你在哪个分支。

node {
    mvnHome = tool 'Maven'
    mvn = "${mvnHome}/bin/mvn"

    stage('Checkout') {
        checkout([
            $class: 'GitSCM',
            branches: scm.branches,
            extensions: scm.extensions + [[$class: 'LocalBranch'], [$class: 'WipeWorkspace']],
            userRemoteConfigs: [[credentialsId: 'Bitbucket', url: 'git@bitbucket.org:NAVFREG/jenkinsfile-tests.git']],
            doGenerateSubmoduleConfigurations: false
        ])
    }

    stage('Release') {
        // Preparing Git
        sh "git branch -u origin/develop develop"
        sh "git config user.email \"jenkins@thomaskasene.com\""
        sh "git config user.name \"Jenkins\""

        // Making and committing new verison
        sh "${mvn} versions:set -DnewVersion=2.0.0 -DgenerateBackupPoms=false"
        sh "git commit -am \"Released version 2.0.0\""

        // Merging new version into master
        sh "git checkout master"
        sh "git merge develop"
        sh "git checkout develop"

        // Making and committing new snapshot version
        sh "${mvn} versions:set -DnewVersion=3.0.0-SNAPSHOT -DgenerateBackupPoms=false"
        sh "git commit -am \"Made new snapshot version 3.0.0-SNAPSHOT\""

        // Pushing everything to remote repository
        sshagent(['Bitbucket']) {
            sh "git push"
            sh "git checkout master"
            sh "git push"
        }
    }
}

如果您查看控制台日志,您可以看到使用您的选项,git插件将执行git checkout -b develop 1694b133.....这就是为什么它在管道中没有额外的手动步骤时无法跟踪远程分支的原因。上面的最后一个参数(提交)是“start_point”,如果设置为分支名称,则git将自动跟踪远程,而不是提交sha。我不知道为什么git插件会这样做,但是除非您持久化本地工作区,否则将检出到附加的HEAD进行构建而不跟踪远程是相当无用的。我还没有找到解决方案。 - Jon

9

我无法让上面的两个答案起作用。我正在通过指定分支来触发Jenkins管道作业,并尝试在作业中检出另一个分支(develop),但失败了,错误信息如下:

error: pathspec 'develop' did not match any file(s) known to git.

我可以在失败的作业中看到这一点,表明只有触发分支被获取:
git fetch --no-tags --progress https://<github URL> +refs/heads/branch-name:refs/remotes/origin/branch-name

我通过更改远程抓取配置并在触发的作业的Jenkinsfile中执行默认的checkout scm步骤后进行以下操作,使其正常工作:

sh """
    git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
    git fetch --all
"""

这要归功于这个答案 https://dev59.com/eV0a5IYBdhLWcg3wCEsw#39986737

这也避免了我为尝试上述两种解决方案而必须配置Jenkins的GitSCM脚本批准


谢谢!这个方法也适用于 bitbucket-pipelines.yml,这样分支上运行的 Bitbucket CI 作业也可以检出主分支。太好了,我找到了这个方法。(点赞!) - pestophagous
是的,这个答案对我很有用,而且比其他答案简单明了得多。谢谢! - Anurag Ashok

8

您可以使用Jenkins Pipeline中为Git克隆和拉取创建的内置函数。我还建议将分支克隆到单独的目录中。

checkout([$class: 'GitSCM',
  branches: [[name: '*/branch_name']],
  doGenerateSubmoduleConfigurations: false,
  extensions: [[$class: 'RelativeTargetDirectory',
    relativeTargetDir: 'different_directory']],
  submoduleCfg: [],
  userRemoteConfigs: [[url: 'git@github.domain:org/repo.git']]])

1
除了搞砸某些东西的潜在风险之外,您建议将克隆放到单独的目录中是否有特定原因? - Thomas Kåsene
1
如果你将两个仓库克隆到同一个目录下,那么这将会导致你的.git目录出现问题。 - Matt Schuchard
假设我不先清空它,我完全同意。计划从未是在同一文件夹克隆多个分支,而是在同一个存储库中在分支之间来回切换,在理论上应该正常工作。顺便感谢您的答复! - Thomas Kåsene
@ThomasKåsene 懂了,那确实有道理。 - Matt Schuchard
@MattSchuchard 你好,我想问一下,使用这个命令时,我们能否将branch_name作为来自Jenkins参数的变量使用?像这样的cmd,但对我来说效果不佳,也许我漏掉了什么?checkout([$class: 'GitSCM', branches: [[name: '*/$branch_name']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'myid', url: 'mygitrepo']]]) - Matrix

3
您可以使用较为基础的git命令,它是checkout命令的简化版。 (参考链接)
stage('Repo Checkout') {
            steps {
                deleteDir()
                dir("repo-one-master") {
                    git url: "ssh://git@host.com/folder/project",
                        branch: 'master'
                }
                dir("repo-one-feature) {
                    git url: "ssh://git@host.com/folder/project",
                        branch: 'some-branch'
                }
            }
        }

3
问题在于Jenkins仅使用发现的分支定义了origin
@swoop81的答案是有效的,但如果您只想检出一个分支,则可以仅获取该分支。
git config --add remote.origin.fetch +refs/heads/<branch-name>:refs/remotes/origin/<branch-name>
git fetch --no-tags https://<github-url> +refs/heads/<branch-name>:refs/remotes/origin/<branch-name>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接