Environment
-
CloudBees CI (CloudBees Core) on modern cloud platforms - Managed controller
-
CloudBees CI (CloudBees Core) on traditional platforms - Client controller
-
CloudBees Jenkins Enterprise
-
CloudBees Jenkins Enterprise - Managed controller
Explanation
Although there is a GitPublisher step available to Freestyle jobs, there is none for Pipeline jobs. There is a known RFE for this JENKINS-28335.
There are, however, several solutions possible to push changes back to Git in Pipeline.
Resolution
there are existing solutions documented in Pipeline Examples - Push Git Repo |
The main challenge is to pass authentication credentials to the push
command temporarily and there are multiple options to do this with Git (see git-credentials). Also solutions are different based on requirements and scenarios (commit and push, tag and push, checkout and push using different protocol, …).
This article provides commons solutions, recommendations and examples.
Pre-requisites
1) Configure Git User
The Git user name and email must be configured on the agent running the build to be able to commit changes / create a tag. If no user is configured in the agent environment, the following error would appear when committing / pushing changes:
*** Please tell me who you are. Run git config --global user.email "you@example.com" git config --global user.name "Your Name"
The user / email can be set in the pipeline like the following:
[...] sh(''' git config user.name 'my-ci-user' git config user.email 'my-ci-user@users.noreply.github.example.com' ''') [...]
(Note: The git username and email can be configured via the SCM Trait (Pipeline Multibranch) or Git Advanced Behavior (Pipeline) "Custom user name/e-mail address")
2) Checkout to a local branch
In most cases, the checkout
step checkout the repository at a specific revision (in a detached HEAD). This is something to be aware of when committing and pushing changes. A good practice is to checkout to a local branch when making changes and ideally before making changes (i.e. right after the checkout) to avoid losing them when doing git operations:
git checkout -B $TARGET_BRANCH
(Note: Pipeline and Pipeline Multibranch respectively proposed the additional behavior "Check out to specific local branch" and trait "Check out to matching local branch" to checkout to a local branch right after the checkout)
Push changes to Git Repository
Changes may be push via SSH or HTTPS. It is simpler and more consistent to push via the same protocol as the one used to do the checkout.
If for any particular reason, the push must be done using a different method the URL needs to be configured accordingly:
-
Add
git config url.git@github.com/.insteadOf https://github.com/
if the checkout was done through HTTPS but push must be done using SSH -
Add
git config url.https://github.com/.insteadOf git@github.com/
if the checkout was done through SSH but push must be done using HTTPS
Push via HTTPS
The Credentials Binding can be used to inject username / password (or token) as environment variable. Those variables can be used to define a credentials helper.
In scripted pipeline, use the Credentials Binding plugin wrapper. Authentication is only possible within the withCredentials
block:
[...] withCredentials([usernamePassword(credentialsId: 'my-credentials-id', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]){ sh(''' git config --local credential.helper "!f() { echo username=\\$GIT_USERNAME; echo password=\\$GIT_PASSWORD; }; f" git push origin HEAD:$TARGET_BRANCH ''') } [...]
In Declarative Pipeline, the environment block can be used. Authentication is only possible within the stage that defines the environment variable:
[...] stage('Push') { environment { GIT_AUTH = credentials('my-predefined-credentials-id') } steps { sh(''' git config --local credential.helper "!f() { echo username=\\$GIT_AUTH_USR; echo password=\\$GIT_AUTH_PSW; }; f" git push origin HEAD:$TARGET_BRANCH ''') } } [...] }
Push via SSH
A simple solution is to use the SSH Agent plugin to inject SSH credentials via ssh-agent. For both scripted and declarative, the solution is the same:
[...] sshagent(['my-ssh-credentials-id']) { sh(''' #!/usr/bin/env bash set +x # If no host key verification is needed, use the option `-oStrictHostKeyChecking=no` export GIT_SSH_COMMAND="ssh -oStrictHostKeyChecking=no" git push origin HEAD:\$TARGET_BRANCH ''') } [...]
Examples
The following scripts adds a file to the current branch of a Pipeline Multibranch build and push the changes via HTTPS:
pipeline { agent any stages { stage("Build") { steps { // Create a dummy file in the repo sh('echo \$BUILD_NUMBER > example-\$BUILD_NUMBER.md') } } stage("Commit") { steps { sh(''' git checkout -B $TARGET_BRANCH git config user.name 'my-ci-user' git config user.email 'my-ci-user@users.noreply.github.example.com' git add . && git commit -am "[Jenkins CI] Add build file" ''') } } stage("Push") { environment { GIT_AUTH = credentials('support-team-up') } steps { sh(''' git config --local credential.helper "!f() { echo username=\\$GIT_AUTH_USR; echo password=\\$GIT_AUTH_PSW; }; f" git push origin HEAD:$TARGET_BRANCH ''') } } } }
The following script creates a tag of the current branch of a Pipeline Multibranch build push it via SSH:
pipeline { agent any stages { stage("Tag and Push") { when { branch 'main' } environment { GIT_TAG = "jenkins-$BUILD_NUMBER" } steps { sh(''' git config user.name 'my-ci-user' git config user.email 'my-ci-user@users.noreply.github.example.com' git tag -a \$GIT_TAG -m "[Jenkins CI] New Tag" ''') sshagent(['my-ssh-credentials-id']) { sh(""" #!/usr/bin/env bash set +x export GIT_SSH_COMMAND="ssh -oStrictHostKeyChecking=no" git push origin \$GIT_TAG """) } } } } }
Tested product/plugin versions
-
CloudBees CI (CloudBees Core) on traditional platforms - Client controller 2.121.3.1 and 2.164.2.1
-
Pipeline plugin 2.5
-
SSH Agent plugin 1.16
-
Credentials Binding plugin 1,15 and 1.18