Skip to content

ci: check the package on its minimum Node.js version - #38

Closed
Kikobeats wants to merge 1 commit into
masterfrom
Kikobeats/ci-engines-floor
Closed

Kikobeats wants to merge 1 commit into
masterfrom
Kikobeats/ci-engines-floor

Conversation

@Kikobeats

@Kikobeats Kikobeats commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Adds .github/workflows/engines.yml, which checks that every published package installs and loads on the lowest Node.js version its engines.node allows. A dependency bump or new syntax that raises the real floor now turns CI red instead of breaking users on that version.

What it does

  1. Pack (Node LTS). pnpm install, then for each non-private workspace package (the root package in a single-package repo): run prepublishOnly if present, and pnpm pack it. The floor is read from that package's engines.node at run time. Nothing is hardcoded in the workflow. A range that is not >= x fails the job.
  2. Floor (one matrix job per package, on that floor). In an empty directory, npm install --engine-strict of the package tarball, plus the tarballs of the workspace packages it depends on. This is what a user does: production dependencies only, lifecycle scripts included. Any package in the tree whose engines excludes the floor fails the install with EBADENGINE. Without --engine-strict, npm only warns.
  3. Load. require every runtime entry in exports (or main when there is no exports). Entries that are import-only fall back to import(). Type-only entries and * patterns are skipped.

It runs as its own workflow on pull_request and on push to the default branch, so the release logic in main.yml is untouched.

Why npm on the floor side

pnpm needs Node 18 or later, so it cannot run on older floors, and npm ships with every Node.js version. The install happens in a fresh directory from tarballs, so devDependencies never enter the tree and the test toolchain (ava, c8, standard), which needs a newer Node, is not involved.

Limits

  • setup-node resolves a major-only floor such as >= 20 to the latest 20.x. The job cannot tell 20.0 from 20.12. A floor written with a minor, such as >= 20.19, is checked at the latest 20.19.x.
  • Loading an entry point runs its top-level code only. A dependency required lazily inside a function is covered by the engines check, not by the load.
  • bin scripts are not executed.
  • For a caret or tilde range, npm installs the newest version whose engines accept the running Node. A user on the floor can get an older dependency than a user on current Node, and the job checks the tree the floor user gets. It goes red only when no version in range supports the floor, which is the case that breaks users.

Proof that it catches a violation

On microlinkhq/keyvhq#294, @keyvhq/redis declares >= 20 and depends on ioredis@6, which requires >=20.0.0. A throwaway commit lowered @keyvhq/redis to >= 18:

  • Run with >= 18: Node 18 @keyvhq/redis fails, the other 15 packages pass.
    npm error code EBADENGINE
    npm error notsup Not compatible with your version of node/npm: ioredis@6.0.0
    npm error notsup Required: {"node":">=20.0.0"}
    npm error notsup Actual:   {"npm":"10.8.2","node":"v18.20.8"}
    
  • Run with the real >= 20, after dropping that commit: all 16 jobs pass.

🤖 Generated with Claude Code


Note

Low Risk
Adds CI-only validation; no application, auth, or runtime deployment changes.

Overview
Adds a dedicated engines GitHub Actions workflow (PRs and pushes to master) that enforces each published package’s declared engines.node floor without touching existing release CI.

The Pack job uses Node LTS + pnpm to install the monorepo, run prepublishOnly where defined, pnpm pack every non-private package, and build a matrix from each package’s >= x engines.node (invalid ranges fail). Workspace workspace: deps are resolved into tarball closures for downstream installs.

Floor matrix jobs run on that minimum Node version, npm install --engine-strict the relevant tarballs in a clean consumer directory (production-like install), then smoke-load every runtime exports entry via require with import() fallback for ESM-only paths—so overstated Node support or syntax/deps that break on the floor fail CI before users do.

Reviewed by Cursor Bugbot for commit eb22277. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 39 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6c7ad177-e044-4316-8e19-049e1e36356b
Commits

