-
Notifications
You must be signed in to change notification settings - Fork 0
/
cancelot.sh
executable file
·149 lines (129 loc) · 5.53 KB
/
cancelot.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env bash
# https://github.com/siberex/cancelot
#
# To download the latest version (if you trust running random bash scripts from the internets!):
# curl -L https://gist.github.com/siberex/bb0540b208019382d08732cc6dd59007/raw -o cancelot.sh && chmod +x cancelot.sh
#
# Provides automation for cancelling Cloud Builds
# Use as a first step to cancel previous builds currently in progress or queued for the same branch name and trigger id.
# Similar to: https://github.com/GoogleCloudPlatform/cloud-builders-community/tree/master/cancelot
# Usage stand-alone (gcloud CLI must be installed and authorised):
# ./cancelot.sh --current_build_id $BUILD_ID --branch_name $BRANCH_NAME [--same_trigger_only] [--project "gcloud-project-id"] [--region ""]
#
# Could be configured with arguments or via ENV (or mixed).
# Arguments will take priority.
#
# Usage within Cloud Build step:
# steps:
# - name: 'gcr.io/google.com/cloudsdktool/google-cloud-cli:alpine'
# entrypoint: bash
# args:
# - cancelot.sh --same_trigger_only
# env:
# - 'CURRENT_BUILD_ID=$BUILD_ID'
# - 'PROJECT_ID=$PROJECT_ID'
# - 'REGION=$LOCATION'
# Exit script when command fails
set -o errexit
# Return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status
set -o pipefail
# Note: GitHub App builds DOES NOT contain Source.RepoSource.Revision.BranchName field.
# The actual source for GitHub repo will be storageSource.bucket, check actual source with:
# $ gcloud builds describe $BUILD_ID --format=json
# We still could use substitutions.BRANCH_NAME
# https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values
CMDNAME=${0##*/}
echoerr() { echo "$@" 1>&2; }
usage() {
cat <<USAGE >&2
Usage:
$CMDNAME --current_build_id \$BUILD_ID [--branch_name \$BRANCH_NAME] [--same_trigger_only] [--project "gcloud-project-id"] [--region "europe-west2"]
--current_build_id \$BUILD_ID Current Build Id
--branch_name \$BRANCH_NAME Trigger branch (aka head branch)
(optional, defaults to current build substitutions.BRANCH_NAME)
--project GCloud Project Id
--region GCloud Region, empty value
--same_trigger_only Only cancel builds with the same Trigger Id as current build’s trigger id
(optional, defaults to false = cancel all matching branch)
USAGE
exit 1
}
SAME_TRIGGER_ONLY=0
# Process arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--current_build_id)
CURRENT_BUILD_ID="$2"
if [[ $CURRENT_BUILD_ID == "" ]]; then break; fi
shift 2
;;
--branch_name)
TARGET_BRANCH="$2"
shift 2
;;
--same_trigger_only)
SAME_TRIGGER_ONLY=1
shift 1
;;
--project)
PROJECT_ID="$2"
shift 2
;;
--region)
REGION="$2"
shift 2
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$CURRENT_BUILD_ID" == "" ]]; then
echo "Error: you need to provide Build Id"
usage
fi
# In case Project Id were not provided via ENV or ARG, get it from the auth config.
# Could be useful to run cancelot locally or in CI environments other than Cloud Build.
GOOGLE_CLOUD_PROJECT=${PROJECT_ID:-$(gcloud config list --format 'get(core.project)')}
# Note: DO NOT guess Region from the Project. Region should be set explicitly (via ENV or ARG) or left empty.
# For example, we could get region for App Engine project like this:
# gcloud app describe --project "$GOOGLE_CLOUD_PROJECT" --format 'get(locationId)'
# But Cloud Builds for AppEngine are multi-regional, so the correct value for region will be empty "" or "(unset)"
GOOGLE_CLOUD_REGION=${REGION:-"(unset)"}
echo "Getting Cloud Builds for ProjectId=$GOOGLE_CLOUD_PROJECT with region filter: $GOOGLE_CLOUD_REGION"
# Note BUILD_BRANCH and BUILD_TRIGGER_ID could be empty
QUERY_BUILD=$(gcloud builds describe "$CURRENT_BUILD_ID" --project="$GOOGLE_CLOUD_PROJECT" --region="$GOOGLE_CLOUD_REGION" --format="csv[no-heading](createTime, buildTriggerId, substitutions.BRANCH_NAME)")
IFS="," read -r BUILD_CREATE_TIME BUILD_TRIGGER_ID BUILD_BRANCH <<<"$QUERY_BUILD"
FILTERS="id!=$CURRENT_BUILD_ID AND createTime<$BUILD_CREATE_TIME"
if [[ -z $TARGET_BRANCH ]]; then
TARGET_BRANCH="$BUILD_BRANCH"
fi
if [[ -n $TARGET_BRANCH ]]; then
FILTERS="$FILTERS AND substitutions.BRANCH_NAME=$TARGET_BRANCH"
fi
if [[ $SAME_TRIGGER_ONLY -eq 1 ]]; then
# Get Trigger Id from current build
FILTERS="$FILTERS AND buildTriggerId=$BUILD_TRIGGER_ID"
echo "Filtering Trigger Id: $BUILD_TRIGGER_ID"
fi
echo "Filtering ongoing builds for branch '$TARGET_BRANCH' created before: $BUILD_CREATE_TIME"
# echo "$FILTERS"
# Get ongoing build ids to cancel (+status)
while IFS=$'\n' read -r line; do CANCEL_BUILDS+=("$line"); done < <(gcloud builds list --ongoing --filter="$FILTERS" --project="$GOOGLE_CLOUD_PROJECT" --region="$GOOGLE_CLOUD_REGION" --format="value(id, status)")
BUILDS_COUNT=${#CANCEL_BUILDS[@]}
echo "Found $BUILDS_COUNT builds to cancel"
if [[ $BUILDS_COUNT -eq 0 ]]; then
exit 0
fi
# Cancel builds one by one to get output for each
# printf '%s\n' "${CANCEL_BUILDS[@]}"
echo "BUILD ID CURRENT STATUS"
for build in "${CANCEL_BUILDS[@]}"; do
echo "$build"
ID=$(echo "$build" | awk '{print $1;}')
gcloud builds cancel "$ID" --project="$GOOGLE_CLOUD_PROJECT" --region="$GOOGLE_CLOUD_REGION" >/dev/null || true
done