@norskvideo/ctl-dev-kit 0.2.60 → 0.2.61
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/build/flake.sdk-app.nix +5 -1
- package/conventions/CLAUDE.core.md +6 -0
- package/conventions/bot-prs.yml +124 -13
- package/conventions/build-docs.yml +7 -3
- package/conventions/build-image.yml +25 -8
- package/conventions/check-drift.ts +2 -2
- package/conventions/integration.yml +32 -7
- package/conventions/publish-docs.yml +7 -3
- package/conventions/smoke.yml +22 -6
- package/conventions/sync-ctl-packages.yml +7 -6
- package/conventions/sync-dev-kit.yml +73 -73
- package/conventions/upgrade-latest.sdk-app.yml +112 -78
- package/conventions/upgrade-latest.yml +101 -93
- package/docs-theme/README.md +2 -2
- package/package.json +1 -1
|
@@ -8,14 +8,20 @@
|
|
|
8
8
|
#
|
|
9
9
|
# It bumps the @norskvideo/ctl-dev-kit pin to the newest published version,
|
|
10
10
|
# reinstalls, re-syncs this product's forced copies from the new canonical
|
|
11
|
-
# (sync-drift),
|
|
11
|
+
# (sync-drift), runs the gate (check:drift + lint + typecheck + unit tests), and
|
|
12
|
+
# ALWAYS opens (or updates) a PR on sync-dev-kit/<version> -- never a push to
|
|
13
|
+
# main. bot-prs merges it once checks, smoke, integration and the guides gate
|
|
14
|
+
# are green on its head. The gate's verdict heads the PR body: red means the new
|
|
15
|
+
# dev-kit genuinely does not fit this product (a convention that fails
|
|
16
|
+
# lint/typecheck/tests, or a copy sync-drift could not converge), and adopting
|
|
17
|
+
# it is real engineering on the branch, not a copy-paste.
|
|
12
18
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
+
# Why a PR even when the gate is green: a new dev-kit changes the copied
|
|
20
|
+
# workflows themselves, and those are exactly what the gate does not run. On
|
|
21
|
+
# 2026-10-09 a newer copied build-image.yml, never run against oracle-reasoning's
|
|
22
|
+
# set-up, broke it on main after a green gate landed it (ci-workflows
|
|
23
|
+
# docs/dashboard/ci-fragility.md, proposal B). On the PR, build-image's build
|
|
24
|
+
# and guides run against the new copy before main has it.
|
|
19
25
|
#
|
|
20
26
|
# sync-drift re-derives each copy from the canonical (never a second opinion that
|
|
21
27
|
# can disagree), preserving the per-repo bits the gate masks -- the product: keys,
|
|
@@ -185,100 +191,94 @@ jobs:
|
|
|
185
191
|
bun run test:unit
|
|
186
192
|
'
|
|
187
193
|
|
|
188
|
-
|
|
189
|
-
|
|
194
|
+
# The bump's only way to main is this PR. bot-prs merges it once every tier
|
|
195
|
+
# that runs on a bot PR -- checks, smoke, integration and build-image's
|
|
196
|
+
# guides gate, wherever this repo has them -- is green on its head
|
|
197
|
+
# (ci-workflows docs/dashboard/ci-fragility.md, proposal B). Landing on a
|
|
198
|
+
# green gate kept lint, typecheck and unit failures off main, but the tiers
|
|
199
|
+
# that break on an upstream change ran only after landing, so a bad
|
|
200
|
+
# upstream release still turned main red in every repo that took it. The
|
|
201
|
+
# gate above still runs: its verdict heads the PR body, and a red one
|
|
202
|
+
# shows on the PR's own checks too. Never a draft: bot-prs reads red from
|
|
203
|
+
# the tiers, and a draft could never become ready again by PATCH.
|
|
204
|
+
- name: Open (or update) the bump PR
|
|
205
|
+
if: steps.sync.outputs.changed == '1'
|
|
190
206
|
env:
|
|
191
207
|
API_TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
|
|
208
|
+
GATE: ${{ steps.gate.outcome }}
|
|
192
209
|
run: |
|
|
193
210
|
set -euo pipefail
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
211
|
+
branch="sync-dev-kit/${{ steps.sync.outputs.new }}"
|
|
212
|
+
prefix="sync-dev-kit/"
|
|
213
|
+
title="Sync dev-kit ${{ steps.sync.outputs.old }} -> ${{ steps.sync.outputs.new }}"
|
|
214
|
+
repo="${{ github.repository }}"; owner="${repo%%/*}"
|
|
215
|
+
api="https://api.github.com/repos/$repo/pulls"
|
|
216
|
+
# An older PR of this bot's family is obsolete once this one is open:
|
|
217
|
+
# left open they piled up (nine were closed by hand on 2026-10-03).
|
|
218
|
+
# Best-effort: a failure here must not fail the bump.
|
|
198
219
|
close_superseded() {
|
|
199
|
-
|
|
220
|
+
local keep="$1" n
|
|
200
221
|
auth=(-H "Authorization: Bearer $API_TOKEN" -H "Accept: application/vnd.github+json")
|
|
201
|
-
for n in $(curl -sS "${auth[@]}" "$api
|
|
202
|
-
| jq -r --arg repo "$
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
222
|
+
for n in $(curl -sS "${auth[@]}" "$api?state=open&per_page=100" \
|
|
223
|
+
| jq -r --arg repo "$repo" --arg prefix "$prefix" --argjson keep "$keep" \
|
|
224
|
+
'.[] | select(.head.repo.full_name == $repo and (.head.ref | startswith($prefix)) and .number != $keep) | .number'); do
|
|
225
|
+
curl -sS -o /dev/null "${auth[@]}" -X POST "https://api.github.com/repos/$repo/issues/$n/comments" \
|
|
226
|
+
-d "$(jq -nc --arg b "Superseded by #$keep, a later run of this bot." '{body:$b}')"
|
|
227
|
+
curl -sS -o /dev/null "${auth[@]}" -X PATCH "$api/$n" -d '{"state":"closed"}'
|
|
206
228
|
echo "closed superseded PR #$n"
|
|
207
229
|
done
|
|
208
230
|
}
|
|
209
231
|
git config user.name "github-actions[bot]"
|
|
210
232
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
233
|
+
git checkout -B "$branch"
|
|
211
234
|
git add -A
|
|
212
235
|
git commit -m "chore: sync dev-kit ${{ steps.sync.outputs.old }} -> ${{ steps.sync.outputs.new }}
|
|
213
236
|
|
|
214
237
|
@norskvideo/ctl-dev-kit pin bumped to the newest published version and the
|
|
215
|
-
forced copies re-synced from the new canonical (sync-drift)
|
|
216
|
-
lint + typecheck + unit tests green against the new bytes. Lockfile
|
|
238
|
+
forced copies re-synced from the new canonical (sync-drift). Lockfile
|
|
217
239
|
regenerated."
|
|
218
|
-
#
|
|
219
|
-
#
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
# a repo that is already complete, where --unshallow is refused.
|
|
233
|
-
git fetch --unshallow origin main 2>/dev/null || git fetch origin main
|
|
234
|
-
git rebase origin/main || { git rebase --abort; echo "rebase hit a real conflict; aborting"; exit 1; }
|
|
235
|
-
done
|
|
236
|
-
echo "push still failing after 5 rebase attempts"; exit 1
|
|
237
|
-
|
|
238
|
-
- name: Open a reconciliation PR (gate red)
|
|
239
|
-
if: steps.sync.outputs.changed == '1' && steps.gate.outcome == 'failure'
|
|
240
|
-
env:
|
|
241
|
-
API_TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
|
|
242
|
-
run: |
|
|
243
|
-
set -euo pipefail
|
|
244
|
-
branch="sync-dev-kit/${{ steps.sync.outputs.new }}"
|
|
245
|
-
git config user.name "github-actions[bot]"
|
|
246
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
247
|
-
git checkout -b "$branch"
|
|
248
|
-
git add -A
|
|
249
|
-
git commit -m "chore: sync dev-kit ${{ steps.sync.outputs.old }} -> ${{ steps.sync.outputs.new }} (needs reconciliation)"
|
|
250
|
-
git push -f origin "$branch"
|
|
251
|
-
title="Sync dev-kit ${{ steps.sync.outputs.old }} -> ${{ steps.sync.outputs.new }}"
|
|
240
|
+
# One branch per bump, force-pushed from today's main -- but only when
|
|
241
|
+
# its tree changed. A red bump PR left standing is rebuilt by every run
|
|
242
|
+
# of this bot, and pushing the same tree again would re-run every slow
|
|
243
|
+
# tier on it for nothing.
|
|
244
|
+
if git fetch -q origin "$branch" 2>/dev/null && [ "$(git rev-parse 'HEAD^{tree}')" = "$(git rev-parse 'FETCH_HEAD^{tree}')" ]; then
|
|
245
|
+
echo "$branch already holds this bump; not re-pushing it"
|
|
246
|
+
else
|
|
247
|
+
git push -f origin "$branch"
|
|
248
|
+
fi
|
|
249
|
+
if [ "$GATE" = "success" ]; then
|
|
250
|
+
verdict="Gate green: check:drift, lint, typecheck and unit tests pass against the new dev-kit."
|
|
251
|
+
else
|
|
252
|
+
verdict="**Gate red**: the new dev-kit does not fit this product yet. Run the gate to see which part: \`bun install\`, then \`bun run check:drift\` (a copy sync-drift could not converge) and \`bun run lint && bun run typecheck && bun run test:unit\` (a convention that breaks real code). Fix it on this branch until green."
|
|
253
|
+
fi
|
|
252
254
|
body="$(printf '%s\n' \
|
|
253
|
-
"Automated dev-kit sync: the @norskvideo/ctl-dev-kit pin moved to the newest" \
|
|
254
|
-
"published version and sync-drift re-synced the forced copies from the new" \
|
|
255
|
-
"canonical -- but the gate is **red**, so this bump needs a human." \
|
|
256
|
-
"" \
|
|
257
|
-
"The mechanical re-sync is already on this branch. A red gate here means the" \
|
|
258
|
-
"new dev-kit genuinely does not fit the product yet -- run the gate to see which:" \
|
|
255
|
+
"Automated dev-kit sync: the @norskvideo/ctl-dev-kit pin moved to the newest published version and sync-drift re-synced the forced copies from the new canonical." \
|
|
259
256
|
"" \
|
|
260
|
-
"
|
|
261
|
-
" bun run check:drift # a copy sync-drift could not mechanically converge" \
|
|
262
|
-
" bun run lint && bun run typecheck && bun run test:unit # a convention that breaks real code" \
|
|
257
|
+
"$verdict" \
|
|
263
258
|
"" \
|
|
264
|
-
"
|
|
259
|
+
"bot-prs merges this once checks, smoke, integration and the guides gate" \
|
|
260
|
+
"(build-image) have all passed on its head, wherever this repo runs them." \
|
|
261
|
+
"Until then main does not move; a red tier keeps it open and reports it on" \
|
|
262
|
+
"the product dashboard.")"
|
|
265
263
|
# gh is not on the self-hosted runner PATH (it lives only inside
|
|
266
264
|
# `nix develop`, and this step runs outside it), so create/update the PR
|
|
267
265
|
# straight against the REST API with the same PAT that pushed the branch.
|
|
268
|
-
#
|
|
269
|
-
#
|
|
270
|
-
# above already uses them).
|
|
271
|
-
repo="${{ github.repository }}"; owner="${repo%%/*}"
|
|
272
|
-
api="https://api.github.com/repos/$repo/pulls"
|
|
266
|
+
# The PAT (not GITHUB_TOKEN) is what lets the PR's own workflows run,
|
|
267
|
+
# which is the whole point: the bump is judged before it lands.
|
|
273
268
|
curl_api=(--fail-with-body -sS -H "Authorization: Bearer $API_TOKEN" -H "Accept: application/vnd.github+json")
|
|
274
269
|
existing="$(curl "${curl_api[@]}" "$api?head=$owner:$branch&state=open" | jq -r '.[0].number // empty')"
|
|
275
270
|
if [ -n "$existing" ]; then
|
|
276
271
|
curl "${curl_api[@]}" -X PATCH "$api/$existing" \
|
|
277
|
-
--data "$(jq -nc --arg t "$title" --arg b "$body" '{title:$t, body:$b}')"
|
|
272
|
+
--data "$(jq -nc --arg t "$title" --arg b "$body" '{title:$t, body:$b}')" > /dev/null
|
|
273
|
+
number="$existing"
|
|
274
|
+
echo "updated PR #$number"
|
|
278
275
|
else
|
|
279
|
-
curl "${curl_api[@]}" -X POST "$api" \
|
|
280
|
-
--data "$(jq -nc --arg t "$title" --arg b "$body" --arg h "$branch"
|
|
276
|
+
number="$(curl "${curl_api[@]}" -X POST "$api" \
|
|
277
|
+
--data "$(jq -nc --arg t "$title" --arg b "$body" --arg h "$branch" \
|
|
278
|
+
'{title:$t, body:$b, head:$h, base:"main"}')" | jq -r .number)"
|
|
279
|
+
echo "opened PR #$number"
|
|
281
280
|
fi
|
|
281
|
+
close_superseded "$number" || true
|
|
282
282
|
|
|
283
283
|
- name: Report "already fresh"
|
|
284
284
|
if: steps.sync.outputs.changed != '1'
|
|
@@ -15,11 +15,14 @@
|
|
|
15
15
|
# template launches, which an sdk-app pins in its own source.
|
|
16
16
|
#
|
|
17
17
|
# Nightly cron + on demand. The bump runs the product's own gate (lint,
|
|
18
|
-
# typecheck, unit tests)
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
18
|
+
# typecheck, unit tests), then ALWAYS opens (or updates) a PR on
|
|
19
|
+
# upgrade-latest/nightly, exactly as upgrade-latest.yml does, for bot-prs to
|
|
20
|
+
# merge once the PR's tiers are green; it never pushes to main (see that file
|
|
21
|
+
# for why). The branch is pushed with CI_DISPATCH_TOKEN (a PAT), so the PR's
|
|
22
|
+
# workflows fire -- a GITHUB_TOKEN push would fire nothing. It also moves
|
|
23
|
+
# flake.nix's ctlVersion, the norsk-ctl the product's CI tiers run, to the
|
|
24
|
+
# `latest` channel: an sdk-app's flake fetches no daemon, so it carries the
|
|
25
|
+
# version alone (ctl-pinned-in-repo.test.ts).
|
|
23
26
|
#
|
|
24
27
|
# Single-sourced in @norskvideo/ctl-dev-kit (conventions/upgrade-latest.sdk-app.yml)
|
|
25
28
|
# and copied verbatim into each sdk-app product repo as
|
|
@@ -113,7 +116,19 @@ jobs:
|
|
|
113
116
|
hub="https://hub.docker.com/v2/repositories/norskvideo/norsk/tags"
|
|
114
117
|
tag_re='^[0-9]+\.[0-9]+\.[0-9]+-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9a-f]+$'
|
|
115
118
|
changed=0
|
|
116
|
-
|
|
119
|
+
parts=()
|
|
120
|
+
|
|
121
|
+
# NORSK-CTL: the version flake.nix's ctlVersion names is the daemon every
|
|
122
|
+
# CI tier runs (ctl-pinned-in-repo.test.ts) as well as the one the dev
|
|
123
|
+
# shell launches, so it moves here like the engine does and the bump
|
|
124
|
+
# PR's tiers are what test it (ci-workflows docs/dashboard/
|
|
125
|
+
# ci-fragility.md, proposal A). Resolved up front, with everything
|
|
126
|
+
# else, so a refusal rewrites nothing.
|
|
127
|
+
ctl_base="https://s3.eu-west-1.amazonaws.com/norsk.video/norsk-ctl"
|
|
128
|
+
old_ctl="$( { grep -oE 'ctlVersion = "[^"]+"' flake.nix 2>/dev/null || true; } | head -1 | sed -E 's/.*"(.*)"/\1/')"
|
|
129
|
+
[ -n "$old_ctl" ] || { echo "::error::flake.nix pins no ctlVersion. CI runs the norsk-ctl it names, so add ctlVersion = \"<version>\"; to flake.nix."; exit 1; }
|
|
130
|
+
new_ctl="$(curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$ctl_base/latest")"
|
|
131
|
+
[ -n "$new_ctl" ] || { echo "::error::could not resolve norsk-ctl's latest channel"; exit 1; }
|
|
117
132
|
|
|
118
133
|
# CURRENT: the product's exact norsk-sdk pin, wherever a tracked
|
|
119
134
|
# package.json carries it (deps, devDeps or overrides). It is the one
|
|
@@ -157,7 +172,34 @@ jobs:
|
|
|
157
172
|
if [ "$old" != "$new" ]; then
|
|
158
173
|
git grep -lF "$old" -- . ':!bun.lock' | xargs -r sed -i "s|$old|$new|g"
|
|
159
174
|
changed=1
|
|
160
|
-
|
|
175
|
+
parts+=("media \`$new\`")
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# --- norsk-ctl pin (flake.nix) ------------------------------------
|
|
179
|
+
# ctlVersion, plus the per-platform hash of each platform the flake
|
|
180
|
+
# fetches the daemon for. A Studio product's flake fetches all four; an
|
|
181
|
+
# sdk-app's fetches none and carries the version alone. Mirrors
|
|
182
|
+
# packages/dev-kit/build/refresh-ctl-pin.sh in the ctl monorepo.
|
|
183
|
+
if [ "$old_ctl" != "$new_ctl" ]; then
|
|
184
|
+
declare -A ctlplat=([x86_64-linux]=linux-x64 [aarch64-linux]=linux-arm64 [aarch64-darwin]=darwin-arm64 [x86_64-darwin]=darwin-x64)
|
|
185
|
+
for sys in "${!ctlplat[@]}"; do
|
|
186
|
+
p="${ctlplat[$sys]}"
|
|
187
|
+
grep -qF "plat = \"$p\";" flake.nix || continue
|
|
188
|
+
hex="$(curl -fsSL --retry 3 --retry-all-errors --retry-delay 2 "$ctl_base/$new_ctl/norsk-ctl-$new_ctl-$p.sha256" | awk '{print $1}')"
|
|
189
|
+
[ -n "$hex" ] || { echo "no sha256 sidecar for $p"; exit 1; }
|
|
190
|
+
sri="$(nix hash convert --hash-algo sha256 --to sri "$hex")"
|
|
191
|
+
perl -0pi -e "s{(plat = \"$p\";\\s*hash = \")[^\"]*(\")}{\${1}$sri\${2}}" flake.nix
|
|
192
|
+
done
|
|
193
|
+
perl -0pi -e "s{(ctlVersion = \")[^\"]*(\")}{\${1}$new_ctl\${2}}" flake.nix
|
|
194
|
+
echo "ctl: $old_ctl -> $new_ctl"
|
|
195
|
+
changed=1
|
|
196
|
+
parts+=("norsk-ctl \`$new_ctl\`")
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
summary=""
|
|
200
|
+
if [ ${#parts[@]} -gt 0 ]; then
|
|
201
|
+
summary="$(printf '%s, ' "${parts[@]}")"
|
|
202
|
+
summary="${summary%, }"
|
|
161
203
|
fi
|
|
162
204
|
{
|
|
163
205
|
echo "changed=$changed"
|
|
@@ -192,111 +234,103 @@ jobs:
|
|
|
192
234
|
bun run test:unit
|
|
193
235
|
'
|
|
194
236
|
|
|
195
|
-
|
|
196
|
-
|
|
237
|
+
# The bump's only way to main is this PR. bot-prs merges it once every tier
|
|
238
|
+
# that runs on a bot PR -- checks, smoke, integration and build-image's
|
|
239
|
+
# guides gate, wherever this repo has them -- is green on its head
|
|
240
|
+
# (ci-workflows docs/dashboard/ci-fragility.md, proposal B). Landing on a
|
|
241
|
+
# green gate kept lint, typecheck and unit failures off main, but the tiers
|
|
242
|
+
# that break on an upstream change ran only after landing, so a bad
|
|
243
|
+
# upstream release still turned main red in every repo that took it. The
|
|
244
|
+
# gate above still runs: its verdict heads the PR body, and a red one
|
|
245
|
+
# shows on the PR's own checks too. Never a draft: bot-prs reads red from
|
|
246
|
+
# the tiers, and a draft could never become ready again by PATCH.
|
|
247
|
+
- name: Open (or update) the bump PR
|
|
248
|
+
if: steps.bump.outputs.changed == '1'
|
|
197
249
|
env:
|
|
198
250
|
API_TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
|
|
199
|
-
|
|
251
|
+
GATE: ${{ steps.gate.outcome }}
|
|
252
|
+
# Through env, never pasted into the script: the summary wraps each
|
|
253
|
+
# version in backticks, and inside a double-quoted shell string those
|
|
254
|
+
# ran as commands ("1.3.1-...: command not found"), dropping every
|
|
255
|
+
# version from the commit message and PR body.
|
|
200
256
|
SUMMARY: ${{ steps.bump.outputs.summary }}
|
|
201
257
|
run: |
|
|
202
258
|
set -euo pipefail
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
259
|
+
branch="upgrade-latest/nightly"
|
|
260
|
+
prefix="upgrade-latest/"
|
|
261
|
+
title="Bump the Norsk family to the SDK nightly"
|
|
262
|
+
repo="${{ github.repository }}"; owner="${repo%%/*}"
|
|
263
|
+
api="https://api.github.com/repos/$repo/pulls"
|
|
264
|
+
# An older PR of this bot's family is obsolete once this one is open:
|
|
265
|
+
# left open they piled up (nine were closed by hand on 2026-10-03).
|
|
266
|
+
# Best-effort: a failure here must not fail the bump.
|
|
207
267
|
close_superseded() {
|
|
208
|
-
|
|
268
|
+
local keep="$1" n
|
|
209
269
|
auth=(-H "Authorization: Bearer $API_TOKEN" -H "Accept: application/vnd.github+json")
|
|
210
|
-
for n in $(curl -sS "${auth[@]}" "$api
|
|
211
|
-
| jq -r --arg repo "$
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
270
|
+
for n in $(curl -sS "${auth[@]}" "$api?state=open&per_page=100" \
|
|
271
|
+
| jq -r --arg repo "$repo" --arg prefix "$prefix" --argjson keep "$keep" \
|
|
272
|
+
'.[] | select(.head.repo.full_name == $repo and (.head.ref | startswith($prefix)) and .number != $keep) | .number'); do
|
|
273
|
+
curl -sS -o /dev/null "${auth[@]}" -X POST "https://api.github.com/repos/$repo/issues/$n/comments" \
|
|
274
|
+
-d "$(jq -nc --arg b "Superseded by #$keep, a later run of this bot." '{body:$b}')"
|
|
275
|
+
curl -sS -o /dev/null "${auth[@]}" -X PATCH "$api/$n" -d '{"state":"closed"}'
|
|
215
276
|
echo "closed superseded PR #$n"
|
|
216
277
|
done
|
|
217
278
|
}
|
|
218
279
|
git config user.name "github-actions[bot]"
|
|
219
280
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
281
|
+
git checkout -B "$branch"
|
|
220
282
|
git add -A
|
|
221
283
|
git commit -m "chore: bump the Norsk family to the SDK nightly
|
|
222
284
|
|
|
223
285
|
${SUMMARY}
|
|
224
286
|
|
|
225
287
|
The norsk-sdk / norsk-api pins and the engine image ref moved together
|
|
226
|
-
via a global version-string replace; lockfile regenerated.
|
|
227
|
-
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
git fetch --unshallow origin main 2>/dev/null || git fetch origin main
|
|
243
|
-
git rebase origin/main || { git rebase --abort; echo "rebase hit a real conflict; aborting"; exit 1; }
|
|
244
|
-
done
|
|
245
|
-
echo "push still failing after 5 rebase attempts"; exit 1
|
|
246
|
-
|
|
247
|
-
- name: Open a reconciliation PR (gate red)
|
|
248
|
-
if: steps.bump.outputs.changed == '1' && steps.gate.outcome == 'failure'
|
|
249
|
-
env:
|
|
250
|
-
API_TOKEN: ${{ secrets.CI_DISPATCH_TOKEN }}
|
|
251
|
-
# Through env, never pasted into the script: the summary wraps each
|
|
252
|
-
# version in backticks, and inside a double-quoted shell string those
|
|
253
|
-
# ran as commands ("1.3.1-...: command not found"), dropping every
|
|
254
|
-
# version from the commit message and PR body.
|
|
255
|
-
SUMMARY: ${{ steps.bump.outputs.summary }}
|
|
256
|
-
run: |
|
|
257
|
-
set -euo pipefail
|
|
258
|
-
branch="upgrade-latest/nightly"
|
|
259
|
-
git config user.name "github-actions[bot]"
|
|
260
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
261
|
-
git checkout -b "$branch"
|
|
262
|
-
git add -A
|
|
263
|
-
git commit -m "chore: bump the Norsk family to the SDK nightly (needs reconciliation)
|
|
264
|
-
|
|
265
|
-
${SUMMARY}"
|
|
266
|
-
git push -f origin "$branch"
|
|
267
|
-
title="Bump the Norsk family to the SDK nightly"
|
|
288
|
+
via a global version-string replace; lockfile regenerated. flake.nix
|
|
289
|
+
ctlVersion, the norsk-ctl CI runs, moved to the latest channel."
|
|
290
|
+
# One branch per bump, force-pushed from today's main -- but only when
|
|
291
|
+
# its tree changed. A red bump PR left standing is rebuilt by every run
|
|
292
|
+
# of this bot, and pushing the same tree again would re-run every slow
|
|
293
|
+
# tier on it for nothing.
|
|
294
|
+
if git fetch -q origin "$branch" 2>/dev/null && [ "$(git rev-parse 'HEAD^{tree}')" = "$(git rev-parse 'FETCH_HEAD^{tree}')" ]; then
|
|
295
|
+
echo "$branch already holds this bump; not re-pushing it"
|
|
296
|
+
else
|
|
297
|
+
git push -f origin "$branch"
|
|
298
|
+
fi
|
|
299
|
+
if [ "$GATE" = "success" ]; then
|
|
300
|
+
verdict="Gate green: lint, typecheck and unit tests pass on the bumped pins."
|
|
301
|
+
else
|
|
302
|
+
verdict="**Gate red**: lint, typecheck or unit tests fail on the bumped pins. That usually means the new SDK nightly is bad, or genuinely changed something the product depends on. Fix it on this branch, or close this PR to skip the bump -- the next nightly run reopens it against whatever the channel holds then."
|
|
303
|
+
fi
|
|
268
304
|
body="$(printf '%s\n' \
|
|
269
|
-
"Automated nightly pin bump: the Norsk nightly
|
|
270
|
-
"pins came with it -- but the product gate is **red**, so this bump needs a" \
|
|
271
|
-
"human before it reaches main." \
|
|
305
|
+
"Automated nightly pin bump: the Norsk SDK nightly and norsk-ctl latest moved, and the pins came with them." \
|
|
272
306
|
"" \
|
|
273
307
|
" ${SUMMARY}" \
|
|
274
308
|
"" \
|
|
275
|
-
"
|
|
276
|
-
"changed something the product depends on. Run it to see which:" \
|
|
277
|
-
"" \
|
|
278
|
-
" bun run lint && bun run typecheck && bun run test:unit" \
|
|
309
|
+
"$verdict" \
|
|
279
310
|
"" \
|
|
280
|
-
"
|
|
281
|
-
"
|
|
311
|
+
"bot-prs merges this once checks, smoke, integration and the guides gate" \
|
|
312
|
+
"(build-image) have all passed on its head, wherever this repo runs them." \
|
|
313
|
+
"Until then main does not move; a red tier keeps it open and reports it on" \
|
|
314
|
+
"the product dashboard.")"
|
|
282
315
|
# gh is not on the self-hosted runner PATH (it lives only inside
|
|
283
316
|
# `nix develop`, and this step runs outside it), so create/update the PR
|
|
284
317
|
# straight against the REST API with the same PAT that pushed the branch.
|
|
285
|
-
# The PAT (not GITHUB_TOKEN) is what lets the PR's own
|
|
286
|
-
# is the whole point: the bump is judged before it lands
|
|
287
|
-
repo="${{ github.repository }}"; owner="${repo%%/*}"
|
|
288
|
-
api="https://api.github.com/repos/$repo/pulls"
|
|
318
|
+
# The PAT (not GITHUB_TOKEN) is what lets the PR's own workflows run,
|
|
319
|
+
# which is the whole point: the bump is judged before it lands.
|
|
289
320
|
curl_api=(--fail-with-body -sS -H "Authorization: Bearer $API_TOKEN" -H "Accept: application/vnd.github+json")
|
|
290
321
|
existing="$(curl "${curl_api[@]}" "$api?head=$owner:$branch&state=open" | jq -r '.[0].number // empty')"
|
|
291
322
|
if [ -n "$existing" ]; then
|
|
292
323
|
curl "${curl_api[@]}" -X PATCH "$api/$existing" \
|
|
293
324
|
--data "$(jq -nc --arg t "$title" --arg b "$body" '{title:$t, body:$b}')" > /dev/null
|
|
294
|
-
|
|
325
|
+
number="$existing"
|
|
326
|
+
echo "updated PR #$number"
|
|
295
327
|
else
|
|
296
|
-
curl "${curl_api[@]}" -X POST "$api" \
|
|
328
|
+
number="$(curl "${curl_api[@]}" -X POST "$api" \
|
|
297
329
|
--data "$(jq -nc --arg t "$title" --arg b "$body" --arg h "$branch" \
|
|
298
|
-
'{title:$t, body:$b, head:$h, base:"main"}')" | jq -r
|
|
330
|
+
'{title:$t, body:$b, head:$h, base:"main"}')" | jq -r .number)"
|
|
331
|
+
echo "opened PR #$number"
|
|
299
332
|
fi
|
|
333
|
+
close_superseded "$number" || true
|
|
300
334
|
|
|
301
335
|
- name: Report "already fresh"
|
|
302
336
|
if: steps.bump.outputs.changed != '1'
|