Jenkinsfile syntax: Commit to Subversion SCM repository from workspace - jenkins

Somehow, I could not find an example of declarative pipeline syntax (Jenkinsfile) for this case.
The idea is very simple. There is some local file in Jenkins pipeline workspace that was checked out from Subversion SCM repository at the beginning of a pipeline stage, it was checked out from under some branch.
I modified this file and at the end of a pipeline stage I need to commit this file back into SCM repository.
Does anybody have Jenkins pipeline DSL syntax example for such commit? This commit should be able to overwrite the file available under the branch in SCM repository.

You don't want to "override" the commit but to add another commit that will update the file, right? Indeed it's a good practice to know where the changes on your repository come from.
For Subversion you should do something like
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId:'mycreds', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
sh "svn commit --message 'jenkins update' --non-interactive --no-auth-cache --username $USERNAME --password $PASSWORD"
}
Note: mycreds would be the id of a username/password credential you stored in jenkins
Finally making jenkins do commit is something you want to discuss with your team before implementing it, it can be hard to maintain if not done properly

Related

GIT_COMMIT envvar incorrect for pipelines that merge master first

Context
This question relates to multibranch pipelines where the behaviour merges the PR with the target branch revision (see screenshot of settings)
In this case, the merge may cause a new merge commit. So for a trigger with a given commit from a repository:
We actually get a different value of the GIT_COMMIT envvar:
If a tool (such as a build reporting tool) needs to use the GIT_COMMIT envvar to pass information onto a service, it cannot then be linked back to the actual commit from the project (this is a screenshot from Bitbucket but this would be the same for any repo hosting service):
Question
How, in a pipeline step, can I find the commit 709502c is the actual genesis of this build, when the GIT_COMMIT is set to 6781a3d1 (which is not an actual commit in the project)?
Maybe looking at git history can help?
REAL_GIT_COMMIT = sh (
script: "git rev-parse HEAD",
returnStdout: true,
).trim()

How do you specify additional trusted files with the Jenkins Github Branch Source plugin?

I've created a Jenkins Multibranch Pipeline with the GitHub Branch Source plugin. The Jenkinsfile essentially just calls a Cake Build script (build.ps1, build.cake) that contains all the build/deploy logic. This allows me to move to another CI service easily.
Unfortunately, I cannot seem to figure out how to add my Cake Build scripts as a trusted file so that PR's from forks will pull the files from the source repo instead. The Trust setting of the Discover pull requests from forks behavior seems to indicate that there can be other trusted files besides Jenkinsfile:
Nobody
Pull requests from forks will all be treated as untrusted. This means that where Jenkins requires a trusted file (e.g. Jenkinsfile) the contents of that file will be retrieved from the target branch on the origin repository and not from the pull request branch on the fork repository.
However, I cannot seem to find any documentation on adding other trusted files. The primary reason for this is to prevent a PR from a fork from accessing credentials from the Cake script. They wouldn't be able to change Jenkinsfile, but they could still change the Cake script to expose the credentials.
Is it actually possible to add other trusted files?
It seems like Jenkins does not support this. My solution is checking out the untrusted files manually from the base version instead. First getting the commit 's hash of the base version with:
def commit = sh(
script: 'git rev-parse HEAD',
returnStdout: true
).trim()
def base = sh(
script: "git rev-list --parents -n 1 ${commit}",
returnStdout: true
).trim().split('\\s+')[2]
git rev-list --parents -n 1 ${commit} will return the hash of current commit, which is a merge commit that was created by Jenkins; the latest commit of the PR and the latest commit of the target branch, separated by a space (e.g. 05e9322574ea03003f87dcbb44f172e6fa62581f b3f6ef892af9c645f490106757d7d05df3a26060 069ffd55ae36414a51b4de166aef86966f9447a8). Hence, we grab the hash of the latest commit of the target branch by trim().split('\\s+')[2].
Now we can do sh "git checkout ${base} FILE" on any file that we don't trust from the PR.
This does not works if the PR is already merged with the latest version of target branch. So what I did is something like this:
// revert untrusted files to the base version and backup it before we execute any untrusted code so the attacker
// don't have a chance to put a malicious content
def latest = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
sh "git checkout origin/${env.CHANGE_TARGET}"
def baseCompose = readFile('docker-compose.yml')
// switch back to latest commit
sh "git checkout ${latest}"
sh 'git clean -d -f -f -q -x'

SVN tagging a release in Jenkins Pipeline

I'm fairly new to Jenkins Pipeline groovy scripts but I have written a script which performs an SVN Checkout, NuGet Restore, etc and eventually copying an msi file to the server. After successfully packaging the .msi file I would like to tag the source in SVN but I'm struggling to find a method of doing this.
The svn check out is performed as follows:
def svn = checkout scm
I was sort of hoping I could just to the following:
svn = copy scm "svn://svn/MyPath/MyApp/tag/${versionNumber}" -m "V${versionNumber}"
I could obviously use the bat command and specify the full svn command but then I'd have to enter the Jenkins credentials into the groovy script which is not ideal.
Any help/pointers would be greatly appreciated.
If you are tied to Subversion you could extract bits of code from this post, but I would HIGHLY recommend, if you can, to migrate your code to Git as the tooling support in Git is so much better and where the world has already gone.
In Git this is all I have to do tag a branch as part of my Jenkins multibranch pipeline:
success {
script {
if( "${env.BRANCH_NAME}" == "develop" ) {
bat "git tag ${JOB_NAME}_${BUILD_NUMBER}"
bat "git push ${env.REPO} --tags"
}
}
}
${env.REPO} is my clone URL.
SO much easier.

