-
-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(packaging): add Debian (.deb) build via nfpm with systemd unit #7559
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
Open
JohnMcLear
wants to merge
2
commits into
ether:develop
Choose a base branch
from
JohnMcLear:chore/packaging-apt
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| 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-lite | ||
| 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" | ||
|
|
||
| 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-lite | ||
| test -f /etc/etherpad-lite/settings.json | ||
| test -L /opt/etherpad-lite/settings.json | ||
| id etherpad | ||
| systemctl cat etherpad-lite.service | ||
| sudo systemctl start etherpad-lite | ||
| 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-lite --no-pager -n 200 || true | ||
| exit 1 | ||
| fi | ||
| sudo systemctl stop etherpad-lite | ||
| sudo dpkg --purge etherpad-lite | ||
| ! id etherpad 2>/dev/null | ||
|
|
||
| - name: Upload artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: etherpad-lite-${{ 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-lite-*-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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # 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-lite # /usr/bin launcher | ||
| scripts/ # preinst / postinst / prerm / postrm | ||
| systemd/etherpad-lite.service | ||
| systemd/etherpad-lite.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. | ||
|
|
||
| ```sh | ||
| pnpm install --frozen-lockfile | ||
| pnpm run build:etherpad | ||
|
|
||
| # Stage the tree the way CI does: | ||
| STAGE=staging/opt/etherpad-lite | ||
| 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-lite_2.6.1_amd64.deb | ||
| sudo systemctl start etherpad-lite | ||
| 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-lite/settings.json`, then | ||
| `sudo systemctl restart etherpad-lite`. | ||
| - Environment overrides: `/etc/default/etherpad-lite`. | ||
| - Logs: `journalctl -u etherpad-lite -f`. | ||
| - Data (dirty-DB default): `/var/lib/etherpad-lite/`. | ||
|
|
||
| ## Upgrading | ||
|
|
||
| `dpkg --install etherpad-lite_<new>.deb` (or `apt install`) replaces the | ||
| app tree under `/opt/etherpad-lite` while preserving | ||
| `/etc/etherpad-lite/*` and `/var/lib/etherpad-lite/*`. The service is | ||
| restarted automatically. | ||
|
|
||
| ## Removing | ||
|
|
||
| - `sudo apt remove etherpad-lite` — keeps config and data. | ||
| - `sudo apt purge etherpad-lite` — 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-lite/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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| #!/bin/sh | ||
| # /usr/bin/etherpad-lite - thin wrapper that runs Etherpad in production mode. | ||
| # Invoked by the etherpad-lite.service systemd unit. | ||
| set -e | ||
|
|
||
| APP_DIR="${ETHERPAD_DIR:-/opt/etherpad-lite}" | ||
| 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" \ | ||
| "$@" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| # nfpm configuration for etherpad-lite Debian/RPM/APK packages. | ||
| # Build with: nfpm package --packager deb --target dist/ | ||
| # See: https://nfpm.goreleaser.com/configuration/ | ||
|
|
||
| name: etherpad-lite | ||
| arch: ${ARCH} # amd64 | arm64 (exported by CI) | ||
| platform: linux | ||
| version: ${VERSION} # e.g. 2.6.1, stripped of leading "v" | ||
| version_schema: semver | ||
| release: "1" | ||
| section: web | ||
| priority: optional | ||
| maintainer: "Etherpad Foundation <contact@etherpad.org>" | ||
| description: | | ||
| Etherpad is a real-time collaborative editor for the web. | ||
| This package installs Etherpad as a systemd service running | ||
| from /opt/etherpad-lite with configuration in /etc/etherpad-lite. | ||
| vendor: "Etherpad Foundation" | ||
| homepage: https://etherpad.org | ||
| license: Apache-2.0 | ||
|
|
||
| depends: | ||
| - nodejs (>= 20) | ||
| - adduser | ||
| - ca-certificates | ||
|
|
||
| recommends: | ||
| - libreoffice # enables DOC/DOCX/PDF/ODT export | ||
| - curl | ||
|
|
||
| suggests: | ||
| - postgresql-client | ||
| - mariadb-client | ||
|
|
||
| conflicts: | ||
| - etherpad # legacy bin/buildDebian.sh package name | ||
|
|
||
| replaces: | ||
| - etherpad | ||
|
|
||
| provides: | ||
| - etherpad | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Contents. staging/ is populated by CI before invoking nfpm: | ||
| # staging/opt/etherpad-lite/ -- source + node_modules + built assets | ||
| # --------------------------------------------------------------------------- | ||
| contents: | ||
| - src: ./staging/opt/etherpad-lite | ||
| dst: /opt/etherpad-lite | ||
| type: tree | ||
| file_info: | ||
| mode: 0755 | ||
| owner: root | ||
| group: root | ||
|
|
||
| - src: ./packaging/systemd/etherpad-lite.service | ||
| dst: /lib/systemd/system/etherpad-lite.service | ||
| file_info: | ||
| mode: 0644 | ||
|
|
||
| # Default environment file (conffile: preserved on upgrade). | ||
| # Mode 0640 + group=etherpad so passwords/secrets admins drop in here | ||
| # are only readable by root and the etherpad service user — /etc/default | ||
| # is world-readable by default (0644), which would leak DB creds etc. | ||
| - src: ./packaging/systemd/etherpad-lite.default | ||
| dst: /etc/default/etherpad-lite | ||
| type: config|noreplace | ||
| file_info: | ||
| mode: 0640 | ||
| owner: root | ||
| group: etherpad | ||
|
|
||
| - src: ./packaging/bin/etherpad-lite | ||
| dst: /usr/bin/etherpad-lite | ||
| file_info: | ||
| mode: 0755 | ||
|
|
||
| # Template used by postinstall to seed /etc/etherpad-lite/settings.json. | ||
| # Intentionally NOT a conffile: postinstall creates the real settings.json | ||
| # once on first install and never touches it again, so upgrades don't | ||
| # prompt with dpkg merge dialogs. | ||
| - src: ./packaging/etc/settings.json.dist | ||
| dst: /usr/share/etherpad-lite/settings.json.dist | ||
| file_info: | ||
| mode: 0644 | ||
|
|
||
| - dst: /etc/etherpad-lite | ||
| type: dir | ||
| file_info: | ||
| mode: 0755 | ||
| - dst: /var/lib/etherpad-lite | ||
| type: dir | ||
| file_info: | ||
| mode: 0750 | ||
| - dst: /var/log/etherpad-lite | ||
| type: dir | ||
| file_info: | ||
| mode: 0750 | ||
|
|
||
| scripts: | ||
| preinstall: ./packaging/scripts/preinstall.sh | ||
| postinstall: ./packaging/scripts/postinstall.sh | ||
| preremove: ./packaging/scripts/preremove.sh | ||
| postremove: ./packaging/scripts/postremove.sh | ||
|
|
||
| overrides: | ||
| deb: | ||
| depends: | ||
| - nodejs (>= 20) | ||
| - adduser | ||
| - ca-certificates | ||
| rpm: | ||
| depends: | ||
| - nodejs >= 20 | ||
| - shadow-utils | ||
| - ca-certificates |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.