Skip to content

feat: require Node.js 22 or newer - #1403

Merged
l2ysho merged 2 commits into
masterfrom
claude/node20-support-removal-e57cdc
Sep 9, 2026
Merged

feat: require Node.js 22 or newer#1403
l2ysho merged 2 commits into
masterfrom
claude/node20-support-removal-e57cdc

Conversation

@l2ysho

@l2ysho l2ysho commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Note

TL;DR Drop node 20 as we discussed.

Changes

  • engines.node: >=20>=22
  • devEngines.runtime.node: drop the ^20.19.0 arm, leaving >=22.12.0
  • Local Tests matrix: [20, 22, 24][22, 24]

No source or docs changes needed. SUPPORTED_NODEJS_VERSION reads pkg.engines.node (src/lib/consts.ts:69), so the runtime guard and all user-facing messages follow automatically. README and the installation docs already said 22+.

Why >=22 and not a patch-level floor

The strictest transitive runtime constraints are which@7 (^22.22.2 || ...) and @inquirer/core (^22.13.0), so no single patch version is "the" true floor for long — it moves with every dependency bump.

engines.node is also enforced by a hard exit here (processVersionCheck, src/entrypoints/_shared.ts:58), not just an npm warning, so an over-strict floor blocks users who would otherwise be fine. A transitive engine mismatch is only a warning.

devEngines keeps the stricter >=22.12.0 that the build tooling needs. That is the contributor requirement; engines.node is the support claim made to users, and it tracks the Node major line — the same one the CI matrix and base images track.

Checked

  • Branch protection requires only Local Tests (…, 22) contexts — no …, 20) context is left permanently pending
  • Standalone bundle users are unaffected: processVersionCheck early-returns for installMethod === 'bundle'
  • pnpm run lint && format && build pass. test:local has 8 pre-existing failures in run.test.ts ("input tests") that reproduce identically on unmodified master — unrelated to this change

Follow-ups

🤖 Generated with Claude Code

@github-actions github-actions Bot added this to the 149th sprint - Tooling team milestone Sep 8, 2026
@github-actions github-actions Bot added the t-tooling Issues with this label are in the ownership of the tooling team. label Sep 8, 2026
Node 20 reached end of life on 2026-04-30 and the dependency tree already
excluded it: chalk@6 declares engines >=22, so `engines: ">=20"` was a
promise the install could not keep. README and the installation docs have
said 22+ for a while — this makes the manifest agree.

Raise engines.node to >=22, drop the ^20.19.0 arm from devEngines, and
remove Node 20 from the Local Tests matrix, which is what is currently
failing on master.

devEngines keeps the stricter >=22.12.0 floor that the build tooling needs.
That is the contributor requirement; engines.node is the support claim made
to users, and it tracks the Node major line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@l2ysho
l2ysho force-pushed the claude/node20-support-removal-e57cdc branch from 17696e7 to 2644f94 Compare September 8, 2026 19:47
@l2ysho l2ysho changed the title feat: require Node.js 22.12 or newer feat: require Node.js 22 or newer Sep 8, 2026
@l2ysho
l2ysho marked this pull request as ready for review September 9, 2026 06:48
@l2ysho
l2ysho requested a review from DaveHanns as a code owner September 9, 2026 06:48
@l2ysho
l2ysho merged commit f26ca62 into master Sep 9, 2026
18 of 22 checks passed
@l2ysho
l2ysho deleted the claude/node20-support-removal-e57cdc branch September 9, 2026 14:25
l2ysho added a commit to apify/actor-templates that referenced this pull request Sep 9, 2026
## Changes

- 21 templates: `>=18.0.0` (14) / `>=20.0.0` (7) → `>=22`
- 3 templates that had no `engines` block at all —
`js-crawlee-playwright-chrome`, `js-crawlee-puppeteer-chrome`,
`ts-playwright-test-runner` — now declare the same floor

## Why `>=22`

It matches the Node major line the base images and CI already track, and
it is a support claim about the Actor, which runs on
`apify/actor-node:24` in the image. A patch-level floor would drift with
every devDependency bump without telling a user anything more.

