Jenkins pipeline: detect if a stage is started with the "Restart from stage" icon - jenkins

Let's say I have a declarative pipeline. I want to run a stage only when 'Restart from stage' icon is used ?
Is there a way to do this (a method, a variable...)? I want to run the stage only if "Restart from stage" is used
stage('Test') {
when {
expression {
// An expression to detect if Restart from this stage is used
}
}
steps {
sh 'echo 1'
}
}

You can define a global variable that will hold a Boolean value representing if the pipeline was executed from the beginning or from a specific stage, update it in your first stage and use it later on in the when condition to determine if a restart from stage has occurred.
Something like:
RESTART = true
pipeline {
agent any
stages {
stage('Setup') {
steps {
script{
// signaling pipeline was executed from the beginning (first stage)
RESTART = false
}
// other setup steps
}
}
stage('Test') {
when {
expression { return RESTART }
}
steps {
sh 'echo 1'
}
}
}
}
Another nice option based on #Pamela's answer for using a cause condition, is to use the built in triggeredBy option in the when directive, thus avoiding the need to use getBuildCauses() and the need to filter all causes, and instaed get the condition out of the box.
Something like:
stage('Test') {
when { triggeredBy 'RestartDeclarativePipelineCause' }
steps {
sh 'echo 1'
}
}

You can use currentBuild.getBuildCauses(): https://www.jenkins.io/doc/pipeline/examples/#get-build-cause
Then, in your Test stage add when expression checking the cause of the build matches the one you need.
stage('Test') {
when {
expression {
return currentBuild.getBuildCauses().any { cause ->
cause._class == 'org.jenkinsci.plugins.pipeline.modeldefinition.causes.RestartDeclarativePipelineCause'
}
}
}
steps {
sh 'echo 1'
}
}

Related

How to configure Jenkinsfile to only run a specific stage when running a daily cron job?

