Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,22 @@ on:
- '*'
permissions:
contents: read

env:
FORCE_COLOR: 1
PREBUILD_NODE_VERSION: '20'
DEFAULT_NODE_VERSION: '24'
ALPINE_VARIANT: 'alpine3.20'

jobs:
env_vars:
runs-on: ubuntu-latest
outputs:
prebuild_node_version: ${{ env.PREBUILD_NODE_VERSION }}
default_node_version: ${{ env.DEFAULT_NODE_VERSION }}
steps:
- run: echo "exposing environment variables for passing to reusable workflows"

verify-version:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -65,7 +78,7 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
node-version: ${{ env.DEFAULT_NODE_VERSION }}
- name: Install dependencies
run: yarn install --frozen-lockfile --ignore-scripts
- name: Run lint
Expand Down Expand Up @@ -128,6 +141,21 @@ jobs:
target: x64
platform: win32-x64
node: 24
- os: windows-11-arm
host: arm64
target: arm64
platform: win32-arm64
node: 20
- os: windows-11-arm
host: arm64
target: arm64
platform: win32-arm64
node: 22
- os: windows-11-arm
host: arm64
target: arm64
platform: win32-arm64
node: 24
- os: macos-latest
host: arm64
target: arm64
Expand Down Expand Up @@ -219,7 +247,7 @@ jobs:

- name: Upload binaries to commit artifacts
uses: actions/upload-artifact@v7
if: matrix.node == 24
if: matrix.node == ${{ env.PREBUILD_NODE_VERSION }}
with:
name: prebuilt-binaries-${{ matrix.platform }}
path: prebuilds/*
Expand All @@ -238,13 +266,13 @@ jobs:
sleep 10
fi
done
if: matrix.node == 24 && startsWith(github.ref, 'refs/tags/')
if: matrix.node == ${{ env.PREBUILD_NODE_VERSION }} && startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ github.token }}

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
if: matrix.node == 24 && matrix.platform == 'linux-x64'
if: matrix.node == ${{ env.DEFAULT_NODE_VERSION }} && matrix.platform == 'linux-x64'
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: gms1/node-sqlite3
Expand Down Expand Up @@ -278,8 +306,8 @@ jobs:
--file ./tools/BinaryBuilder.Dockerfile \
--tag sqlite-builder \
--no-cache \
--build-arg VARIANT=alpine3.20 \
--build-arg NODE_VERSION=24 \
--build-arg VARIANT=${{ env.ALPINE_VARIANT }} \
--build-arg NODE_VERSION=${{ env.PREBUILD_NODE_VERSION }} \
.
CONTAINER_ID=$(docker create -it sqlite-builder)
docker cp $CONTAINER_ID:/usr/src/build/prebuilds/ ./prebuilds
Expand Down Expand Up @@ -321,7 +349,7 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
node-version: ${{ env.PREBUILD_NODE_VERSION }}

- name: Install dependencies
run: yarn install --frozen-lockfile --ignore-scripts
Expand Down Expand Up @@ -359,11 +387,11 @@ jobs:
GH_TOKEN: ${{ github.token }}

test-package:
needs: [package]
needs: [package, env_vars]
if: >-
!cancelled() &&
needs.package.result == 'success'
uses: ./.github/workflows/test-npm-package.yml
with:
target_run_id: ${{ github.run_id }}
node_version: '22'
node_version: ${{ needs.env_vars.outputs.default_node_version }}
3 changes: 2 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ permissions:

env:
FORCE_COLOR: 1
DEFAULT_NODE_VERSION: '24'

jobs:
publish:
Expand All @@ -36,7 +37,7 @@ jobs:

- uses: actions/setup-node@v6
with:
node-version: 24
node-version: ${{ env.DEFAULT_NODE_VERSION }}
registry-url: https://registry.npmjs.org

- name: Download tarball from GitHub Release
Expand Down
36 changes: 20 additions & 16 deletions memory-bank/build-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ The `NAPI_VERSION` define is set via `napi_build_version` variable in binding.gy
**How it works**:
- The `napi_build_version` variable is automatically set by node-gyp based on the target Node.js version
- For local builds, it's stored in `build/config.gypi` (e.g., `"napi_build_version": "9"`)
- For prebuilds, `prebuildify` passes it via the `--napi` flag which builds a single NAPI-version-agnostic binary (named `@homeofthings+sqlite3.<libc>.node`). The actual NAPI version used at compile time is determined by the Node.js version running the build (e.g., Node 24 supports NAPI v9). Since NAPI is backward compatible, a binary built with NAPI v9 runs on any Node.js supporting v9 or lower.
- For prebuilds, `prebuildify` passes it via the `--napi` flag which builds a single NAPI-version-agnostic binary (named `@homeofthings+sqlite3.<libc>.node`). The actual NAPI version used at compile time is determined by the Node.js version running the build. Since NAPI is forward-compatible, prebuilts must be built on the lowest supported Node version to maximize compatibility — building on Node 20 (NAPI v9) produces prebuilts compatible with all Node.js 20+ versions.

### NAPI Versions Configuration

With `prebuildify --napi`, the NAPI version is auto-detected from the build Node.js version — it is not explicitly configured. The CI builds prebuilds on Node 24 (which supports NAPI v9), producing a single `@homeofthings+sqlite3.glibc.node` binary per platform. The historical `[3, 6]` configuration from the old `binary.napi_versions` package.json field is no longer used.
With `prebuildify --napi`, the NAPI version is auto-detected from the build Node.js version — it is not explicitly configured. The CI builds prebuilds on Node 20 (which supports NAPI v9), producing a single `@homeofthings+sqlite3.glibc.node` binary per platform. The `PREBUILD_NODE_VERSION` workflow variable controls which Node version is used for prebuilds.

**Why multiple versions?**

Expand All @@ -146,18 +146,14 @@ NAPI versions are independent of Node.js versions - they represent API feature t
| v9 | External strings, syntax error creation |
| v10 | Latin1 external strings |

**Backward Compatibility**:
**Forward Compatibility**:

NAPI is backward compatible - a binary built for NAPI v3 will run on any Node.js that supports v3 or higher. Since Node.js 20.17.0+ supports NAPI v9, it can run binaries built for v3, v6, or v9.
NAPI is forward-compatible a binary built with NAPI_VERSION=X requires a Node.js version supporting NAPI vX or higher. Since our prebuilts are built with NAPI v9 on Node 20, they are compatible with all Node.js 20+ versions (which support NAPI v9 or higher).

**Code Conditionals**:

The source code uses `#if NAPI_VERSION < 6` conditionals in [`src/database.h`](../src/database.h) and [`src/database.cc`](../src/database.cc) to provide backward compatibility for NAPI versions below v6. When building for NAPI v6+, these conditionals are disabled.

**Current Configuration Rationale**:

The `[3, 6]` configuration is historical from when this fork supported older Node.js versions. Since the project now requires Node.js >= 20.17.0 (which supports NAPI v9), both prebuilt variants work correctly. Future versions could simplify to a single NAPI version (e.g., v6 or v9).

## Assert Control

### Asserts in Debug Mode
Expand Down Expand Up @@ -230,7 +226,7 @@ The `install` script runs `node-gyp-build` which tests whether the prebuilt bina
## Platform Support

- Node.js >= 20.17.0
- NAPI: version-agnostic (`@homeofthings+sqlite3.*.node`), built with NAPI v9 on Node 24
- NAPI: version-agnostic (`@homeofthings+sqlite3.*.node`), built with NAPI v9 on Node 20 (PREBUILD_NODE_VERSION)
- Platforms: Linux (glibc + musl), macOS, Windows (see CI configuration)

## Security Hardening
Expand Down Expand Up @@ -317,13 +313,21 @@ The project uses three GitHub Actions workflows for continuous integration and r

**Triggers**: `workflow_dispatch`, `pull_request`, push to `main`, tags (`*`)

**Workflow Environment Variables**:

| Variable | Value | Purpose |
|----------|-------|---------|
| `PREBUILD_NODE_VERSION` | `'20'` | Node version for building prebuilts (NAPI v9, compatible with all Node 20+) |
| `DEFAULT_NODE_VERSION` | `'24'` | Node version for lint, Codecov, smoke tests, packaging |
| `ALPINE_VARIANT` | `'alpine3.20'` | Alpine variant for musl Docker builds |

**Jobs**:

1. **verify-version** — On tag events, checks that the tag version matches `package.json` version. Fails the build if they don't match.

2. **create-release** — On tag events, creates a draft GitHub Release using `gh release create --draft`. This ensures the release exists before `build` and `build-musl` jobs try to upload binaries. Skipped for non-tag events (PRs, pushes to main).

3. **lint** — Runs `yarn lint` on `ubuntu-latest` with Node 24.
3. **lint** — Runs `yarn lint` on `ubuntu-latest` with `DEFAULT_NODE_VERSION`.

4. **build** — Builds and tests native binaries across a 14-target matrix. Depends on `[verify-version, lint, create-release]`.

Expand All @@ -343,15 +347,15 @@ The project uses three GitHub Actions workflows for continuous integration and r
- Print binary info (Linux: `ldd`, `nm`, `file`)
- **Debug async hook stack integrity** (macOS only): Runs `async_hooks_stress.test.js` with `SQLITE3_DEBUG_ASYNC_HOOKS=1`
- Run tests (`yarn test`)
- Upload binaries as commit artifacts (Node 24 only, 7-day retention)
- Upload binaries to GitHub Release (Node 24 + tag events only, uses `prebuilds/*/*.node` glob to avoid matching directories)
- Upload coverage to Codecov (linux-x64 + Node 24 only)
- Upload binaries as commit artifacts (`PREBUILD_NODE_VERSION` only, 7-day retention)
- Upload binaries to GitHub Release (`PREBUILD_NODE_VERSION` + tag events only, uses `prebuilds/*/*.node` glob to avoid matching directories)
- Upload coverage to Codecov (linux-x64 + `DEFAULT_NODE_VERSION` only)

5. **build-musl** — Builds Linux musl binaries using Docker (`tools/BinaryBuilder.Dockerfile` with Alpine 3.20). Only runs on tag events or `workflow_dispatch`. Depends on `[verify-version, create-release]`. Two targets: `linux/amd64` and `linux/arm64`.
5. **build-musl** — Builds Linux musl binaries using Docker (`tools/BinaryBuilder.Dockerfile` with `ALPINE_VARIANT`). Only runs on tag events or `workflow_dispatch`. Depends on `[verify-version, create-release]`. Two targets: `linux/amd64` and `linux/arm64`.

6. **package** — Merges all prebuilt binary artifacts, creates npm tarball (`npm pack`), uploads tarball as artifact and to GitHub Release. Runs after `build` and `build-musl` succeed.

7. **test-package** — Calls `.github/workflows/test-npm-package.yml` as a reusable workflow to smoke-test the npm tarball on 4 platforms (linux-x64, linux-arm64, macos-arm64, win32-x64) with Node 22.
7. **test-package** — Calls `.github/workflows/test-npm-package.yml` as a reusable workflow to smoke-test the npm tarball on 4 platforms (linux-x64, linux-arm64, macos-arm64, win32-x64) with `DEFAULT_NODE_VERSION`.

### Publish Workflow (`.github/workflows/publish.yml`)

Expand Down Expand Up @@ -406,4 +410,4 @@ The project uses three GitHub Actions workflows for continuous integration and r

- [Project Overview](project-overview.md) - Architecture and components
- [Development Workflow](development.md) - Testing and contributing
- [Decision Log](decisionLog.md) - Technical decisions including hardening rationale
- [Decision Log](decisionLog.md) - Technical decisions including hardening rationale
3 changes: 2 additions & 1 deletion memory-bank/decisionLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
**Key workflow features**:
- 14-target build matrix (macOS x64/arm64, Linux x64/arm64, Windows x64 x Node 20/22/24)
- Docker-based musl builds using `tools/BinaryBuilder.Dockerfile` with Alpine 3.20
- Prebuilt binaries uploaded to GitHub Release on tag events (Node 24 only)
- Prebuilt binaries uploaded to GitHub Release on tag events (using `PREBUILD_NODE_VERSION`, currently Node 20)
- CI workflow uses configurable env variables: `PREBUILD_NODE_VERSION` (20), `DEFAULT_NODE_VERSION` (24), `ALPINE_VARIANT` (alpine3.20)
- npm tarball created via `npm pack` and smoke-tested on 4 platforms
- ESM smoke tests verify both default and named imports, plus promise API
- Publish workflow uses OIDC/trusted publishing (no npm token stored in secrets)
Expand Down
2 changes: 1 addition & 1 deletion tools/BinaryBuilder.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NODE_VERSION=24
ARG NODE_VERSION=20
ARG VARIANT=bookworm

FROM node:$NODE_VERSION-$VARIANT
Expand Down
Loading