fix(deb): keep plugin_packages in-tree so admin-installed plugins can resolve ep_etherpad-lite#7750
Conversation
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Review Summary by QodoFix admin-installed plugins by keeping plugin_packages in-tree
WalkthroughsDescription• Keep plugin_packages as real in-tree directory instead of symlink • Fixes admin-installed plugins unable to resolve ep_etherpad-lite module • Make plugin_packages and .versions subdirectory group-writable by etherpad • Migrate existing symlinked plugin packages back in-tree on upgrade Diagramflowchart LR
A["Old Layout:<br/>plugin_packages symlink<br/>→ /var/lib/etherpad"] -->|"Node.js resolves<br/>to realpath"| B["Resolver at<br/>/var/lib/etherpad"]
B -->|"Cannot reach"| C["❌ ep_etherpad-lite<br/>in /opt/etherpad"]
D["New Layout:<br/>plugin_packages real<br/>directory in /opt"] -->|"Resolver walks<br/>in-tree"| E["/opt/etherpad/src<br/>node_modules"]
E -->|"Finds"| F["✅ ep_etherpad-lite<br/>symlink"]
File Changes1. packaging/scripts/postinstall.sh
|
Code Review by Qodo
1.
|
dbc9cd1 to
7d25613
Compare
With plugin_packages now living under /opt/etherpad/src/plugin_packages, admin-installed plugins land in dpkg-unmanaged paths (the .versions/ stage that live-plugin-manager populates plus matching ep_* symlinks under src/node_modules/). dpkg --purge would leave them behind because the manifest never recorded them. In postremove's purge branch: explicitly rm -rf plugin_packages and any runtime ep_* symlinks in node_modules, then rm -rf the whole APP_DIR as belt-and-braces against other runtime drift. Verified in a sandbox that a staged ep_runtime@1.0.0 + node_modules/ep_runtime symlink are both gone after purge runs. Add post-purge assertions to both packaging/test-local.sh and the deb-package workflow: /opt/etherpad/src/plugin_packages and /var/lib/etherpad must not exist after dpkg --purge. Addresses Qodo PR #7750 review item 3 (\"Purge cleanup misses plugins\", Reliability).
35d090e to
a6ec3cc
Compare
|
Addressed Qodo review item 3 ("Purge cleanup misses plugins") in a6ec3cc:
Test coverage added: both |
The .deb postinstall symlinked /opt/etherpad/src/plugin_packages to
/var/lib/etherpad/plugin_packages so the etherpad user could install
plugins under ProtectSystem=strict. Node.js resolves symlinks to their
realpath before walking node_modules, so a plugin installed via the
admin UI lived under /var/lib/etherpad/... and could no longer reach
the bundled ep_etherpad-lite in /opt/etherpad/node_modules. Every
require('ep_etherpad-lite/...') in installed plugins (and the matching
esbuild client-bundle build) failed with MODULE_NOT_FOUND, etherpad
exited, and the systemd unit restart-looped (ether/ep_comments_page#416).
Keep plugin_packages as a real in-tree directory, make it (and its
.versions/ subdir) group-writable by etherpad like node_modules already
is, and add it to ReadWritePaths= in the unit. Migrate the contents of
any pre-existing /var/lib/etherpad/plugin_packages symlink target back
in-tree on upgrade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous assertions in packaging/test-local.sh and the deb-package workflow checked that /opt/etherpad/src/plugin_packages was a symlink to /var/lib/etherpad/plugin_packages -- the layout this PR is removing. They would have failed under the new postinst. - Assert plugin_packages is a real directory (not a symlink), owned by group etherpad with mode 2775, matching node_modules. - After the happy-path /health check, simulate a pre-fix install by recreating the symlink with a marker plugin, re-run the postinst via dpkg-reconfigure, and assert the marker payload was migrated back in-tree and the symlink is gone. Locks in regression coverage for the upgrade path (ether/ep_comments_page#416).
Layout assertions alone don't actually exercise the failure mode from ether/ep_comments_page#416: they confirm /opt/etherpad/src/plugin_packages is a real directory but never load a plugin whose index.js does require('ep_etherpad-lite/...') from the on-disk realpath. Ship a tiny test plugin under packaging/test-fixtures/ep_layout_trip_wire that exercises the four require() patterns from the bug report (eejs, Settings, log4js, pad_utils) and emits a marker line from expressCreateServer. Both packaging/test-local.sh and the deb-package workflow now stage it into plugin_packages/.versions/, wire up the toplevel + node_modules symlinks live-plugin-manager would create, list it in installed_plugins.json, restart etherpad, and assert: * marker line appears in the journal (every require resolved), and * no "Cannot find module 'ep_etherpad-lite" appears anywhere. Verified locally that the fixture loads under the in-tree layout (marker present) and fails under a symlinked-out-of-tree layout (marker absent, MODULE_NOT_FOUND in the log) -- so the gate catches the regression in both directions.
With plugin_packages now living under /opt/etherpad/src/plugin_packages, admin-installed plugins land in dpkg-unmanaged paths (the .versions/ stage that live-plugin-manager populates plus matching ep_* symlinks under src/node_modules/). dpkg --purge would leave them behind because the manifest never recorded them. In postremove's purge branch: explicitly rm -rf plugin_packages and any runtime ep_* symlinks in node_modules, then rm -rf the whole APP_DIR as belt-and-braces against other runtime drift. Verified in a sandbox that a staged ep_runtime@1.0.0 + node_modules/ep_runtime symlink are both gone after purge runs. Add post-purge assertions to both packaging/test-local.sh and the deb-package workflow: /opt/etherpad/src/plugin_packages and /var/lib/etherpad must not exist after dpkg --purge. Addresses Qodo PR #7750 review item 3 (\"Purge cleanup misses plugins\", Reliability).
a6ec3cc to
c84f75d
Compare
Summary
/opt/etherpad/src/plugin_packagesto/var/lib/etherpad/plugin_packages; it stays a real in-tree directory and is made group-writable by theetherpaduser, same pattern already used forsrc/node_modules.etherpad.serviceadds/opt/etherpad/src/plugin_packagestoReadWritePaths=so the admin UI can still write underProtectSystem=strict.Why
Reproduced from ether/ep_comments_page#416: admin-installs of
ep_comments_pageon a.deb-based deployment crash the service on every restart with:Root cause is in our packaging, not the plugin. The old postinstall created
/opt/etherpad/src/plugin_packages → /var/lib/etherpad/plugin_packages.live-plugin-managerwrites new plugins to the symlink target's realpath, and Node.js resolves the realpath before walkingnode_modulesupward — so the resolver visits/var/lib/etherpad/,/var/lib/,/var/and never reaches the bundledep_etherpad-litesymlink in/opt/etherpad/node_modules. Everyrequire('ep_etherpad-lite/...')(and the matching esbuild client-bundle build) throwsMODULE_NOT_FOUND, etherpad exits withError occurred while starting Etherpad, and systemd restart-loops.Keeping
plugin_packagesin-tree means the resolver walks/opt/etherpad/src/plugin_packages/.versions/...→/opt/etherpad/src/node_modules→/opt/etherpad/node_modules/ep_etherpad-lite(which symlinks to../src). Lookups succeed and the service comes up cleanly.Reproduction (before this PR)
Verified locally on a fresh
ether/etherpad@v2.7.3clone withpnpm run plugins i ep_comments_page:src/plugin_packagesreal directory (workspace dev install)src/plugin_packages→/tmp/...symlink (current.deblayout)After this PR the
.deblayout matches the working case.Test plan
.debartifacts cleanly with updated postinstall and unit..debinstall → admin UI installsep_comments_page→systemctl restart etherpad→journalctl -u etherpadshows no hook-load errors and the pad loads with the comment toolbar..debthat has/opt/etherpad/src/plugin_packagespointing at/var/lib/etherpad/plugin_packages(with existing third-party plugins inside) → migration copies them in-tree, etherpad restarts cleanly.🤖 Generated with Claude Code