* feat(packaging): publish Etherpad as a Snap
Adds first-class Snap packaging so Ubuntu / snapd users can install via
`sudo snap install etherpad-lite`.
- snap/snapcraft.yaml — core24, strict confinement, builds with pnpm
against a pinned Node.js 22 runtime. Version is auto-derived from
src/package.json so `snap info` tracks upstream release numbering.
- snap/local/bin/etherpad-service — launch wrapper that seeds
$SNAP_COMMON/etc/settings.json on first run (rewriting the default
dirty-DB path to a writable $SNAP_COMMON location) and execs Etherpad
via `node --import tsx/esm`.
- snap/local/bin/etherpad-healthcheck-wrapper — HTTP probe for external
supervisors, falling back to Node if curl isn't staged.
- snap/local/bin/etherpad-cli — thin passthrough to Etherpad's bin/
scripts (importSqlFile, checkPad, etc.).
- snap/hooks/configure — exposes `snap set etherpad-lite port=<n>` and
`ip=<addr>` with validation, restarts the service when running.
- snap/README.md — build / install / configure / publish instructions.
- .github/workflows/snap-publish.yml — builds on every v* tag, uploads
a short-lived artifact, publishes to `edge`, and then promotes to
`stable` through a manually-approved GitHub Environment. Requires a
one-time `snapcraft register etherpad-lite` plus provisioning of the
`SNAPCRAFT_STORE_CREDENTIALS` repo secret (instructions inline).
Pad data (dirty DB, logs) lives in /var/snap/etherpad-lite/common/ and
survives snap refreshes. The read-only $SNAP squashfs is never written
to at runtime.
Refs #7529
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(snap): pass --settings flag, env-subst ip/port, 2-space indent
Addresses Qodo review feedback on #7558:
1. Settings file ignored: Etherpad's Settings loader reads `argv.settings`,
not the `EP_SETTINGS` env var. Without `--settings`, the launcher's
seeded $SNAP_COMMON/etc/settings.json is never loaded; Etherpad falls
back to <install-root>/settings.json, which lives on the read-only
squashfs — so the default dirty-DB path ends up unwritable and the
daemon fails to persist pads. Fix: pass `--settings "${SETTINGS}"` to
node; drop the EP_SETTINGS export.
2. `snap set` overrides were no-ops: the seeded settings.json carries the
template's literal `"ip": "0.0.0.0"` / `"port": 9001` values, which
override the env-based defaults Etherpad exposes via ${…}
substitution. Users following the README saw the listener stay put
after `snap set etherpad-lite port=…`. Fix: after copying the
template on first run, rewrite the top-level `ip` and `port` lines
to `"${IP:0.0.0.0}"` / `"${PORT:9001}"`. Use `0,/…/` anchors so the
`dbSettings.port` entry further down stays literal.
3. Indentation: reflow the new shell scripts from 4-space to 2-space to
match the repo style rule.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(snap): default seeded settings to sqlite, not dirty
settings.json.template's own comment says dirty is for testing only.
A Snap install is the "not testing" case — shipping it by default
means every `sudo snap install etherpad-lite` starts on a DB the
project explicitly recommends against.
Rewrite the postinstall sed to switch dbType: "dirty" → "sqlite" and
point filename at $SNAP_COMMON/var/etherpad.db. sqlite is already
shipped in-tree via ueberdb2 → rusty-store-kv (prebuilt napi-rs
binary, no build deps), so this works under strict confinement with
zero snap.yaml changes.
Only affects first-run seeding; existing $SNAP_COMMON/etc/settings.json
is never touched on refresh.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(snap): rename to "etherpad", glob tag filter, harden cli
- Snap is registered as `etherpad` (the project's only name) — drops the
legacy `etherpad-lite` from the name, app, paths, install dir, configure
hook, README and workflow artifact. The daemon app shares the snap name,
so `snap install etherpad` exposes a bare `etherpad` command; the bin/
passthrough is now `etherpad.cli`.
- snap-publish.yml: GitHub Actions tag filters use globs, not regex. The
prior `v?[0-9]+.[0-9]+.[0-9]+` pattern would never match a real release
tag (Qodo review). Replace with two glob entries covering `vX.Y.Z` and
`X.Y.Z`.
- etherpad-cli: reject path-traversal in the `<bin-script>` arg (anything
containing `/`, `..`, or empty) and add a default `*)` case so files
with unsupported extensions fail loud instead of silently exiting 0
(Qodo review).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(snap): unbreak build — refresh corepack, drop pnpm prune
Two issues hit on the first real `snapcraft pack` of this recipe:
- `corepack prepare pnpm@10.33.0 --activate` failed with
`Cannot find matching keyid` because Node 22.12's bundled corepack
ships a stale signing-key list and rejects newer pnpm releases
(nodejs/corepack#612). Refresh corepack itself via npm before
preparing pnpm.
- `pnpm prune --prod` is interactive on workspace projects: it asks
"The modules directories will be removed and reinstalled from
scratch. Proceed? (Y/n)" and deadlocks on stdin under sudo + tee.
Replace it with the explicit "wipe node_modules + prod reinstall"
pattern, which is non-interactive, faster (pnpm resolves the prod
graph from its CAS cache), and byte-identical in result.
Verified locally: `snapcraft pack --destructive-mode` produces
`etherpad_2.6.1_amd64.snap` end-to-end in ~3 min.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(snap): unbreak runtime — tsx resolution, var/ writability, env
Three runtime crashes surfaced when actually installing the built snap
under strict confinement. Fixed each, plus a smoke-test script.
- `tsx` is in the `src` workspace's node_modules under pnpm hoisting,
not at the snap install root. The wrapper now `cd "${APP_DIR}/src"`
and uses bare `--import tsx` (matching `bin/cleanRun.sh`); the prior
`--import tsx/esm` triggered ERR_REQUIRE_CYCLE on Etherpad's mixed
CJS/ESM source tree.
- Etherpad's plugin installer writes `var/installed_plugins.json` via
__dirname-relative paths, which resolve to absolute paths inside the
read-only snap squashfs (EROFS). snap layouts can't intercept paths
inside `$SNAP`, so replace the shipped `var/` dir with a symlink to
`/var/snap/etherpad/common/etherpad-app-var/` (auto-created by the
wrapper on first run). Persistent state survives `snap refresh`.
- Drop the unused `EP_SETTINGS` and `EP_DATA_DIR` env vars from the
app's `environment:` block. Etherpad's settings loader doesn't read
them — it reads `argv.settings`, which the wrapper already passes via
`--settings`. They were producing `[WARN] settings - Unknown Setting`
noise on every start.
Add `snap/tests/smoke.sh`: rebuild + install + configure test port 9003
+ assert listener + curl /health + tail logs. Local verified output:
HTTP 200, body {"status":"pass","releaseId":"2.6.1"}, server logs
`Etherpad is running` on `http://0.0.0.0:9003/`.
.gitignore now excludes destructive-mode build outputs (parts/, stage/,
prime/, .craft/, *.snap).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(snap): wrapper unit tests, PR CI build, expanded docs
Coverage in snap/tests/ (47 assertions, ~5s, no snapd/sudo/network):
- test-snapcraft-yaml.sh: required keys, name validity, daemon-app
matches snap name, no etherpad-lite regression, env-var whitelist.
- test-cli.sh: path-traversal rejection, .ts/.sh dispatch, default-case
rejection, no-args usage.
- test-configure.sh: port (1-65535) and ip (v4/v6) validation via
mocked snapctl.
- test-service-bootstrap.sh: first-run seeding from
settings.json.template, sed rewrite of dbType/filename/ip/port,
writable-dir creation, snapctl override propagation to node env,
idempotency on second run, default fallbacks.
- run-all.sh: bash -n syntax check on every wrapper + hook, then
sources each test file and reports totals. All assertions use port
9003 (project test convention).
CI in .github/workflows/snap-build.yml:
- Triggers on PR / push-to-develop touching snap/, settings.json.template,
or the workflow itself.
- Job 1 wrapper-tests: runs run-all.sh.
- Job 2 snap-pack: snapcraft pack --destructive-mode, uploads .snap as
PR artifact for sideload.
- Stays separate from snap-publish.yml (tag-triggered, store-bound).
snap/README.md fully rewritten:
- User-facing usage, install, configure
- Architecture: file layout, var/-symlink rationale, settings.json
rewrite rationale, double-pnpm-install rationale, daemon-name-shares-
snap-name rationale
- Three test layers with exactly when/why to run each
- Dev workflow loop
- Publishing maintainer setup
- Troubleshooting for every failure mode hit during this PR (EROFS,
tsx not found, ERR_REQUIRE_CYCLE, snap-store-down, pnpm prune hang)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(snap): replace dead snapcraft.io/docs/releasing-to-the-snap-store link
That URL now 404s. Point at the canonical documentation.ubuntu.com
locations instead, broken out into the specific pages a maintainer
actually needs:
- Register a snap (to claim the name)
- snapcraft export-login (to generate the SNAPCRAFT_STORE_CREDENTIALS
secret)
- Publishing how-to index (root index for everything else)
Same fix in the snap-publish.yml header comment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(packaging): add Debian (.deb) build via nfpm with systemd unit
First-class Debian packaging for Etherpad, producing
etherpad_<version>_<arch>.deb artefacts for amd64 and arm64 from a
single nfpm manifest. Installing the package gives users:
- /opt/etherpad with a prebuilt, self-contained node_modules/ — no
pnpm required at runtime, just `nodejs (>= 20)`.
- etherpad system user/group, created via `adduser` in preinst.
- /etc/etherpad/settings.json seeded from the template on first
install, preserved across upgrades, removed on `purge`. Seed rewrites
dbType from the template's dev-only `dirty` default to `sqlite`,
pointed at /var/lib/etherpad/etherpad.db so fresh installs get an
ACID-safe DB without manual config. sqlite is shipped by ueberdb2
(rusty-store-kv), so no additional apt deps are needed.
- /var/lib/etherpad owned by etherpad:etherpad, writable under the
hardened unit's ProtectSystem=strict.
- /lib/systemd/system/etherpad.service — hardened unit
(NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp,
RestrictAddressFamilies) with Restart=on-failure.
- /usr/bin/etherpad CLI wrapper running `node --import tsx/esm`.
CI (.github/workflows/deb-package.yml) triggers on v* tags, builds both
arches via native runners (ubuntu-latest + ubuntu-24.04-arm),
smoke-tests the amd64 package end-to-end (install → verify sqlite
default → systemctl start → curl /health → purge → confirm user
removed), and attaches the artefacts to the GitHub Release.
Re-introduces the work from #7559 (reverted in #7582) with two
corrections:
1. Package name and all installed paths use `etherpad`, not
`etherpad-lite` — matches the repo rename. Kept replaces/conflicts
on `etherpad-lite` so any dev builds of the reverted PR upgrade
cleanly.
2. Default dbType is `sqlite`, not `dirty`. The template's own comment
says dirty is for testing only; shipping it by default to everyone
who runs `apt install etherpad` is the wrong tradeoff for a
production package.
Publishing to an APT repo (Cloudsmith, Launchpad PPA, self-hosted
reprepro) is intentionally out of scope — needs a governance decision
on who holds the signing key. Recipes are documented in
packaging/README.md.
Refs #7529, #7559, #7582
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(packaging): address PR review — startup crashes, supply chain, Node LTS
Addresses Qodo and SamTV12345 review feedback on #7583:
- postinstall: symlink /opt/etherpad/var → /var/lib/etherpad/var so
ProtectSystem=strict doesn't block runtime writes (var/js,
installed_plugins.json, etc.). Existing ReadWritePaths covers it.
- postinstall: seed installed_plugins.json with ep_etherpad-lite so
checkForMigration() does not spawn `pnpm ls` on first boot — pnpm is
not a runtime dep, and the bundled node_modules already contains
every shipped plugin. Prevents network plugin installs at first run.
- postremove: clean up the new var symlink on remove.
- workflow: verify nfpm .deb sha256 against upstream checksums.txt
before sudo dpkg -i (defense in depth).
- workflow: bump Node 22 → 24 (current LTS, per SamTV12345). The deb
Depends stays at nodejs (>= 20) to match Etherpad's engines.node.
- workflow: smoke-test now asserts the var symlink and seeded
installed_plugins.json exist post-install.
- workflow: publish stable etherpad-latest_{amd64,arm64}.deb aliases
alongside the versioned files in the GitHub Release.
- README: bump Node guidance to 24, document /releases/latest URL,
link to engines.node floor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(packaging): tsx CJS hook, plugin paths writable, glob tag triggers
Addresses second-round Qodo review on #7583:
- bin/etherpad: switch from `--import tsx/.../esm` to `--require
tsx/cjs`. server.ts uses `exports.start = ...` which throws under
the ESM loader; the prod script in src/package.json uses tsx/cjs
for the same reason.
- postinstall: symlink /opt/etherpad/src/plugin_packages →
/var/lib/etherpad/plugin_packages and chgrp /opt/etherpad/src/node_modules
to etherpad with mode 2775. Otherwise admin-UI plugin install
EACCESes — those are the dirs LinkInstaller writes to.
- systemd unit: add /opt/etherpad/src/node_modules to ReadWritePaths
so symlink creation by the etherpad user is allowed under
ProtectSystem=strict. plugin_packages is already covered via the
symlink into /var/lib/etherpad.
- postremove: clean up the new plugin_packages symlink on remove.
- workflow: tag filters were `v[0-9]+.[0-9]+.[0-9]+`, but Actions tag
filters are globs, not regex. `[0-9]+` matches one character, so
multi-digit tags like v2.10.0 would never trigger. Switch to
`v*.*.*` / `v*.*.*-*`, matching handleRelease.yml.
- workflow smoke test now asserts plugin_packages symlink target,
ownership of plugin_packages and node_modules.
- test-local.sh: new script that builds the .deb and runs the same
smoke test in a throwaway systemd-enabled Docker container, so
failures are caught before pushing.
- README: document test-local.sh.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(packaging): test-local.sh — fix cgroups v2, add --no-systemd mode
- systemd-in-docker on cgroups v2 needs --cgroupns=host and a writable
/sys/fs/cgroup mount; the previous :ro version booted to nothing.
- New --no-systemd mode: drops the systemd container in favour of plain
ubuntu:24.04 + manual launch under the etherpad user. Validates the
postinstall, wrapper, plugin paths, and /health without depending on
the host's systemd-in-docker setup. Use it when --privileged systemd
containers don't boot on your kernel/docker combo.
- On systemd container exit the script now dumps the last 50 log lines
and points at --no-systemd as the fallback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(packaging): test-local.sh — reuse cached image in --no-systemd
If ubuntu:24.04 isn't on disk and the registry is unreachable, fall
back to whichever ubuntu/debian image is already cached (e.g. the
jrei/systemd-ubuntu image we pulled for the systemd path). Avoids a
registry round-trip on flaky networks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: handle spawn errors in run_cmd; deb-package install order + offline-safe test
src/node/utils/run_cmd.ts:
Without `proc.on('error', ...)` a spawn failure (e.g. ENOENT for a
missing binary) is emitted as an unlistened 'error' event, which
Node treats as an uncaught exception that bypasses the awaiting
try/catch and kills the process. The .deb hits this on first boot
because plugins.ts spawns `pnpm --version` for a startup log line
and pnpm isn't a runtime dep — Etherpad logs "Starting" then
immediately stops. Reject the promise on 'error' so the existing
try/catch in the caller actually catches it.
packaging/scripts/postinstall.sh:
chown /var/lib/etherpad/plugin_packages AFTER `cp -a` from the
staged tree — `cp -a` preserves source (root) ownership and was
re-rooting the directory we'd just chowned to etherpad. Same
ordering the var symlink block already used.
packaging/test-local.sh:
Run `CI=1 pnpm install --frozen-lockfile` before staging so the
package is built from a fresh, lockfile-consistent tree (matches
CI). Fixes spurious "Cannot find module 'X'" failures from stale
local symlinks pointing at out-of-date pnpm store paths.
End-to-end test now passes: postinstall asserts pass, /health
returns 200, dpkg --purge cleans up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: gitignore packaging build artefacts; drop accidental commit
Drop packaging/etc/settings.json.dist that snuck into the previous
commit (generated at build time by test-local.sh / CI from
settings.json.template). Add /staging/, /dist/, /packaging/etc/ to
.gitignore so they don't recur.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(plugins): downgrade missing-pnpm log from ERROR to debug
The startup IIFE that logs the pnpm version is informational only.
pnpm is a dev-only dependency: admin-UI plugin install goes through
live-plugin-manager directly, and plugin migration is short-circuited
when var/installed_plugins.json is present (e.g. on packaged
installs). A missing pnpm on PATH is therefore expected on hardened
deployments and shouldn't surface as a red ERROR in journalctl.
Detect ENOENT specifically and log at debug; treat other errors
(permission denied, etc.) as warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(packaging): smoke deb on PRs + backend test for run_cmd spawn errors
CI gap: deb-package.yml only fired on v* tag pushes, so a PR that
broke the .deb wasn't caught until release time. Wire it to PRs and
develop pushes via a paths filter covering packaging files and the
runtime files Etherpad needs at first boot. The release job already
gates on `if: startsWith(github.ref, 'refs/tags/v')` so PR runs
won't try to publish.
Test gap: the run_cmd.ts spawn-error fix (commit 5eee7895a) had no
test, which is how the bug shipped originally — plugins.ts spawned
`pnpm --version` at startup, the rejection was never caught, and
the .deb crashed mid-boot. Add a backend spec that exercises:
- ENOENT for a missing binary -> rejects (regression test)
- successful command -> resolves stdout
- non-zero exit -> rejects with code
backend-tests.yml's recursive mocha glob picks up the new spec
automatically; no workflow change needed there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(packaging-ci): use NodeSource LTS for the smoke test (was Ubuntu's node 18)
ubuntu-latest's default apt nodejs is 18.19.1, but our package requires
nodejs (>= 20). The smoke test was doing `apt-get install nodejs`
followed by `dpkg -i ... || apt-get install -f`, which on a node-18
host fails the dep check, then `-f` "fixes" by REMOVING the etherpad
package — and the next assertion (test -x /usr/bin/etherpad) crashes.
Match what packaging/test-local.sh and the README recommend: install
node from NodeSource (current LTS) before installing the .deb.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(packaging-ci): sudo-prefix smoke assertions that read /etc/etherpad
postinstall sets /etc/etherpad to 0750 root:etherpad (DB creds live
here) and /var/lib/etherpad similarly. The GH Actions runner user
isn't in the etherpad group, so 'test -f /etc/etherpad/settings.json'
hits EACCES. Add sudo to each check that crosses one of those dirs.
(Wrapping the whole block in `sudo bash <<EOF` would have been
cleaner but YAML literal-block + heredoc terminator don't play well
together at this indent.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(packaging): close chown -R symlink-deref escalation; Pre-Depends adduser
postinstall:
Use `chown -hR` instead of `chown -R` on /var/lib/etherpad/var and
/var/lib/etherpad/plugin_packages. Both directories are writable by
the unprivileged etherpad service user, so a symlink planted there
could redirect root's chown onto arbitrary system files (e.g.
/etc/shadow) on the next `apt upgrade`. -hR makes chown act on the
symlink itself rather than its target — standard mitigation for this
TOCTOU-style local privilege escalation.
nfpm:
Move adduser from Depends to Pre-Depends. preinst creates the
etherpad user before unpacking; with plain `dpkg -i` (no apt) the
Depends list isn't installed beforehand, so a minimal system without
adduser would fail preinst before unpack and apt-get -f couldn't
recover. Pre-Depends guarantees adduser is configured first.
Both flagged in Qodo's persistent review of 3daf300f0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(packaging): predepends lives at top-level deb:, not under overrides
nfpm's Overridables schema doesn't include predepends; it's a deb-only
top-level field. Previous commit nested it under overrides.deb, which
caused nfpm to reject the entire manifest with "field predepends not
found in type nfpm.Overridables" and broke both arch builds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(packaging): four Qodo follow-ups (CI ordering, secure node install, disable on remove, writable settings)
deb-package.yml:
- Move 'Resolve version' (which calls `node -p`) to AFTER setup-node
so it doesn't depend on the runner image preinstalling node.
- Replace `curl ... | sudo bash` NodeSource installer with the
explicit gpg-key + sources.list approach. Same outcome (NodeSource
LTS apt repo), but no execution of network-fetched code as root.
Reduces blast radius if NodeSource's setup endpoint is ever
compromised — we only trust the signed apt repo metadata.
postinstall.sh:
- /etc/etherpad/settings.json now etherpad:etherpad mode 0660 (was
root:etherpad 0640). The admin /admin/settings UI persists changes
by writing back to settings.settingsFilename; with the previous
perms the etherpad user could read but not write, so saving via
the admin UI failed silently. Group-only access preserved (DB
creds still unreadable by other users).
postremove.sh:
- On `dpkg --remove`, run `systemctl disable etherpad.service` before
`daemon-reload` so the wants/ symlink doesn't dangle after dpkg
deletes the unit file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(packaging): narrow workflow token scope; pin local nfpm to NFPM_VERSION
deb-package.yml:
Workflow-level permissions was `contents: write` so the build job got
write access on every PR run, even though only the release job needs
it (to attach release assets). Narrow the workflow default to
`contents: read` and let the release job opt back in to write — it
already declares its own job-level `contents: write` block, so this
is just removing an over-broad default.
test-local.sh:
The script defined NFPM_VERSION but then unconditionally ran
`goreleaser/nfpm:latest`, so local builds could diverge from CI's
pinned v2.43.0. Use the variable in the docker tag (stripping the
leading "v" to match the image's tag scheme).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat!: replace Abiword with LibreOffice and add DOCX export (#4805)
The Abiword converter is dropped. Abiword's DOCX export is weak and the
project is niche on modern platforms; LibreOffice (soffice) is the
common deployment path and now serves as the sole converter backend.
DOCX is added as an export format and becomes the new target for the
"Microsoft Word" UI button. The /export/doc URL still works for legacy
API consumers.
BREAKING CHANGE: The 'abiword' setting, the INSTALL_ABIWORD Dockerfile
build arg, the abiwordAvailable clientVar, and the
#importmessageabiword UI element (with locale key
pad.importExport.abiword.innerHTML) are removed. Deployments relying on
Abiword must configure 'soffice' instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: add docxExport feature flag and abiword deprecation WARN
- Add `docxExport: true` setting to opt out of DOCX (use legacy DOC)
- Pass `docxExport` to client via clientVars
- Use `docxExport` flag in pad_impexp.ts for Word button format
- Emit a specific WARN when deprecated `abiword` config is detected
- Update settings.json.template and settings.json.docker with docxExport
- Add docxExport to ClientVarPayload type in SocketIOMessage.ts
Agent-Logs-Url: https://github.com/ether/etherpad/sessions/9afc5291-73b2-4b66-b028-feed39e7056f
Co-authored-by: JohnMcLear <220864+JohnMcLear@users.noreply.github.com>
* refactor: extract wordFormat variable and improve docxExport comment
Agent-Logs-Url: https://github.com/ether/etherpad/sessions/9afc5291-73b2-4b66-b028-feed39e7056f
Co-authored-by: JohnMcLear <220864+JohnMcLear@users.noreply.github.com>
* fix: restore import-limitation message when no converter is configured
The abiword removal dropped both the #importmessageabiword DOM element
and its locale key, but Copilot's refactor still expected the show()
call to surface a message when exportAvailable === 'no'. Result: users
with no soffice binary got silent failure instead of an explanation.
Add #importmessagenoconverter back with updated, LibreOffice-focused
copy (new locale key pad.importExport.noConverter.innerHTML) and flip
the hidden prop when the client knows no converter is available.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* i18n: inline English fallback for noConverter import message
The original abiword message existed in ~70 locale files and was
removed from all of them by this PR. The replacement key was only
added to en.json, so non-English users had an empty div until
translators localize. Follow the project's usual pad.html pattern
(e.g. line 146's "Font type:") and include the English text inside
the div as the fallback content; html10n replaces it when a
translation is available.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "i18n: inline English fallback for noConverter import message"
This reverts commit f336f24d. Follow the project convention: add the
new locale key to en.json only and let translations catch up via the
translation system, rather than putting inline fallback in the template.
* i18n: leave non-English locale files untouched
The PR had removed pad.importExport.abiword.innerHTML from ~82 locale
files alongside its removal from en.json. The replacement message uses
a new key (pad.importExport.noConverter.innerHTML) in en.json only, so
churning every localisation file for a key that is no longer referenced
produces useless translation diffs. Restore every non-en locale file to
its pre-PR state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JohnMcLear <220864+JohnMcLear@users.noreply.github.com>
* fix bin folder and workflows as far its possible
cleanup of dockerfile
changed paths of scripts
add lock file
fix working directory for workflows
fix windows bin
fix travis (is travis used anyway?)
fix package refs
remove pnpm-lock file in root as these conflicts with the docker volume setup
optimize comments
use install again
refactor prod image call to run
fix --workspace can only be used inside a workspace
correct comment
try fix pipeline
try fix pipeline for upgrade-from-latest-release
install all deps
smaller adjustments
save
update dockerfile
remove workspace command
fix run test command
start repair latest release workflow
start repair latest release workflow
start repair latest release workflow
further repairs
* remove test plugin from docker compose
* Added plugin live view.
* Implemented PoC for managing plugins with live-plugin-manager
* Add migration for plugins installed in node_modules and load plugins on start
* Create installed_plugins.json even if no plugin is installed
* Reload plugins and hooks after all (un)installs are done
* Add installed_plugins.json to gitignore
* Only write plugins to json file in Dockerfile
* Install live-plugin-manager
* Also persist plugin version
* Do not call hooks during migration of plugins
* Fix install of plugins in Dockerfile
* Revert Dockerfile changes
* Fixed package-lock.json
---------
Co-authored-by: SamTV12345 <40429738+samtv12345@users.noreply.github.com>
Co-authored-by: Hossein M <marzban98@gmail.com>
These files cause problems with Docker images and read-only
directories/mounts, and they have dubious value (any install-time
setup should instead be done at startup).
Advantages of a top-level `package.json`:
* It prevents npm from printing benign warnings about missing
`package.json` whenever a plugin is installed.
* Semantically, it is "the right thing to do" if plugins are to be
installed in the top-level directory. This avoids violating
assumptions various tools make about `package.json`, which makes
it more likely that we can easily switch to a new version of npm
or to an npm alternative.
Disadvantages of a top-level `package.json`:
* Including a dependency of `ep_etherpad-lite@file:src` in the
top-level `package.json`, which is required to keep npm from
deleting the `node_modules/ep_etherpad-lite` symlink each time a
package is installed, drastically slows down plugin installation
because npm recursively walks the ep_etherpad-lite dependencies.
* npm has a horrible dependency hoisting behavior: It moves
dependencies from `src/node_modules/` to the top-level
`node_modules/` directory when possible. This causes numerous
mysterious problems, such as silent failures in `npm outdated` and
`npm update`, and it breaks plugins that do
`require('ep_etherpad-lite/node_modules/foo')`.
Right now, with npm v6.x, eliminating the disadvantages is far more
valuable than keeping the advantages. (This might change with npm
v7.x.)
For a long time there was no top-level `package.json` and it worked
fairly well, although users were often confused by npm's benign
warnings. The top-level `package.json` was added because we needed a
place to put ESLint config for the stuff in `bin/` and `tests/`, and
we wanted the advantages listed above. Unfortunately we were unaware
of the disadvantages at the time. The `bin/` and `tests/` directories
were moved under `src/` so we no longer need the top-level
`package.json` for ESLint.
An alternative to a top-level `package.json`: Create
`plugins/package.json` and install all plugins in `plugins/`. If
`plugins/package.json` has a dependency of
`ep_etherpad-lite@file:../src` then plugin installation will still be
slow (npm will still recursively walk the dependencies in
`src/package.json`) but it should avoid npm's nasty dependency
hoisting behavior. To avoid slow plugin installation we could create a
lightweight `etherpad-pluginlib` package that Etherpad plugins would
use to indirectly access Etherpad's internals. As an added bonus, this
intermediate package could become an adaptor that provides a stable
interface to plugins even when Etherpad core rapidly evolves.
- Tests pass ✅
- Added openapi-backend hook
- Generating OpenAPI v3 definitions for each API version
- Definitions served /api/openapi.json /api/{version}/openapi.json
By leveraging the templating mechanism in `settings.json`, this change allows a
Docker client to run a prebuilt image and change some basic configuration
settings, like the instance name or, more importantly, the database
coordinates.
By default, the image runs witho no administrative user enabled. If a value is
given to ADMIN_PASSWORD, the `admin` user will be activated.
Also closes https://github.com/ether/etherpad-lite/issues/3623
---
Modified by muxator to support conditional user activation at runtime.