@norskvideo/ctl-dev-kit 0.2.55 → 0.2.56

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.
@@ -13,12 +13,27 @@
13
13
  # So, for every open PR whose branch a bot owns:
14
14
  # - every check on its head has passed and it merges cleanly -> squash-merge it
15
15
  # (the PR's checks + smoke ARE the verdict; integration then runs on main);
16
- # - a check failed, it conflicts with main, or it is a draft (the bot's own
17
- # gate was red) -> list it, and report this repo's `bot-prs` cell RED on the
18
- # product dashboard, which is the Discord message people actually read;
16
+ # - it conflicts with main -> re-run the bot that owns the branch, ONCE per
17
+ # head, so it rebuilds the branch from today's main (see below);
18
+ # - a check failed, it still conflicts after that rebuild, or it is a draft
19
+ # (the bot's own gate was red) -> list it, and report this repo's `bot-prs`
20
+ # cell RED on the product dashboard, which is the Discord message people
21
+ # actually read;
19
22
  # - checks still running -> leave it for the next trigger.
20
23
  # The cell goes green again once nothing is red.
21
24
  #
25
+ # Why the conflict rebuild: the three bots fire within minutes of each other
26
+ # each morning and all write package.json and bun.lock, each from main as it
27
+ # stood. Whichever lands first leaves the other's open PR conflicting, and the
28
+ # bot only rebuilds its branch on its next DAILY run -- so on 2026-10-09
29
+ # commentary, probe and reuters each had a sync-ctl-packages PR "needing a
30
+ # human" that a re-run of sync-ctl-packages fixed outright. Every bot rebuilds
31
+ # from main when run: sync-ctl-packages force-pushes a fresh branch, and
32
+ # upgrade-latest / sync-dev-kit either land on main and close the PR they
33
+ # superseded, or force-push their branch. The marker comment caps it at one
34
+ # rebuild per head, so a rebuild that still conflicts goes to a human instead
35
+ # of looping every hour.
36
+ #
22
37
  # Runs when a PR's checks or smoke finish (the prompt path), hourly as a sweep,
23
38
  # and on demand. No checkout: it only talks to the GitHub API, so it needs none
24
39
  # of the runner-workspace steps the building workflows carry.
@@ -41,6 +56,8 @@ permissions:
41
56
  contents: read
42
57
  checks: read
43
58
  pull-requests: read
59
+ # The rebuild dispatch and its marker comment go through the PAT (WRITE_TOKEN),
60
+ # like the merges; this token only reads.
44
61
 
45
62
  concurrency:
46
63
  # One at a time per repo, and never cancelled: a merge half-done by a run that
@@ -86,8 +103,19 @@ jobs:
86
103
  | select(.head.ref | test("^(upgrade-latest/|sync-dev-kit/|chore/sync-ctl-packages$)"))
87
104
  | {number, ref: .head.ref, sha: .head.sha, draft, url: .html_url}]' <<<"$pulls")"
88
105
 
106
+ # The workflow that owns each bot branch, i.e. the one that rebuilds it.
107
+ producer() {
108
+ case "$1" in
109
+ upgrade-latest/*) echo upgrade-latest.yml ;;
110
+ sync-dev-kit/*) echo sync-dev-kit.yml ;;
111
+ chore/sync-ctl-packages) echo sync-ctl-packages.yml ;;
112
+ *) echo "" ;;
113
+ esac
114
+ }
115
+
89
116
  red=()
90
117
  merged=()
118
+ rebuilt=()
91
119
  while IFS= read -r pr; do
92
120
  [ -n "$pr" ] || continue
93
121
  n="$(jq -r .number <<<"$pr")"; ref="$(jq -r .ref <<<"$pr")"; sha="$(jq -r .sha <<<"$pr")"
@@ -106,7 +134,29 @@ jobs:
106
134
  [ "$mergeable" != "null" ] && break
107
135
  sleep 3
108
136
  done
109
- if [ "$mergeable" != "true" ]; then red+=("#$n $ref: does not merge cleanly with main"); continue; fi
137
+ if [ "$mergeable" != "true" ]; then
138
+ # One rebuild per head: the marker names the sha it was asked for.
139
+ marker="bot-prs: rebuild requested for head $sha"
140
+ wf="$(producer "$ref")"
141
+ comments="$(gh_api "$api/issues/$n/comments?per_page=100")"
142
+ expect "reading comments on #$n" 'type == "array"' "$comments"
143
+ if [ -z "$wf" ]; then
144
+ red+=("#$n $ref: does not merge cleanly with main, and no bot is known to rebuild it")
145
+ elif jq -e --arg m "$marker" 'any(.[]; .body | contains($m))' >/dev/null <<<"$comments"; then
146
+ red+=("#$n $ref: still does not merge cleanly with main after $wf rebuilt it")
147
+ else
148
+ code="$(gh_write -o /dev/null -w '%{http_code}' -X POST "$api/actions/workflows/$wf/dispatches" -d '{"ref":"main"}')"
149
+ if [ "$code" = "204" ]; then
150
+ body="$marker -- it conflicted with main (another bot landed first), so $wf was re-run to rebuild it from today's main. If the rebuilt branch conflicts too, this PR is reported for a human."
151
+ gh_write -o /dev/null -X POST "$api/issues/$n/comments" -d "$(jq -nc --arg b "$body" '{body:$b}')" || true
152
+ rebuilt+=("#$n")
153
+ echo "#$n $ref: conflicts with main; re-ran $wf to rebuild it"
154
+ else
155
+ red+=("#$n $ref: does not merge cleanly with main, and re-running $wf failed (HTTP $code)")
156
+ fi
157
+ fi
158
+ continue
159
+ fi
110
160
  # sha pins the merge to what was checked: a bot force-pushing the
111
161
  # branch meanwhile makes this refuse rather than land unchecked code.
112
162
  res="$(gh_write -X PUT "$api/pulls/$n/merge" -d "$(jq -nc --arg sha "$sha" '{merge_method:"squash", sha:$sha}')")"
@@ -128,7 +178,9 @@ jobs:
128
178
  state=success
129
179
  # Never empty: the dashboard step reads an empty detail as missing.
130
180
  merged_detail="${merged[*]:+merged ${merged[*]}}"
131
- detail="${merged_detail:-no bot PRs waiting}"
181
+ rebuilt_detail="${rebuilt[*]:+rebuilding ${rebuilt[*]}}"
182
+ detail="$(printf '%s' "$merged_detail${merged_detail:+${rebuilt_detail:+; }}$rebuilt_detail")"
183
+ detail="${detail:-no bot PRs waiting}"
132
184
  fi
133
185
  echo "state=$state" >> "$GITHUB_OUTPUT"
134
186
  echo "detail=${detail%; }" >> "$GITHUB_OUTPUT"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.2.55",
3
+ "version": "0.2.56",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./create-product": "./create-product/create-product.ts",