Skip to content

Support Node 25#7747

Merged
JohnMcLear merged 1 commit into
ether:developfrom
JohnMcLear:chore/node25-corepack-clean
May 15, 2026
Merged

Support Node 25#7747
JohnMcLear merged 1 commit into
ether:developfrom
JohnMcLear:chore/node25-corepack-clean

Conversation

@JohnMcLear
Copy link
Copy Markdown
Member

@JohnMcLear JohnMcLear commented May 15, 2026

Summary

Bumps the workflow Node version to 25 and the pinned pnpm to 11.1.2.

Corepack is gone from the diff. Node 25 stopped distributing Corepack, and the earlier approach in #7741 (corepack pnpm everywhere + dynamic resolver + bootstrap helpers) was over-engineering. End-users install pnpm with npm install -g pnpm exactly as before — that's all that's needed.

What this changes (16 files, +42/-38)

  • All workflows move to Node 25 on PR runs; push runs keep the full [22, 24, 25] matrix.
  • packageManagerpnpm@11.1.2, engines.pnpm>=11.1.2.
  • upgrade-from-latest-release.yml now checks out the latest release tag instead of ref: develop #FIXME, finally exercising what its name implies.
  • installer-test.yml resolves ETHERPAD_REPO / ETHERPAD_BRANCH from the PR head when running on a fork, so the installer smoke test runs against the PR branch.

What this does NOT change

  • No src/ changes. No new files. No corepack shimming. The runtime updater, plugin migration, bin/installDeps.sh, bin/run.sh, package.json scripts — all untouched vs develop.

Verification

End-to-end test in real node:25-bookworm-slim (confirmed no corepack on PATH):

=== Node + npm + no corepack ===
v25.9.0 / 11.12.1 / (no corepack)
=== npm install -g pnpm → 11.1.2 ===
=== pnpm i → Done in 10.4s ===
=== pnpm run build:etherpad → admin SPA + OIDC built ===
OK: admin SPA built
=== pnpm run prod → HTTP server listening for connections ===

Local tests: ts-check, test-utils (mocha) 20/20, vitest 598/598, updater-integration 5/5, updater-scheduler-integration 2/2.

Closes #7741.

@qodo-code-review
Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Support Node 25 with dynamic pnpm resolution and Corepack fallback

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Support Node 25 by resolving pnpm dynamically via Corepack fallback
• Add unified pnpm resolver in src/node/updater/pnpm.ts for updater and plugin code
• Auto-bootstrap Corepack in bin/functions.sh when neither pnpm nor Corepack present
• Update all CI workflows to Node 25 as default PR target, full matrix on push
• Use corepack pnpm in root package.json scripts to honor pinned version
Diagram
flowchart LR
  A["Node 25<br/>no Corepack"] -->|resolvePnpmCommandSync| B["pnpm resolver<br/>src/node/updater/pnpm.ts"]
  B -->|prefers| C["pnpm on PATH"]
  B -->|fallback| D["corepack pnpm"]
  C -->|used by| E["Updater<br/>RollbackHandler<br/>UpdateExecutor"]
  D -->|used by| E
  C -->|used by| F["Plugin installer<br/>pluginfw"]
  D -->|used by| F
  G["bin/functions.sh<br/>ensure_pnpm"] -->|bootstrap| D
  H["package.json scripts"] -->|corepack pnpm| D
Loading

Grey Divider

File Changes

1. src/node/updater/pnpm.ts ✨ Enhancement +28/-0

New unified pnpm resolver with Corepack fallback

src/node/updater/pnpm.ts


2. src/node/updater/UpdateExecutor.ts ✨ Enhancement +20/-4

Integrate pnpm resolver for install and build steps

src/node/updater/UpdateExecutor.ts


3. src/node/updater/RollbackHandler.ts ✨ Enhancement +15/-3

Use pnpm resolver for rollback install command

