Skip to content

chore: enforce the Bun install policy with Strictness - #37

Draft
0rierel wants to merge 2 commits into
masterfrom
task/t-3hz0d7qrep-bun-install-policy
Draft

0rierel wants to merge 2 commits into
masterfrom
task/t-3hz0d7qrep-bun-install-policy

Conversation

@0rierel

@0rierel 0rierel commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What changed

  • bunfig.toml (new, root). It sets the organization's Bun install policy:

    • [install] ignoreScripts = true: an install runs no lifecycle scripts, the repository's own included. Bun reads this key. The [install.lifecycle] ignore = true that other org repositories write is not read by Bun, so it never stopped any script.
    • [install] minimumReleaseAge = 604800: an install takes no version published less than 7 days ago (7 × 24 × 60 × 60 seconds).

    echoform had no bunfig.toml before, so lifecycle scripts ran and any fresh version could be installed.

  • playstrictness.config.json (new). It holds one Strictness check, bun, of the new settings kind. The check holds bunfig.toml and every **/bunfig.toml to those two keys, and fails if the root file is missing. The check only reads the two keys; every other setting stays the repository's own choice.

  • tools/strictness/ (new). This is the Strictness install: @playfast/strictness is a devDependency here, with its own bun.lock, .npmrc and bunfig.toml.

    • echoform is public and publishes the @playfast scope to npm. Strictness is private to the organization, on Artifact Registry. A root devDependency would make every bun install need an Artifact Registry token: CI, the release and deploy workflows, fork PRs and outside contributors. A root .npmrc mapping of @playfast would also send changeset publish to Artifact Registry instead of npm.
    • So Strictness installs on its own. It is not in workspaces, and the workspace install and bun.lock are unchanged.
    • Its bunfig.toml holds the same policy. It also lists the five @playfast/strictness* packages in minimumReleaseAgeExcludes, the way data-solution does, so a new Strictness release can be added the day it ships. Without the excludes, bun install refuses 0.8.3 (published 2026-10-19).
  • package.json. Two new scripts:

    • strictness:install runs cd tools/strictness && bun install --frozen-lockfile. It needs NPM_REGISTRY_TOKEN, for example gcloud auth print-access-token.
    • strictness:check runs playstrictness check.
  • .github/workflows/ci.yml. A new strictness job:

    • It gets an Artifact Registry token as gha-ar-reader@spring-452820.iam.gserviceaccount.com, the way muxmaxing and office-suite do on GitHub-hosted runners. It hands that token to the Strictness install step alone, then runs bun run strictness:check.
    • It is skipped for fork PRs, which get no id-token.
    • build-and-test, release.yml and deploy-wmux-client.yml are unchanged.

Depends on springdotnew/strictness#214

The settings kind is in springdotnew/strictness#214, which is not merged or released yet. Released @playfast/strictness 0.8.3 refuses this configuration:

$ bun run strictness:check
invalid configuration: unknown variant `settings`, expected one of `boundaries`, `oxlint`, `vendor`, `platform`, `bundle`, `docs`, `format`, `toolchain`, `rust`, `commits`, `contracts`, `suffixes`

So the strictness CI job fails until that release exists. Before this PR is marked ready, bump @playfast/strictness in tools/strictness/package.json (and its bun.lock) to the release that contains the settings kind.

The job also needs one IAM grant before it can install Strictness at all. Today it stops at the Artifact Registry token step:

failed to generate Google Cloud OAuth 2.0 Access Token for gha-ar-reader@spring-452820.iam.gserviceaccount.com:
Permission 'iam.serviceAccounts.getAccessToken' denied … IAM_PERMISSION_DENIED

Grant echoform's principal roles/iam.workloadIdentityUser on gha-ar-reader@spring-452820.iam.gserviceaccount.com. The principal is principalSet://iam.googleapis.com/projects/59831676433/locations/global/workloadIdentityPools/github/attribute.repository/springdotnew/echoform, or whatever attribute the other repositories' bindings use. The first push used the workflow's own federated token without impersonation, and Artifact Registry answered 403 on the @playfast/strictness-0.8.3.tgz download.

Check output, before and after

These runs use playstrictness built from #214's branch (cargo build --release --locked -p strictness-cli).

