ci: run development, quality checks and CI on Bun 1.4.3

Pin Bun 1.4.3 with packageManager, and force every bun run script onto
the Bun runtime through bunfig so ng, ESLint and Prettier never fall
back to an installed Node. Type checking uses bun check for the
application and test projects, next to the Angular build.

CI jobs install the pinned release from a checksum-verified download
instead of npm and run project steps in a shell without Node on PATH,
which stays available only for the checkout and upload actions. The
image test's fake docker runs on Bun.

Refs #290, #293

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
nessarandClaude Opus 5.5 committed 2026-10-17 14:25:46 +02:00
1 parent f74d744f9b
commit ac69a41fc8
12 files changed
+206 -42

No files matched your search

+43 -7
View File
@@ -18,14 +18,14 @@ configuration; the Compose files remain manual deployment options.
| Job | Behavior |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `policy` | Tests the policy and publisher, then validates the checked-out commit, GitFlow, PR title, commits, and stable release version. |
| `validate` | Runs lint, formatting, and application/test type checks. |
| `policy` | Tests the policy and publisher with Bun, then validates the checked-out commit, GitFlow, PR title, commits, and stable release version. |
| `validate` | Runs ESLint, Prettier (application, `scripts/ci-*.mjs` and workflow files) and `bun check` type checks of application and test code, all on Bun with the frozen lockfile. |
| `test` | Runs functional coverage with `bun test --isolate` (line coverage only), converts it to HTML and Cobertura, and uploads coverage for seven days even on failure. |
| `benchmark` | Required seven-scenario protocol (one warm-up, two measured repetitions) outside coverage in a container limited to 3 GiB. It first times the frozen engine on `two years` (best of three) and fails any scenario whose mean planning time exceeds its multiplier times that calibration; the 600-second timeout is only a hang guard. |
| `build` | Builds browser/server production bundles and retains `dist` for seven days. |
| `security` | Runs Semgrep JavaScript/TypeScript SAST and Trivy dependency/secret scans; retains SARIF reports for seven days. Scanner execution must succeed; findings remain reports rather than a new severity gate. |
| `images` | Publishes branch/SHA images after required validation, including the benchmark, or promotes an existing SHA image for a stable tag. Runs only for pushes and same-repository PRs. |
| `required` | Fails on failed, cancelled, or skipped required validation, including the benchmark. Requires image publication when expected; accepts its intentional skip for fork PRs and manual dispatch. |
| `build` | Builds the client-only Angular production bundle (the only check of Angular templates; `bun check` does not replace it) and the Bun static-server bundle, and retains `dist` for seven days. |
| `security` | Runs Semgrep JavaScript/TypeScript SAST and Trivy dependency/secret scans; retains SARIF reports for seven days. Scanner execution must succeed; findings remain reports rather than a new severity gate. The scanners are not project JavaScript, so this job installs no Bun. |
| `images` | Publishes branch/SHA images after required validation, including the benchmark, or promotes an existing SHA image for a stable tag, running the publisher with Bun. Runs only for pushes and same-repository PRs. |
| `required` | Fails on failed, cancelled, or skipped required validation, including the benchmark. Requires image publication when expected; accepts its intentional skip for fork PRs and manual dispatch. It is shell only and needs no Bun. |
The benchmark runs on both push and pull_request events: the push run gates image
publication (`images.needs` includes `benchmark`) and the `required` aggregate.
@@ -41,7 +41,7 @@ branch slug; stable promotions share one group and are not cancelled in progress
## Runners and permissions
Validation uses Node 24.21.0 and Bun 1.4.2 with the frozen lockfile; the test and benchmark jobs use Bun 1.4.3. The image
Every job that runs project commands uses Bun 1.4.3 with the frozen lockfile (see [Bun toolchain and Node](#bun-toolchain-and-node)). Job containers stay on Node 24.21.0. The image
job uses Docker 29.8.2 and its matching Docker-in-Docker service. Scanner
versions are Semgrep 1.180.0 and Trivy 0.75.0; Trivy downloads are checksum-checked.
Trivy downloads its vulnerability database from Aqua's public ECR mirror because
@@ -72,6 +72,42 @@ Keep secret values in Gitea's Actions secret store. The configured tea account's
administrator token is not a CI publishing credential. No SSH or deployment
secret is required by this workflow.
## Bun toolchain and Node
Project commands run on Bun 1.4.3 only. `package.json` (`packageManager`),
`@types/bun`, both Docker stages and `scripts/install-bun.sh` carry the same
version. To upgrade, change all of them together and replace the installer's
four SHA-256 values with those of the new release's `SHASUMS256.txt`; the
installer refuses a `package.json` that names a different version, so a bump of
only one place fails CI instead of drifting.
- **Install.** `policy`, `validate`, `test`, `benchmark`, `build` and `images`
run `sh scripts/install-bun.sh`. It downloads the release zip for the
runner's architecture (x64 or aarch64) and libc (glibc, or musl on Alpine),
verifies its pinned SHA-256, and installs `/opt/bun/bin/bun`. It uses no npm.
- **Node-free project steps.** The five Debian jobs set
`defaults.run.shell: env PATH=/opt/bun/bin:/usr/bin:/bin bash -e {0}`. Every
`run:` step then sees Bun but not `/usr/local/bin`, where the `node` image keeps
`node`, `npm` and `yarn`. `bun run` additionally runs Node-shebang tools on Bun
through `bunfig.toml`. The security scanners are not project JavaScript, so
`security` has no Bun and no custom shell.
- **Why the containers keep Node.** `actions/checkout@v4` (`node20`) and
`actions/upload-artifact@v3` (`node16`) are JavaScript actions. The runner
starts them with the container's `node`; the custom shell does not affect
`uses:` steps. This is a platform requirement, not permission to run project
commands on Node.
- **`images` exception.** Alpine's `nodejs` package installs `node` into
`/usr/bin`, beside `git`, and checkout's post step needs it, so `PATH` cannot
hide it there. The project command is `/opt/bun/bin/bun scripts/ci-image.mjs`;
it starts only `process.execPath`, `docker` and `git`. Hiding Node there needs
an infrastructure decision (for example a runner image that ships Node outside
`PATH`), which this conversion does not make.
- **Needs live validation.** The custom shell template is a GitHub Actions
feature that Gitea's runner implements. A local run of the real engine
(`gitea-runner exec` v5.1.0, with the job containers above) executed it, but
only a live Gitea run confirms the instance's runner version, the checkout
action and the privileged Docker-in-Docker `images` job.
## Images and releases
Images use the [Gitea container registry](https://docs.gitea.com/usage/packages/container/)