src/node/updater/RollbackHandler.ts


View more (25)
4. src/node/updater/index.ts ✨ Enhancement +4/-5

Wire pnpm resolver to executor and rollback dependencies

src/node/updater/index.ts


5. src/node/hooks/express/updateActions.ts ✨ Enhancement +4/-5

Pass resolved pnpm command to update pipeline

src/node/hooks/express/updateActions.ts


6. src/static/js/pluginfw/installer.ts ✨ Enhancement +3/-1

Use unified pnpm resolver for plugin migration

src/static/js/pluginfw/installer.ts


7. src/static/js/pluginfw/plugins.ts ✨ Enhancement +4/-2

Use unified pnpm resolver for version logging

src/static/js/pluginfw/plugins.ts


8. src/tests/backend-new/specs/updater/pnpm.test.ts 🧪 Tests +30/-0

Add unit tests for pnpm resolver and invocation

src/tests/backend-new/specs/updater/pnpm.test.ts


9. bin/functions.sh ✨ Enhancement +34/-0

Add ensure_pnpm, run_pnpm, exec_pnpm helper functions

bin/functions.sh


10. bin/installDeps.sh ✨ Enhancement +3/-3

Use ensure_pnpm to auto-bootstrap Corepack if needed

bin/installDeps.sh


11. bin/run.sh ✨ Enhancement +3/-3

Use run_pnpm and exec_pnpm helpers for dynamic resolution

bin/run.sh


12. package.json ✨ Enhancement +22/-21

Update pnpm to 11.1.2 and use corepack in all scripts

package.json


13. .github/workflows/backend-tests.yml ⚙️ Configuration changes +2/-2

Change PR matrix from Node 24 to Node 25

.github/workflows/backend-tests.yml


14. .github/workflows/frontend-tests.yml ⚙️ Configuration changes +4/-4

Update Node version from 22 to 25 across all jobs

.github/workflows/frontend-tests.yml


15. .github/workflows/frontend-admin-tests.yml ⚙️ Configuration changes +1/-1

Change PR matrix from Node 24 to Node 25

.github/workflows/frontend-admin-tests.yml


16. .github/workflows/build-and-deploy-docs.yml ⚙️ Configuration changes +1/-1

Update Node version from 22 to 25

.github/workflows/build-and-deploy-docs.yml


17. .github/workflows/docker.yml ⚙️ Configuration changes +1/-1

Update Node version from 22 to 25

.github/workflows/docker.yml


18. .github/workflows/handleRelease.yml ⚙️ Configuration changes +1/-1

Update Node version from 22 to 25

.github/workflows/handleRelease.yml


19. .github/workflows/installer-test.yml ⚙️ Configuration changes +21/-10

Provision pnpm via corepack prepare and update Node to 25

.github/workflows/installer-test.yml


20. .github/workflows/load-test.yml ⚙️ Configuration changes +3/-3

Update Node version from 22 to 25

.github/workflows/load-test.yml


21. .github/workflows/perform-type-check.yml ⚙️ Configuration changes +1/-1

Update Node version from 22 to 25

.github/workflows/perform-type-check.yml


22. .github/workflows/rate-limit.yml ⚙️ Configuration changes +1/-1

Update Node version from 22 to 25

.github/workflows/rate-limit.yml


23. .github/workflows/release.yml ⚙️ Configuration changes +1/-1

Update Node version from 22 to 25

.github/workflows/release.yml


24. .github/workflows/releaseEtherpad.yml ⚙️ Configuration changes +2/-3

Update Node version from 22 to 25 for trusted publishing

.github/workflows/releaseEtherpad.yml


25. .github/workflows/update-plugins.yml ⚙️ Configuration changes +1/-1

Update Node version from 22 to 25

.github/workflows/update-plugins.yml


26. .github/workflows/upgrade-from-latest-release.yml ⚙️ Configuration changes +9/-9

Checkout latest release tag and update Node to 25

.github/workflows/upgrade-from-latest-release.yml


27. CHANGELOG.md 📝 Documentation +5/-1

Document Node 25 support and pnpm 11.1.2 requirement

CHANGELOG.md


28. README.md 📝 Documentation +3/-1

Update installation instructions for Node 25 and Corepack

README.md


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 15, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0)

Grey Divider


Action required

1. Scripts require corepack 🐞 Bug ≡ Correctness
Description
Root package.json scripts now invoke corepack pnpm ..., so pnpm run prod / `pnpm run
build:etherpad will fail on systems that have pnpm installed but no corepack` binary. This
contradicts the README’s documented “install pnpm directly” path and the shell wrappers
(run_pnpm/exec_pnpm) that are designed to work without corepack.
Code

package.json[R16-34]

+    "lint": "corepack pnpm --filter ep_etherpad-lite run lint",
+    "test": "corepack pnpm --filter ep_etherpad-lite run test",
+    "test-utils": "corepack pnpm --filter ep_etherpad-lite run test-utils",
+    "test-container": "corepack pnpm --filter ep_etherpad-lite run test-container",
+    "dev": "corepack pnpm --filter ep_etherpad-lite run dev",
+    "prod": "corepack pnpm --filter ep_etherpad-lite run prod",
+    "ts-check": "corepack pnpm --filter ep_etherpad-lite run ts-check",
+    "ts-check:watch": "corepack pnpm --filter ep_etherpad-lite run ts-check:watch",
+    "test-ui": "corepack pnpm --filter ep_etherpad-lite run test-ui",
+    "test-ui:ui": "corepack pnpm --filter ep_etherpad-lite run test-ui:ui",
+    "test-admin": "corepack pnpm --filter ep_etherpad-lite run test-admin",
+    "test-admin:ui": "corepack pnpm --filter ep_etherpad-lite run test-admin:ui",
+    "plugins": "corepack pnpm --filter bin run plugins",
+    "install-plugins": "corepack pnpm --filter bin run plugins i",
+    "remove-plugins": "corepack pnpm --filter bin run remove-plugins",
+    "list-plugins": "corepack pnpm --filter bin run list-plugins",
+    "build:etherpad": "corepack pnpm --filter admin run build-copy && corepack pnpm --filter ui run build-copy",
+    "build:ui": "corepack pnpm --filter ui run build-copy && corepack pnpm --filter admin run build-copy",
+    "makeDocs": "corepack pnpm --filter bin run makeDocs"
Evidence
The README tells users to install pnpm directly and then run pnpm run ... commands, but those
scripts now call corepack pnpm and will error if corepack is not installed. The new
run_pnpm/exec_pnpm helpers demonstrate the intended behavior is to work with pnpm-only installs,
which the package.json scripts currently bypass.

package.json[15-35]
README.md[159-171]
bin/functions.sh[15-47]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Root npm scripts hardcode `corepack pnpm`, but the repo explicitly supports environments that have `pnpm` on PATH without Corepack. This makes `pnpm run <script>` fail when Corepack is missing.

### Issue Context
- README documents `npm install -g pnpm` as a supported setup.
- `bin/functions.sh` implements `run_pnpm`/`exec_pnpm` to use `pnpm` when available and fall back to `corepack pnpm`.
- Root `package.json` scripts bypass that fallback and always call `corepack`.

### Fix Focus Areas
- package.json[15-35]

### Suggested implementation direction
- Replace `corepack pnpm ...` with a small cross-platform wrapper (Node script) that:
 1) prefers `pnpm` if it is runnable,
 2) otherwise uses `corepack pnpm` if available,
 3) errors with a clear message if neither works.
