ci(packaging): publish signed apt repository to etherpad.org/apt (closes #7610)#7624
ci(packaging): publish signed apt repository to etherpad.org/apt (closes #7610)#7624JohnMcLear merged 2 commits intoether:developfrom
Conversation
Review Summary by QodoPublish signed apt repository to GitHub Pages
WalkthroughsDescription• Adds apt-publish workflow job to publish signed Debian repository • Generates Packages.gz and signs Release/InRelease files with GPG • Publishes repository to gh-pages branch served at https://ether.github.io/etherpad/apt/ • Provides 3-line end-user installation recipe with automatic apt upgrade support Diagramflowchart LR
A["Release Job"] -->|produces .deb artifacts| B["apt-publish Job"]
B -->|generates Packages.gz| C["Signed Repository"]
B -->|signs Release/InRelease| C
C -->|pushes to gh-pages| D["GitHub Pages"]
D -->|serves at /apt/| E["End Users"]
E -->|curl key.asc + apt install| F["Etherpad Installed"]
File Changes1. packaging/apt/generate-signing-key.sh
|
Code Review by Qodo
1. Latest alias duplicated
|
|
/review |
|
Persistent review updated to latest commit 1ea6403 |
| run: | | ||
| set -euo pipefail | ||
| # Pick the first non-"latest" deb per arch — those are the | ||
| # versioned files (etherpad_<v>_amd64.deb). The release job | ||
| # also stages etherpad-latest_<arch>.deb aliases, but we | ||
| # publish under stable filenames anyway, so either works. | ||
| AMD64=$(ls dist/etherpad_*_amd64.deb 2>/dev/null | head -n1) | ||
| ARM64=$(ls dist/etherpad_*_arm64.deb 2>/dev/null | head -n1) | ||
| if [ -z "${AMD64}" ] || [ -z "${ARM64}" ]; then | ||
| echo "::error::Missing per-arch .deb artefact (amd64='${AMD64}' arm64='${ARM64}')" | ||
| exit 1 |
There was a problem hiding this comment.
1. Artifact glob exits early 🐞 Bug ☼ Reliability
The Resolve artefact paths step runs ls ... | head under set -euo pipefail, so if the glob matches nothing the step exits before reaching the explicit empty-check and custom error message. This can break the release publish job with a non-obvious failure mode whenever the artifact names or download step change.
Agent Prompt
### Issue description
`Resolve artefact paths` uses `ls ... | head` under `set -euo pipefail`. If no files match, `ls` exits non-zero and the step terminates before the intended `-z` checks and friendly `::error::...` message.
### Issue Context
This job is release-gated (`refs/tags/v*`) and is expected to fail with a clear message if artifacts are missing. Current behavior fails earlier and more opaquely.
### Fix Focus Areas
- .github/workflows/deb-package.yml[250-263]
### Suggested change
Replace the `ls | head` pipelines with a non-fatal glob match, for example:
- `AMD64=$(ls ... 2>/dev/null | head -n1 || true)` (and same for ARM64), or
- use `shopt -s nullglob` and pick from an array, or
- use `compgen -G 'dist/etherpad_*_amd64.deb'` to test existence before `ls`.
Ensure the step reaches the explicit missing-artifact error path when no matches exist.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
/review |
|
Persistent review updated to latest commit cb675c5 |
|
/review |
|
Persistent review updated to latest commit 6ef9483 |
5e4b07f to
bf7980e
Compare
|
/review |
|
/review |
|
Persistent review updated to latest commit bf7980e |
| # Drop the .debs into pool/. Skip the etherpad-latest_*.deb | ||
| # filename aliases — apt resolves by package name + version, | ||
| # not filename, and including the alias would create a | ||
| # duplicate Packages entry. | ||
| shopt -s nullglob | ||
| DEBS=(dist/etherpad_*_amd64.deb dist/etherpad_*_arm64.deb) | ||
| shopt -u nullglob |
There was a problem hiding this comment.
1. Latest alias duplicated 🐞 Bug ≡ Correctness
apt-publish claims to skip etherpad-latest_*.deb aliases, but the glob it uses will include them, so apt-ftparchive will emit duplicate entries for the same package/version and can break apt update for repo users.
Agent Prompt
### Issue description
The apt repo publish job is intended to exclude `etherpad-latest_*.deb` alias files, but the current glob includes them, which will generate duplicate package entries in `Packages`.
### Issue Context
The `release` job creates stable aliases (`etherpad-latest_amd64.deb`, `etherpad-latest_arm64.deb`). The `apt-publish` job then builds an apt repository by globbing `dist/etherpad_*_amd64.deb` and `dist/etherpad_*_arm64.deb`, which also matches the alias files.
### Fix Focus Areas
- .github/workflows/deb-package.yml[211-222]
- .github/workflows/deb-package.yml[326-342]
### Suggested approach
Update the `DEBS=(...)` selection to only include versioned artifacts (for example, explicitly exclude `dist/etherpad-latest_*.deb`, or use a tighter pattern such as `dist/etherpad_[0-9]*_amd64.deb` / `dist/etherpad_[0-9]*_arm64.deb`). Ensure the job still fails fast if either arch artifact is missing.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| cat > "dists/${SUITE}/Release" <<RELEASE | ||
| Origin: Etherpad | ||
| Label: Etherpad | ||
| Suite: ${SUITE} | ||
| Codename: ${SUITE} | ||
| Architectures: amd64 arm64 | ||
| Components: ${COMP} | ||
| Description: Etherpad official apt repository (${SUITE} channel) | ||
| Date: $(date -Ru) | ||
| RELEASE |
There was a problem hiding this comment.
2. Indented release headers 🐞 Bug ≡ Correctness
The Release file header is written via a heredoc whose field lines are indented, so the resulting dists/stable/Release will contain leading spaces before Origin:, Suite:, etc., which can make the Release metadata unparsable/invalid for apt clients.
Agent Prompt
### Issue description
The workflow writes `dists/${SUITE}/Release` with leading whitespace on each header line, which can cause apt parsers to treat the fields as malformed.
### Issue Context
Bash heredocs preserve indentation unless using `<<-` (tabs-only stripping) or emitting unindented lines. The current heredoc is indented.
### Fix Focus Areas
- .github/workflows/deb-package.yml[352-361]
### Suggested approach
Emit the Release header lines starting at column 1. Options:
- Remove indentation inside the heredoc.
- Or use `<<-RELEASE` and convert indentation to tabs (not spaces) so bash strips it.
After the change, keep the `apt-ftparchive release ... >> Release` line unchanged.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Addressed both findings in cebd1cb: 1. Indented release headers — confirmed real bug. The YAML-scoped heredoc was producing 10-space-prefixed field lines. Replaced with 2. Latest alias duplicated — the analysis as written is incorrect: the existing glob |
ether#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>
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>
cebd1cb to
f5d7a37
Compare
Summary
Adds an
apt-publishworkflow job that turns the existing.debbuild artefacts into a signed apt repository hosted at:
End-user install on any Debian/Ubuntu/Mint:
apt upgradeworks going forward — every tagged release republishesthe repo metadata.
Change type: patch (CI/distribution; no production behavior change).
Why etherpad.org/apt and not ether.github.io/etherpad/apt
The original draft pushed to a
gh-pagesbranch onether/etherpad.That doesn't work in this repo: GitHub Pages here is configured as
build-from-workflowondevelopwith CNAMEdocs.etherpad.org, anda repo can only have one Pages source. Pushing to
gh-pageswouldeither be ignored, or — if Pages were switched to it — would kill the
docs site.
ether/ether.github.comis a separate Next.js site that alreadydeploys
etherpad.org, with apublic/directory served verbatim.Cross-pushing the apt repo into
public/apt/lands it at thecanonical Etherpad URL, doesn't touch
docs.etherpad.org, and reusesexisting build infrastructure.
What this PR ships
apt-publishjob in.github/workflows/deb-package.yml. Runs afterreleaseonv*tag pushes:ether/ether.github.comover SSH using a deploy key.site/public/apt/and rebuilds it from the per-arch.debartefacts usingapt-ftparchive.Release+ emitsInRelease/Release.gpgusing the keypair inAPT_SIGNING_KEY.key.ascintosite/public/key.asc.master— the site repo's existing build pipeline picks it up.packaging/apt/key.asc— Etherpad APT Repository public key, fingerprint6953FA0C6431F30347D65B03AF0CD687D51A6E63. Served athttps://etherpad.org/key.ascafter the next release.packaging/apt/generate-signing-key.sh— one-shot helper that generated the keypair (already used to produce the key currently inAPT_SIGNING_KEY). Documented for future key rotation.packaging/README.md— apt-repo install recipe is now the recommended path.Required secrets before the next tagged release
The
apt-publishjob needs two secrets onether/etherpad:APT_SIGNING_KEY— ASCII-armoured private key for the Etherpad APT Repository keypair (long key idAF0CD687D51A6E63). Generated withpackaging/apt/generate-signing-key.sh.SITE_DEPLOY_KEY— SSH private key. The public half must be registered as a deploy key with write access onether/ether.github.com(Settings → Deploy keys → "Allow write access").Setup steps for the deploy key (maintainer):
If either secret is missing, the job fails fast with a clear error — it won't silently skip.
What this PR does not change
releasejob still attaches both versioned (etherpad_<v>_<arch>.deb) and stable-aliased (etherpad-latest_<arch>.deb) artefacts to the GitHub Release. Anyone currently pulling fromreleases/latest/download/etherpad-latest_amd64.debkeeps working.buildjob (start the service under systemd, hit/health, purge) is unchanged — every PR still validates the package end-to-end.docs.etherpad.orgis untouched; this PR never pushes toether/etherpad'sgh-pages(no such branch exists here).Test plan
Build .deb amd64/arm64) passes/healthapt-publishsucceeds, thatetherpad.org/apt/andetherpad.org/key.ascresolve, and that the install one-liner above produces a working install on a fresh Debian/Ubuntu container.