feat: canonical Node 24 base image with sqlite3 bundled
Node 24.18.0 (LTS Krypton) on alpine. sqlite3 bundled two ways, both
proven at build time: require('node:sqlite') builtin works, and native
better-sqlite3/sqlite3 compile against build-base+python3+sqlite-dev via
node-gyp. One base, one way: services do FROM ghcr.io/hanzoai/nodejs:v24.18.0.
Self-contained CI on hanzo-build-linux-amd64, amd64-only, semver-only
(vX.Y.Z + :v24), never sha/latest.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.git
|
||||
.github
|
||||
*.md
|
||||
@@ -0,0 +1,62 @@
|
||||
name: Docker
|
||||
# Canonical Node base image (Node 24 + sqlite3) for all Hanzo Node services.
|
||||
# Self-contained build on the Hanzo self-hosted ARC pool (hanzo-build-linux-amd64).
|
||||
# Semver only: a `v*` git tag publishes ghcr.io/hanzoai/nodejs:<tag> + the moving
|
||||
# major tag :v24. NEVER :latest / :sha (per Hanzo semver-only policy).
|
||||
# amd64-only — DOKS has no arm64 droplets (LLM.md, 2026-04-27).
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags: ['v*']
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
jobs:
|
||||
docker:
|
||||
# ARC v0.14 routes by scale-set NAME, not labels — name the pool directly.
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Compute tags
|
||||
id: tags
|
||||
run: |
|
||||
set -euo pipefail
|
||||
REF="${GITHUB_REF_NAME}"
|
||||
case "$REF" in
|
||||
v[0-9]*) VER="$REF" ;; # tag push: v24.18.0
|
||||
*) VER="v$(grep -oE 'node:[0-9]+\.[0-9]+\.[0-9]+' Dockerfile | head -1 | cut -d: -f2)" ;;
|
||||
esac # dispatch: derive from FROM pin
|
||||
MAJOR="$(echo "$VER" | grep -oE '^v[0-9]+')"
|
||||
echo "ver=$VER" >> "$GITHUB_OUTPUT"
|
||||
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
|
||||
echo "Publishing: ghcr.io/hanzoai/nodejs:$VER + :$MAJOR"
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push (linux/amd64)
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/hanzoai/nodejs:${{ steps.tags.outputs.ver }}
|
||||
ghcr.io/hanzoai/nodejs:${{ steps.tags.outputs.major }}
|
||||
cache-from: type=gha,scope=hanzoai-nodejs
|
||||
cache-to: type=gha,scope=hanzoai-nodejs,mode=max
|
||||
|
||||
- name: Verify sqlite3 in the published image
|
||||
run: |
|
||||
set -euo pipefail
|
||||
IMG="ghcr.io/hanzoai/nodejs:${{ steps.tags.outputs.ver }}"
|
||||
docker pull "$IMG"
|
||||
docker run --rm "$IMG" node -v
|
||||
docker run --rm "$IMG" node -e "require('node:sqlite');console.log('node:sqlite loads on '+process.version)"
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
*.log
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# hanzoai/nodejs — the ONE canonical Node base image for every Hanzo Node service.
|
||||
#
|
||||
# Node 24 (LTS "Krypton") + sqlite3 bundled two ways, BOTH proven at build time:
|
||||
# 1. node:sqlite — the built-in DatabaseSync (stable since Node 22.5; present in
|
||||
# 24). `require('node:sqlite')` works out of the box, zero dependencies.
|
||||
# 2. native npm sqlite — better-sqlite3 / sqlite3 compile against the baked-in
|
||||
# toolchain (build-base + python3 + sqlite-dev) through node-gyp.
|
||||
#
|
||||
# One base, one way. Services do: FROM ghcr.io/hanzoai/nodejs:v24.18.0
|
||||
# Bump the pinned node:<ver>-alpine below to move the whole fleet's runtime;
|
||||
# the git tag (vX.Y.Z) must match the Node version so consumers can reason about it.
|
||||
FROM node:24.18.0-alpine
|
||||
|
||||
# Native addon build toolchain (node-gyp needs python3 + make + g++, all via
|
||||
# build-base), the system sqlite CLI + headers, and the glibc shim so prebuilt
|
||||
# (glibc) native addons load on musl. corepack ships pnpm/yarn shims.
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
python3 \
|
||||
sqlite \
|
||||
sqlite-dev \
|
||||
libc6-compat \
|
||||
&& corepack enable
|
||||
|
||||
# Build-time proof gate — the image FAILS TO BUILD if either sqlite path breaks.
|
||||
# (1) node:sqlite builtin round-trips an in-memory DB.
|
||||
# (2) better-sqlite3 compiles from source against the toolchain and round-trips.
|
||||
# The verify scratch dir is removed so it adds no weight to the published layer.
|
||||
RUN node -e "const {DatabaseSync}=require('node:sqlite');const d=new DatabaseSync(':memory:');d.exec('create table t(x int)');d.prepare('insert into t values (?)').run(7);if(d.prepare('select x from t').get().x!==7)throw new Error('node:sqlite roundtrip failed');console.log('node:sqlite OK on '+process.version);" \
|
||||
&& mkdir -p /tmp/verify && cd /tmp/verify && npm init -y >/dev/null 2>&1 \
|
||||
&& npm install --no-audit --no-fund better-sqlite3 >/dev/null 2>&1 \
|
||||
&& node -e "const D=require('better-sqlite3');const db=new D(':memory:');db.exec('create table t(x int)');db.prepare('insert into t values (?)').run(42);if(db.prepare('select x from t').get().x!==42)throw new Error('better-sqlite3 roundtrip failed');console.log('better-sqlite3 '+require('better-sqlite3/package.json').version+' OK');" \
|
||||
&& cd / && rm -rf /tmp/verify /root/.npm
|
||||
|
||||
# Match the upstream node image default so this is a transparent drop-in.
|
||||
CMD ["node"]
|
||||
@@ -0,0 +1,33 @@
|
||||
# LLM.md — hanzoai/nodejs
|
||||
|
||||
Canonical Node base image for the whole Hanzo Node fleet. One base, one way.
|
||||
|
||||
## What this is
|
||||
|
||||
`FROM node:24.18.0-alpine` + the sqlite3 toolchain, published as
|
||||
`ghcr.io/hanzoai/nodejs:v24.18.0` (+ `:v24`). Every Hanzo Node service does
|
||||
`FROM ghcr.io/hanzoai/nodejs:v24.18.0` so Node 24 and sqlite3 are uniform.
|
||||
|
||||
## sqlite3, two ways (both build-gated)
|
||||
|
||||
- `require('node:sqlite')` (builtin `DatabaseSync`, stable Node ≥22.5) — no deps.
|
||||
- `better-sqlite3` / `sqlite3` compile via node-gyp against `build-base` +
|
||||
`python3` + `sqlite-dev`. The Dockerfile compiles better-sqlite3 at build time
|
||||
and round-trips it; the image fails to build if that breaks.
|
||||
|
||||
## Why alpine
|
||||
|
||||
Majority of Hanzo Node services already use `node:*-alpine`. better-sqlite3
|
||||
compiles cleanly on musl with `build-base`; `node:sqlite` is builtin regardless.
|
||||
`libc6-compat` lets prebuilt glibc addons load. Debian-slim consumers
|
||||
(paas, platform, base-studio, market, embeddings, store-api, insights, gallery)
|
||||
need an `apt-get`→`apk` port before switching to this base — tracked as the sweep tail.
|
||||
|
||||
## Release / bump
|
||||
|
||||
1. Edit the `FROM node:<X.Y.Z>-alpine` pin in `Dockerfile` to move the runtime.
|
||||
2. Tag `vX.Y.Z` (must equal the Node version). CI publishes `:vX.Y.Z` + `:vX`.
|
||||
3. Consumers bump their `FROM ghcr.io/hanzoai/nodejs:vX.Y.Z` pin on their own release.
|
||||
|
||||
CI: `.github/workflows/build.yml`, self-contained, routes to `hanzo-build-linux-amd64`.
|
||||
amd64-only (DOKS has no arm64). Semver only, never sha/latest.
|
||||
@@ -0,0 +1,32 @@
|
||||
# hanzoai/nodejs
|
||||
|
||||
The **one** canonical Node base image for every Hanzo Node service.
|
||||
|
||||
- **Node 24** (LTS "Krypton", pinned `node:24.18.0-alpine`)
|
||||
- **sqlite3 bundled, two ways** — both proven at build time:
|
||||
1. `require('node:sqlite')` — the built-in `DatabaseSync` (Node ≥ 22.5). Zero deps.
|
||||
2. Native npm sqlite (`better-sqlite3`, `sqlite3`) compiles against the baked-in
|
||||
toolchain (`build-base` + `python3` + `sqlite-dev`) via node-gyp.
|
||||
|
||||
## Use
|
||||
|
||||
One way. In any Hanzo Node service's Dockerfile:
|
||||
|
||||
```dockerfile
|
||||
FROM ghcr.io/hanzoai/nodejs:v24.18.0
|
||||
```
|
||||
|
||||
Pin the exact semver (`v24.18.0`). The moving major tag `v24` also exists but
|
||||
consumers must pin the patch for reproducible builds.
|
||||
|
||||
## Verify
|
||||
|
||||
```sh
|
||||
docker run --rm ghcr.io/hanzoai/nodejs:v24.18.0 node -e "require('node:sqlite');console.log('ok')"
|
||||
```
|
||||
|
||||
## Release
|
||||
|
||||
Tag `vX.Y.Z` where `X.Y.Z` matches the pinned Node version in the `Dockerfile`.
|
||||
CI (`hanzo-build-linux-amd64`, self-hosted) builds `linux/amd64` and publishes
|
||||
`ghcr.io/hanzoai/nodejs:vX.Y.Z` + `:vX`. Never `:latest`, never `:sha`.
|
||||
Reference in New Issue
Block a user