- Update scripts to call `node ./<wrapper> <pnpm args>` so both Windows and POSIX behave consistently.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Sync pnpm probe 🐞 Bug ➹ Performance
Description
resolvePnpmCommandSync() uses spawnSync() and is consulted during the /admin/update/apply
preflight (pnpmOnPath), so the HTTP request handler can block the Node event loop while the probe
executes. This is limited to an admin-only flow, but it can still delay unrelated requests during
that check.
Code

src/node/updater/pnpm.ts[R9-11]

+const probe = (cwd: string, command: string, args: string[]): boolean => {
+  const result = spawnSync(command, args, {cwd, stdio: 'ignore', shell: isWindows});
+  return result.status === 0;
Evidence
The pnpm resolver uses spawnSync, and the Express update action’s preflight (pnpmOnPath) calls
it while serving a request, which blocks the event loop for the duration of the synchronous spawn.

src/node/updater/pnpm.ts[1-18]
src/node/hooks/express/updateActions.ts[68-90]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`resolvePnpmCommandSync()` relies on `spawnSync()`. It is called from the `/admin/update/apply` path (preflight `pnpmOnPath`), which blocks the event loop during the probe.

### Issue Context
This endpoint is admin-only, so impact is bounded, but avoiding synchronous child-process calls in request handlers reduces tail latency risk.

### Fix Focus Areas
- src/node/updater/pnpm.ts[1-18]
- src/node/hooks/express/updateActions.ts[68-105]

### Suggested implementation direction
- Add an async resolver (e.g., `resolvePnpmCommand()` using `spawn` with timeouts) and update preflight deps to use it.
- Optionally cache the resolved result for the process lifetime (or for a short TTL) to avoid repeated process spawning.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread package.json Outdated
Comment on lines +16 to +34
"lint": "corepack pnpm --filter ep_etherpad-lite run lint",
"test": "corepack pnpm --filter ep_etherpad-lite run test",
"test-utils": "corepack pnpm --filter ep_etherpad-lite run test-utils",
"test-container": "corepack pnpm --filter ep_etherpad-lite run test-container",
"dev": "corepack pnpm --filter ep_etherpad-lite run dev",
"prod": "corepack pnpm --filter ep_etherpad-lite run prod",
"ts-check": "corepack pnpm --filter ep_etherpad-lite run ts-check",
"ts-check:watch": "corepack pnpm --filter ep_etherpad-lite run ts-check:watch",
"test-ui": "corepack pnpm --filter ep_etherpad-lite run test-ui",
"test-ui:ui": "corepack pnpm --filter ep_etherpad-lite run test-ui:ui",
"test-admin": "corepack pnpm --filter ep_etherpad-lite run test-admin",
"test-admin:ui": "corepack pnpm --filter ep_etherpad-lite run test-admin:ui",
"plugins": "corepack pnpm --filter bin run plugins",
"install-plugins": "corepack pnpm --filter bin run plugins i",
"remove-plugins": "corepack pnpm --filter bin run remove-plugins",
"list-plugins": "corepack pnpm --filter bin run list-plugins",
"build:etherpad": "corepack pnpm --filter admin run build-copy && corepack pnpm --filter ui run build-copy",
"build:ui": "corepack pnpm --filter ui run build-copy && corepack pnpm --filter admin run build-copy",
"makeDocs": "corepack pnpm --filter bin run makeDocs"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Scripts require corepack 🐞 Bug ≡ Correctness

Root package.json scripts now invoke corepack pnpm ..., so pnpm run prod / `pnpm run
build:etherpad will fail on systems that have pnpm installed but no corepack` binary. This
contradicts the README’s documented “install pnpm directly” path and the shell wrappers
(run_pnpm/exec_pnpm) that are designed to work without corepack.
Agent Prompt
### Issue description
Root npm scripts hardcode `corepack pnpm`, but the repo explicitly supports environments that have `pnpm` on PATH without Corepack. This makes `pnpm run <script>` fail when Corepack is missing.

### Issue Context
- README documents `npm install -g pnpm` as a supported setup.
- `bin/functions.sh` implements `run_pnpm`/`exec_pnpm` to use `pnpm` when available and fall back to `corepack pnpm`.
- Root `package.json` scripts bypass that fallback and always call `corepack`.

### Fix Focus Areas
- package.json[15-35]

### Suggested implementation direction
- Replace `corepack pnpm ...` with a small cross-platform wrapper (Node script) that:
  1) prefers `pnpm` if it is runnable,
  2) otherwise uses `corepack pnpm` if available,
  3) errors with a clear message if neither works.
