* ci(packaging): publish signed apt repository to etherpad.org/apt (closes #7610) Adds an `apt-publish` workflow job that turns the existing `.deb` build artefacts into a signed apt repository hosted at: https://etherpad.org/apt/ End-user install on any Debian/Ubuntu/Mint: curl -fsSL https://etherpad.org/key.asc \ | sudo gpg --dearmor -o /usr/share/keyrings/etherpad.gpg echo "deb [signed-by=/usr/share/keyrings/etherpad.gpg] \ https://etherpad.org/apt stable main" \ | sudo tee /etc/apt/sources.list.d/etherpad.list sudo apt update && sudo apt install etherpad `apt upgrade` works going forward — every tagged release republishes the repo metadata. Change type: patch (CI/distribution; no production behaviour change). ## Why etherpad.org/apt and not ether.github.io/etherpad/apt ether/etherpad's GitHub Pages is already configured as build-from-workflow on `develop` with CNAME `docs.etherpad.org`, and a repo can only have one Pages source. Pushing the apt repo to a gh-pages branch would either be ignored (Pages is reading from the docs workflow) or, if Pages were switched to it, would kill the docs site. ether/ether.github.com is a separate Next.js site that already deploys etherpad.org and serves `public/` verbatim, so cross-pushing the apt repo into `public/apt/` lands it at the canonical Etherpad URL with no infrastructure conflicts. ## What this PR ships 1. `apt-publish` job in `.github/workflows/deb-package.yml`. Runs after `release` on `v*` tag pushes: - Clones ether/ether.github.com over SSH using a deploy key. - Wipes site/public/apt/ and rebuilds it from the per-arch .deb artefacts using apt-ftparchive. - Signs Release + emits InRelease/Release.gpg using the keypair in APT_SIGNING_KEY. - Drops key.asc into site/public/key.asc. - Asserts both per-arch .debs are present before the wipe takes effect — refuses to publish a partial / empty repo if an artefact is missing or renamed. - Commits and pushes to master; the site repo's existing build pipeline picks it up. 2. `packaging/apt/key.asc` — Etherpad APT Repository public key, fingerprint 6953FA0C6431F30347D65B03AF0CD687D51A6E63. Served at https://etherpad.org/key.asc after the next release. 3. `packaging/apt/generate-signing-key.sh` — one-shot helper that generated the keypair, kept for documented future rotation. 4. `packaging/README.md` — apt-repo install recipe is now the recommended path. ## Required secrets before the next tagged release Two secrets on ether/etherpad before the next `v*` tag push: - APT_SIGNING_KEY — ASCII-armoured private key for the Etherpad APT Repository keypair (long key id AF0CD687D51A6E63), generated with packaging/apt/generate-signing-key.sh. - SITE_DEPLOY_KEY — SSH private key. The public half registered as a deploy key with WRITE access on ether/ether.github.com. If either is missing the job fails fast with a clear error. ## What this PR does not change - The release job still attaches both versioned (etherpad_<v>_<arch>.deb) and stable-aliased (etherpad-latest_<arch>.deb) artefacts to the GitHub Release. Anyone pulling from releases/latest/download/etherpad-latest_amd64.deb keeps working. - The build-job smoke test (start under systemd, /health, purge) is unchanged. - docs.etherpad.org is untouched; this PR never pushes to gh-pages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(packaging): emit unindented Release headers + tighten artefact glob Two corrections from a fresh Qodo review of the rebased apt-publish job: 1. The dists/${SUITE}/Release heredoc was indented with the workflow's YAML scope, which means the resulting file had 10-space-prefixed field lines (` Origin: Etherpad`). apt parsers reject any leading whitespace on header fields per RFC 822 / Debian control format, so the entire suite would have failed to parse on `apt update` even before checksums were appended. Replace the heredoc with `printf '%s\n' ...` so the indentation is entirely under workflow control and impossible to break with a future YAML re-indent. 2. Tighten the artefact glob from `etherpad_*_amd64.deb` to `etherpad_[0-9]*_amd64.deb`. The hyphen-separator distinction (etherpad_<v>_… vs etherpad-latest_…) already kept the alias out of the array — Qodo's analysis of a duplicate-Packages bug was incorrect. But pinning to a leading-digit version segment makes the contract explicit and defends against any future alias that accidentally lands on `dist/etherpad_<word>_<arch>.deb`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Etherpad Debian / RPM packaging
Produces native .deb (and, with the same manifest, .rpm / .apk)
packages for Etherpad using nfpm.
Layout
packaging/
nfpm.yaml # nfpm package manifest
bin/etherpad # /usr/bin launcher
scripts/ # preinst / postinst / prerm / postrm
systemd/etherpad.service
systemd/etherpad.default
etc/settings.json.dist # populated in CI from settings.json.template
Built artefacts land in ./dist/.
Building locally
Prereqs: Node 24 (current LTS; engines.node floor is 20), pnpm 10+, nfpm.
pnpm install --frozen-lockfile
pnpm run build:etherpad
# Stage the tree the way CI does:
STAGE=staging/opt/etherpad
mkdir -p "$STAGE"
cp -a src bin package.json pnpm-workspace.yaml README.md LICENSE \
node_modules "$STAGE/"
printf 'packages:\n - src\n - bin\n' > "$STAGE/pnpm-workspace.yaml"
cp settings.json.template packaging/etc/settings.json.dist
VERSION=$(node -p "require('./package.json').version") \
ARCH=amd64 \
nfpm package --packager deb -f packaging/nfpm.yaml --target dist/
End-to-end test (Docker, no real systemd needed)
packaging/test-local.sh builds the .deb and runs the same smoke
test the CI workflow does, inside a throwaway systemd-enabled
container:
packaging/test-local.sh # build + smoke + purge
packaging/test-local.sh --shell # leave the container up so you can poke around
packaging/test-local.sh --build-only # just produce dist/*.deb
This is the fastest way to validate that the systemd hardening, plugin path symlinks, and tsx wrapper actually work together before pushing.
Installing via the Etherpad apt repository (recommended)
The release workflow publishes a signed apt repository at
https://etherpad.org/apt/ on every tagged release. Three lines on
any Debian/Ubuntu/Mint:
curl -fsSL https://etherpad.org/key.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/etherpad.gpg
echo "deb [signed-by=/usr/share/keyrings/etherpad.gpg] https://etherpad.org/apt stable main" \
| sudo tee /etc/apt/sources.list.d/etherpad.list
sudo apt update && sudo apt install etherpad
apt upgrade works going forward. Repo metadata is signed with the
GPG keypair documented in packaging/apt/key.asc (long key id
AF0CD687D51A6E63).
Installing a single .deb directly
The release page publishes both versioned and stable filenames per arch:
# Stable URL — always points at the most recent release:
curl -fsSL -o etherpad-latest_amd64.deb \
https://github.com/ether/etherpad/releases/latest/download/etherpad-latest_amd64.deb
sudo apt install ./etherpad-latest_amd64.deb
# Or pin to a specific version:
sudo apt install ./dist/etherpad_<version>_amd64.deb
sudo systemctl start etherpad
curl http://localhost:9001/health
apt will pull in nodejs (>= 22) (matches Etherpad's engines.node).
Recommended runtime is the current Node.js LTS (24); on distros without a
new enough Node, add NodeSource first:
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
Configuration
- Edit
/etc/etherpad/settings.json, thensudo systemctl restart etherpad. - Environment overrides:
/etc/default/etherpad. - Logs:
journalctl -u etherpad -f. - Data (sqlite default):
/var/lib/etherpad/etherpad.db.
The shipped settings template defaults to dbType: "dirty", which the
template itself warns is for testing only. postinstall rewrites the
seeded /etc/etherpad/settings.json to sqlite and points it at
/var/lib/etherpad/etherpad.db so fresh installs get an ACID-safe DB
out of the box. Existing /etc/etherpad/settings.json is never touched
on upgrade.
Upgrading
dpkg --install etherpad_<new>.deb (or apt install) replaces the app
tree under /opt/etherpad while preserving /etc/etherpad/* and
/var/lib/etherpad/*. The service is restarted automatically.
Removing
sudo apt remove etherpad— keeps config and data.sudo apt purge etherpad— also removes config, data, and theetherpadsystem user.
Publishing to an APT repository (follow-up)
Out of scope here — requires credentials and ownership decisions. Recipes once a repo is picked:
- Cloudsmith (easiest, free OSS tier):
cloudsmith push deb ether/etherpad/any-distro/any-version dist/*.deb - Launchpad PPA: requires signed source packages (a
debian/tree), which nfpm does not produce — usedebuildseparately. - Self-hosted reprepro:
reprepro -b /srv/apt includedeb stable dist/*.deb
Wire the chosen option into .github/workflows/deb-package.yml after
the release job.