From 92e9091790c9aef3d23a3faca448974965c7c22f Mon Sep 17 00:00:00 2001 From: DavidBabinec Date: Sat, 5 Sep 2026 16:25:45 +0200 Subject: [PATCH] chore(bun): upgrade to Bun 1.4.2 and pin the version in one place Bun moves from 1.3.11 to 1.4.2, and the version stops being written in ten places. package.json now carries "packageManager": "bun@1.4.2", which oven-sh/setup-bun reads by default, so the six workflow steps in ci.yml and release.yml drop their explicit bun-version. The Dockerfile declares ARG BUN_VERSION=1.4.2 once and the three stages build from it. The engines range widens to >=1.4.0 <1.5.0. Bumping Bun is now two lines (the packageManager field and the ARG), and the Docker gate test asserts that shape. The bootstrap determinism note in scripts/sync-plugin-bootstrap.ts names the new range and image. Nothing else had to change: install with the frozen lockfile, tsc, the Vite build, lint, and the full test suite all pass under 1.4.2 with the lockfile untouched, and the committed QuickJS bootstrap artifacts are byte-identical under the new bundler (bootstrap:check). Why now: 1.4 brings a native React Compiler in bun build, 2x faster startup and less than half the memory on Linux, and 1.4.1 fixed a run of node:http and socket lifecycle bugs (Vite port-retry hang, paused sockets never emitting end, ws handleUpgrade after await, WebSocket backpressure) that map onto the dev-server workarounds this repo carries. Those get re-tested and, where the fixes hold, deleted in a follow-up on top of this pin bump, so each step stays reviewable on its own. Verification (isolated Bun 1.4.2, global stays 1.3.11): bun install --frozen-lockfile clean, lockfile unchanged bun run bootstrap:check fresh, 2 artifacts byte-identical bun run icons:check clean bun run build tsc + vite clean bun run lint clean bun test 6836 pass, 0 fail --- .github/workflows/ci.yml | 6 ------ .github/workflows/release.yml | 6 ------ Dockerfile | 10 +++++++--- package.json | 3 ++- scripts/sync-plugin-bootstrap.ts | 2 +- src/__tests__/server/dockerConfig.test.ts | 5 +++-- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d362178d..face64517 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,8 +26,6 @@ jobs: - name: Set up Bun uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 - name: Install dependencies run: bun install --frozen-lockfile @@ -45,8 +43,6 @@ jobs: - name: Set up Bun uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 - name: Install dependencies run: bun install --frozen-lockfile @@ -63,8 +59,6 @@ jobs: - name: Set up Bun uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4c334049..fa36cf0a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,8 +19,6 @@ jobs: - name: Set up Bun uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 - name: Install dependencies run: bun install --frozen-lockfile @@ -100,8 +98,6 @@ jobs: - name: Set up Bun uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 - name: Install dependencies (all platforms' native packages) run: bun install --frozen-lockfile --os='*' --cpu='*' @@ -145,8 +141,6 @@ jobs: - name: Set up Bun uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 - name: Install dependencies run: bun install --frozen-lockfile diff --git a/Dockerfile b/Dockerfile index cdb73b77f..15766106f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,10 @@ # syntax=docker/dockerfile:1 -FROM oven/bun:1.3.11 AS build +# The one place the Bun version lives for the image; CI reads it from +# package.json "packageManager" instead, so bumping Bun is two lines. +ARG BUN_VERSION=1.4.2 + +FROM oven/bun:${BUN_VERSION} AS build WORKDIR /app # vendor/pixel-art-icons is a `file:` dep — `bun install` needs it on disk to # resolve the dependency, so copy it alongside the manifest before installing. @@ -10,13 +14,13 @@ RUN bun install --frozen-lockfile COPY . . RUN bun run build -FROM oven/bun:1.3.11 AS production-deps +FROM oven/bun:${BUN_VERSION} AS production-deps WORKDIR /app COPY package.json bun.lock ./ COPY vendor ./vendor RUN bun install --frozen-lockfile --production -FROM oven/bun:1.3.11 AS runtime +FROM oven/bun:${BUN_VERSION} AS runtime WORKDIR /app ARG INSTATIC_VERSION=dev diff --git a/package.json b/package.json index 9fee8320d..624591d9a 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,9 @@ "private": true, "version": "0.0.18", "engines": { - "bun": ">=1.3.0 <1.4.0" + "bun": ">=1.4.0 <1.5.0" }, + "packageManager": "bun@1.4.2", "description": "Self-hosted CMS with an integrated visual editor.", "author": "David Babinec", "license": "MIT", diff --git a/scripts/sync-plugin-bootstrap.ts b/scripts/sync-plugin-bootstrap.ts index 86f9949e3..fc62e5772 100644 --- a/scripts/sync-plugin-bootstrap.ts +++ b/scripts/sync-plugin-bootstrap.ts @@ -28,7 +28,7 @@ * Bundler determinism: `Bun.build` output is stable within a Bun minor but is * NOT guaranteed bit-identical across minors, so a Bun upgrade can legitimately * change the bytes. The expected Bun is pinned by `engines.bun` in package.json - * (`>=1.3.0 <1.4.0`) and by the `oven/bun:1.3` base image in the Dockerfile. On + * (`>=1.4.0 <1.5.0`) and by the `oven/bun:1.4` base image in the Dockerfile. On * a deliberate Bun-minor bump, regenerate (`bun run bootstrap:sync`) in the same * change so the gate fails only on real source drift, never on a routine * upgrade. diff --git a/src/__tests__/server/dockerConfig.test.ts b/src/__tests__/server/dockerConfig.test.ts index 0a6645aba..52c7460a1 100644 --- a/src/__tests__/server/dockerConfig.test.ts +++ b/src/__tests__/server/dockerConfig.test.ts @@ -22,9 +22,10 @@ describe('self-host docker config', () => { it('defines a production Docker image that builds assets before runtime startup', () => { const dockerfile = readFileSync('Dockerfile', 'utf8') - expect(dockerfile).toContain('FROM oven/bun:1.3.11 AS build') + expect(dockerfile).toContain('ARG BUN_VERSION=1.4.2') + expect(dockerfile).toContain('FROM oven/bun:${BUN_VERSION} AS build') expect(dockerfile).toContain('RUN bun run build') - expect(dockerfile).toContain('FROM oven/bun:1.3.11 AS runtime') + expect(dockerfile).toContain('FROM oven/bun:${BUN_VERSION} AS runtime') expect(dockerfile).toContain('ARG INSTATIC_VERSION=dev') expect(dockerfile).toContain('LABEL org.opencontainers.image.version="${INSTATIC_VERSION}"') expect(dockerfile).toContain('CMD ["bun", "run", "server/index.ts"]')