- Update scripts to call `node ./<wrapper> <pnpm args>` so both Windows and POSIX behave consistently.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@JohnMcLear
Copy link
Copy Markdown
Member Author

CI failures here are unrelated to Node 25 — all cascade from a develop-side bug

Every red job on this PR fails at the same step in admin/'s build-copy:

src/pages/AuthorPage.tsx(6,27): error TS2307: Cannot find module '../components/SearchField.tsx'
src/pages/AuthorPage.tsx(9,32): error TS2307: Cannot find module '../utils/sorting.ts'
src/pages/AuthorPage.tsx(207,29): error TS7006: Parameter 'v' implicitly has an 'any' type.

SearchField.tsx and sorting.ts were removed from admin/src/ on develop but AuthorPage.tsx still imports them. This breaks corepack pnpm build:etherpad, which every workflow that builds the admin SPA invokes — backend tests, Docker test, with-plugins, end-to-end install, etc.

This is exactly the bug PR #7746 fixes. Once #7746 merges into develop I'll rebase this PR and CI should go fully green — every Node-25-only check (perform type check, shellcheck, Playwright, build, dependency-review, the Linux with Plugins (25) variant on internal-PR path) is already passing.

Verified Node-25 paths green:

  • perform type check
  • shellcheck
  • Playwright Chrome / Firefox (with & without plugins)
  • Linux with Plugins (25) (internal-PR variant) ✅
  • dependency-review, CodeQL, Analyze, build

@JohnMcLear JohnMcLear force-pushed the chore/node25-corepack-clean branch 2 times, most recently from 32a86c0 to 20082bc Compare May 15, 2026 07:58
@JohnMcLear JohnMcLear changed the title Support Node 25 with corepack-managed pnpm (clean) Support Node 25 May 15, 2026
Bumps the workflow Node version (PR matrix → [25], full push matrix
stays at [22, 24, 25]) and the pinned pnpm to 11.1.2 with a matching
`engines.pnpm` minimum. End-users install pnpm the same way they
always have (`npm install -g pnpm` works on Node 25 — only Corepack
was dropped from the official Node 25 distribution).

Also includes two workflow fixes that were entangled with the
Node-version edits in the same files:

- `upgrade-from-latest-release.yml` now actually checks out the
  latest release tag instead of `ref: develop #FIXME`, so the job
  finally exercises what its name implies.
- `installer-test.yml` resolves `ETHERPAD_REPO` / `ETHERPAD_BRANCH`
  from the PR head when running on a fork, so the smoke test exercises
  the PR branch rather than the base.

Verified end-to-end against `node:25-bookworm-slim` (no corepack):
`npm install -g pnpm` → `pnpm i` → `pnpm run build:etherpad` →
`pnpm run prod` boots and listens on 9001.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JohnMcLear JohnMcLear force-pushed the chore/node25-corepack-clean branch from 20082bc to 7e7ccd9 Compare May 15, 2026 08:30
@JohnMcLear JohnMcLear merged commit 99f8258 into ether:develop May 15, 2026
28 of 29 checks passed
JohnMcLear added a commit to JohnMcLear/etherpad that referenced this pull request May 15, 2026
ether#7747 added Node 25 *support* but left the floor at Node 22. This
commit completes the cutover so the runtime requirement matches the
documentation bumped in the previous commit.

