The after_script keyword in GitLab CI/CD allows you to define a list of shell commands that should be executed after the main script commands of a job. A crucial characteristic of after_script is that its commands are executed regardless of the success or failure of the job’s script or before_script sections.
This makes after_script ideal for tasks like:
- Cleaning up temporary files or resources.
- Logging final status or collecting diagnostic information.
- De-authenticating from services.
- Uploading artifacts even if the job failed.
You can define after_script at two main levels:
- Globally (as a default): Using
default:after_script:. These commands will run after every job unless a job overrides it. - Job-level: Defined within a specific job. This overrides any global
after_scriptfor that particular job.
Example .gitlab-ci.yml with after_script:
YAML
# .gitlab-ci.yml
# 1. Default after_script: Applied to all jobs unless overridden
default:
image: alpine:latest # Using a common default image
after_script:
- echo "--- Default after_script (Global) ---"
- echo "Job finished at: $(date)"
- echo "Job status was: $CI_JOB_STATUS" # CI_JOB_STATUS reflects the script's outcome
- echo "Cleaning up default resources..."
stages:
- build
- test
- cleanup_test
# Job 1: Successful job that uses the default after_script
successful_job:
stage: build
before_script:
- echo "Before script for successful_job"
script:
- echo "--- Main script for successful_job ---"
- echo "This job will succeed."
- exit 0 # Explicitly succeed
# Job 2: Failing job that also uses the default after_script
failing_job:
stage: test
before_script:
- echo "Before script for failing_job"
script:
- echo "--- Main script for failing_job ---"
- echo "This job will intentionally fail."
- exit 1 # Explicitly fail
# Job 3: Job with a custom after_script (overrides the default)
job_with_custom_after_script:
stage: cleanup_test
after_script: # Job-level after_script overrides the default one
- echo "--- Custom after_script for job_with_custom_after_script ---"
- echo "Performing specific cleanup for this job."
- echo "Job status was: $CI_JOB_STATUS"
script:
- echo "--- Main script for job_with_custom_after_script ---"
- echo "This job uses its own custom after_script."
# Job 4: Job that disables after_script (even the default one)
job_without_any_after_script:
stage: cleanup_test
after_script: [] # Explicitly set to an empty array to disable
# You could also use `after_script: null` in some GitLab versions.
script:
- echo "--- Main script for job_without_any_after_script ---"
- echo "No after_script (neither default nor custom) will be executed for this job."
Code language: PHP (php)
Explanation:
default:after_script:- The
default:block allows setting default configurations. after_script:withindefault:defines commands that execute after every job, regardless of whether the job passed or failed (unless the job has its ownafter_scriptor disables it).- In this example:
- It prints introductory lines and the finish time.
- It prints the
CI_JOB_STATUS(a predefined variable indicating if the job’s main script succeeded, failed, etc.). - It simulates a default cleanup.
- The
successful_job:- This job completes its
scriptsection successfully (exit 0). - It does not have its own
after_script. - Therefore, it inherits and executes the commands from the
default:after_script:section after itsscriptblock. TheCI_JOB_STATUSwill likely besuccess.
- This job completes its
failing_job:- This job intentionally fails in its
scriptsection (exit 1). - It also does not have its own
after_script. - Despite the failure, it will still inherit and execute the commands from the
default:after_script:section. This demonstrates the key behavior ofafter_script. TheCI_JOB_STATUSwill likely befailed.
- This job intentionally fails in its
job_with_custom_after_script:- This job defines its own
after_script:block. - This job-level
after_scriptcompletely overrides (replaces) thedefault:after_script:. The commands fromdefault:after_script:will not run for this job. - Only the commands listed in this job’s
after_scriptwill execute after its mainscript, regardless of the main script’s outcome.
- This job defines its own
job_without_any_after_script:- This job explicitly sets
after_script: [](an empty array). - This tells GitLab CI not to run any
after_scriptcommands for this job, not even the ones defined in thedefault:after_script:section.
- This job explicitly sets
Key Concepts and Behavior:
- Purpose:
after_scriptis intended for cleanup, final logging, or any actions that must occur regardless of the job’s outcome. - Execution Order: For any given job:
before_script(if any) runs.- Main
scriptruns. after_script(if any) runs.
- Runs on Success or Failure: This is the most important characteristic. Commands in
after_scriptare executed even if thebefore_scriptorscriptsections fail. - Failure Handling in
after_script:- The exit code of commands in
after_scriptdoes not affect the overall job status. The job’s status is determined by thebefore_scriptandscriptsections. - However, if a command in
after_scriptfails, you might see errors in the job log for theafter_scriptphase.
- The exit code of commands in
- Array of Commands:
after_scripttakes an array of strings, where each string is a shell command executed sequentially. - Inheritance and Overriding:
- Job-level
after_scriptoverridesdefault:after_script. - To prevent any
after_script(including a global one) from running for a specific job, setafter_script: []orafter_script: null.
- Job-level
- Execution Context:
after_scriptcommands run on the same GitLab Runner that executed the job.- They generally have access to the same environment variables, files in the workspace (unless cleaned up by the job’s script), and services as the main
script. - The
CI_JOB_STATUSpredefined variable is particularly useful inafter_scriptto know the outcome of the job’s main execution.
- Timeout:
after_scripthas its own separate timeout, which is configured by the GitLab administrator (usually shorter than the main job timeout). - Common Use Cases:
- Removing temporary files or directories.
- Stopping services started during the job.
- Logging out from authenticated sessions.
- Uploading test reports or logs, especially if the job failed and artifacts wouldn’t be uploaded otherwise (though
artifacts:when:alwaysis often preferred for artifact uploading). - Sending notifications.
Using after_script ensures that critical cleanup and finalization steps are performed consistently, contributing to a more robust and manageable CI/CD pipeline.