I've set a cron job to run every night, however I only want it to run stage B within the Jenkinsfile not all of them.
pipeline {
agent any
triggers {
cron('#midnight')
}
}
stages {
stage('A') {
...
}
stage('B'){
when {
allOf {
expression { env.CHANGE_TARGET == "master" }
branch "PR-*"
}
}
steps {
sh """
echo 'running script'
make run-script
"""
}
}
stage('C') {
...
}
Without removing the conditionals in Stage B, I can't seem to figure out how to specify the cron to explicitly only run Stage B of the Jenkinsfile - I need to run that makefile script only when those conditionals are met OR during the daily midnight cron job
You can achieve what you want with the Parameterized Scheduler Plugin which enables you to define cron triggers that trigger the job with a specific environment variable, you can then use this variable as a condition to determine which step to execute.
in your case you can use that environment variable in the when directive of each stage to determine if it should run or not according to the variable.
Something like:
pipeline {
agent any
parameters {
booleanParam(name: 'MIDNIGHT_BUILD', defaultValue: 'true', description: 'Midnight build')
}
triggers {
parameterizedCron('''
0 * * * * %MIDNIGHT_BUILD=true
''')
}
stages {
stage('A') {
when {
expression { !env.MIDNIGHT_BUILD }
}
steps {
...
}
}
stage('B') {
when {
expression { env.MIDNIGHT_BUILD || env.CHANGE_TARGET == "master" }
}
steps {
sh """
echo 'running script'
make run-script
"""
}
}
stage('C') {
when {
expression { !env.MIDNIGHT_BUILD }
}
steps {
...
}
}
}
}

Scripted jenkinsfile parallel stage

I am attempting to write a scripted Jenkinsfile using the groovy DSL which will have parallel steps within a set of stages.
Here is my jenkinsfile:
node {
stage('Build') {
sh 'echo "Build stage"'
}
stage('API Integration Tests') {
parallel Database1APIIntegrationTest: {
try {
sh 'echo "Build Database1APIIntegrationTest parallel stage"'
}
finally {
sh 'echo "Finished this stage"'
}
}, Database2APIIntegrationTest: {
try {
sh 'echo "Build Database2APIIntegrationTest parallel stage"'
}
finally {
sh 'echo "Finished this stage"'
}
}, Database3APIIntegrationTest: {
try {
sh 'echo "Build Database3APIIntegrationTest parallel stage"'
}
finally {
sh 'echo "Finished this stage"'
}
}
}
stage('System Tests') {
parallel Database1APIIntegrationTest: {
try {
sh 'echo "Build Database1APIIntegrationTest parallel stage"'
}
finally {
sh 'echo "Finished this stage"'
}
}, Database2APIIntegrationTest: {
try {
sh 'echo "Build Database2APIIntegrationTest parallel stage"'
}
finally {
sh 'echo "Finished this stage"'
}
}, Database3APIIntegrationTest: {
try {
sh 'echo "Build Database3APIIntegrationTest parallel stage"'
}
finally {
sh 'echo "Finished this stage"'
}
}
}
}
I want to have 3 stages: Build; Integration Tests and System Tests.
Within the two test stages, I want to have 3 sets of the tests executed in parallel, each one against a different database.
I have 3 available executors. One on the master, and 2 agents and I want each parallel step to run on any available executor.
What I've noticed is that after running my pipeline, I only see the 3 stages, each marked out as green. I don't want to have to view the logs for that stage to determine whether any of the parallel steps within that stage were successful/unstable/failed.
I want to be seeing the 3 steps within my test stages - marked as either green, yellow or red (Success, unstable or failed).
I've considered expanding the tests out into their own stages, but have realised that parallel stages are not supported (Does anyone know whether this will ever be supported?), so I cannot do this as the pipeline would take far too long to complete.
Any insight would be much appreciated, thanks
In Jenkins scripted pipeline, parallel(...) takes a Map describing each stage to be built. Therefore you can programatically construct your build stages up-front, a pattern which allows flexible serial/parallel switching.
I've used code similar to this where the prepareBuildStages returns a List of Maps, each List element is executed in sequence whilst the Map describes the parallel stages at that point.
// main script block
// could use eg. params.parallel build parameter to choose parallel/serial
def runParallel = true
def buildStages
node('master') {
stage('Initialise') {
// Set up List<Map<String,Closure>> describing the builds
buildStages = prepareBuildStages()
println("Initialised pipeline.")
}
for (builds in buildStages) {
if (runParallel) {
parallel(builds)
} else {
// run serially (nb. Map is unordered! )
for (build in builds.values()) {
build.call()
}
}
}
stage('Finish') {
println('Build complete.')
}
}
// Create List of build stages to suit
def prepareBuildStages() {
def buildStagesList = []
for (i=1; i<5; i++) {
def buildParallelMap = [:]
for (name in [ 'one', 'two', 'three' ] ) {
def n = "${name} ${i}"
buildParallelMap.put(n, prepareOneBuildStage(n))
}
buildStagesList.add(buildParallelMap)
}
return buildStagesList
}
def prepareOneBuildStage(String name) {
return {
stage("Build stage:${name}") {
println("Building ${name}")
sh(script:'sleep 5', returnStatus:true)
}
}
}
The resulting pipeline appears as:
There are certain restrictions on what can be nested within a parallel block, refer to the pipeline documentation for exact details. Unfortunately much of the reference seems biased towards declarative pipeline, despite it being rather less flexible than scripted (IMHO).
The pipeline examples page was the most helpful.
Here's a simple example without loops or functions based on #Ed Randall's post:
node('docker') {
stage('unit test') {
parallel([
hello: {
echo "hello"
},
world: {
echo "world"
}
])
}
stage('build') {
def stages = [:]
stages["mac"] = {
echo "build for mac"
}
stages["linux"] = {
echo "build for linux"
}
parallel(stages)
}
}
...which yields this:
Note that the values of the Map don't need to be stages. You can give the steps directly.
Here is an example from their docs:
Parallel execution
The example in the section above runs tests across two different platforms in a linear series. In practice, if the make check execution takes 30 minutes to complete, the "Test" stage would now take 60 minutes to complete!
Fortunately, Pipeline has built-in functionality for executing portions of Scripted Pipeline in parallel, implemented in the aptly named parallel step.
Refactoring the example above to use the parallel step:
// Jenkinsfile (Scripted Pipeline)
stage('Build') {
/* .. snip .. */
}
stage('Test') {
parallel linux: {
node('linux') {
checkout scm
try {
unstash 'app'
sh 'make check'
}
finally {
junit '**/target/*.xml'
}
}
},
windows: {
node('windows') {
/* .. snip .. */
}
}
}
To simplify the answer of #Ed Randall here.
Remember this is Jenkinsfile scripted (not declarative)
stage("Some Stage") {
// Stuff ...
}
stage("Parallel Work Stage") {
// Prealocate dict/map of branchstages
def branchedStages = [:]
// Loop through all parallel branched stage names
for (STAGE_NAME in ["Branch_1", "Branch_2", "Branch_3"]) {
// Define and add to stages dict/map of parallel branch stages
branchedStages["${STAGE_NAME}"] = {
stage("Parallel Branch Stage: ${STAGE_NAME}") {
// Parallel stage work here
sh "sleep 10"
}
}
}
// Execute the stages in parallel
parallel branchedStages
}
stage("Some Other Stage") {
// Other stuff ...
}
Please pay attention to the curly braces.
This will result in the following result (with the BlueOcean Jenkins Plugin):
I was also trying similar sort of steps to execute parallel stages and display all of them in a stage view. You should write a stage inside a parallel step as shown in the following code block.
// Jenkinsfile (Scripted Pipeline)
stage('Build') {
/* .. Your code/scripts .. */
}
stage('Test') {
parallel 'linux': {
stage('Linux') {
/* .. Your code/scripts .. */
}
}, 'windows': {
stage('Windows') {
/* .. Your code/scripts .. */
}
}
}
The above example with a FOR is wrong, as varible STAGE_NAME will be overwritten everytime, I had the same problem as Wei Huang.
Found the solution here:
https://www.convalesco.org/notes/2020/05/26/parallel-stages-in-jenkins-scripted-pipelines.html
def branchedStages = [:]
def STAGE_NAMES = ["Branch_1", "Branch_2", "Branch_3"]
STAGE_NAMES.each { STAGE_NAME ->
// Define and add to stages dict/map of parallel branch stages
branchedStages["${STAGE_NAME}"] = {
stage("Parallel Branch Stage: ${STAGE_NAME}") {
// Parallel stage work here
sh "sleep 10"
}
}
}
parallel branchedStages
I have used as below where the three stages are parallel.
def testCases() {
stage('Test Cases') {
def stages = [:] // declaring empty list
stages['Unit Testing'] = {
sh "echo Unit Testing completed"
}
stages['Integration Testing'] = {
sh "echo Integration Testing completed"
}
stages['Function Testing'] = {
sh "echo Function Testing completed"
}
parallel(stages) // declaring parallel stages
}
}
I have used stage{} in parallel blocks several times. Then each stage shows up in the Stage view. The parent stage that contains parallel doesn't include the timing for all the parallel stages, but each parallel stage shows up in stage view.
In blue ocean, the parallel stages appear separately instead of the stages showing. If there is a parent stage, it shows as the parent of the parallel stages.
If you don't have the same experience, maybe a plugin upgrade is due.

How to lock multiple stages of declarative Jenkins pipeline?

I want to run multiple stages inside a lock within a declarative Jenkins pipeline:
pipeline {
agent any
stages {
lock(resource: 'myResource') {
stage('Stage 1') {
steps {
echo "my first step"
}
}
stage('Stage 2') {
steps {
echo "my second step"
}
}
}
}
}
I get the following error:
Started by user anonymous
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 10: Expected a stage # line 10, column 9.
lock(resource: 'myResource') {
^
WorkflowScript: 10: Stage does not have a name # line 10, column 9.
lock(resource: 'myResource') {
^
WorkflowScript: 10: Nothing to execute within stage "null" # line 10, column 9.
lock(resource: 'myResource') {
^
3 errors
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1085)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:603)
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:581)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:558)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268)
at groovy.lang.GroovyShell.parseClass(GroovyShell.java:688)
at groovy.lang.GroovyShell.parse(GroovyShell.java:700)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:116)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:430)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:393)
at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:257)
at hudson.model.ResourceController.execute(ResourceController.java:97)
at hudson.model.Executor.run(Executor.java:405)
Finished: FAILURE
What's the problem here? The documentation explicitly states:
lock can be also used to wrap multiple stages into a single
concurrency unit
It should be noted that you can lock all stages in a pipeline by using the lock option:
pipeline {
agent any
options {
lock resource: 'shared_resource_lock'
}
stages {
stage('will_already_be_locked') {
steps {
echo "I am locked before I enter the stage!"
}
}
stage('will_also_be_locked') {
steps {
echo "I am still locked!"
}
}
}
}
This has since been addressed.
You can lock multiples stages by grouping them in a parent stage, like this :
stage('Parent') {
options {
lock('something')
}
stages {
stage('one') {
...
}
stage('two') {
...
}
}
}
(Don't forget you need the Lockable Resources Plugin)
The problem is that, despite the fact that declarative pipelines were technically available in beta in September, 2016, the blog post you reference (from October) is documenting scripted pipelines, not declarative (it doesn't say as much, so I feel your pain). Lockable resources hasn't been baked in as a declarative pipeline step in a way that would enable the feature you're looking for yet.
You can do:
pipeline {
agent { label 'docker' }
stages {
stage('one') {
steps {
lock('something') {
echo 'stage one'
}
}
}
}
}
But you can't do:
pipeline {
agent { label 'docker' }
stages {
lock('something') {
stage('one') {
steps {
echo 'stage one'
}
}
stage('two') {
steps {
echo 'stage two'
}
}
}
}
}
And you can't do:
pipeline {
agent { label 'docker' }
stages {
stage('one') {
lock('something') {
steps {
echo 'stage one'
}
}
}
}
}
You could use a scripted pipeline for this use case.
If the resource is only used by this pipeline you could also disable concurrent builds:
pipeline {
agent any
options {
disableConcurrentBuilds()
}
stages {
stage('will_already_be_locked') {
steps {
echo "I am locked before I enter the stage!"
}
}
stage('will_also_be_locked') {
steps {
echo "I am still locked!"
}
}
}
}
Altho the options{} block offers this functionality it is not posible to use it in some use cases.
Lets say that you have to name your lock() with a specific name depending on a branch or an environment. You have a pipeline which you dont want to be block by disableConcurrentBuilds() and lock resources depending on a discriminator. You can not name your lock() inside the options{} block by using a environment variable or any other variable from the pipeline because the block is evaluated outside the agent.
The best solution in my opinion is the following:
pipeline {
agent { label 'docker' }
stages {
stage('Wrapper') {
steps {
script {
lock(env.BRANCH_NAME) {
stage('Stage 1') {
sh('echo "stage1"')
}
stage('Stage 2') {
sh('echo "stage2"')
}
}
}
}
}
}
}
Keep in mind that the script {} block takes a block of Scripted Pipeline and executes that in the Declarative Pipeline so no steps{} are allowed inside.
I run multiple build and test containers on the same build nodes. The test containers must lock up the node name as db username for the tests.
lock(resource: "${env.NODE_NAME}" as String, variable: 'DBUSER')
Locks in options are computed at load time, but NODE_NAME is unknown that early. In order to lock multiple stages for visual effect, we can create stages inside script block, i.e. 'run test' stage in the snippet. The stage visualization is just as good as other stage blocks.
pipeline {
agent any
stages {
stage('refresh') {
steps {
echo "freshing on $NODE_NAME"
lock(resource: "${env.NODE_NAME}" as String, variable: 'DBUSER') {
sh '''
printenv | sort
'''
script {
stage('run test')
sh '''
printenv | sort
'''
}
}
}
}
}
}

Use a lightweight executor for a declarative pipeline stage (agent none)

I'm using Jenkins Pipeline with the declarative syntax, currently with the following stages:
Prepare
Build (two parallel sets of steps)
Test (also two parallel sets of steps)
Ask if/where to deploy
Deploy
For steps 1, 2, 3, and 5 I need and agent (an executor) because they do actual work on the workspace. For step 4, I don't need one, and I would like to not block my available executors while waiting for user input. This seem to be referred to as either a "flyweight" or "lightweight" executor for the classic, scripted syntax, but I cannot find any information on how to achieve this with the declarative syntax.
So far I've tried:
Setting an agent directly in the pipeline options, and then setting agent none on the stage. This has no effect, and the pipeline runs as normalt, blocking the executor while waiting for input. It is also mentioned in the documentation that it will have no effect, but I thought I'd give it a shot anyway.
Setting agent none in the pipeline options, and then setting an agent for each stage except #4. Unfortunately, but expectedly, this allocates a new workspace for every stage, which in turn requires me to stash and unstash. This is both messy and gives me further problems in the parallel stages (2 and 3) because I cannot have code outside the parallel construct. I assume the parallel steps run in the same workspace, so stashing/unstashing in both would have unfortunate results.
Here is an outline of my Jenkinsfile:
pipeline {
agent {
label 'build-slave'
}
stages {
stage("Prepare build") {
steps {
// ...
}
}
stage("Build") {
steps {
parallel(
frontend: {
// ...
},
backend: {
// ...
}
)
}
}
stage("Test") {
steps {
parallel(
jslint: {
// ...
},
phpcs: {
// ...
},
)
}
post {
// ...
}
}
stage("Select deploy target") {
steps {
script {
// ... code that determines choiceParameterDefinition based on branch name ...
try {
timeout(time: 5, unit: 'MINUTES') {
deployEnvironment = input message: 'Deploy target', parameters: [choiceParameterDefinition]
}
} catch(ex) {
deployEnvironment = null
}
}
}
}
stage("Deploy") {
when {
expression {
return binding.variables.get("deployEnvironment")
}
}
steps {
// ...
}
}
}
post {
// ...
}
}
Am I missing something here, or is it just not possible in the current version?
Setting agent none at the top level, then agent { label 'foo' } on every stage, with agent none again on the input stage seems to work as expected for me.
i.e. Every stage that does some work runs on the same agent, while the input stage does not consume an executor on any agent.
pipeline {
agent none
stages {
stage("Prepare build") {
agent { label 'some-agent' }
steps {
echo "prepare: ${pwd()}"
}
}
stage("Build") {
agent { label 'some-agent' }
steps {
parallel(
frontend: {
echo "frontend: ${pwd()}"
},
backend: {
echo "backend: ${pwd()}"
}
)
}
}
stage("Test") {
agent { label 'some-agent' }
steps {
parallel(
jslint: {
echo "jslint: ${pwd()}"
},
phpcs: {
echo "phpcs: ${pwd()}"
},
)
}
}
stage("Select deploy target") {
agent none
steps {
input message: 'Deploy?'
}
}
stage("Deploy") {
agent { label 'some-agent' }
steps {
echo "deploy: ${pwd()}"
}
}
}
}
However, there are no guarantee that using the same agent label within a Pipeline will always end up using the same workspace, e.g. as another build of the same job while the first build is waiting on the input.
You would have to use stash after the build steps. As you note, this cannot be done normally with parallel at the moment, so you'd have to additionally use a script block, in order to write a snippet of Scripted Pipeline for the stashing/unstashing after/before the parallel steps.
There is a workaround to use the same build slave in the other stages.
You can set a variable with the node name and use it in the others.
ie:
pipeline {
agent none
stages {
stage('First Stage Gets Agent Dynamically') {
agent {
node {
label "some-agent"
}
}
steps {
echo "first stage running on ${NODE_NAME}"
script {
BUILD_AGENT = NODE_NAME
}
}
}
stage('Second Stage Setting Node by Name') {
agent {
node {
label "${BUILD_AGENT}"
}
}
steps {
echo "Second stage using ${NODE_NAME}"
}
}
}
}
As of today (2021), you can use nested stages (https://www.jenkins.io/doc/book/pipeline/syntax/#sequential-stages) to group all the stages that must run in the same workspace before the input step, and all the stages that must be run in the same workspace after the input step. Of course, you need to stash or to store artifacts in some external repository before the input step, because the second workspace may not be the same than the first one:
pipeline {
agent none
stages {
stage('Deployment to Preproduction') {
agent any
stages {
stage('Stage PRE.1') {
steps {
echo "StagePRE.1"
sleep(10)
}
}
stage('Stage PRE.2') {
steps {
echo "Stage PRE.2"
sleep(10)
}
}
}
}
stage('Stage Ask Deploy') {
steps {
input message: 'Deploy to production?'
}
}
stage('Deployment to Production') {
agent any
stages {
stage('Stage PRO.1') {
steps {
echo "Stage PRO.1"
sleep(10)
}
}
stage('Stage PRO.2') {
steps {
echo "Stage PRO.2"
sleep(10)
}
}
}
}
}
}

How do I assure that a Jenkins pipeline stage is always executed, even if a previous one failed?

I am looking for a Jenkinsfile example of having a step that is always executed, even if a previous step failed.
I want to assure that I archive some builds results in case of failure and I need to be able to have an always-running step at the end.
How can I achieve this?
We switched to using Jenkinsfile Declarative Pipelines, which lets us do things like this:
pipeline {
agent any
stages {
stage('Test') {
steps {
sh './gradlew check'
}
}
}
post {
always {
junit 'build/reports/**/*.xml'
}
}
}
References:
Tests and Artifacts
Jenkins Pipeline Syntax
try {
sh "false"
} finally {
stage 'finalize'
echo "I will always run!"
}
Another possibility is to use a parallel section in combination with a lock. For example:
pipeline {
stages {
parallel {
stage('Stage 1') {
steps {
lock('MY_LOCK') {
echo 'do stuff 1'
}
}
}
stage('Stage 2') {
steps {
lock('MY_LOCK') {
echo 'do stuff 2'
}
}
}
}
}
}
Parallel stages in a parallel section only abort other stages in the same parallel section if the fail fast option for the parallel section is set. See the docs.

Resources