Reviewing files that changed from the base of the PR and between ac192d8 and eb22277.

Files selected for processing: 1
  • .github/workflows/engines.yml

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e5237cae-ab92-413c-b365-e46b2868fd17
Commits

Reviewing files that changed from the base of the PR and between 8fe68a1 and ac192d8.

Files selected for processing: 1
  • .github/workflows/engines.yml

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

This change adds a GitHub Actions workflow that packs non-private workspace packages, then installs each package and its workspace dependencies under its declared Node.js floor. The workflow attempts to load eligible exported entry points and fails when any load fails or returns undefined.

Changes: Engine-Floor Checks

Layer / File(s) Summary
Package selection and tarball matrix
.github/workflows/engines.yml
The workflow selects non-private workspace packages, validates each engines.node floor, runs available prepublishOnly scripts, packs the packages, and builds a matrix that includes workspace dependency tarballs.
Install packages and discover exports
.github/workflows/engines.yml
For each package and Node.js floor, the workflow installs the tarballs with npm --engine-strict and discovers eligible entry points from the installed package’s exports.
Load and validate entry points
.github/workflows/engines.yml
The workflow loads each discovered entry point. It uses dynamic import for specified require errors and fails if a load fails or returns undefined.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Other

Sequence Diagram(s)

sequenceDiagram
  participant Workflow as GitHub Actions
  participant Pack as pack job
  participant Artifact as tarballs artifact
  participant Floor as floor job
  participant Npm as npm
  participant Exports as package exports
  Workflow->>Pack: generate package matrix and tarballs
  Pack->>Artifact: upload tarballs
  Artifact->>Floor: provide tarballs for each Node floor
  Floor->>Npm: install packages with engine-strict
  Floor->>Exports: discover and load entry points
Loading

Possibly related PRs

  • microlinkhq/tinyspawn#37: PR #37 standardizes pnpm build approval through workflow environment variables; this workflow uses the related pnpm install configuration.
Merge Risk: Minimal · previous review ac192

The workflow adds a Node-floor installation and load check. No merge-blocking issue was established.

Pre-merge checks: Passed 4
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: adding CI coverage to check packages on their minimum supported Node.js version.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinyspawn

Details
Benchmark suite Current: eb22277 Previous: 8fe68a1 Ratio
spawn node -e 0 339.3 ms (330-357) 341.6 ms (327.9-351.3) 0.99
spawn 50MB stdout 95 ms (94.3-98.3) 94.8 ms (93.6-98.2) 1.00
read 50MB stdout x30 0 ms (0-0) 0 ms (0-0) 1

This comment was automatically generated by workflow using github-action-benchmark.

@Kikobeats
Kikobeats force-pushed the Kikobeats/ci-engines-floor branch 2 times, most recently from 94a6062 to aca15fd Compare October 29, 2026 09:03
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Kikobeats
Kikobeats force-pushed the Kikobeats/ci-engines-floor branch from aca15fd to eb22277 Compare October 29, 2026 09:06

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit eb22277. Configure here.


const OUT = path.resolve('tarballs')
const FLOOR = /^>=\s*(\d+(?:\.\d+){0,2})$/
const WORKSPACE_DEPENDENCY_FIELDS = ['dependencies', 'optionalDependencies']

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional workspace tarballs installed as required

Medium Severity

optionalDependencies workspace packages are packed into the consumer install and passed to npm install --engine-strict as top-level required tarballs. npm skips engine mismatches for optional deps, so a floor that an optional workspace package (or its tree) does not support fails this job even though a user install of the parent would succeed and omit that optional package.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit eb22277. Configure here.

@Kikobeats

Copy link
Copy Markdown
Member Author

Closing: not pursuing the floor CI job.

@Kikobeats Kikobeats closed this Oct 29, 2026
@Kikobeats
Kikobeats deleted the Kikobeats/ci-engines-floor branch October 29, 2026 09:35
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.

1 participant