Skip to content

Commit 5ced9a9

Browse files
committed
added curlable script to cancel circleci workflows
1 parent 34bdac7 commit 5ced9a9

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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+
GITHUB_HEAD_REF
22+
GITHUB_REPOSITORY
23+
"
24+
25+
CCI_GITHUB_APP="${CCI_GITHUB_APP:-}" # set to true if you access CircleCi with Github App
26+
CCI_API="https://circleci.com/api/v2"
27+
28+
# If you access circleci with github app and not oauth
29+
# set CCI_ORG_NAME to your Circleci org id (see your org settings)
30+
if [[ -n "$CCI_GITHUB_APP" ]]; then
31+
if [[ -z "$CCI_ORG_NAME" ]]; then
32+
echo >&2 "ERROR: using CCI_GITHUB_APP: set env var CCI_ORG_NAME to your CircleCI org ID"
33+
exit 1
34+
fi
35+
CCI_VCS_SLUG="circleci"
36+
else
37+
# defaults when using CircleCI oauth, not github app
38+
CCI_VCS_SLUG="gh"
39+
CCI_ORG_NAME="${GITHUB_REPOSITORY}"
40+
fi
41+
42+
declare -a WORKFLOWS_TO_CANCEL=()
43+
44+
set_GH_BRANCH() {
45+
GH_BRANCH=""
46+
case "${GITHUB_EVENT_NAME:-}" in
47+
pull_request*) GH_BRANCH="$GITHUB_HEAD_REF" ;;
48+
*) GH_BRANCH="$GITHUB_REF_NAME" ;;
49+
esac
50+
51+
if [[ -z "$GH_BRANCH" ]]; then
52+
echo >&2 "ERROR: couldn't set GH_BRANCH based on trigger event [${GITHUB_EVENT_NAME:-}]"
53+
return 1
54+
fi
55+
56+
export GH_BRANCH
57+
return 0
58+
}
59+
60+
required_vars() {
61+
local rc=0
62+
local required_vars="$1"
63+
local this_var=""
64+
for this_var in $required_vars; do
65+
if ! check_var_defined $this_var
66+
then
67+
failed="${failed}\$$this_var "
68+
rc=1
69+
fi
70+
done
71+
[[ $rc -ne 0 ]] && echo >&2 "ERROR: following vars must be set in env:\n$failed"
72+
return $rc
73+
}
74+
75+
check_var_defined() { [[ ! -z "${!1}" ]] ; }
76+
77+
pipelines_for_branch() {
78+
local o=""
79+
o=$(
80+
curl -sS --request GET \
81+
--url "$CCI_API/project/${CCI_VCS_SLUG}/${CCI_ORG_NAME}/pipeline?branch=$GH_BRANCH" \
82+
--header "Circle-Token: ${CIRCLE_TOKEN}" \
83+
| jq -r '.items[].id'
84+
)
85+
86+
if [[ $? -ne 0 ]] || [[ -z "${o:-}" ]]; then
87+
echo >&2 "ERROR: failed to get pipelines for branch $GH_BRANCH"
88+
return 1
89+
else
90+
echo "$o"
91+
return 0
92+
fi
93+
}
94+
95+
cancel_workflows() {
96+
local rc=0
97+
for id in ${WORKFLOWS_TO_CANCEL[@]}; do
98+
echo "INFO: attempting to cancel workflow id $id"
99+
curl --request POST \
100+
--url "$CCI_API/workflow/$id/cancel" \
101+
--header "Circle-Token: ${CIRCLE_TOKEN}" \
102+
|| rc=1
103+
done
104+
105+
return $rc
106+
}
107+
108+
workflow_for_pipeline() {
109+
local pipeline_id="$1"
110+
local o=""
111+
o=$(
112+
curl -sS --request GET \
113+
--url "${CCI_API}/pipeline/${pipeline_id}/workflow" \
114+
--header "Circle-Token: ${CIRCLE_TOKEN}" \
115+
| jq -r '.items[] | select(.status | test("running|failing|on_hold")) | .id'
116+
)
117+
if [[ $? -ne 0 ]]; then
118+
echo >&2 "ERROR: failed to get single workflow for pipeline $pipeline_id"
119+
return 1
120+
else
121+
echo "$o"
122+
return 0
123+
fi
124+
}
125+
126+
main() {
127+
set_GH_BRANCH || return 1
128+
required_vars "$REQUIRED_VARS" || return 1
129+
130+
echo "INFO: Getting pipelines for branch $GH_BRANCH"
131+
pipeline_ids=$(pipelines_for_branch) || return 1
132+
133+
local rc=0
134+
for pipeline_id in $pipeline_ids; do
135+
! workflow_id=$(workflow_for_pipeline "$pipeline_id") && rc=1 && continue
136+
[[ -n "$workflow_id" ]] && WORKFLOWS_TO_CANCEL+=("$workflow_id")
137+
done
138+
139+
if [[ ${#WORKFLOWS_TO_CANCEL[@]} -lt 1 ]]; then
140+
echo "INFO: found no active workflows to cancel."
141+
return 0
142+
fi
143+
144+
echo "INFO: will cancel these workflow ids:" ${WORKFLOWS_TO_CANCEL[*]}
145+
if cancel_workflows
146+
then
147+
echo "INFO: all currently running workflows cancelled successfully"
148+
return 0
149+
else
150+
echo "INFO: Didn't kill all workflows successfully."
151+
echo "INFO: Please clean them up manually via the circleci dashboard"
152+
return 1
153+
fi
154+
155+
}
156+
157+
main
158+
exit 0

0 commit comments

Comments
 (0)