Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ jobs:
# Regression test for #7718. Reproduces the production
# docker-compose layout reported in that issue: a named volume
# mounted on src/plugin_packages with no TTY allocated. Under
# the previous `CMD ["pnpm", "run", "prod"]`, pnpm 11's
# the previous `CMD ["pnpm", "run", "prod"]`, pnpm's
# runDepsStatusCheck spuriously decided node_modules was out of
# sync at boot and tried to wipe + reinstall, aborting with
# ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY before the HTTP
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
cache-dependency-path: etherpad/pnpm-lock.yaml
- name: Install dependencies ether.github.com
# ether.github.com depends on sharp (Next.js image pipeline), whose
# install script must run to fetch the platform binary. pnpm 11
# install script must run to fetch the platform binary. pnpm
# turned ignored-builds into an error; allow all builds for this
# external repo since we don't control its pnpm-workspace.yaml.
run: pnpm install --frozen-lockfile --config.dangerously-allow-all-builds=true
Expand Down
32 changes: 15 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
# docker build --build-arg BUILD_ENV=copy .
ARG BUILD_ENV=git

# NOTE: this intentionally lags the "packageManager" pin in package.json. pnpm
# 11.1.x enforces the minimum-release-age supply-chain policy during install,
# which the frozen-lockfile Docker build can't satisfy, so the image stays on
# 11.0.x. The version gap is made harmless by pnpm_config_pm_on_fail=ignore in
# the build stage below — see ether/etherpad#7911.
ARG PnpmVersion=11.0.6
# NOTE: this tracks the "packageManager" pin in package.json. pnpm's
# minimum-release-age supply-chain policy (enforced during install) is satisfied
# by the `minimumReleaseAgeExclude` list in pnpm-workspace.yaml, so the image no
# longer needs to lag the pin the way it did during the pnpm 11.1.x era — see
# ether/etherpad#7911.
ARG PnpmVersion=12.4.2

FROM node:24-alpine AS adminbuild
# Install pnpm directly via npm (rather than via corepack) so the same
Expand All @@ -33,15 +33,13 @@ RUN pnpm run build:ui
FROM node:24-alpine AS build
LABEL maintainer="Etherpad team, https://github.com/ether/etherpad"

# The image's pnpm intentionally lags the "packageManager" pin (see the ARG
# note above). pnpm would otherwise try to self-provision the pinned version on
# invocation — including the informational `pnpm --version` probe Etherpad runs
# at startup — which fails closed with no network and breaks air-gapped boots
# (ether/etherpad#7911). pm_on_fail=ignore makes pnpm use the installed version
# instead. Inherited by the development and production runtime stages, so it
# also covers the updater's pnpm-on-PATH check and ad-hoc `pnpm` in an exec
# shell. It does not change which pnpm runs the build-time install (still the
# installed 11.0.x), so the frozen-lockfile build is unaffected.
# The image's pnpm matches the "packageManager" pin (see the ARG note above),
# so pnpm does not self-provision on invocation. pm_on_fail=ignore is kept as
# belt-and-suspenders: if the pin and the image ever drift again, pnpm falls
# back to the installed version instead of downloading the pinned build — which
# fails closed with no network and breaks air-gapped boots (ether/etherpad#7911).
# Inherited by the development and production runtime stages, so it also covers
# the updater's pnpm-on-PATH check and ad-hoc `pnpm` in an exec shell.
ENV pnpm_config_pm_on_fail=ignore

# Set these arguments when building the image from behind a proxy
Expand Down Expand Up @@ -202,7 +200,7 @@ ENV ETHERPAD_PRODUCTION=true
# needed at runtime. Overwrite it with a production-only version so
# pnpm install doesn't warn about missing workspace directories.
# Preserve the build-script policy from the source workspace file so
# pnpm 11 doesn't error out with ERR_PNPM_IGNORED_BUILDS for transitive
# pnpm doesn't error out with ERR_PNPM_IGNORED_BUILDS for transitive
# postinstalls (e.g. @scarf/scarf via swagger-ui-dist).
RUN printf 'packages:\n - src\n - bin\nonlyBuiltDependencies:\n - esbuild\nignoredBuiltDependencies:\n - "@scarf/scarf"\nstrictDepBuilds: false\n' > pnpm-workspace.yaml

Expand Down Expand Up @@ -239,7 +237,7 @@ HEALTHCHECK --interval=5s --timeout=3s \
CMD wget -qO- http://127.0.0.1:9001/health | grep -E "pass|ok|up" > /dev/null || exit 1

