Signing was already live and the docs were half right about it. `RPM_GPG_PRIVATE_KEY` is an ORG-level secret on unom, so it is invisible in this repo's Actions secrets — which reads exactly like "never set up", and both the rpm.yml step name and sign-rpms.sh's header still said "dormant". Checked it on the wire instead: a published punktfunk-web RPM carries an OpenPGP V4 EdDSA header signature from af245c506f4e4763, the same key committed at packaging/rpm/RPM-GPG-KEY-punktfunk. The real gap was the failure mode. README.md hands users a repo file with gpgcheck=1, but sign-rpms.sh exits 0 when the key is missing — so an org secret that got rotated, renamed, or not inherited would publish an unsigned release into a repo that rejects unsigned packages, and every user's `dnf upgrade` would break with us none the wiser. On refs/tags/v* that is now a build failure. Other builds still fall through unsigned so forks and local builds keep working. Docs corrected to match: the org-level location (with a wire-level check that doesn't depend on where the secret lives), the fail-closed rule, and a note that `rpmkeys --checksig` reporting NOKEY still means signed. Guard tested locally: exit 1 on refs/tags/v0.21.0, exit 0 on refs/heads/main and on an unset ref. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
58 lines
3.1 KiB
Bash
Executable File
58 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Detached-GPG-sign the built dist/*.rpm so the Gitea RPM registry can be served with gpgcheck=1.
|
|
#
|
|
# ACTIVE: RPM_GPG_PRIVATE_KEY is set as an ORG-level secret on `unom` (packages@unom.io,
|
|
# AF245C506F4E4763 — the public half is committed at packaging/rpm/RPM-GPG-KEY-punktfunk and served
|
|
# from the registry). Every published RPM carries its EdDSA header signature; the repo file in
|
|
# README.md tells users gpgcheck=1, which only works because of that.
|
|
#
|
|
# Without the key this exits 0 and leaves the RPMs unsigned, so a fork or a local build still works
|
|
# — but NOT on a release. On refs/tags/v* a missing key is a hard failure: an org secret that got
|
|
# rotated, renamed, or not inherited would otherwise publish an unsigned release into a repo whose
|
|
# own instructions say gpgcheck=1, and every user's `dnf upgrade` would break on it. Better to fail
|
|
# the build than to find out from users. (Same fail-closed rule as the Windows pack scripts.)
|
|
#
|
|
# Requires a DEDICATED, PASSPHRASE-LESS signing key (the one the runbook generates with
|
|
# %no-protection), distinct from the Gitea instance's repo-metadata key — rpm's default signer
|
|
# can't supply a passphrase non-interactively here.
|
|
#
|
|
# Usage (in rpm.yml, after build-rpm.sh): RPM_GPG_PRIVATE_KEY=... bash packaging/rpm/sign-rpms.sh
|
|
set -euo pipefail
|
|
|
|
if [ -z "${RPM_GPG_PRIVATE_KEY:-}" ]; then
|
|
case "${GITHUB_REF:-}" in
|
|
refs/tags/v*)
|
|
echo "RPM_GPG_PRIVATE_KEY unset on a release build (${GITHUB_REF}) — refusing to publish" >&2
|
|
echo "unsigned RPMs into a gpgcheck=1 repo. Restore the org secret (packaging/rpm/README.md)." >&2
|
|
exit 1 ;;
|
|
esac
|
|
echo "RPM_GPG_PRIVATE_KEY unset — leaving dist/*.rpm UNSIGNED (non-release build)."
|
|
exit 0
|
|
fi
|
|
|
|
command -v rpmsign >/dev/null 2>&1 || dnf -y install rpm-sign >/dev/null
|
|
|
|
GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; chmod 700 "$GNUPGHOME"
|
|
trap 'rm -rf "$GNUPGHOME"' EXIT
|
|
# Non-interactive in CI (no TTY): force loopback pinentry via gpg.conf so even rpm's default
|
|
# signing macro's gpg call won't try to prompt. The passphrase-less key needs no prompt anyway.
|
|
printf 'pinentry-mode loopback\n' > "$GNUPGHOME/gpg.conf"
|
|
printf 'allow-loopback-pinentry\n' > "$GNUPGHOME/gpg-agent.conf"
|
|
|
|
printf '%s' "$RPM_GPG_PRIVATE_KEY" | gpg --batch --import
|
|
KEYID="$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')"
|
|
[ -n "$KEYID" ] || { echo "no secret key imported from RPM_GPG_PRIVATE_KEY" >&2; exit 1; }
|
|
|
|
# Sign with rpm's DEFAULT __gpg_sign_cmd — it expands %{__signature_filename}/%{__plaintext_filename}
|
|
# correctly. (A custom __gpg_sign_cmd passed via --define reached gpg with those filename macros
|
|
# UNEXPANDED -> "No such file or directory".) Just point rpm at our key; the GNUPGHOME above
|
|
# (passphrase-less key + loopback) lets gpg sign headless.
|
|
for rpm in dist/*.rpm; do
|
|
rpmsign --define "_gpg_name $KEYID" --addsign "$rpm"
|
|
done
|
|
|
|
# Verify locally so a bad signature fails the build before publishing.
|
|
rpm --import <(gpg --export --armor "$KEYID")
|
|
rpmkeys --checksig dist/*.rpm
|
|
echo "signed + verified $(find dist -name '*.rpm' | wc -l) RPM(s) with key $KEYID"
|