variable does not get resolved in my jenkinsfile - jenkins

I am new to jenkins/groovy and I am currently facing following issue...
I defined a deployment pipeline in a jenkinsfile, it consists in deploying scripts on two different environments running under linux. Deployment script copy_files.sh has to run under a specific user to preserve permissions:
def my_dst = '/opt/scripts'
pipeline {
agent { label '<a generic label>' }
stages {
stage('Deployment env1') {
agent { label '<a specific label>' }
steps {
script {
echo 'Deploying scripts...'
sh '/bin/sudo su -c "${WORKSPACE}/copy_files.sh ${WORKSPACE} ${my_dst}" - <another user>'
}
}
}
stage('Deployment env2') {
agent { label '<another specific label>' }
steps {
script {
echo 'Deploying scripts...'
sh '/bin/sudo su -c "${WORKSPACE}/copy_files.sh ${WORKSPACE} ${my_dst}" - <another user>'
}
}
}
}
}
I defined the destination path where files are supposed to be copied as a variable (my_dst same on both envs). While the env variable $WORKSPACE gets resolved my variable my_dst does not, resulting in an abort of the copy_files.sh script because of missing argument...
How can I quote my command properly in order my variable to be resolved? running sudo command with hard-coded destination path /opt/scripts works.
txs in advance

So you want Groovy to inject the my_dst, but the WORKSPACE to come from the shell environment, so you will need to use double quotes (so groovy does the templating), and escape the $ in WORKSPACE so Groovy doesn't template it and it gets passed to the shell as-is:
sh "/bin/sudo su -c \"\${WORKSPACE}/copy_files.sh \${WORKSPACE} ${my_dst}\" - <another user>"

Related

Jenkins build failed due to command not being recognized

I have this build error saying pandoc command is not recognize, when I build my pipeline on Jenkins :
But when I run the exact same command using cmd.exe from the same repository it works perfectly :
So what's wrong here, my command pandoc is well installed and can perfectly be used from cmd.exe, why doesn't it works from Jenkins ?
Here is my Jenkins code (the part causing the error is in the "Build" stage):
pipeline {
agent any
stages {
stage('Prerequisites') {
steps {
//bat 'RMDIR C:\\wamp64\\www\\html\\doc'
bat 'MKDIR C:\\wamp64\\www\\html\\doc'
}
}
stage('Build') {
steps {
bat 'pandoc -s C:\\wamp64\\www\\index.md -o C:\\wamp64\\www\\index.html'
bat 'pandoc -s C:\\wamp64\\www\\index.md -o C:\\wamp64\\www\\index.docx'
}
}
stage('Deploy') {
steps {
bat 'COPY C:\\wamp64\\www\\index.html COPY C:\\wamp64\\www\\html\\index.html'
bat 'COPY C:\\wamp64\\www\\index.docx COPY C:\\wamp64\\www\\html\\doc\\index.docx'
}
}
}
}
Thanks for helping.
Jenkins doesn't automatically take your Windows (path) environment variables. Instead, what you need to do is to go to Jenkins -> Configure System -> Global properties -> Environment variables and add a new variable called Path. For the value, set $Path, and your path variables should start getting registered.
The issue has been discussed extensively in this question.

Jenkins pipeline script global variable

I am learning jenkins, and am working on a sample pipeline
pipeline {
agent any
stages {
stage('Stage1') {
steps {
bat '''
cd C:/Users/roger/project/
python -u script1.py
'''
}
}
stage('Stage2') {
steps {
bat '''
cd cd C:/Users/roger/project/abc/
python -u script2.py
'''
}
}
stage('Stage3') {
steps {
bat '''
cd cd C:/Users/roger/project/abc/new_dir/
python -u demo.py
'''
}
}
}
}
is there a way to store the base path of project C:/Users/roger/project/ as a variable, so that it can be used to append new path to it instead of writing the whole path.
How could I write above stages, so that I don't have to repeat writing the same base path each time to each stage
You have several options, the easiest way will be to define the parameter inside the environment directive (read more) which will make the parameter available for all stages in the pipeline and will also load it to the execution environment of any interpreter step like sh, bat and powershell thus making the parameter also available to the scripts you execute as an environment variable.
In addition the environment directive supports credential parameters which is very useful.
In your case it will look like:
pipeline {
agent any
environment {
BASE_PATH = 'C:/Users/roger/project/'
}
stages {
stage('Stage1') {
steps {
// Using the parameter as a runtime environment variable with bat syntax %%
bat '''
cd %BASE_PATH%
python -u script1.py
'''
}
}
stage('Stage2') {
steps {
// Using groovy string interpolation to construct the command with the parameter value
bat """
cd ${env.BASE_PATH}abc/
python -u script2.py
"""
}
}
}
}
Another option you have is to use global variables defined at the top section of the pipeline, which will behave like any groovy variable and will be available for all stages in your pipeline (but not for the execution environment of interpreter steps).
Something like:
BASE_PATH = 'C:/Users/roger/project/'
pipeline {
agent any
stages {
stage('Stage1') {
steps {
// Using the parameter insdie a dir step to change directory
dir(BASE_PATH) {
bat 'python -u script1.py'
}
}
}
stage('Stage2') {
steps {
// Using groovy string interpolation to construct the command with the parameter value
bat """
cd ${BASE_PATH}abc/
python -u script2.py
"""
}
}
}
}

