Skip to content

build: Build and run on Node and npm, not Bun - #54

Merged
ecv merged 1 commit into
mainfrom
build/node-not-bun
Nov 3, 2026
Merged

ecv merged 1 commit into
mainfrom
build/node-not-bun

Conversation

@ecv

@ecv ecv commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Bun checks outbound TLS against root certificates compiled into its binary, so this app drops a distrusted root like eMudhra's only when Bun ships a release and we rebuild. Dependabot also can't read bun.lock: it saw only the direct ranges in package.json and none of the ~830 packages they resolve to. This PR removes Bun from the image, the scripts, the Pages workflow and the lock file, and replaces bun.lock with a package-lock.json that pins the same tree.

Nothing changes on www.cloudvalid.com when this merges. The live site is GitHub Pages, last deployed on 5 March 2026. Every Pages build since then has failed with The requested module 'js-yaml' does not provide an export named 'default', which #46 tracks. That failure doesn't depend on Bun: this branch reproduces it unchanged under Node and npm, so the Pages deploy stays red until #46 lands. No workflow builds or publishes the Docker image, and nothing in datum-cloud/infra deploys it.

What changes

  • Image: every stage uses node:26.12.1-alpine3.24, pinned by digest, in place of oven/bun:1.3-slim. npm ci installs the build dependencies, and the production stage runs npm ci --omit=dev instead of copying the build stage's node_modules, so devDependencies no longer ship in the runtime image. The bun.lock already pinned sharp's linuxmusl binaries, so Alpine is covered.
  • Pages workflow: actions/setup-node@v7 with .nvmrc and the npm cache, then npm ci and npm run build. The shared CI workflow now detects npm from package-lock.json.
  • Scripts: start is node ./server.mjs, and lint chains its steps with npm run. .gitignore and .prettierignore drop their Bun entries.
  • Server: keepAliveTimeout is 75 s and headersTimeout 76 s. Node's default of 5 s closes idle connections well inside Envoy's 60 s upstream idle timeout, so Envoy could reuse a socket the server was closing and return a 503. On SIGTERM the server stops accepting, closes idle connections, and force-closes the rest after 25 s, inside a 30 s grace period. A second SIGTERM does nothing.

After this PR, a repository-wide grep for bun finds only "a bunch" in the font demo pages.

The lock pins what Bun pinned

This isn't a fresh npm install. Each package's path, version and integrity hash came from bun.lock, and the registry manifest for that exact version supplied the dependency and metadata fields. Every one of Bun's 852 hashes matched the registry's. npm install --package-lock-only then normalized the lock, adding only the dev, optional and peer flags.

817 of Bun's 852 entries are in the lock at the same path, version and hash. Bun doesn't enforce peer dependencies, and npm refused the tree as Bun laid it out, so the rest differ in four ways:

  • @astrojs/markdown-remark: Bun kept 6.3.10 at the top level, which nothing depends on, and nested the 7.2.2 that @astrojs/mdx pins. Astro and Starlight ask for 7.x as a peer, so the lock moves 7.2.2 to the top level and drops 6.3.10 and the 15 packages only it used. Astro now resolves the major it declares; under Bun it would have found 6.3.10.
  • @types/node (dev only): the top-level 17.0.45 fails Vite's @types/node peer range, so the 24.13.3 that Bun nested under sitemap moves to the top level.
  • ajv-draft-04 moves under yaml-language-server, its only dependent, so its ajv ^8 peer finds the ajv 8 nested there rather than ESLint's ajv 6.
  • typescript-eslint and @typescript-eslint/* (dev only) move from 8.56.1 to 8.66.0, with ts-api-utils from 2.4.0 to 2.5.0. eslint-plugin-astro 3.x requires >=8.61.0, which no 8.56.1 tree satisfies. 8.66.0 is the version Bun already pinned for astro-eslint-parser, so those nested copies fold into one, and the ^8.56.1 ranges in package.json are unchanged.

Verified locally

  • npm ci leaves the lock unchanged, and npm ls --all is clean.
  • npm run lint passes. prettier --check . flags the same six files it flags on main, none touched here.
  • npm run build fails at generating static routes with the same js-yaml error as main under Bun (fix(deps): resolve a single compatible js-yaml so the Astro build completes #46).
  • docker build --target dependencies succeeds. A production-stage image with a stub dist runs on Node v26.12.1, loads sharp, ships no ESLint packages, and returns 200 on /healthz.
  • Keep-alive: responses carry Keep-Alive: timeout=75, and a connection left idle for 30 s is reused.
  • Drain: on SIGTERM an idle keep-alive client is disconnected and the process exits at once. A request still in flight is cut at 25 s, after which the process exits.

What Dependabot will see

npm audit reports 1 critical, 18 high, 19 moderate and 3 low findings in the versions Bun pinned, 1 critical and 14 high of them in production dependencies. This PR leaves them alone so the swap stays like-for-like, so expect Dependabot alerts once it merges. #48 covers most of them.

Open pull requests this touches

#46 and #48 edit bun.lock, which this PR deletes, so each will need its change rebuilt against package-lock.json. #47 edits server.mjs in different lines from this PR. server.mjs still imports dist/server/entry.mjs, which the static build doesn't emit, so the container can't start until #47 or an equivalent fix lands.

Fixes #53
Part of datum-cloud/infra#7480

Bun checks TLS against roots compiled into its binary, so a distrusted
root leaves only with a Bun release and a rebuild, and Dependabot cannot
read bun.lock, so it never saw the packages behind the direct ranges.

Key changes:
- Image: node 26.12.1 on Alpine, pinned by digest; npm ci builds it and
  npm ci --omit=dev installs the runtime dependencies
- Lock: package-lock.json built from bun.lock, same versions and hashes
  except where npm's peer checks needed a change
- Pages workflow: setup-node with .nvmrc and the npm cache
- Server: keep-alive 75s, above Envoy's 60s idle timeout; SIGTERM closes
  idle connections and force-closes the rest after 25s
@ecv
ecv requested review from AriaEdo and kevwilliams November 2, 2026 21:41
@ecv
ecv enabled auto-merge November 2, 2026 22:27
@ecv
ecv requested a review from yahyafakhroji November 2, 2026 23:43
@ecv
ecv merged commit 8e53d0c into main Nov 3, 2026
2 checks passed
@ecv
ecv deleted the build/node-not-bun branch November 3, 2026 03:15
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.

Run cloudvalid.com on Node instead of Bun

2 participants