Using variable in another stage in declarative Jenkinsfile - jenkins

I'm writing a declarative Jenkinsfile which looks like this. In the stage "build" I define the variable customImage which I would like to use in the stage "Push".
Unfortunately I cannot get this to work.
pipeline {
agent any
stages {
stage("Build") {
steps {
script {
def commitHash = GIT_COMMIT.take(7)
echo "Building Docker image for commit hash: " + commitHash
def customImage = docker.build("myimage:${commitHash}")
}
}
}
stage("Push") {
steps {
echo "Pushing Docker image to registry..."
script {
docker.withRegistry(REGISTRY_SERVER, REGISTRY_CREDENTIALS) {
$customImage.push()
}
}
}
}
}
}

You just have to define the variable at a scope, where you can access it later, i.e.
def customImage
pipeline {
agent any
stages {
stage("Build") {
steps {
script {
def commitHash = GIT_COMMIT.take(7)
echo "Building Docker image for commit hash: " + commitHash
customImage = docker.build("myimage:${commitHash}")
}
}
}
stage("Push") {
steps {
echo "Pushing Docker image to registry..."
script {
docker.withRegistry(REGISTRY_SERVER, REGISTRY_CREDENTIALS) {
customImage.push()
}
}
}
}
}
}

Related

Skip Stages in Jenkins shared library based on repository

I have a common Jenkins shared library for all the repositories as below.
vars/_publish.groovy
pipeline {
environment {
abc= credentials(’abc')
def= credentials(‘def’)
}
stages {
stage('Build') {
steps{
sh ‘docker build'
}
}
stage('Unit-test') {
steps{
sh ‘mvn test'
}
}
jenkinsfile
#Library('my-shared-library#branch') _
_publish() {
}
I have 10 Repository each has its own Jenkinsfile as shown above which refers to the jenkins shared library(vars/_publish.groovy). I have a condition here that I need to Pass. For few repository I want to skip the Unit test and just execute the build stage. For rest other repository I want both the stages. Is there anyone I can skip the particular stage based on the repository or repository name
Yes it's possible you can use when expression like this
pipeline {
agent any
stages {
stage('Test') {
when { expression { return repositoryName.contains('dev') } } <---------Add put your repository name 'dev' so whenever the repository names is ''dev' then execute this stage
steps {
script {
}
}
}
}
}
def repositoryName() {
def repositoryName = ['dev', 'test'] <----Add here the 10 repo name
return repositoryName
}
Here in my case repo names are dev and test so you can add yours accondigly
I would decorate my shared library and Jenkinsfile like this to achieve your scenario.
vars/_publish.groovy
def call(body={}) {
def pipelineParams = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
pipeline {
agent any;
stages {
stage('build') {
steps {
echo "BUILD"
}
}
stage('unitest') {
when {
anyOf {
equals expected: true, actual: pipelineParams.isEmpty();
equals expected: false, actual: pipelineParams.skipUnitest
}
}
steps {
echo "UNITEST"
}
}
}
}
}
I am enabling my shared library to accept parameter from Jenkinsfile and with when{} DSL deciding whether to skip unitest stage or not
Jenkinsfile
If your Jenkins file from the repo has below details, will skip the unitest stage
#Library('jenkins-shared-library')_
_publish(){
skipUnitest = true
}
below both scenario will run the unitest stage
#Library('jenkins-shared-library')_
_publish(){
skipUnitest = false
}
and
#Library('jenkins-shared-library')_
_publish(){
}

How to install .NetCore dotnet (not MSBuid plugin) to Jenkins

I was assigned to create CI/CD workflow for our project combining Openshift + Jenkins pipeline. I can build by using following:
stage('build') {
steps {
script {
openshift.withCluster() {
openshift.withProject() {
def buildSelector = openshift.selector("bc", "test")
buildSelector.startBuild()
buildSelector.logs('-f')
}
}
} // script
} // steps
} // stage
But I want to also use dotnet restore and other commands but pipeline returns:
/var/lib/jenkins/jobs/test-namespace/jobs/test-pipeline/workspace#tmp/durable-4265d26a/script.sh: line 1: dotnet: command not found
also when trying to use
agent {
docker { image 'mcr.microsoft.com/dotnet/aspnet:3.1' }
}
/var/lib/jenkins/jobs/test-namespace/jobs/test-pipeline/workspace#tmp/durable-4265d26a/script.sh: line 1: docker: command not found
This is my current script
pipeline {
agent {
node {
label ''
}
}
options {
// set a timeout of 20 minutes for this pipeline
timeout(time: 20, unit: 'MINUTES')
}
stage('build') {
steps {
script {
openshift.withCluster() {
openshift.withProject() {
def buildSelector = openshift.selector("bc", "test")
buildSelector.startBuild()
buildSelector.logs('-f')
}
}
} // script
} // steps
} // stage
stage('clean'){
steps {
script{
echo "${workspace} I want to call dotnet clean"
}
} // steps
} // stage
stage('restore'){
steps {
script{
echo "${workspace} I want to call dotnet restore"
}
} // steps
} // stage
stage('tests'){
steps {
script{
echo "${workspace} I want to call dotnet test *.sln"
}
} // steps
} // stage
stage('deploy') {
steps {
script {
openshift.withCluster() {
openshift.withProject() {
def deploySelector = openshift.selector("dc", "test")
deploySelector.startDeploy()
deploySelector.logs('-f')
}
}
} // script
} // steps
} // stage
}
}
How can I install those binaries to Jenkins? I am using minishift on MacOS.

How to create stages dynamically / concatenate closures?

What i want to achieve is building a list of stages with avoiding using when{}. Im trying to run parallel pipelines
Here is example code
def stage_pull = {
stage('pulling') {
echo 'pulling'
}
}
def stage_build = {
stage(pulling) {
echo 'building'
}
}
def stage_deb = {
stage(pulling) {
echo 'deb file'
}
}
def transformIntoStages(stage1,stage2) {
//return stage1 + stage2
//return {stage1;stage2}
return stage1 << stage2
}
def agent_list = ["agent1", "agent2"]
stepsForParallel = [:]
stepsForParallel['agent1'] = transformIntoStages(stage_pull,stage_build)
stepsForParallel['agent2'] = transformIntoStages(stage_pull,stage_deb)
pipeline{
agent any
options {
timestamps()
}
stages{
stage('BUILD'){
steps{
script{
parallel stepsForParallel
}
}
}
}
}
This is simplified version. In real project, the number of used stages will be different for each agent.
I also have version with closures inside methods ...
https://pastebin.com/gPJjPx59
But none of this work.
PS. I know matrix{}, i use it often but I dont want to use it in this particular case.
I think i managed to achieve the goal by using strings and evaluate() function.
def stage_pull() {
return """
stage('pulling') {
echo 'pulling'
}
"""
}
def stage_build() {
return """
stage('building') {
echo 'building'
}
"""
}
def stage_deb() {
return """
stage('deb') {
echo 'deb file'
}
"""
}
def transformIntoStages(stage1,stage2) {
echo "{" + stage1 + stage2 + "}"
return { evaluate(stage1 + stage2) }
}
stepsForParallel = [:]
stepsForParallel['agent1'] = transformIntoStages(stage_pull(),stage_build())
stepsForParallel['agent2'] = transformIntoStages(stage_pull(),stage_deb())
stepsForParallel['agent3'] = transformIntoStages(stage_pull(),'')
pipeline{
agent any
options {
timestamps()
}
stages{
stage('BUILD'){
steps{
script{
parallel stepsForParallel
}
}
}
}
}
However im afraid that in case of more complicated stages/functions/structures with different kinds of parenthesis it will start to be a mess. And Blue Ocean cant show this properly. But in logs with timestamps and most of all in "Pipeline Steps" section i can see that it works as it should.
So im still open to some suggestions

How to pass a variable from remote ssh connection to other stages in groovy script

I am working on a groovy script for a Jenkins pipeline and am struggling to find how to pass a variable across stages when the variable is obtained from a remote ssh connection.
I found Example 1 and Example 2 on this site and I want to merge them together as seen in "My attempt" below. Note that the output of the file on the remote server is 4. I'm trying pass 4 to a_var.
Example 1: works fine. SSH connection. This reads the file and outputs value to the Jenkins console
def sshCredId = 'myid_cred'
def sshUser = 'myid'
def sshServer = 'myserver'
pipeline {
agent { label 'docker-maven-slave' }
stages {
stage('one') {
steps {
script {
sshagent([sshCredId]){
sh "ssh -o StrictHostKeyChecking=no ${sshUser}#${sshServer} cat /mydir/myfile.csv"
}
}
}
}
stage('two') {
steps {
echo "something"
}
}
stage('three') {
steps {
echo "do stuff"
}
}
}
}
Example 2: works fine. This passes a parameter across stages
pipeline {
agent {
label 'docker-maven-slave'
}
parameters {
string(name: 'a_var', defaultValue: '')
}
stages {
stage("one") {
steps {
script {
tmp_param = sh (script: 'echo something', returnStdout: true).trim()
env.a_var = tmp_param
}
}
}
stage("two") {
steps {
echo "${env.a_var}"
}
}
}
}
**My attempt: stage two output is null. I'm expecting '4' **
def sshCredId = 'myid_cred'
def sshUser = 'myid'
def sshServer = 'myserver'
pipeline {
agent { label 'docker-maven-slave' }
parameters {
string(name: 'a_var', defaultValue: 'nothing')
}
stages {
stage('one') {
steps {
script {
tmp_param=sshagent([sshCredId]){
sh "ssh -o StrictHostKeyChecking=no ${sshUser}#${sshServer} cat /mydir/myfile.csv"
}
env.a_var=tmp_param
}
}
}
stage('two') {
steps {
echo "${env.a_var}"
}
}
stage('three') {
steps {
echo "do stuff"
}
}
}
}
Update the answer based on comments and feedback from MayJoAnneBeth
Try below snippet
sshagent([sshCredId]){
env.a_var = sh (script: "ssh -o StrictHostKeyChecking=no ${sshUser}#${sshServer} cat /mydir/myfile.csv", returnStdout: true).trim()
}

buildingTag() always returns false

Whenever I try to create a conditional stage with buildingTag(), the stage always gets skipped, even when the current commit is a tag. Here is my Jenkinsfile:
pipeline {
agent {
docker {
image 'node:10'
}
}
stages {
stage('Build') {
steps {
sh 'yarn install'
sh 'node scripts/build.js'
}
}
stage('Lint') {
steps {
sh 'yarn lint'
}
}
stage('Deploy') {
when {
buildingTag()
}
environment {
}
steps {
sh 'node scripts/deploy.js'
sh 'node scripts/publish.js'
}
}
}
}
Likely due to this bug:
https://issues.jenkins-ci.org/browse/JENKINS-55987
Workaround is:
when {
expression {
return !isVersionTag(readCurrentTag())
}
}
with:
def boolean isVersionTag(String tag) {
echo "checking version tag $tag"
if (tag == null) {
return false
}
// use your preferred pattern
def tagMatcher = tag =~ /\d+\.\d+\.\d+/
return tagMatcher.matches()
}
// workaround https://issues.jenkins-ci.org/browse/JENKINS-55987
def String readCurrentTag() {
return sh(returnStdout: true, script: "git describe --tags").trim()
}
buildingTag() requires that the TAG_NAME environment varible is set.
This is not set automatically in a simple (not multibranch) pipeline.
pipeline {
agent any
environment {
// To get the tag like shown soru's answer:
// TAG_NAME = sh(returnStdout: true, script: "git describe --tags").trim()
// In my case I already have a tag saved as an environment variable:
// gitlabBranch=refs/tags/tagname
TAG_NAME = "${env.gitlabBranch.split('/')[2]}"
}
stages {
stage('buildingTag') {
when { buildingTag() }
steps {
echo 'buildingTag works here.'
}
}
}
}
I also come across this problem. All you need to do enable Advanced clone behaviours -> Fetch tags in the project settings, and set TAG_NAME environment variable in the Jenkins file.
1- Advanced clone behaviours
2- Fetch tags
3- And set TAG_NAME variable in pipeline (buildingTag function requires this)
pipeline {
environment {
TAG_NAME = sh(returnStdout: true, script: "git --no-pager tag --points-at HEAD").trim()
}
agent {
...
4- Use Jenkins's buildingTag function to check whether commit has tag or not
...
stage("Publish Release Artifact") {
when {
buildingTag()
}
...
I have been using soru's solution, but I had problems when I was building a branch that is tagged, so I tried this and it seems to work:
def boolean isVersionTag(String tag) {
echo "checking version tag $tag"
if (tag == null) {
return false
}
// use your preferred pattern
def tagMatcher = tag =~ /\d+\.\d+\.\d+/
return tagMatcher.matches()
}
def String readCurrentTag() {
return sh(returnStdout: true, script: 'echo ${TAG_NAME}').trim()
}

Resources