Passing parameter in Jenkinsfile to a shell command within a Docker container

I have a Jenkinsfile with a String parameter env_vars. With this parameter I want to set custom environment variables which I want to set later with a shell command within the started Docker container. It is important to set such environment variables on runtime.
This is my simple Jenkinsfile:
pipeline {
options {
timestamps()
}
agent {
node {
label 'master'
}
}
parameters {
string(name: 'env_vars', defaultValue: 'MY_USER_PASSWORD=abc MY_USER_NAME=def', description: 'the ENV variables to set before starting the tests')
}
stages {
stage ('TESTS') {
steps {
script {
withDockerRegistry([credentialsId: 'XXX', url: 'http://example.com']) {
withDockerContainer(image: 'myDockerImage:latest') {
withCredentials([string(credentialsId: 'cred1', variable: 'cred1'), string(credentialsId: 'cred2', variable: 'cred2')]) {
sh '''
# here we go to run npm
${env_vars} npm run test -- chrome --tag=enabled
'''
}
}
}
}
}
}
}
}
And this error I will get in Jenkins:
/var/lib/jenkins/jenkins3/jobs/zTestMG/workspace#tmp/durable-40340d0e/script.sh: line 4: MY_USER_PASSWORD=abc: command not found
One possible workaround is using eval for the shell command:
eval "${env_vars} npm run test -- chrome --tag=enabled"
But I don't want to use eval, because later I have to evaluate the result of the npm run command. And when using eval I will get new problems.
How can I solve the problem to use the String parameter in the shell command within the Docker container?
I have found a possible solution for me. I replace my shell command in two different once:
export ${env_vars}
npm run ${run_script_method} -- ${browser} --tag=${tags}

Pass variables between Jenkins stages

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.
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}"
}
}
}
}
}
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'
}
}
}
}

Jenkins Pipeline: Executing a shell script

I have create a pipeline like below and please note that I have the script files namely- "backup_grafana.sh" and "gitPush.sh" in source code repository where the Jenkinsfile is present. But I am unable to execute the script because of the following error:-
/home/jenkins/workspace/grafana-backup#tmp/durable-52495dad/script.sh:
line 1: backup_grafana.sh: not found
Please note that I am running jenkins master on kubernetes in a pod. So copying scripts files as suggested by the error is not possible because the pod may be destroyed and recreated dynamically(in this case with a new pod, my scripts will no longer be available in the jenkins master)
pipeline {
agent {
node {
label 'jenkins-slave-python2.7'
}
}
stages {
stage('Take the grafana backup') {
steps {
sh 'backup_grafana.sh'
}
}
stage('Push to the grafana-backup submodule repository') {
steps {
sh 'gitPush.sh'
}
}
}
}
Can you please suggest how can I run these scripts in Jenkinsfile? I would like to also mention that I want to run these scripts on a python slave that I have already created finely.
If the command 'sh backup_grafana.sh' fails to execute when it actually should have successfully executed, here are two possible solutions.
1) Maybe you need a dot slash in front of those executable commands to tell your shell where they are. if they are not in your $PATH, you need to tell your shell that they can be found in the current directory. here's the fixed Jenkinsfile with four non-whitespace characters added:
pipeline {
agent {
node {
label 'jenkins-slave-python2.7'
}
}
stages {
stage('Take the grafana backup') {
steps {
sh './backup_grafana.sh'
}
}
stage('Push to the grafana-backup submodule repository') {
steps {
sh './gitPush.sh'
}
}
}
}
2) Check whether you have declared your file as a bash or sh script by declaring one of the following as the first line in your script:
#!/bin/bash
or
#!/bin/sh

Resources