etherpad-lite/.github/workflows/backend-tests.yml
John McLear 7d537f3deb
Require Node.js >= 25 (engines, installers, Dockerfile, snap, CI, docs) (#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>
2026-05-15 10:04:24 +01:00

335 lines
11 KiB
YAML

name: "Backend tests"
# any branch is useful for testing before a PR is submitted
on:
push:
paths-ignore:
- "doc/**"
pull_request:
paths-ignore:
- "doc/**"
permissions:
contents: read
jobs:
withoutpluginsLinux:
env:
PNPM_HOME: ~/.pnpm-store
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: Linux without plugins
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Etherpad requires Node >= 25 (see package.json engines.node).
node: ${{ fromJSON('[25]') }}
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Cache pnpm store
with:
path: ${{ env.PNPM_HOME }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: pnpm
-
name: Install libreoffice
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libreoffice libreoffice-pdfimport
version: 1.0
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: pnpm i --frozen-lockfile
- name: Install admin ui
working-directory: admin
run: pnpm install
- name: Build admin ui
working-directory: admin
run: pnpm build
-
name: Run the backend tests
env:
# --report-on-fatalerror and friends write a Node diagnostic report
# (V8 stack, libuv handles, OS info) on fatal errors that bypass JS
# handlers — the failure mode we've been chasing on Windows + Node
# 24 since PR #7663. Reports land in node-report/ and are uploaded
# as an artifact if the step fails.
NODE_OPTIONS: "--report-on-fatalerror --report-uncaught-exception --report-on-signal --report-compact --report-directory=${{ github.workspace }}/node-report"
run: |
mkdir -p "${{ github.workspace }}/node-report"
pnpm test
- name: Upload Node diagnostic reports on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v7
with:
name: node-diagnostic-report-${{ runner.os }}-node${{ matrix.node }}-${{ github.job }}
path: node-report/
if-no-files-found: ignore
retention-days: 7
- name: Run the new vitest tests
working-directory: src
run: pnpm run test:vitest
withpluginsLinux:
env:
PNPM_HOME: ~/.pnpm-store
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: Linux with Plugins
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: ${{ fromJSON('[25]') }}
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Cache pnpm store
with:
path: ${{ env.PNPM_HOME }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: pnpm
-
name: Install libreoffice
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libreoffice libreoffice-pdfimport
version: 1.0
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: pnpm install --frozen-lockfile
- name: Build admin ui
working-directory: admin
run: pnpm build
-
name: Install Etherpad plugins
run: >
pnpm add -w
ep_align
ep_author_hover
ep_cursortrace
ep_font_color
ep_font_size
ep_hash_auth
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents
-
name: Run the backend tests
env:
NODE_OPTIONS: "--report-on-fatalerror --report-uncaught-exception --report-on-signal --report-compact --report-directory=${{ github.workspace }}/node-report"
run: |
mkdir -p "${{ github.workspace }}/node-report"
pnpm test
- name: Upload Node diagnostic reports on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v7
with:
name: node-diagnostic-report-${{ runner.os }}-node${{ matrix.node }}-${{ github.job }}
path: node-report/
if-no-files-found: ignore
retention-days: 7
- name: Run the new vitest tests
working-directory: src
run: pnpm run test:vitest
# Windows tests only run on push to develop/master, not on PRs
withoutpluginsWindows:
env:
PNPM_HOME: ~\\.pnpm-store
if: |
github.event_name != 'pull_request'
strategy:
fail-fast: false
matrix:
node: [22, 24, 25]
name: Windows without plugins
runs-on: windows-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Cache pnpm store
with:
path: ${{ env.PNPM_HOME }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: pnpm
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: pnpm install --frozen-lockfile
- name: Build admin ui
working-directory: admin
run: pnpm build
-
name: Fix up the settings.json
run: |
powershell -Command "(gc settings.json.template) -replace '\"max\": 10', '\"max\": 10000' | Out-File -encoding ASCII settings.json.holder"
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
-
name: Run the backend tests
shell: bash
working-directory: src
env:
NODE_OPTIONS: "--report-on-fatalerror --report-uncaught-exception --report-on-signal --report-compact --report-directory=${{ github.workspace }}/node-report"
run: |
mkdir -p "${{ github.workspace }}/node-report"
# --exit forces process.exit(failures) after the suite completes,
# closing the post-suite event-loop drain window where Windows +
# Node 24 hard-kills the process. Scoped to Windows so Linux/local
# runs still surface real handle leaks via natural drain.
pnpm test -- --exit
- name: Upload Node diagnostic reports on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v7
with:
name: node-diagnostic-report-${{ runner.os }}-node${{ matrix.node }}-${{ github.job }}
path: node-report/
if-no-files-found: ignore
retention-days: 7
- name: Run the new vitest tests
working-directory: src
run: pnpm run test:vitest
withpluginsWindows:
env:
PNPM_HOME: ~\\.pnpm-store
if: |
github.event_name != 'pull_request'
strategy:
fail-fast: false
matrix:
node: [22, 24, 25]
name: Windows with Plugins
runs-on: windows-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Cache pnpm store
with:
path: ${{ env.PNPM_HOME }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Build admin ui
working-directory: admin
run: pnpm build
-
name: Install Etherpad plugins
run: >
pnpm add -w
ep_align
ep_author_hover
ep_cursortrace
ep_font_color
ep_font_size
ep_hash_auth
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents
# Etherpad core dependencies must be installed after installing the
# plugin's dependencies, otherwise npm will try to hoist common
# dependencies by removing them from src/node_modules and installing them
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
# to be buggy, because it sometimes removes dependencies from
# src/node_modules but fails to add them to the top-level node_modules.
# Even if npm correctly hoists the dependencies, the hoisting seems to
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
# rules.
-
name: Install all dependencies and symlink for ep_etherpad-lite
run: pnpm install --frozen-lockfile
-
name: Fix up the settings.json
run: |
powershell -Command "(gc settings.json.template) -replace '\"max\": 10', '\"max\": 10000' | Out-File -encoding ASCII settings.json.holder"
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
-
name: Run the backend tests
shell: bash
working-directory: src
env:
NODE_OPTIONS: "--report-on-fatalerror --report-uncaught-exception --report-on-signal --report-compact --report-directory=${{ github.workspace }}/node-report"
run: |
mkdir -p "${{ github.workspace }}/node-report"
# --exit forces process.exit(failures) after the suite completes,
# closing the post-suite event-loop drain window where Windows +
# Node 24 hard-kills the process. Scoped to Windows so Linux/local
# runs still surface real handle leaks via natural drain.
pnpm test -- --exit
- name: Upload Node diagnostic reports on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v7
with:
name: node-diagnostic-report-${{ runner.os }}-node${{ matrix.node }}-${{ github.job }}
path: node-report/
if-no-files-found: ignore
retention-days: 7
- name: Run the new vitest tests
working-directory: src
run: pnpm run test:vitest