- package.json: engines.node ">=22.13.0" → ">=25.0.0"
- bin/functions.sh, bin/installer.sh, bin/installer.ps1: REQUIRED_NODE
  bumped to 25 (controls the error message users see when they invoke
  the installer or pnpm scripts on an older Node)
- Dockerfile: base image node:22-alpine → node:25-alpine (×2). Corepack
  comment updated: Node 25 no longer ships corepack at all, so we
  install it from npm rather than refreshing a stale signing-key list
- snap/snapcraft.yaml: pinned NODE_VERSION 22.22.2 → 25.9.0 and the
  surrounding design notes rewritten to reflect Node 25 instead of 22
- .github/workflows/*.yml: matrix dropped from [22, 24, 25] to just
  [25] (anything older now fails engines anyway). Stale comments in
  build-and-deploy-docs.yml referencing vite 8's 22.12 floor cleaned up
- bin/plugins/lib/npmpublish.yml: setup-node 22 → 25 so the plugin
  template propagated to every ether/* plugin matches the new minimum

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
JohnMcLear added a commit to JohnMcLear/etherpad that referenced this pull request May 15, 2026
node:25-alpine doesn't ship corepack but does pre-install yarn at
/usr/local/bin/yarn, so `npm install -g corepack@latest` fails with
EEXIST trying to register its yarn shim. Per ether#7747, end-users install
pnpm via plain `npm install -g pnpm` on Node 25 — use the same flow in
the Dockerfile (and remove the unused yarn binary so it doesn't sit on
PATH inside the image). Drops COREPACK_HOME and the related
issue-7687 cache-sharing tweak since there's no corepack shim to share.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
JohnMcLear added a commit that referenced this pull request May 15, 2026
…s) (#7749)

* docs: bump documented Node.js minimum to 25

Etherpad is moving its supported Node.js floor to >= 25 (CI matrix is
already pinned to 25 across all workflows on the node25-corepack-pnpm11
work). Sync the user-facing documentation so the install instructions,
requirements section, and plugin metadata example all reflect the new
minimum instead of Node 22 / 12.17.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: require Node.js >= 25 (engines, installers, Dockerfile, snap, CI)

#7747 added Node 25 *support* but left the floor at Node 22. This
commit completes the cutover so the runtime requirement matches the
documentation bumped in the previous commit.

- package.json: engines.node ">=22.13.0" → ">=25.0.0"
- bin/functions.sh, bin/installer.sh, bin/installer.ps1: REQUIRED_NODE
  bumped to 25 (controls the error message users see when they invoke
  the installer or pnpm scripts on an older Node)
- Dockerfile: base image node:22-alpine → node:25-alpine (×2). Corepack
  comment updated: Node 25 no longer ships corepack at all, so we
  install it from npm rather than refreshing a stale signing-key list
- snap/snapcraft.yaml: pinned NODE_VERSION 22.22.2 → 25.9.0 and the
  surrounding design notes rewritten to reflect Node 25 instead of 22
- .github/workflows/*.yml: matrix dropped from [22, 24, 25] to just
  [25] (anything older now fails engines anyway). Stale comments in
  build-and-deploy-docs.yml referencing vite 8's 22.12 floor cleaned up
- bin/plugins/lib/npmpublish.yml: setup-node 22 → 25 so the plugin
  template propagated to every ether/* plugin matches the new minimum

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(docker): install pnpm directly on Node 25 (no corepack)

node:25-alpine doesn't ship corepack but does pre-install yarn at
/usr/local/bin/yarn, so `npm install -g corepack@latest` fails with
EEXIST trying to register its yarn shim. Per #7747, end-users install
pnpm via plain `npm install -g pnpm` on Node 25 — use the same flow in
the Dockerfile (and remove the unused yarn binary so it doesn't sit on
PATH inside the image). Drops COREPACK_HOME and the related
issue-7687 cache-sharing tweak since there's no corepack shim to share.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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