13

I want to pass a variable which I read in stage A towards stage B somehow. I see in some examples that people write it to a file, but I guess that is not really a nice solution. I tried writing it to an environment variable, but I'm not really successful on that. How can I set it up properly?

To get it working I tried a lot of things and read that I should use the """ instead of ''' to start a shell and escape those variables to \${foo} for example.

Below is what I have as a pipeline:

#!/usr/bin/env groovy

pipeline {

    agent { node { label 'php71' } }

    environment {
        packageName='my-package'
        packageVersion=''
        groupId='vznl'
        nexus_endpoint='http://nexus.devtools.io'
        nexus_username='jenkins'
        nexus_password='J3nkins'
    }

    stages{

        // Package dependencies
        stage('Install dependencies') {
            steps {
                sh '''
                    echo Skip composer installation
                    #composer install  --prefer-dist --optimize-autoloader --no-interaction
                '''
            }
        }

        // Unit tests
        stage('Unit Tests') {
            steps {
                sh '''
                    echo Running PHP code coverage tests...
                    #composer test
                '''
            }
        }

        // Create artifact
        stage('Package') {
            steps {
                echo 'Create package refs'
                sh """
                    mkdir -p ./build/zpk
                    VERSIONTAG=\$(grep 'version' composer.json)
                    REGEX='"version": "([0-9]+.[0-9]+.[0-9]+)"'
                    if [[ \${VERSIONTAG} =~ \${REGEX} ]]
                    then
                        env.packageVersion=\${BASH_REMATCH[1]}
                        /usr/bin/zs-client packZpk --folder=. --destination=./build/zpk --name=${env.packageName}-${env.packageVersion}.zpk --version=${env.packageVersion}
                    else
                        echo "No version found!"
                        exit 1
                    fi
                """
            }
        }

        // Publish ZPK package to Nexus
        stage('Publish packages') {
            steps {
                echo "Publish ZPK Package"
                sh "curl -u ${env.nexus_username}:${env.nexus_password} --upload-file ./build/zpk/${env.packageName}-${env.packageVersion}.zpk ${env.nexus_endpoint}/repository/zpk-packages/${groupId}/${env.packageName}-${env.packageVersion}.zpk"
                archive includes: './build/**/*.{zpk,rpm,deb}'
            }
        }
    }
}

As you can see the packageVersion which I read from stage Package needs to be used in stage Publish as well.

Overall tips against the pipeline are of course always welcome as well.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dirkos
  • 488
  • 1
  • 10
  • 33
  • Try "env variable" plugin it might be help you – Guardian Nov 23 '17 at 19:53
  • see https://stackoverflow.com/a/44101004/6090676 (it happens to be reading from a file to set the variable, but you could set it any way you want). – burnettk Nov 23 '17 at 23:14
  • try reusing node with `reuseNode true` when defining agent – toske Nov 24 '17 at 01:51
  • @toske, problem is not the reuse of the node but the assignment of variables since there is only 1 node. Problem is when i run above command i receive this as output: `+ env.packageVersion=1.71.0 /var/lib/jenkins-slave/workspace/l_omnius-vf-nl-code_develop-IFXWAOWKAO7SA3MSTIVC4SLDHWGNQ63ALTYVVLGNHRM6SYQNYZTA@tmp/durable-acda6886/script.sh: line 9: env.packageVersion=1.71.0: command not found` The solution of writing to file is very evil imo? – Dirkos Nov 24 '17 at 08:31
  • @Dirkos answered below, solution bit better than writing to file, still makes memory space bit dirty and not able to parellize. You could write to file and `stash` / `unstash` - this would make it thread safe. – toske Nov 26 '17 at 23:04

2 Answers2

23

A problem in your code is that you are assigning version of environment variable within the sh step. This step will execute in its own isolated process, inheriting parent process environment variables.

However, the only way of passing data back to the parent is through STDOUT/STDERR or exit code. As you want a string value, it is best to echo version from the sh step and assign it to a variable within the script context.

If you reuse the node, the script context will persist, and variables will be available in the subsequent stage. A working example is below. Note that any try to put this within a parallel block can be of failure, as the version information variable can be written to by multiple processes.

#!/usr/bin/env groovy

pipeline {

    environment {
        AGENT_INFO = ''
    }

    agent {
        docker {
            image 'alpine'
            reuseNode true
        }
    }

    stages {

        stage('Collect agent info'){
            steps {
                echo "Current agent  info: ${env.AGENT_INFO}"
                script {
                    def agentInfo = sh script:'uname -a', returnStdout: true
                    println "Agent info within script: ${agentInfo}"
                    AGENT_INFO = agentInfo.replace("/n", "")
                    env.AGENT_INFO = AGENT_INFO
                }
            }
        }

        stage("Print agent info"){
            steps {
                script {
                    echo "Collected agent info: ${AGENT_INFO}"
                    echo "Environment agent info: ${env.AGENT_INFO}"
                }
            }
        }
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
toske
  • 1,744
  • 13
  • 24
4

Another option which doesn't involve using script, but is just declarative, is to stash things in a little temporary environment file.

You can then use this stash (like a temporary cache that only lives for the run) if the workload is sprayed out across parallel or distributed nodes as needed.

Something like:

pipeline {
    agent any

    stages {
        stage('first stage') {
            steps {
                // Write out any environment variables you like to a temporary file
                sh 'echo export FOO=baz > myenv'

                // Stash away for later use
                stash 'myenv'
            }
        }

        stage ("later stage") {
            steps {

                // Unstash the temporary file and apply it
                unstash 'myenv'

                // use the unstashed vars
                sh 'source myenv && echo $FOO'

            }
        }
    }
}
Michael Neale
  • 19,248
  • 19
  • 77
  • 109
  • 1
    Since every "sh" block is executed in a different shell process, you cannot source first and then use a variable in a different "sh" command. You could do it in the same one though. sh 'source myenv ; echo $FOO' – Raúl Salinas-Monteagudo Jul 03 '19 at 12:20
  • Remark that you don't need to prefix "./" in order to SOURCE a file into the current shell. You need that for executing commands when "." is not in the current path, but "source" simply reads the given file. – Raúl Salinas-Monteagudo Jul 03 '19 at 12:21