Skip job in a pipeline based on exit code
Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.
This is a proposal based on the discussion in #16733 (comment 900108650) which inspired by the proposal in #273157 (closed)
Problem
Users want to skip a specific job based on the job exit code
Proposal
test_job:
script:
- execute_script
# if the script exit code is 137 or 255 this specific job will be skiped and the pipeline will continue to run
allow_skip: # Or Skip or skip_job
exit_codes: # User defined exit code
- 137
- 255
in case we have and test job had skipped
build job:
needs: test_job
build job will also get skipped
History, Explanation, and Proposal
Today, we have the allow_failure:exit_codes keyword, which you dynamically mark your job allow_failure: true depending on the job exit code.
test_job_1:
script:
- echo "Run a script that results in exit code 1. This job fails."
- exit 1
allow_failure:
exit_codes: 137
test_job_2:
script:
- echo "Run a script that results in exit code 137. This job is allowed to fail."
- exit 137
allow_failure:
exit_codes:
- 137
- 255
Now, in this issue, the ask is something like this;
test_job_1:
script:
- echo "Run a script that results in exit code 1. This job fails."
- exit 1
skip:
exit_codes: 137
test_job_2:
script:
- echo "Run a script that results in exit code 137. This job is skipped."
- exit 137
skip:
exit_codes:
- 137
- 255
Of course, I'd prefer to combine them;
test:
script:
- ...
exit_codes: # or `statuses` as in Marcel's suggestion: https://gitlab.com/gitlab-org/gitlab/-/issues/357819#note_902816616
skip:
- 1
- 2
allow_failure:
- 3
- 4
Now, let's see if this is technically possible...
When a job finishes, the Runner sends an update to GitLab about it with some parameters including state, exit_code, etc. The API endpoint calls Ci::UpdateBuildStateService and runs the code that updates the job status.
The decision of whether a job is completed success or failure is on the Runner. When a job finishes other than the exit code 0, it's a failed status. Let's say we want this;
test:
script: exit 123
exit_codes:
skip: [123]
- This job will fail because it completes its exits with a non-zero status.
- The Runner will send
state: failed, exit_code: 123. - The GitLab runs
Ci::UpdateBuildStateServicefor the build. - Since it's a failed status, we'll run
build.drop_with_exit_code!(params[:failure_reason], params[:exit_code]).
Let's hack this for now;
def drop_with_exit_code!(failure_reason, exit_code)
skip! if exit_code == 123 # <<-- HACK
failure_reason ||= :unknown_failure
result = drop!(::Gitlab::Ci::Build::Status::Reason.new(self, failure_reason, exit_code))
::Ci::TrackFailedBuildWorker.perform_async(id, exit_code, failure_reason)
result
end
Click to expand
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1115,6 +1115,8 @@ def debug_mode?
end
def drop_with_exit_code!(failure_reason, exit_code)
+ skip! if exit_code == 123
+
failure_reason ||= :unknown_failure
result = drop!(::Gitlab::Ci::Build::Status::Reason.new(self, failure_reason, exit_code))
::Ci::TrackFailedBuildWorker.perform_async(id, exit_code, failure_reason)
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -147,7 +147,7 @@ class CommitStatus < Ci::ApplicationRecord
end
event :skip do
- transition [:created, :waiting_for_resource, :preparing, :pending] => :skipped
+ transition [:created, :waiting_for_resource, :preparing, :pending, :running] => :skipped
end
event :drop do
Result;
It "works"
