@norskvideo/ctl-dev-kit 0.2.26 → 0.2.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/conventions/bot-prs.yml +34 -11
- package/package.json +1 -1
package/conventions/bot-prs.yml
CHANGED
|
@@ -38,8 +38,9 @@ on:
|
|
|
38
38
|
workflow_dispatch:
|
|
39
39
|
|
|
40
40
|
permissions:
|
|
41
|
-
contents:
|
|
42
|
-
|
|
41
|
+
contents: read
|
|
42
|
+
checks: read
|
|
43
|
+
pull-requests: read
|
|
43
44
|
|
|
44
45
|
concurrency:
|
|
45
46
|
# One at a time per repo, and never cancelled: a merge half-done by a run that
|
|
@@ -54,18 +55,36 @@ jobs:
|
|
|
54
55
|
- name: Merge green bot PRs, report red ones
|
|
55
56
|
id: triage
|
|
56
57
|
env:
|
|
57
|
-
|
|
58
|
+
# Reads go through the job's own token, which the permissions above
|
|
59
|
+
# scope to exactly what is read; CI_DISPATCH_TOKEN (a PAT) has no
|
|
60
|
+
# Checks access, and the check-runs call returned an error object.
|
|
61
|
+
# Merges and branch deletes go through the PAT, because a push made
|
|
62
|
+
# with GITHUB_TOKEN does not trigger main's own workflows.
|
|
63
|
+
READ_TOKEN: ${{ github.token }}
|
|
64
|
+
WRITE_TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
|
|
58
65
|
REPO: ${{ github.repository }}
|
|
59
66
|
run: |
|
|
60
67
|
set -euo pipefail
|
|
61
68
|
api="https://api.github.com/repos/$REPO"
|
|
62
|
-
|
|
69
|
+
curl_as() { local tok="$1"; shift; curl -sS --retry 3 --retry-all-errors -H "Authorization: Bearer $tok" -H "Accept: application/vnd.github+json" "$@"; }
|
|
70
|
+
gh_api() { curl_as "$READ_TOKEN" "$@"; }
|
|
71
|
+
gh_write() { curl_as "$WRITE_TOKEN" "$@"; }
|
|
72
|
+
# An API error is a JSON object with a message; say what it was rather
|
|
73
|
+
# than letting jq fail later on a missing field.
|
|
74
|
+
expect() {
|
|
75
|
+
local what="$1" filter="$2" body="$3"
|
|
76
|
+
jq -e "$filter" >/dev/null 2>&1 <<<"$body" && return 0
|
|
77
|
+
echo "::error::$what: $(jq -r '.message // .' <<<"$body" 2>/dev/null || echo "$body")"
|
|
78
|
+
exit 1
|
|
79
|
+
}
|
|
63
80
|
|
|
64
81
|
# The bots own exactly these branches (see each workflow's PR step).
|
|
65
|
-
|
|
82
|
+
pulls="$(gh_api "$api/pulls?state=open&per_page=100")"
|
|
83
|
+
expect "listing open PRs" 'type == "array"' "$pulls"
|
|
84
|
+
prs="$(jq -c --arg repo "$REPO" '
|
|
66
85
|
[.[] | select(.head.repo.full_name == $repo)
|
|
67
86
|
| select(.head.ref | test("^(upgrade-latest/|sync-dev-kit/|chore/sync-ctl-packages$)"))
|
|
68
|
-
| {number, ref: .head.ref, sha: .head.sha, draft, url: .html_url}]')"
|
|
87
|
+
| {number, ref: .head.ref, sha: .head.sha, draft, url: .html_url}]' <<<"$pulls")"
|
|
69
88
|
|
|
70
89
|
red=()
|
|
71
90
|
merged=()
|
|
@@ -73,6 +92,7 @@ jobs:
|
|
|
73
92
|
[ -n "$pr" ] || continue
|
|
74
93
|
n="$(jq -r .number <<<"$pr")"; ref="$(jq -r .ref <<<"$pr")"; sha="$(jq -r .sha <<<"$pr")"
|
|
75
94
|
runs="$(gh_api "$api/commits/$sha/check-runs?per_page=100")"
|
|
95
|
+
expect "reading checks for #$n" '.check_runs | type == "array"' "$runs"
|
|
76
96
|
failed="$(jq -r '[.check_runs[] | select(.conclusion | IN("failure","cancelled","timed_out","action_required","startup_failure")) | .name] | unique | join(", ")' <<<"$runs")"
|
|
77
97
|
pending="$(jq -r '[.check_runs[] | select(.status != "completed")] | length' <<<"$runs")"
|
|
78
98
|
passed="$(jq -r '[.check_runs[] | select(.conclusion == "success")] | length' <<<"$runs")"
|
|
@@ -89,12 +109,12 @@ jobs:
|
|
|
89
109
|
if [ "$mergeable" != "true" ]; then red+=("#$n $ref: does not merge cleanly with main"); continue; fi
|
|
90
110
|
# sha pins the merge to what was checked: a bot force-pushing the
|
|
91
111
|
# branch meanwhile makes this refuse rather than land unchecked code.
|
|
92
|
-
res="$(
|
|
112
|
+
res="$(gh_write -X PUT "$api/pulls/$n/merge" -d "$(jq -nc --arg sha "$sha" '{merge_method:"squash", sha:$sha}')")"
|
|
93
113
|
if [ "$(jq -r '.merged // false' <<<"$res")" = "true" ]; then
|
|
94
114
|
merged+=("#$n")
|
|
95
115
|
echo "merged #$n ($ref)"
|
|
96
116
|
# The bots recreate their branch on the next bump.
|
|
97
|
-
|
|
117
|
+
gh_write -X DELETE "$api/git/refs/heads/$ref" -o /dev/null || true
|
|
98
118
|
else
|
|
99
119
|
red+=("#$n $ref: merge refused: $(jq -r '.message // "unknown"' <<<"$res")")
|
|
100
120
|
fi
|
|
@@ -112,10 +132,13 @@ jobs:
|
|
|
112
132
|
echo "detail=${detail%; }" >> "$GITHUB_OUTPUT"
|
|
113
133
|
|
|
114
134
|
- name: Report to the product dashboard
|
|
135
|
+
# Always: a triage that broke is exactly what nobody would otherwise see.
|
|
136
|
+
if: always()
|
|
115
137
|
env:
|
|
116
138
|
TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
|
|
117
|
-
|
|
118
|
-
|
|
139
|
+
READ_TOKEN: ${{ github.token }}
|
|
140
|
+
STATUS: ${{ steps.triage.outputs.state || 'failure' }}
|
|
141
|
+
DETAIL: ${{ steps.triage.outputs.detail || 'the bot-PR triage itself failed; see the run' }}
|
|
119
142
|
SHA: ${{ github.sha }}
|
|
120
143
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
121
144
|
REPO: ${{ github.repository }}
|
|
@@ -123,7 +146,7 @@ jobs:
|
|
|
123
146
|
set -euo pipefail
|
|
124
147
|
# The dashboard key is the one upgrade-latest.yml reports under, read
|
|
125
148
|
# from it so this file can stay byte-identical across products.
|
|
126
|
-
product="$(curl -sS -H "Authorization: Bearer $
|
|
149
|
+
product="$(curl -sS -H "Authorization: Bearer $READ_TOKEN" -H "Accept: application/vnd.github.raw" \
|
|
127
150
|
"https://api.github.com/repos/$REPO/contents/.github/workflows/upgrade-latest.yml" \
|
|
128
151
|
| sed -nE 's/^[[:space:]]*product:[[:space:]]*([^[:space:]]+).*/\1/p' | head -1)"
|
|
129
152
|
if [ -z "$product" ] || [ "$product" = "__PRODUCT__" ]; then
|