I have the following situation:
.
.
.
stage('My stage')
{
steps
{
sh 'my_command1.sh'
sh 'my_command2.sh'
sh 'my_command3.sh'
}
}
.
.
.
It is possible for example to execute the step "my_command2.sh" only if there is a particular condition? I know that it is possible to define conditional statement with "when" token, but the condition has an impact to all the stage. I want to limit this behavior only for a single step.
Thanks.
You can use script
Then your stage will be like:
stage('My Stage') {
steps {
script{
sh 'my_command1.sh'
if(condition){
sh 'my_command2.sh'
}
sh 'my_command3.sh'
}
}
}
You can also check declarative step from official documentation
Related
I would like to install maven and npm via docker agent using Jenkins declarative pipeline. But When I would like to use below script Jenkins throws an error as below. It might be using agent none but how can I use node with docker agent via declarative pipeline jenkins.
ERROR: Attempted to execute a step that requires a node context while
‘agent none’ was specified. Be sure to specify your own ‘node { ... }’
blocks when using ‘agent none’.
I try to set agent any but this time I received an error "Still waiting to schedule task
Waiting for next available executor"
pipeline {
agent none
// environment{
proxy = https://
// stable_revision = sh(script: 'curl -H "Authorization: Basic $base64encoded"
// }
stages {
stage('Build') {
agent {
docker { image 'maven:3-alpine'}
}
steps {
sh 'mvn --version'
echo "$apigeeUsername"
echo "Stable Revision: ${env.stable_revision}"
}
}
stage('Test') {
agent { docker { image 'maven:3-alpine' image 'node:8.12.0' } }
environment {
HOME = '.'
}
steps {
script{
try{
sh 'npm install'
sh 'node --version'
//sh 'npm test/unit/*.js'
}catch(e){
throw e
}
}
}
}
// stage('Policy-Code Analysis') {
// steps{
// sh "npm install -g apigeelint"
// sh "apigelint -s wiservice_api_v1/apiproxy/ -f codeframe.js"
// }
// }
stage('Promotion'){
steps{
timeout(time: 2, unit: 'DAYS') {
input 'Do you want to Approve?'
}
}
}
stage('Deployment'){
steps{
sh "mvn -f wiservice_api_v1/pom.xml install -Ptest -Dusername=${apigeeUsername} -Dpassword=${apigeePassword} -Dapigee.config.options=update"
//sh "mvn apigee-enterprise:install -Ptest -Dusername=${apigeeUsername} -Dpassword=${apigeePassword} "
}
}
}
}
Basically your error message tells you everything you need to know:
ERROR: Attempted to execute a step that requires a node context while
‘agent none’ was specified. Be sure to specify your own ‘node { ... }’
blocks when using ‘agent none’.
so what is the issue here? You use agent none for your pipeline which means you do not specify a specific agent for all stages. An agent executes a specific stage. If a stage has no agent it can't be executed and this is your issue here.
The following 2 stage have no agent which means no docker-container / server or whatever where it can be executed.
stage('Promotion'){
steps{
timeout(time: 2, unit: 'DAYS') {
input 'Do you want to Approve?'
}
}
}
stage('Deployment'){
steps{
sh "mvn -f wiservice_api_v1/pom.xml install -Ptest -Dusername=${apigeeUsername} -Dpassword=${apigeePassword} -Dapigee.config.options=update"
//sh "mvn apigee-enterprise:install -Ptest -Dusername=${apigeeUsername} -Dpassword=${apigeePassword} "
}
}
so you have to add agent { ... } to both stage seperately or use a global agent like following and remove the agent from your stages:
pipeline {
agent {
docker { image 'maven:3-alpine'}
} ...
For further information see guide to set up master and agent machines or distributed jenkins builds or the official documentation.
I think you meant to add agent any instead of agent none, because each stage requires at least one agent (either declared at the top for the pipeline or per stage).
Also, I see some more issues.
Your Test stage specifies two images for the same stage.
agent { docker { image 'maven:3-alpine' image 'node:8.12.0' } } although, your stage is executing only npm commands. I believe only one of the image will be downloaded.
To clarify bit more on mkemmerz answer, your Promotion stage is designed correctly. If you plan to have an input step in the pipeline, do not add an agent for the pipeline because input steps block the executor context. See this link https://jenkins.io/blog/2018/04/09/whats-in-declarative/
I want to wrap steps and post in a function.
This works fine:
pipeline {
agent any
stages {
stage('Test') {
steps {
whateverFunction()
}
post {
sh 'echo test'
}
}
}
}
void whateverFunction() {
sh 'ls /'
}
But as soon as I pack steps and post in my function it does not work. (Fail's with the error: steps in a stage must be in a ‘steps’ block.)
pipeline {
agent any
stages {
stage('Test') {
whateverFunction()
}
}
}
void whateverFunction() {
steps {
sh 'echo test'
}
post {
sh 'echo test'
}
}
What I also tried is to have steps and then call my function in that step with a steps inside. Basically warping steps in steps which leads to the behavior that no step is executed. (But apparently it would be a valid Jenkins-file)
Is it possible to have a function which contains steps and post inside a stage. Or is there a way to achieve a similar functionality?
Seems to be that you cannot create similar functionality with post inside a method in declarative pipeline. To achieve this you can try to use scripted pipelines.
For declarative pipelines you can use post section only inside stage (example) or after stages block (example).
I'm trying to create a Jenkins pipeline where I in my first stage I define a variable in a sh shell script.
Then I want to run the next stages using a "when" condition depending on the previous defined variable.
pipeline {
agent { label 'php71' }
stages {
stage('Prepare CI...') {
steps{
sh '''
# Get the comment that was made on the PR
COMMENT=`echo $payload | jq .comment.body | tr -d '"'`
if [ "$COMMENT" = ":repeat: Jenkins" ]; then
BUILD="build"
fi
'''
}
}
stage('Build Pre Envrionment') {
agent { label 'php71' }
when {
expression { return $BUILD == "build" }
}
steps('Build') {
sh '''
echo $BUILD
echo $COMMENT
'''
}
}
}
}
This gives me an error:
groovy.lang.MissingPropertyException: No such property: $BUILD for class: groovy.lang.Binding
How can I do it? Is it possible?
Thank you!
Probably use Jenkins scripted pipeline which is more flexible than declarative.
Print the value in the sh script and use returnStdout to make it available to the pipeline script. See How to do I get the output of a shell command executed using into a variable from Jenkinsfile (groovy)? for more details.
I have multibranch with following code, i want that pipeline should start building only if
if commit message doesn't contain word like "INTERIM"
or how i can check "changed since last successful Build" condition
pipeline {
agent any
stages {
stage('Build') {
when { changelog '.*INTERIM' }
steps {
echo 'I am building '+scm.branches[0].name+' branch.'
sh "echo Shell ${CHANGE_author}"
echo "echo Shell ${CHANGE_author}"
}
}
}
}
First condition is working but how i can achieve second?
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'
}
}
}
}