-
-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(packaging): add Debian (.deb) build via nfpm with systemd unit (v2) #7583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| name: Debian package | ||
| on: | ||
| push: | ||
| tags: | ||
| - 'v[0-9]+.[0-9]+.[0-9]+' | ||
| - 'v[0-9]+.[0-9]+.[0-9]+-*' | ||
| workflow_dispatch: | ||
| inputs: | ||
| ref: | ||
| description: 'Git ref to package (defaults to current)' | ||
| required: false | ||
|
|
||
| permissions: | ||
| contents: write # attach release assets | ||
|
|
||
| env: | ||
| NFPM_VERSION: v2.43.0 | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build .deb (${{ matrix.arch }}) | ||
| runs-on: ${{ matrix.runner }} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - arch: amd64 | ||
| runner: ubuntu-latest | ||
| - arch: arm64 | ||
| runner: ubuntu-24.04-arm | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| ref: ${{ inputs.ref || github.ref }} | ||
|
|
||
| - name: Resolve version | ||
| id: v | ||
| run: | | ||
| if [ "${GITHUB_REF_TYPE}" = "tag" ]; then | ||
| VERSION="${GITHUB_REF_NAME#v}" | ||
| else | ||
| VERSION="$(node -p "require('./package.json').version")" | ||
| fi | ||
| echo "version=${VERSION}" >>"$GITHUB_OUTPUT" | ||
| echo "Packaging version: ${VERSION}" | ||
|
|
||
| - uses: pnpm/action-setup@v6 | ||
| with: | ||
| version: 10 | ||
|
|
||
| - name: Setup Node | ||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: '22' | ||
| cache: pnpm | ||
|
|
||
| - name: Install dependencies | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Build UI + admin | ||
| run: pnpm run build:etherpad | ||
|
|
||
| - name: Install nfpm | ||
| run: | | ||
| set -e | ||
| NFPM_ARCH=amd64 | ||
| [ "${{ matrix.arch }}" = "arm64" ] && NFPM_ARCH=arm64 | ||
| curl -fsSL -o /tmp/nfpm.deb \ | ||
| "https://github.com/goreleaser/nfpm/releases/download/${NFPM_VERSION}/nfpm_${NFPM_VERSION#v}_${NFPM_ARCH}.deb" | ||
| sudo dpkg -i /tmp/nfpm.deb | ||
|
|
||
| - name: Stage tree for packaging | ||
| run: | | ||
| set -eux | ||
| STAGE=staging/opt/etherpad | ||
| mkdir -p "${STAGE}" | ||
|
|
||
| # Production footprint = src/ + bin/ + node_modules/ + metadata. | ||
| cp -a src bin package.json pnpm-workspace.yaml README.md LICENSE \ | ||
| node_modules "${STAGE}/" | ||
|
|
||
| # Make pnpm-workspace.yaml production-only (same trick Dockerfile uses). | ||
| printf 'packages:\n - src\n - bin\n' > "${STAGE}/pnpm-workspace.yaml" | ||
|
|
||
|
Comment on lines
+73
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Plugin migration startup failure On fresh installs, Etherpad startup runs checkForMigration(), which falls back to running pnpm ls and then writing /opt/etherpad/var/installed_plugins.json if the file is missing; the package does not ship/create that file, and the systemd unit disallows writes under /opt/etherpad. This can cause the service to crash during startup and never reach /health. Agent Prompt
|
||
| mkdir -p packaging/etc | ||
| cp settings.json.template packaging/etc/settings.json.dist | ||
|
|
||
| # Purge test fixtures and dev caches from node_modules to shrink size. | ||
| find "${STAGE}/node_modules" -type d \ | ||
| \( -name test -o -name tests -o -name '__tests__' \ | ||
| -o -name example -o -name examples -o -name docs \) \ | ||
| -prune -exec rm -rf {} + 2>/dev/null || true | ||
| find "${STAGE}/node_modules" -type f \ | ||
| \( -name '*.md' -o -name '*.ts.map' -o -name '*.map' \ | ||
| -o -name 'CHANGELOG*' -o -name 'HISTORY*' \) \ | ||
| -delete 2>/dev/null || true | ||
|
|
||
| - name: Build .deb | ||
| env: | ||
| VERSION: ${{ steps.v.outputs.version }} | ||
| ARCH: ${{ matrix.arch }} | ||
| run: | | ||
| mkdir -p dist | ||
| nfpm package --packager deb -f packaging/nfpm.yaml --target dist/ | ||
|
|
||
| - name: Smoke-test the package (amd64 only) | ||
| if: matrix.arch == 'amd64' | ||
| run: | | ||
| set -eux | ||
| sudo apt-get update | ||
| sudo apt-get install -y nodejs | ||
| sudo dpkg -i dist/*.deb || sudo apt-get install -f -y | ||
| test -x /usr/bin/etherpad | ||
| test -f /etc/etherpad/settings.json | ||
| test -L /opt/etherpad/settings.json | ||
| # Confirm the postinstall actually rewrote the template to sqlite | ||
| # — shipping the dev-only "dirty" default would defeat the point. | ||
| grep -q '"dbType": "sqlite"' /etc/etherpad/settings.json | ||
| id etherpad | ||
| systemctl cat etherpad.service | ||
| sudo systemctl start etherpad | ||
| ok= | ||
| for i in $(seq 1 30); do | ||
| if curl -fsS http://127.0.0.1:9001/health; then | ||
| ok=1 | ||
| break | ||
| fi | ||
| sleep 2 | ||
| done | ||
| if [ -z "${ok}" ]; then | ||
| # Attach logs so the failing run is diagnosable. | ||
| sudo journalctl -u etherpad --no-pager -n 200 || true | ||
| exit 1 | ||
| fi | ||
| sudo systemctl stop etherpad | ||
| sudo dpkg --purge etherpad | ||
| ! id etherpad 2>/dev/null | ||
|
|
||
| - name: Upload artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: etherpad-${{ steps.v.outputs.version }}-${{ matrix.arch }}-deb | ||
| path: dist/*.deb | ||
| if-no-files-found: error | ||
|
|
||
| release: | ||
| name: Attach to GitHub Release | ||
| needs: build | ||
| if: startsWith(github.ref, 'refs/tags/v') | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - uses: actions/download-artifact@v4 | ||
| with: | ||
| path: dist | ||
| pattern: etherpad-*-deb | ||
| merge-multiple: true | ||
| - name: Attach .deb files to release | ||
| uses: softprops/action-gh-release@v2 | ||
| with: | ||
| files: dist/*.deb | ||
| fail_on_unmatched_files: true | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # Etherpad Debian / RPM packaging | ||
|
|
||
| Produces native `.deb` (and, with the same manifest, `.rpm` / `.apk`) | ||
| packages for Etherpad using [nfpm](https://nfpm.goreleaser.com). | ||
|
|
||
| ## 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 22, pnpm 10+, nfpm. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Node 24 makes more sense I think |
||
|
|
||
| ```sh | ||
| 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/ | ||
| ``` | ||
|
|
||
| ## Installing | ||
|
|
||
| ```sh | ||
| sudo apt install ./dist/etherpad_2.6.1_amd64.deb | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to make this more generic? This is already out of date. |
||
| sudo systemctl start etherpad | ||
| curl http://localhost:9001/health | ||
| ``` | ||
|
|
||
| `apt` will pull in `nodejs (>= 20)`; on Ubuntu 22.04 add NodeSource first: | ||
|
|
||
| ```sh | ||
| curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - | ||
| ``` | ||
|
|
||
| ## Configuration | ||
|
|
||
| - Edit `/etc/etherpad/settings.json`, then | ||
| `sudo 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 the | ||
| `etherpad` system 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 — use `debuild` separately. | ||
| - **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. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| #!/bin/sh | ||
| # /usr/bin/etherpad - thin wrapper that runs Etherpad in production mode. | ||
| # Invoked by the etherpad.service systemd unit. | ||
| set -e | ||
|
|
||
| APP_DIR="${ETHERPAD_DIR:-/opt/etherpad}" | ||
| cd "${APP_DIR}" | ||
|
|
||
| : "${NODE_ENV:=production}" | ||
| export NODE_ENV | ||
| export ETHERPAD_PRODUCTION=true | ||
|
|
||
| # Run the server through tsx's ESM loader (shipped in node_modules). | ||
| # No pnpm needed at runtime. | ||
| exec node \ | ||
| --import "file://${APP_DIR}/src/node_modules/tsx/dist/esm/index.mjs" \ | ||
| "${APP_DIR}/src/node/server.ts" \ | ||
| "$@" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current lts is 24