Kept at 22, not 24, so the Node 22 CI leg keeps exercising the templates
— `test/templates.test.js:114` skips any template whose `engines.node`
excludes the running Node.

## Not in this PR

- Dockerfiles, CI matrices, `.nvmrc`, `manifest.json` — already 20-free
- `dist/templates/*.zip` — `build_archives.yaml` regenerates and commits
these on push to master
- `ts-start-bun` — Bun-only, no `engines.node` by design

## Companion

apify/apify-cli#1403 drops Node 20 from the CLI itself.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
l2ysho added a commit that referenced this pull request Sep 9, 2026
## Why master is red

apify/actor-templates
[#866](apify/actor-templates#866) added
`.actor/input_schema.json` to every template on 2026-09-08 15:15 UTC.
The tests download the live template, so the change hit CI with no
commit here — the run before it was green.

With a schema present, `apify run --purge` writes a merged temp input
file, so it turns crawlee's purge-on-start off (that purge only
preserves `INPUT*` and would delete the temp file) and purges by itself
instead. That path was broken:

- `useModuleVersion` ran the crawlee version probe without `cwd`, so
under the test cwd mock it looked in the repo root instead of the
project. Production is unaffected — execa defaults to `process.cwd()` —
but nothing could exercise the branch.
- `purgeDefault*` resolved storage paths against the OS working
directory, so under the same mock they returned early and deleted
nothing — a silent no-op that reads exactly like a pass.

## Two user-facing fixes

Found while reviewing the code around the above, both pre-existing:

- **Windows:** `checkIfStorageIsEmpty` interpolated
`storage\key_value_stores\default` into glob patterns, which reads
backslashes as escapes. The `!…/INPUT.*` negations never matched, so
`apify run` without `--purge` always warned "the storage directory
contains a previous state" — even with nothing but the input file.
Confirmed empirically against the pinned tinyglobby.
- **Absolute `APIFY_LOCAL_STORAGE_DIR`:** `getLocalInput` and the input
paths in `validateAndStoreInput` used `join(cwd, …)`, producing
`<cwd>/<abs>`. Now `resolve`, matching the purge helpers.

## Changes

| File | Change |
|---|---|
| `src/lib/utils.ts` | Windows glob fix; purge helpers and
`getLocalInput` resolve against the command's directory; redundant
`existsSync` guards dropped |
| `src/commands/run.ts` | Legacy-storage rename and input/temp-input
paths resolve against the command's directory |
| `src/lib/hooks/useModuleVersion.ts` | Pass `cwd` to the version probe
|
| `test/local/lib/utils-purge-storages.test.ts` | New — the purge
helpers and `checkIfStorageIsEmpty`, directly |
| `test/local/lib/useModuleVersion.test.ts` | New — the version probe
resolves modules from the project |
| `test/local/commands/run-without-crawlee.test.ts` | New — the purge
branch taken when crawlee is absent |
| `test/local/commands/run.test.ts` | Input tests own the schema instead
of racing the template's |

The last one fixes the Windows/macOS half of the failure.
`.actor/INPUT_SCHEMA.json` is first in `DEFAULT_INPUT_SCHEMA_PATHS`, and
on case-insensitive file systems it matched the template's
`.actor/input_schema.json`, so an empty schema won over the test's own.
The tests now delete the template's file, so exactly one schema exists
on every platform.

## Coverage

Every change here has a test that fails without it.
`checkIfStorageIsEmpty` had no coverage at all before. The no-crawlee
test asserts a marker file the Actor writes — without it, "the Actor
never ran" and "the purge worked" both leave an empty storage folder,
which is how the original bug stayed hidden.

`pnpm run test:local`: 550 passed, 4 skipped, 0 failed. Lint, format,
`tsc --noEmit`, build clean. No dependency changes, so no install-size
impact.

## Merge order

Node 20 legs fail here on `create.test.ts > should skip installing
optional dependencies` — red on master since 2026-09-04, four days
before the templates change, and unrelated to this PR.
[#1403](#1403) drops those legs;
its own legs fail on the bug this PR fixes. Merging #1403 first lets
this one get a fully green run before it lands.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants