Skip to content
Draft
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
30 changes: 29 additions & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
name: Set up Node and pnpm
description: Install the repository's pinned Node and pnpm toolchain and frozen dependencies.

inputs:
node-version:
description: Optional Node major version override. The repository .nvmrc remains the default.
required: false
default: ""

runs:
using: composite
steps:
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
- name: Set up Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
# setup-node gives node-version precedence when it is provided; otherwise it reads .nvmrc.
node-version: ${{ inputs.node-version }}
node-version-file: .nvmrc
cache: pnpm
- name: Verify the selected toolchain
env:
NODE_VERSION_OVERRIDE: ${{ inputs.node-version }}
run: |
expected_node="$NODE_VERSION_OVERRIDE"
if [ -z "$expected_node" ]; then
expected_node="$(tr -d '[:space:]' < .nvmrc)"
fi
case "$expected_node" in
*[!0-9]*) echo "node-version must be a numeric major, received: $expected_node" >&2; exit 1 ;;
esac
actual_major="$(node --print 'process.versions.node.split(".")[0]')"
test "$actual_major" = "$expected_node"

expected_pnpm="$(node --print 'JSON.parse(require("node:fs").readFileSync("package.json", "utf8")).packageManager.split("@").at(-1)')"
actual_pnpm="$(pnpm --version)"
test "$actual_pnpm" = "$expected_pnpm"
echo "Using Node $(node --version) and pnpm $actual_pnpm"
shell: bash
- run: pnpm install --frozen-lockfile
shell: bash
96 changes: 96 additions & 0 deletions .github/workflows/node-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Node compatibility

on:
workflow_dispatch:
schedule:
- cron: "23 5 * * 2"

concurrency:
group: node-compatibility-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
application:
name: Node 26 application gate
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
with:
node-version: 26
- run: pnpm run gate

server:
name: Node 26 server gate and migration rehearsal
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
with:
node-version: 26
- run: pnpm run gate:server
- name: Rehearse released database migrations
run: timeout --signal=TERM --kill-after=30s 7m pnpm run rehearse:migrations

packaged-server:
name: Node 26 deployed server and import worker
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
with:
node-version: 26
- name: Test the packaged smoke harness
run: pnpm run policy:packaged-smoke:test
- name: Build deployed server bundles
run: pnpm --filter capacitylens-server build:runtime
- name: Assemble the production dependency tree
run: pnpm --filter capacitylens-server deploy --prod "$RUNNER_TEMP/capacitylens-server"
- name: Smoke test deployed server, import worker, and backups
env:
CAPACITYLENS_SMOKE_ARTIFACT_DIR: ${{ runner.temp }}/packaged-smoke-artifacts
run: timeout --signal=TERM --kill-after=30s 3m pnpm run smoke:packaged-server "$RUNNER_TEMP/capacitylens-server"
- name: Upload packaged smoke diagnostics
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: node-26-packaged-smoke-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/packaged-smoke-artifacts/
if-no-files-found: warn
retention-days: 7

chromium:
name: Node 26 Chromium E2E
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
with:
node-version: 26
- run: pnpm exec playwright install --with-deps chromium
- run: pnpm run e2e
- name: Upload browser diagnostics
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: node-26-chromium-${{ github.run_id }}-${{ github.run_attempt }}
path: |
playwright-report/
test-results/
if-no-files-found: warn
retention-days: 7
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ new features and **patch** versions carry fixes.

### Changed

- Prepare experimental Node 26 compatibility checks while retaining Node 24 as the default;
official fixed-runtime acceptance remains pending (#710).
- Removed legacy account environment aliases; the server refuses them and names the
`SMALLSASS_ACCOUNT_*` replacement (#1201).
- Managed-VPS builds now activate a production-only runtime artifact instead of retaining build and test dependencies in each release.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ asked "can we take this on?" and need a shared, honest answer.
## Run it for real

Node 24 and pnpm are required; the pinned version is in `.nvmrc`.
Node 26 compatibility is still under investigation; see [the compatibility notes](docs-src/reference/development.md#check-node-26-compatibility).

```bash
nvm use
Expand Down
1 change: 1 addition & 0 deletions docs-src/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ const referenceSidebar = [
{ text: "OpenSSF Baseline assessment", link: "/security/OpenSSF-best-practices-dev" },
{ text: "Control inventories", link: "/security/control-inventories" },
{ text: "Development guide", link: "/reference/development" },
{ text: "Node 26 discovery", link: "/reference/node26-discovery" },
{ text: "Code conventions", link: "/reference/conventions" },
{ text: "Open source and contributing", link: "/open-source" },
],
Expand Down
62 changes: 62 additions & 0 deletions docs-src/reference/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,68 @@ corepack enable
pnpm install
```

## Check Node 26 compatibility

Node 24 remains the default development and deployment runtime. Node 26 coverage is
experimental: Node 26.8.2 can delay SQLite backup completion until another timer fires.
Official [Node 26.9.0](https://nodejs.org/en/blog/release/v26.9.0) includes the upstream
[callback-scope fix](https://github.com/nodejs/node/pull/65666). Project acceptance
against that release is still pending. Follow [issue #710](https://github.com/Kevinjohn/capacitylens/issues/710)
for the current decision and the [consolidated discovery record](/reference/node26-discovery)
for the evidence, corrected findings and remaining acceptance checks. Proposed minimums
are 24.19.0 and, conditionally, 26.9.0; these are not yet enforced support ranges.
Do not use a locally patched runtime for deployment.

The `node-compatibility.yml` workflow prepares separate application, server and Chromium
checks using Node 26. Its manual and weekly triggers take effect only after the workflow
is merged; activation is held pending the compatibility decision in #710. Existing
workflows continue selecting `.nvmrc`. The shared setup action accepts a `node-version`
override containing a numeric major (for example, `26`) and verifies the repository's pnpm pin.

For isolated compatibility testing on macOS or Linux, select the intended Node 26 binary
in your shell without changing `.nvmrc`. Confirm its exact version:

```bash
node --version
```

Node 26 does not bundle Corepack. Install the pinned pnpm with the
[standalone installer](https://pnpm.io/installation#using-a-standalone-script).
From the repository root, download the installer:

```bash
curl --fail --show-error --location https://get.pnpm.io/install.sh --output /tmp/install-pnpm.sh
```

Install the version named by `packageManager`:

```bash
env PNPM_VERSION="$(node --input-type=module -e 'import { readFileSync } from "node:fs"; console.log(JSON.parse(readFileSync("package.json", "utf8")).packageManager.split("@")[1])')" sh /tmp/install-pnpm.sh
```

Follow the installer's printed shell setup instructions, then check the selected versions:

```bash
node --version
```

```bash
pnpm --version
```

The pnpm version must match `package.json` (currently `11.4.0`). Install dependencies
without changing the lockfile:

```bash
pnpm install --frozen-lockfile
```

Run the normal application and server gates, migration rehearsal and Chromium suite listed
below. Also run the packaged server/import-worker smoke from the compatibility workflow:
source-based browser tests alone do not execute those production bundles. Record the
commit, exact runtime and platform with the results. A failed check remains a failed
compatibility result; do not add timers or relax timeouts to conceal the backup defect.

## Run modes

```bash
Expand Down
Loading
Loading