Skip to content

Instantly share code, notes, and snippets.

@artemptushkin
Last active August 19, 2024 10:34
Show Gist options
  • Save artemptushkin/c932f9ab668457de0551371739fc4db1 to your computer and use it in GitHub Desktop.
Save artemptushkin/c932f9ab668457de0551371739fc4db1 to your computer and use it in GitHub Desktop.
gitlab-cancel-redundant-pipeline

Problem

There is a known problem that in Gitlab it's not possible to avoid pipeline duplication in some scenarios.

Context

Let's assume you combine merge and branch pipelines. You can follow the Gitlab guideline or here is an improved one:

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_PIPELINE_SOURCE == "web"'
    - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
      when: never
    - if: '$CI_COMMIT_BRANCH'

When you push and then you open a MR immediately it's not possible to avoid getting a second pipeline (though if you have one open then it's not an issue).

Solution with git options

git push -o merge_request.create

This solution with the script below

Here is a script that supposed to be run after the push to cancel the duplication.

How to use?

  1. Create an alias in your bash profile / .zshrc file, e.g.: alias cancel-branch-pipeline="sh ~/gitlab/gitlab-cancel-branch-pipeline.sh"
  2. Set you Gitlab personal access token with scopes api and read_api as GITLAB_TOKEN environment variable (you can set it in your bash profile file to have in every session: export GITLAB_TOKEN=<your-token>)
  3. Call it after with the alias anytime you push, and you want to open a MR: $ cancel-branch-pipeline
#!/usr/bin/env bash
set -euo pipefail
function get_project_name_from_git() {
remote_url=$(git remote -v | awk '/origin.*\(push\)/ {print $2}')
url_without_prefix_suffix=${remote_url#"git@"}
url_without_prefix_suffix=${url_without_prefix_suffix%".git"}
desired_part=${url_without_prefix_suffix#*":"}
echo "$desired_part"
}
# The script part
CI_COMMIT_REF_NAME=$(git rev-parse --abbrev-ref HEAD)
echo "The branch resolved: $CI_COMMIT_REF_NAME"
CI_PROJECT_PATH=$(get_project_name_from_git)
echo "The project path resolved: $CI_PROJECT_PATH"
MERGE_REQUEST_ID=$(curl -s -H "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/${CI_PROJECT_PATH//\//%2F}/merge_requests?state=opened&source_branch=${CI_COMMIT_REF_NAME//\//%2F}" | jq '.[0] | .iid')
echo "Merge request id: $MERGE_REQUEST_ID"
if [[ -z "$MERGE_REQUEST_ID" || $MERGE_REQUEST_ID == "null" ]]; then
echo "Merge request is not open for the current branch $CI_COMMIT_REF_NAME, the script won't be executed, exiting."
exit 0
fi
NUMBER_OF_MERGE_REQUEST_PIPELINES=$(curl -s -H "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/${CI_PROJECT_PATH//\//%2F}/pipelines?status=running&ref=refs/merge-requests/$MERGE_REQUEST_ID/head" | jq 'length')
echo "Number of running merge request pipelines: $NUMBER_OF_MERGE_REQUEST_PIPELINES"
BRANCH_PIPELINE_RESPONSE=$(curl -s -H "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/${CI_PROJECT_PATH//\//%2F}/pipelines?ref=${CI_COMMIT_REF_NAME//\//%2F}&status=running")
BRANCH_PIPELINE_ID=$(echo "$BRANCH_PIPELINE_RESPONSE" | jq '.[0] .id')
NUMBER_OF_BRANCH_PIPELINES=$(echo "$BRANCH_PIPELINE_RESPONSE" | jq 'length')
echo "Number of running branch pipelines: $NUMBER_OF_BRANCH_PIPELINES"
if (( $NUMBER_OF_BRANCH_PIPELINES > 0 && $NUMBER_OF_MERGE_REQUEST_PIPELINES > 0 )); then
echo "Branch pipeline id to be canceled: $BRANCH_PIPELINE_ID"
curl -s -o /dev/null -H "PRIVATE-TOKEN: $GITLAB_TOKEN" --request POST "https://gitlab.com/api/v4/projects/${CI_PROJECT_PATH//\//%2F}/pipelines/$BRANCH_PIPELINE_ID/cancel"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment