How to Auto-Cancel Duplicate Pipelines
If an instance of a pipeline starts before the previous instance has been completed, use the outlined approach to abort the new instance so that multiple pipelines do not run concurrently.
It is, of course, possible to use resource groups to "pause" the new pipeline and wait for the previous jobs to complete. But this approach can be safer if we run hourly-ci.yml on a schedule and each run takes longer than 60 minutes to complete.
Sample Code
The following job can be included in a pipeline to check the currently-running pipelines and abort if there's already one running the same pipeline file on the same branch. The newer pipeline will fail with an error so that notifications will be sent (depending on the project's configuration), and action can be taken as appropriate.
Check for Running Pipeline:
extends: .agent_tag
image: $DATAOPS_UTILS_RUNNER_IMAGE
stage: Pipeline Initialisation
variables:
API_AUTH_HEADER: "PRIVATE-TOKEN: $LOCAL_PROJECT_TOKEN"
API_URL_PROJECT: https://app.dataops.live/api/v4/projects/$CI_PROJECT_ID
PIPELINE_URL_BASE: https://app.dataops.live/dataops-internal/sam/am-i-running/-/pipelines/
script: |
echo "This is project $CI_PROJECT_ID (ref $CI_COMMIT_REF_NAME) running pipeline $CI_PIPELINE_ID ($_PIPELINE_FILE_NAME)"
api_response=$(curl -s -H "$API_AUTH_HEADER" "$API_URL_PROJECT/pipelines?ref=$CI_COMMIT_REF_NAME&status=running")
pipeline_ids=$(echo "$api_response" | jq -r --arg current_id "$CI_PIPELINE_ID" --arg pname "$_PIPELINE_FILE_NAME" 'if type == "array" then .[] | select(.id != ($current_id|tonumber)) | select(.name == $pname) | .id else empty end')
for id in $pipeline_ids; do
echo -e "\e[31m[ERROR] Another pipeline is also running $_PIPELINE_FILE_NAME in $CI_COMMIT_REF_NAME:\e[0m $PIPELINE_URL_BASE$id"
exit 1
done
echo -e "\e[92m[OK] No other pipelines are running $_PIPELINE_FILE_NAME in $CI_COMMIT_REF_NAME\e[0m"
API Authentication
Since the scope of the built-in CI_JOB_TOKEN variable does not cover these parts of the DataOps.live API, it is necessary to provide alternative credentials. One approach that we have used here is to create a project access token against this project, using the api scope, and store this in a project variable (we have used LOCAL_PROJECT_TOKEN in this code).
Explanation of the Code
The following variables have been defined in the job for clarity:
API_AUTH_HEADERHeader passed to the curl command for authentication against the DataOps.live API.API_URL_PROJECTBase URL fragment for the DataOps.live API endpoint that relates to the current project.PIPELINE_URL_BASEBase URL fragment for constructing data product platform URLs to pipelines (for convenience in error messages).
echo "This is project $CI_PROJECT_ID (ref $CI_COMMIT_REF_NAME) running pipeline $CI_PIPELINE_ID ($_PIPELINE_FILE_NAME)"
api_response=$(curl -s -H "$API_AUTH_HEADER" "$API_URL_PROJECT/pipelines?ref=$CI_COMMIT_REF_NAME&status=running")
pipeline_ids=$(echo "$api_response" | jq -r --arg current_id "$CI_PIPELINE_ID" --arg pname "$_PIPELINE_FILE_NAME" 'if type == "array" then .[] | select(.id != ($current_id|tonumber)) | select(.name == $pname) | .id else empty end')
for id in $pipeline_ids; do
echo -e "\e[31m[ERROR] Another pipeline is also running $_PIPELINE_FILE_NAME in $CI_COMMIT_REF_NAME:\e[0m $PIPELINE_URL_BASE$id"
exit 1
done
echo -e "\e[92m[OK] No other pipelines are running $_PIPELINE_FILE_NAME in $CI_COMMIT_REF_NAME\e[0m"
- Line 2: Calls the pipelines API and stores the raw JSON response in
api_response. - Line 3: Pipes
api_responsethroughjqwith two shell variables injected —$current_id(the current pipeline ID) and$pname(the current pipeline file name). If the response is an array, it filters out the current pipeline by ID and then filters to only pipelines whose.namematches$_PIPELINE_FILE_NAME, returning their IDs. If the response is not an array (e.g. an API error object), it returns nothing safely viaempty. - Lines 4-7: For each matching pipeline ID found, print an error message with a direct link to that pipeline and exit with a non-zero code, causing the job and pipeline to fail.