Skip to content

Commit 2a4b9a0

Browse files
committed
added curlable script to cancel circleci workflows
1 parent 34bdac7 commit 2a4b9a0

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#!/usr/bin/env bash
2+
# vim: et sr sw=4 ts=4 smartindent:
3+
# Cancels redundant PR builds on circleci pipelines.
4+
# Expected to be run from Github Actions.
5+
#
6+
# This only works for PR branches as it relies on
7+
# env var GITHUB_HEAD_REF which only exists for PRs
8+
# in github actions.
9+
#
10+
# This is by design - generally we don't want to cancel
11+
# builds of commits on release or main branches as sometimes
12+
# we need to deploy different commits sequentially rather than
13+
# bundled. However commits on a PR generally should get squashed
14+
# as they are all part of the same feature or bugfix and we only
15+
# need to built the HEAD of it.
16+
17+
REQUIRED_VARS="
18+
CIRCLE_TOKEN
19+
CCI_ORG_NAME
20+
GH_BRANCH
21+
"
22+
23+
CCI_GITHUB_APP="${CCI_GITHUB_APP:-}" # set to true if you access CircleCi with Github App
24+
CCI_API="https://circleci.com/api/v2"
25+
26+
# If you access circleci with github app and not oauth
27+
# set CCI_ORG_NAME to your Circleci org id (see your org settings)
28+
if [[ -n "$CCI_GITHUB_APP" ]]; then
29+
if [[ -z "$CCI_ORG_NAME" ]]; then
30+
echo >&2 "ERROR: using CCI_GITHUB_APP: set env var CCI_ORG_NAME to your CircleCI org ID"
31+
exit 1
32+
fi
33+
CCI_VCS_SLUG="circleci"
34+
else
35+
# defaults when using CircleCI oauth, not github app
36+
CCI_VCS_SLUG="gh"
37+
CCI_ORG_NAME="${GITHUB_REPOSITORY}"
38+
fi
39+
40+
declare -a WORKFLOWS_TO_CANCEL=()
41+
42+
set_GH_BRANCH() {
43+
GH_BRANCH=""
44+
case "${GITHUB_EVENT_NAME:-}" in
45+
pull_request*) GH_BRANCH="$GITHUB_HEAD_REF" ;;
46+
*) GH_BRANCH="$GITHUB_REF_NAME" ;;
47+
esac
48+
49+
if [[ -z "$GH_BRANCH" ]]; then
50+
echo >&2 "ERROR: couldn't set GH_BRANCH based on trigger event [${GITHUB_EVENT_NAME:-}]"
51+
return 1
52+
fi
53+
54+
export GH_BRANCH
55+
return 0
56+
}
57+
58+
required_vars() {
59+
local rc=0
60+
local required_vars="$1"
61+
local this_var=""
62+
for this_var in $required_vars; do
63+
if ! check_var_defined $this_var
64+
then
65+
failed="${failed}\$$this_var "
66+
rc=1
67+
fi
68+
done
69+
[[ $rc -ne 0 ]] && echo >&2 "ERROR: following vars must be set in env:\n$failed"
70+
return $rc
71+
}
72+
73+
check_var_defined() { [[ ! -z "${!1}" ]] ; }
74+
75+
pipelines_for_branch() {
76+
local o=""
77+
o=$(
78+
curl -sS --request GET \
79+
--url "$CCI_API/project/${CCI_VCS_SLUG}/${CCI_ORG_NAME}/pipeline?branch=$GH_BRANCH" \
80+
--header "Circle-Token: ${CIRCLE_TOKEN}" \
81+
| jq -r '.items[].id'
82+
)
83+
84+
if [[ $? -ne 0 ]] || [[ -z "${o:-}" ]]; then
85+
echo >&2 "ERROR: failed to get pipelines for branch $GH_BRANCH"
86+
return 1
87+
else
88+
echo "$o"
89+
return 0
90+
fi
91+
}
92+
93+
cancel_workflows() {
94+
local rc=0
95+
for id in ${WORKFLOWS_TO_CANCEL[@]}; do
96+
echo "INFO: attempting to cancel workflow id $id"
97+
curl --request POST \
98+
--url "$CCI_API/workflow/$id/cancel" \
99+
--header "Circle-Token: ${CIRCLE_TOKEN}" \
100+
|| rc=1
101+
done
102+
103+
return $rc
104+
}
105+
106+
workflow_for_pipeline() {
107+
local pipeline_id="$1"
108+
local o=""
109+
o=$(
110+
curl -sS --request GET \
111+
--url "${CCI_API}/pipeline/${pipeline_id}/workflow" \
112+
--header "Circle-Token: ${CIRCLE_TOKEN}" \
113+
| jq -r '.items[] | select(.status | test("running|failing|on_hold")) | .id'
114+
)
115+
if [[ $? -ne 0 ]]; then
116+
echo >&2 "ERROR: failed to get single workflow for pipeline $pipeline_id"
117+
return 1
118+
else
119+
echo "$o"
120+
return 0
121+
fi
122+
}
123+
124+
main() {
125+
set_GH_BRANCH || return 1
126+
required_vars "$REQUIRED_VARS" || return 1
127+
128+
echo "INFO: Getting pipelines for branch $GH_BRANCH"
129+
pipeline_ids=$(pipelines_for_branch) || return 1
130+
131+
local rc=0
132+
for pipeline_id in $pipeline_ids; do
133+
! workflow_id=$(workflow_for_pipeline "$pipeline_id") && rc=1 && continue
134+
[[ -n "$workflow_id" ]] && WORKFLOWS_TO_CANCEL+=("$workflow_id")
135+
done
136+
137+
if [[ ${#WORKFLOWS_TO_CANCEL[@]} -lt 1 ]]; then
138+
echo "INFO: found no active workflows to cancel."
139+
return 0
140+
fi
141+
142+
echo "INFO: will cancel these workflow ids:" ${WORKFLOWS_TO_CANCEL[*]}
143+
if cancel_workflows
144+
then
145+
echo "INFO: all currently running workflows cancelled successfully"
146+
return 0
147+
else
148+
echo "INFO: Didn't kill all workflows successfully."
149+
echo "INFO: Please clean them up manually via the circleci dashboard"
150+
return 1
151+
fi
152+
153+
}
154+
155+
main
156+
exit 0

0 commit comments

Comments
 (0)