Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions .github/workflows/deb-package.yml
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
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
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
89 changes: 89 additions & 0 deletions packaging/README.md
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.
18 changes: 18 additions & 0 deletions packaging/bin/etherpad-lite
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" \
"$@"
117 changes: 117 additions & 0 deletions packaging/nfpm.yaml
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
Loading
Loading