Before: master with only this playstrictness.config.json added. master has no bunfig.toml at all.

$ playstrictness check
bun: configured scope matches zero files.
exit=1

The same run with a root bunfig.toml that holds only the legacy org form [install.lifecycle] ignore = true:

bun: 1 files, 2 findings
error bunfig.toml: required-setting: `install.ignoreScripts` is not set; the policy requires `true`.
error bunfig.toml: required-setting: `install.minimumReleaseAge` is not set; the policy requires `604800`.
exit=1

After: this branch.

$ playstrictness check
bun: 2 files, 0 findings
exit=0

The two files are bunfig.toml and tools/strictness/bunfig.toml.

Bun install evidence

All runs use Bun 1.3.11, the version packageManager and CI pin. Each scratch project is outside the repository. Before this PR, echoform had no bunfig.toml, so each control run has no bunfig.toml.

a. Lifecycle scripts

The scratch project has a root postinstall and a file:./local-dep dependency with its own postinstall. local-dep is in trustedDependencies. Each script writes a marker file. No key of the fixed bunfig.toml had to be dropped.

bunfig.toml bun install Markers written
this PR's root file ok none
this PR's tools/strictness/bunfig.toml ok none
none (control, as on master) ok, prints $ node -e …root-postinstall… root-postinstall, dep-postinstall
[install.lifecycle] ignore = true only (legacy org form) ok root-postinstall, dep-postinstall

b. Minimum release age

The test package is undici. Its versions 8.12.0 (2026-10-14T09:04Z), 8.12.1 (2026-10-14T09:25Z), 8.12.2 (2026-10-15T15:05Z) and 8.12.3 (latest, 2026-10-19T14:02Z) are all newer than 7 days at the time of the test (cutoff 2026-10-13T21:04Z). 8.11.2 was published 2026-09-24T08:37Z.

bunfig.toml Command Result
this PR's root file bun add undici installed undici@8.11.2, so the newest 7-day-old version was resolved
this PR's root file bun add undici@8.12.3 exit 1: No version matching "undici" found for specifier "8.12.3" (blocked by minimum-release-age: 604800 seconds)
none (control) bun add undici installed undici@8.12.3

c. The repository's own install

bun install --frozen-lockfile with this branch's root bunfig.toml succeeds: 950 packages installed, the same as on master. The new bunfig.toml turns off three dependency scripts. echoform has no lifecycle scripts or trustedDependencies of its own.

  • esbuild@0.25.12 postinstall: the binary still comes from @esbuild/linux-x64, and the Vite builds work.
  • bufferutil@4.0.7 and utf-8-validate@5.0.10 install: node-gyp-build: both ship prebuilds, and ws treats them as optional.

On this branch after that install, every CI step passes locally: bun run lint (0 errors, same 8 warnings as master), bun run typecheck, bun run build, the wmux-client build, packages/echoform bun run test (8 pass) and the Playwright tests of todo-app (6 passed), file-editor (5 passed) and dev-server (4 passed). No CI step needed an explicit replacement for a script.

actionlint .github/workflows/ci.yml reports nothing. bun run knip already exits 1 on master. It reports one new line, Unlisted binaries: tools/strictness.bin/playstrictness, for the strictness:check script. knip is not part of CI.

Task: t-3hz0d7qrep

Commit a root bunfig.toml with the organization's Bun install policy:
[install] ignoreScripts = true, so an install runs no lifecycle scripts,
and minimumReleaseAge = 604800, so it installs no version published less
than 7 days ago.

A Strictness settings check in playstrictness.config.json holds every
committed bunfig.toml to those two keys, and a CI job runs it. Strictness
is private to the organization, on Artifact Registry, so it installs on
its own in tools/strictness (with its own bunfig.toml under the same
policy) and the public workspace install keeps needing no credentials.

The settings kind is not released yet (springdotnew/strictness#214):
bump @playfast/strictness to the release that ships it before merging.

Task: t-3hz0d7qrep
Signed-off-by: Ori Erel <orierel1i12@gmail.com>
The workflow's own identity gets a 403 from spring-452820/npm. Impersonate the reader service account the organization's other GitHub-hosted jobs use, and hand its token to the install step alone.

Signed-off-by: Ori Erel <orierel1i12@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant