@norskvideo/ctl-dev-kit 0.2.93 → 0.2.95

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.
@@ -177,9 +177,9 @@ ln -s ../../../../../docs/generated/<slug> website/src/content/docs/guides/<slug
177
177
 
178
178
  `docs:site` runs the dev-kit's `docs/guide-stubs.ts stub` first, so the site
179
179
  builds on a checkout that has never captured. The captures come from the
180
- product's nightly `publish-docs.yml`, which saves `docs/generated` to the
181
- Actions cache after a passing engine tier; `publish-docs-site.yml` restores
182
- the newest set before its build (and runs when `publish-docs` completes, so a
180
+ product's nightly `publish-docs.yml`, which keeps `docs/generated` as the
181
+ `docs-captures` artifact after a passing engine tier; `publish-docs-site.yml`
182
+ downloads the newest one before its build (and runs when `publish-docs` completes, so a
183
183
  fresh capture publishes without waiting for a push). It then runs
184
184
  `guide-stubs.ts check` and refuses to deploy while any guide is still a
185
185
  placeholder: until the guide has been captured once, the publish stops rather
@@ -214,9 +214,11 @@ where the repo has a site and is not a turnkey. On every push to `main` (and on
214
214
  demand) it installs `website/`, builds the public variant and deploys
215
215
  `website/dist-public` to Cloudflare Pages with `wrangler`. It never cancels a
216
216
  run in flight, because a cancelled deploy is a half-published site. Before the
217
- build it restores the newest recorded-guide captures from the Actions cache
218
- (`docs-captures-*`, saved by `publish-docs.yml`); a site with no recorded
219
- guides restores nothing it reads, and builds from its committed prose.
217
+ build it downloads the newest recorded-guide captures (the `docs-captures`
218
+ artifact `publish-docs.yml` keeps for 30 days, not the Actions cache, whose
219
+ entries are versioned by a compression that varies across runners); a site
220
+ with no recorded guides restores nothing it reads, and builds from its
221
+ committed prose.
220
222
 
221
223
  Where it may exist is the gate's business (ADR-0012, and the documentation
222
224
  plan): a repo with `website/astro.config.mjs` may carry it; a repo without a
@@ -229,10 +231,11 @@ What the repo needs, per site, and none of it to build or review one locally:
229
231
 
230
232
  1. **A Cloudflare Pages project**, e.g. `norsk-acme-docs`.
231
233
  2. **A custom domain** on it — `<product>.norsk.video` — and the DNS record.
232
- 3. **Two repo secrets.** `CLOUDFLARE_API_TOKEN`, a Pages:Edit token — the
233
- existing one is deliberately narrow and must cover the new project: widen it
234
- to all Pages projects in the account, or issue a second token — and
235
- `CLOUDFLARE_ACCOUNT_ID`. The workflow verifies the token at the API (both the
234
+ 3. **The Cloudflare credentials reaching the repo.** The secret
235
+ `CLOUDFLARE_API_TOKEN`, a Pages:Edit token that covers the new project
236
+ (Pages permissions are account-wide), and the variable
237
+ `CLOUDFLARE_ACCOUNT_ID`. Both are org-level, shared with selected
238
+ repositories: add the repo to each. The workflow verifies the token at the API (both the
236
239
  user-owned and the account-owned endpoint), never with `wrangler whoami`,
237
240
  which a Pages:Edit token cannot answer.
238
241
  4. **One repo variable.** `NORSK_DOCS_SITE_PROJECT`, the project name from
@@ -8,7 +8,7 @@
8
8
  # rides every push (conventions/docs-site.md, "Two documentation channels").
9
9
  # A site that also symlinks RECORDED guides (website/src/content/docs/guides/
10
10
  # <slug> -> docs/generated/<slug>) publishes them from the captures that nightly
11
- # run saves to the Actions cache: they are restored before the build, which
11
+ # run keeps as an artifact: they are restored before the build, which
12
12
  # stubs only a guide never captured, and the gate below refuses to deploy a
13
13
  # stub. So the site also runs when publish-docs completes, to publish a fresh
14
14
  # capture without waiting for the next push.
@@ -26,7 +26,9 @@
26
26
  # site publishes the day the variable is
27
27
  # set, with no change here.
28
28
  # secret CLOUDFLARE_API_TOKEN a Pages:Edit token covering that project
29
- # secret CLOUDFLARE_ACCOUNT_ID the account the project lives in
29
+ # variable CLOUDFLARE_ACCOUNT_ID the account the project lives in: not a
30
+ # secret, and the org already shares it as
31
+ # a variable (norsk-ctl reads it the same way)
30
32
  #
31
33
  # The site is BUILT whether or not it will be deployed: if the build is only
32
34
  # exercised on the day the Pages project appears, that day is when we find out
@@ -50,6 +52,7 @@ on:
50
52
 
51
53
  permissions:
52
54
  contents: read
55
+ actions: read # the newest docs-captures artifact, from the capture run
53
56
 
54
57
  # Never cancel in flight: a cancelled `wrangler pages deploy` is a half-published
55
58
  # site, and the next push rebuilds from the same inputs anyway. Queue instead.
@@ -122,17 +125,28 @@ jobs:
122
125
  clean: true
123
126
 
124
127
  # The recorded guides' captures, from the product's nightly capture run
125
- # (publish-docs.yml saves docs/generated after a passing engine tier):
126
- # the newest set, restored where docs:site:public expects it. None yet --
127
- # a site that records no guides, or before the first capture -- restores
128
- # nothing; the build stubs what is missing and the gate below refuses to
129
- # deploy a stub.
128
+ # (publish-docs.yml keeps docs/generated as the docs-captures artifact
129
+ # after a passing engine tier): the newest unexpired one, put where
130
+ # docs:site:public expects it. None yet -- a site that records no guides,
131
+ # or before the first capture -- restores nothing; the build stubs what is
132
+ # missing and the gate below refuses to deploy a stub. gh comes from the
133
+ # build shell (the runners have no system gh).
130
134
  - name: Restore the recorded guides' captures
131
- uses: actions/cache/restore@v4
132
- with:
133
- path: docs/generated
134
- key: docs-captures-${{ github.sha }}
135
- restore-keys: docs-captures-
135
+ env:
136
+ GH_TOKEN: ${{ github.token }}
137
+ run: |
138
+ nix develop .#build --command bash -c '
139
+ set -euo pipefail
140
+ run="$(gh api "repos/$GITHUB_REPOSITORY/actions/artifacts?name=docs-captures&per_page=20" \
141
+ --jq "[.artifacts[] | select(.expired | not)][0].workflow_run.id // empty")"
142
+ if [ -z "$run" ]; then
143
+ echo "no docs-captures artifact yet: nothing to restore"
144
+ exit 0
145
+ fi
146
+ mkdir -p docs/generated
147
+ gh run download "$run" --repo "$GITHUB_REPOSITORY" --name docs-captures --dir docs/generated
148
+ echo "restored the recorded guides captured by run $run"
149
+ '
136
150
 
137
151
  # website/ is a STANDALONE package -- its own bun.lock, outside the root
138
152
  # workspaces -- so the repo-root install never covers it, and the build
@@ -197,7 +211,7 @@ jobs:
197
211
  - name: Deploy to Cloudflare Pages
198
212
  env:
199
213
  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
200
- CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
214
+ CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
201
215
  NORSK_DOCS_SITE_PROJECT: ${{ vars.NORSK_DOCS_SITE_PROJECT }}
202
216
  PAGES_BRANCH: ${{ github.ref_name }}
203
217
  run: |
@@ -210,13 +224,13 @@ jobs:
210
224
  echo " 1. Create a Cloudflare Pages project for this site (e.g. $(basename "$GITHUB_REPOSITORY")-docs)."
211
225
  echo " 2. Add its custom domain${origin:+ (${origin#https://})} and the DNS record."
212
226
  echo " 3. Make sure CLOUDFLARE_API_TOKEN (Pages:Edit) covers that project, and that"
213
- echo " CLOUDFLARE_ACCOUNT_ID is set as a repo secret."
227
+ echo " the variable CLOUDFLARE_ACCOUNT_ID reaches this repo."
214
228
  echo " 4. Set the repo variable NORSK_DOCS_SITE_PROJECT to the project name."
215
229
  echo " Nothing in this workflow changes. See @norskvideo/ctl-dev-kit conventions/docs-site.md, Publishing."
216
230
  exit 0
217
231
  fi
218
232
  if [ -z "${CLOUDFLARE_API_TOKEN:-}" ] || [ -z "${CLOUDFLARE_ACCOUNT_ID:-}" ]; then
219
- echo "::error::NORSK_DOCS_SITE_PROJECT=$NORSK_DOCS_SITE_PROJECT is set, but CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID must both be repo secrets"
233
+ echo "::error::NORSK_DOCS_SITE_PROJECT=$NORSK_DOCS_SITE_PROJECT is set, but this repo needs both the secret CLOUDFLARE_API_TOKEN and the variable CLOUDFLARE_ACCOUNT_ID"
220
234
  exit 1
221
235
  fi
222
236
  cf_verify() {
@@ -319,16 +319,21 @@ jobs:
319
319
  run: nix develop .#build --command bun run docs:manual
320
320
 
321
321
  # The captures, for the product's own docs site: publish-docs-site.yml
322
- # restores the newest set, so its recorded guides publish from this run,
323
- # the only one that can make them. Only after a passing engine tier, so a
324
- # site never takes a half-captured set over the last good one. Each run
325
- # its own key: a cache entry is never overwritten.
326
- - name: Cache the guide captures for the docs site
322
+ # downloads the newest docs-captures artifact, so its recorded guides
323
+ # publish from this run, the only one that can make them. Only after a
324
+ # passing engine tier, so a site never takes a half-captured set over the
325
+ # last good one. An artifact, not actions/cache: a cache entry is
326
+ # versioned by its compression, and the runner pool mixes machines with
327
+ # and without zstd, so a save on one never matched a restore on another
328
+ # (relay, 2026-10-21: saved on ashton-nuc, missed on test2-runner-1).
329
+ - name: Keep the guide captures for the docs site
327
330
  if: ${{ steps.engine.outcome == 'success' }}
328
- uses: actions/cache/save@v4
331
+ uses: actions/upload-artifact@v4
329
332
  with:
333
+ name: docs-captures
330
334
  path: docs/generated
331
- key: docs-captures-${{ github.sha }}-${{ github.run_id }}
335
+ retention-days: 30
336
+ if-no-files-found: error
332
337
 
333
338
  # Said three ways, so a failure is never silent: a warning on the run, a
334
339
  # line in its summary, and the cell's detail (the notify job).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.2.93",
3
+ "version": "0.2.95",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./create-product": "./create-product/create-product.ts",