How to configure a Jenkins 2 Pipeline so that Jenkinsfile uses a predefined variable

I have several projects that use a Jenkinsfile which is practically the same. The only difference is the git project that it has to checkout. This forces me to have one Jenkinsfile per project although they could share the same one:
node{
def mvnHome = tool 'M3'
def artifactId
def pomVersion
stage('Commit Stage'){
echo 'Downloading from Git...'
git branch: 'develop', credentialsId: 'xxx', url: 'https://bitbucket.org/xxx/yyy.git'
echo 'Building project and generating Docker image...'
sh "${mvnHome}/bin/mvn clean install docker:build -DskipTests"
...
Is there a way to preconfigure the git location as a variable during the job creation so I can reuse the same Jenkinsfile?
...
stage('Commit Stage'){
echo 'Downloading from Git...'
git branch: 'develop', credentialsId: 'xxx', url: env.GIT_REPO_LOCATION
...
I know I can set it up this way:
This project is parameterized -> String Parameter -> GIT_REPO_LOCATION, default= http://xxxx, and access it with env.GIT_REPO_LOCATION.
The downside is that the user is promted to start the build with the default value or change it. I would need that it were transparent to he user. Is there a way to do it?
You can use the Pipeline Shared Groovy Library plugin to have a library that all your projects share in a git repository. In the documentation you can read about it in detail.
If you have a lot of Pipelines that are mostly similar, the global variable mechanism provides a handy tool to build a higher-level DSL that captures the similarity. For example, all Jenkins plugins are built and tested in the same way, so we might write a step named buildPlugin:
// vars/buildPlugin.groovy
def call(body) {
// evaluate the body block, and collect configuration into the object
def config = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = config
body()
// now build, based on the configuration provided
node {
git url: "https://github.com/jenkinsci/${config.name}-plugin.git"
sh "mvn install"
mail to: "...", subject: "${config.name} plugin build", body: "..."
}
}
Assuming the script has either been loaded as a Global Shared Library
or as a Folder-level Shared Library the resulting Jenkinsfile will be
dramatically simpler:
Jenkinsfile (Scripted Pipeline)
buildPlugin {
name = 'git'
}
The example shows how a jenkinsfile passes name = git to the library.
I currently use a similar setup and am very happy with it.
Instead of having a Jenkinsfile in each Git repository, you can have an additional git repository from where you get the common Jenkinsfile - this works when using Pipeline type Job and selecting the option Pipeline script from SCM. This way Jenkins checks out the repo where you have the common Jenkinsfile before checking out the user repo.
In case the job can be triggered automatically, you can create a post-receive hook in each git repo that calls the Jenkins Pipeline with the repo as a parameter, so that the user does not have to manually run the job entering the repo as a parameter (GIT_REPO_LOCATION).
In case the job cannot be triggered automatically, the least annoying method I can think of is having a Choice parameter with a list of repositories instead of a String parameter.

What is the branch name variable for Jenkins multibranch pipelines?

I need to know which branch is being built in my Jenkins multibranch pipeline in order for it to run steps correctly.
We are using a gitflow pattern with dev, release, and master branches that all are used to create artifacts. The dev branch auto deploys, the other two do not. Also there are feature, bugfix and hotfix branches. These branches should be built, but not produce an artifact. They should just be used to inform the developer if there is a problem with their code.
In a standard build, I have access to the $GIT_BRANCH variable to know which branch is being built, but that variable isn't set in my multibranch pipeline. I have tried env.GIT_BRANCH too, and I tried to pass $GIT_BRANCH as a parameter to the build. Nothing seems to work. I assumed that since the build knows about the branch being built (I can see the branch name at the top of the console output) that there is something that I can use - I just can't find any reference to it.
The env.BRANCH_NAME variable contains the branch name.
As of Pipeline Groovy Plugin 2.18, you can also just use BRANCH_NAME
(env isn't required but still accepted.)
There is not a dedicated variable for this purpose yet (JENKINS-30252). In the meantime you can take advantage of the fact that the subproject name is taken from the branch name, and use
env.JOB_NAME.replaceFirst('.+/', '')
This has now been resolved, see Krzysztof KrasoĊ„'s answer.
There are 2 branches to consider in a Jenkins multibranch pipeline job:
The Jenkins job branch - env.BRANCH_NAME. This may have the same name as a git branch, but might also be called PR-123 or similar
The git branch - env.GIT_BRANCH. This is the actual branch name in git.
So a job might have BRANCH_NAME=PR-123 and GIT_BRANCH=my-scm-branch-name
Jenkins documentation has a list of all the env variable for your perusal here
Another way is using the git command to obtain the branch name on the current jenkins pipeline. For example, you can add the following snippet to print the branch name in your Jenkinsfile.
...
script {
def BRANCH = sh(returnStdout: true, script: 'git rev-parse --abbrev-ref HEAD').trim()
echo ${BRANCH}
}
...
I found this stackoverflow post example useful: Git Variables in Jenkins Workflow plugin
sh '//...
git rev-parse --abbrev-ref HEAD > GIT_BRANCH'
git_branch = readFile('GIT_BRANCH').trim()
echo git_branch
//...
'

Resources