EXPOSE 9001
# Run node directly instead of via `pnpm run prod`. pnpm 11's
# Run node directly instead of via `pnpm run prod`. pnpm's
# `runDepsStatusCheck` fires before every `pnpm run …` and spuriously
# decides node_modules is out of sync on first start under the named-
# volume layout used by docker-compose (mounting src/plugin_packages).
Expand Down
4 changes: 3 additions & 1 deletion bin/installLocalPlugins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ if [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then
plugin=$(trim "$plugin")
if [ -d "local_plugins/${plugin}" ]; then
echo "Installing plugin: '${plugin}'"
pnpm install -w ${PNPM_OPTIONS:-} "local_plugins/${plugin}/"
# The `./` prefix is required: without it pnpm treats `local_plugins/<pkg>`
# as a scoped package name (or git URL) rather than a local directory.
pnpm install -w ${PNPM_OPTIONS:-} "./local_plugins/${plugin}/"
else
( echo "Error. Directory 'local_plugins/${plugin}' for local plugin " \
"'${plugin}' missing" >&2 )
Expand Down
2 changes: 1 addition & 1 deletion bin/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ try {
run('git pull --ff-only', {cwd: '../ether.github.com/'});
console.log('Committing documentation...');
run(`cp -R out/doc/ ../ether.github.com/public/doc/v'${newVersion}'`);
// pnpm 11 refuses `pnpm version` on a dirty tree (the doc copy above
// pnpm refuses `pnpm version` on a dirty tree (the doc copy above
// dirties it) even with --no-git-tag-version, so write the bump with jq —
// same pattern used for the etherpad package.json files at the top of
// this script. The git add+commit below picks up both the bump and the
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
},
"engines": {
"node": ">=24.0.0",
"pnpm": ">=11.1.2"
"pnpm": ">=12.0.0"
},
"packageManager": "pnpm@11.10.0",
"packageManager": "pnpm@12.4.2",
"repository": {
"type": "git",
"url": "https://github.com/ether/etherpad.git"
Expand Down
2 changes: 1 addition & 1 deletion packaging/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Built artefacts land in `./dist/`.

## Building locally

Prereqs: Node 24 (current LTS; matches `engines.node` floor), pnpm 11.1.2+, nfpm.
Prereqs: Node 24 (current LTS; matches `engines.node` floor), pnpm 12+, nfpm.

```sh
pnpm install --frozen-lockfile
Expand Down
158 changes: 158 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion snap/snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ parts:
"${CRAFT_PART_INSTALL}/opt/node/bin/npm" install \
--prefix "${CRAFT_PART_INSTALL}/opt/node" -g corepack@latest
corepack enable --install-directory "${CRAFT_PART_INSTALL}/opt/node/bin"
corepack prepare pnpm@11.0.6 --activate
corepack prepare pnpm@12.4.2 --activate
# -- 3. Copy source into install dir and build.
APP_DIR="${CRAFT_PART_INSTALL}/opt/etherpad"
Expand Down
9 changes: 5 additions & 4 deletions src/static/js/pluginfw/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ const wrapTaskCb = (cb: Function | null) => taskQueue.wrap(cb);
const migratePluginsFromNodeModules = async () => {
logger.info('start migration of plugins in node_modules');
// Notes:
// * Do not pass `--prod` otherwise `npm ls` will fail if there is no `package.json`.
// * The `--no-production` flag is required (or the `NODE_ENV` environment variable must be
// unset or set to `development`) because otherwise `npm ls` will not mention any packages
// * Do not pass `--prod` otherwise `pnpm ls` will fail if there is no `package.json`.
// * The `--no-prod` flag is required (or the `NODE_ENV` environment variable must be
// unset or set to `development`) because otherwise `pnpm ls` will not mention any packages
// that are not included in `package.json` (which is expected to not exist).
const cmd = ['pnpm', 'ls', '--long', '--json', '--depth=0', '--no-production'];
// (pnpm 12 renamed `--no-production` to `--no-prod`.)
const cmd = ['pnpm', 'ls', '--long', '--json', '--depth=0', '--no-prod'];
const [{dependencies = {}}] = JSON.parse(await runCmd(cmd,
{stdio: [null, 'string']}));

Expand Down
12 changes: 6 additions & 6 deletions src/tests/backend/specs/dockerfilePnpmPin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
// download fails and pnpm exits non-zero, surfacing as `Failed to get pnpm
// version` and breaking offline boots.
//
// The image deliberately lags the pin (pnpm 11.1.x enforces a minimum-release-
// age policy the frozen-lockfile build can't satisfy), so the guard is not to
// force the versions equal but to neutralise the gap: the Dockerfile must set
// pnpm_config_pm_on_fail=ignore so pnpm uses the installed version instead of
// reaching for the network. This test fails if that guard is dropped while a
// version gap exists.
// The image and the pin are kept in sync, so the guard is belt-and-suspenders
// rather than strictly required. It exists to neutralise any future gap: if the
// image pnpm ever drifts from the "packageManager" pin again, the Dockerfile
// must set pnpm_config_pm_on_fail=ignore so pnpm uses the installed version
// instead of reaching for the network. This test fails if that guard is dropped
// while a version gap exists.

const assert = require('assert').strict;
import fs from 'fs';
Expand Down
Loading