@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.
@@ -38,8 +38,9 @@ on:
38
38
  workflow_dispatch:
39
39
 
40
40
  permissions:
41
- contents: write
42
- pull-requests: write
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
- GH_TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
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
- gh_api() { curl -sS --retry 3 --retry-all-errors -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$@"; }
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
- prs="$(gh_api "$api/pulls?state=open&per_page=100" | jq -c --arg repo "$REPO" '
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="$(gh_api -X PUT "$api/pulls/$n/merge" -d "$(jq -nc --arg sha "$sha" '{merge_method:"squash", sha:$sha}')")"
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
- gh_api -X DELETE "$api/git/refs/heads/$ref" -o /dev/null || true
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
- STATUS: ${{ steps.triage.outputs.state }}
118
- DETAIL: ${{ steps.triage.outputs.detail }}
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 $TOKEN" -H "Accept: application/vnd.github.raw" \
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./create-product": "./create-product/create-product.ts",