@@ -24,11 +24,9 @@ set -euo pipefail
2424# creating a fresh pair of demo PRs so only this run's changes remain
2525# afterwards. --keep N keeps the N most-recently-created matches instead.
2626#
27- # Requires: the overmind CLI, jq, curl
27+ # Requires: the overmind CLI, jq, curl, python3
2828# Requires OVM_API_KEY with the "changes:write" scope (the same scope
29- # already used by start-change/end-change/submit-plan). Not yet tested
30- # end-to-end since no OVM_API_KEY is available in the environment this was
31- # written in - run with --dry-run first.
29+ # already used by start-change/end-change/submit-plan).
3230
3331APP=" https://app.overmind.tech"
3432TITLE_PATTERN=" narrow internal ingress"
@@ -59,7 +57,10 @@ MATCHES="$(jq -s --arg pattern "$TITLE_PATTERN" '
5957
6058MATCH_COUNT=" $( jq ' length' <<< " $MATCHES" ) "
6159echo " Found $MATCH_COUNT change(s) matching /$TITLE_PATTERN /i:"
62- jq -r ' .[] | " - \(.metadata.createdAt) \(.properties.title) (uuid b64: \(.metadata.UUID // "?"))"' <<< " $MATCHES"
60+ # NB: .metadata.UUID here is the CLI's human-readable hyphenated UUID string
61+ # (list-changes renders it via the SDK's ToMap(), not raw proto JSON) - it is
62+ # NOT the base64 the raw DeleteChange call below needs. See uuid_to_base64().
63+ jq -r ' .[] | " - \(.metadata.createdAt) \(.properties.title) (uuid: \(.metadata.UUID // "?"))"' <<< " $MATCHES"
6364
6465TO_DELETE=" $( jq -c --argjson keep " $KEEP " ' .[$keep:]' <<< " $MATCHES" ) "
6566DELETE_COUNT=" $( jq ' length' <<< " $TO_DELETE" ) "
7172
7273echo " Deleting $DELETE_COUNT change(s), keeping $KEEP most-recent match(es)..."
7374
74- # The CLI never prints the token it authenticates with internally, so this
75- # is the one bit of auth we have to redo ourselves for the delete call.
76- API_URL=" $( curl -sf " $APP /api/public/instance-data" | jq -r ' .api_url' ) "
77- TOKEN=" $( curl -sf -X POST " $API_URL /apikeys.ApiKeyService/ExchangeKeyForToken" \
78- -H " Content-Type: application/json" -H " Connect-Protocol-Version: 1" \
79- -d " {\" apiKey\" : \" $OVM_API_KEY \" }" | jq -r ' .accessToken' ) "
75+ if [[ " $DRY_RUN " != " true" ]]; then
76+ # The CLI never prints the token it authenticates with internally, so this
77+ # is the one bit of auth we have to redo ourselves for the delete call.
78+ # Skipped entirely in --dry-run so a dry run never needs a delete-capable
79+ # key, just something that can list.
80+ API_URL=" $( curl -sf " $APP /api/public/instance-data" | jq -r ' .api_url' ) "
81+ TOKEN=" $( curl -sf -X POST " $API_URL /apikeys.ApiKeyService/ExchangeKeyForToken" \
82+ -H " Content-Type: application/json" -H " Connect-Protocol-Version: 1" \
83+ -d " {\" apiKey\" : \" $OVM_API_KEY \" }" | jq -r ' .accessToken' ) "
8084
81- if [[ -z " $TOKEN " || " $TOKEN " == " null" ]]; then
82- echo " Failed to exchange OVM_API_KEY for a token" >&2
83- exit 1
85+ if [[ -z " $TOKEN " || " $TOKEN " == " null" ]]; then
86+ echo " Failed to exchange OVM_API_KEY for a token" >&2
87+ exit 1
88+ fi
8489fi
8590
86- echo " $TO_DELETE " | jq -c ' .[]' | while read -r change; do
87- UUID_B64=" $( jq -r ' .metadata.UUID' <<< " $change" ) "
91+ # DeleteChangeRequest.UUID is a proto `bytes` field, so the raw Connect+JSON
92+ # call needs it base64-encoded - convert the CLI's hyphenated string back to
93+ # the raw 16 bytes first.
94+ uuid_to_base64 () {
95+ python3 -c " import sys, uuid, base64; print(base64.b64encode(uuid.UUID(sys.argv[1]).bytes).decode())" " $1 "
96+ }
97+
98+ FAILURES=0
99+ RESPONSE_FILE=" $( mktemp) "
100+ trap ' rm -f "$RESPONSE_FILE"' EXIT
101+
102+ # Read from a process substitution rather than piping into the while loop:
103+ # piping meant a failure in the loop body raced against jq still writing
104+ # more output, which surfaced as a confusing "jq: broken pipe" error on top
105+ # of whatever actually failed.
106+ while IFS= read -r change; do
107+ UUID_STR=" $( jq -r ' .metadata.UUID' <<< " $change" ) "
88108 TITLE=" $( jq -r ' .properties.title' <<< " $change" ) "
89109
90110 if [[ " $DRY_RUN " == " true" ]]; then
91- echo " [dry-run] would delete: $TITLE "
111+ echo " [dry-run] would delete: $TITLE ( $UUID_STR ) "
92112 continue
93113 fi
94114
95- curl -sf -X POST " $API_URL /changes.ChangesService/DeleteChange" \
115+ UUID_B64=" $( uuid_to_base64 " $UUID_STR " ) "
116+
117+ HTTP_STATUS=" $( curl -s -o " $RESPONSE_FILE " -w ' %{http_code}' -X POST " $API_URL /changes.ChangesService/DeleteChange" \
96118 -H " Content-Type: application/json" -H " Connect-Protocol-Version: 1" \
97119 -H " Authorization: Bearer $TOKEN " \
98- -d " {\" UUID\" : \" $UUID_B64 \" }" > /dev/null
99- echo " deleted: $TITLE "
100- done
120+ -d " {\" UUID\" : \" $UUID_B64 \" }" ) "
121+
122+ if [[ " $HTTP_STATUS " == " 200" ]]; then
123+ echo " deleted: $TITLE ($UUID_STR )"
124+ else
125+ echo " FAILED to delete: $TITLE ($UUID_STR ) - HTTP $HTTP_STATUS : $( cat " $RESPONSE_FILE " ) " >&2
126+ FAILURES=$(( FAILURES + 1 ))
127+ fi
128+ done < <( jq -c ' .[]' <<< " $TO_DELETE" )
129+
130+ if [[ " $FAILURES " -gt 0 ]]; then
131+ echo " $FAILURES deletion(s) failed" >&2
132+ exit 1
133+ fi
0 commit comments