fix(ci): sign with a key kept on the runner host
Some checks failed
Flatpak / flatpak (push) Failing after 1h23m15s
Some checks failed
Flatpak / flatpak (push) Failing after 1h23m15s
The runner is our own machine, so the repo signing key does not need to pass through the forge as a secret. It is generated once under /etc/crafter-flatpak on the host and bind-mounted read-only into the job container, where each run imports it into a throwaway keyring. One less secret to manage, and the private key never leaves the box that uses it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
2b7c8f7f2d
commit
7eb837fd55
2 changed files with 25 additions and 8 deletions
|
|
@ -7,9 +7,10 @@ name: Flatpak
|
||||||
# tag X.Y.Z -> publish as `stable` and create release X.Y.Z
|
# tag X.Y.Z -> publish as `stable` and create release X.Y.Z
|
||||||
#
|
#
|
||||||
# The `flatpak` runner label is the privileged runner on the Hetzner box
|
# The `flatpak` runner label is the privileged runner on the Hetzner box
|
||||||
# (bubblewrap needs it) with three volumes allowed: the Sdk install, the
|
# (bubblewrap needs it) with four volumes allowed: the Sdk install, the
|
||||||
# flatpak-builder state (ccache and the LLVM module, so only crafter-build
|
# flatpak-builder state (ccache and the LLVM module, so only crafter-build
|
||||||
# itself rebuilds on a normal run) and the published repo's web root.
|
# itself rebuilds on a normal run), the published repo's web root, and the
|
||||||
|
# repo signing key, which lives on the runner host and never in the forge.
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
|
|
@ -35,6 +36,7 @@ jobs:
|
||||||
-v crafter-flatpak-system:/var/lib/flatpak
|
-v crafter-flatpak-system:/var/lib/flatpak
|
||||||
-v crafter-flatpak-builder:/root/.cache/flatpak-builder
|
-v crafter-flatpak-builder:/root/.cache/flatpak-builder
|
||||||
-v /var/www/flatpak:/srv/flatpak
|
-v /var/www/flatpak:/srv/flatpak
|
||||||
|
-v /etc/crafter-flatpak:/run/crafter-flatpak:ro
|
||||||
steps:
|
steps:
|
||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -118,13 +120,18 @@ jobs:
|
||||||
|
|
||||||
- name: Publish to the Catcrafts repo
|
- name: Publish to the Catcrafts repo
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
env:
|
|
||||||
FLATPAK_GPG_PRIVATE_KEY: ${{ secrets.FLATPAK_GPG_PRIVATE_KEY }}
|
|
||||||
FLATPAK_GPG_KEY_ID: ${{ secrets.FLATPAK_GPG_KEY_ID }}
|
|
||||||
run: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
|
# The signing key is generated once on the runner host
|
||||||
|
# (/etc/crafter-flatpak: signing-key.asc and key-id) and mounted
|
||||||
|
# read-only; it is imported into a throwaway keyring per run.
|
||||||
|
if [ ! -s /run/crafter-flatpak/signing-key.asc ] || [ ! -s /run/crafter-flatpak/key-id ]; then
|
||||||
|
echo "no signing key under /etc/crafter-flatpak on the runner host; generate it as described in AGENTS.md (CI and publishing)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
export GNUPGHOME=$(mktemp -d)
|
export GNUPGHOME=$(mktemp -d)
|
||||||
printf '%s' "$FLATPAK_GPG_PRIVATE_KEY" | gpg --batch --quiet --import
|
gpg --batch --quiet --import /run/crafter-flatpak/signing-key.asc
|
||||||
|
FLATPAK_GPG_KEY_ID=$(cat /run/crafter-flatpak/key-id)
|
||||||
# The live repo is the canonical copy; builds are promoted into it,
|
# The live repo is the canonical copy; builds are promoted into it,
|
||||||
# never built into it.
|
# never built into it.
|
||||||
[ -d "$REPO_DIR" ] || ostree init --repo="$REPO_DIR" --mode=archive
|
[ -d "$REPO_DIR" ] || ostree init --repo="$REPO_DIR" --mode=archive
|
||||||
|
|
|
||||||
14
AGENTS.md
14
AGENTS.md
|
|
@ -143,12 +143,22 @@ These were decided deliberately. Do not undo them without the maintainer's say-s
|
||||||
- Unpinned dependencies `git pull` on every invocation, so builds and tests need network. A missing or private repo surfaces as a clone failure before anything is built.
|
- Unpinned dependencies `git pull` on every invocation, so builds and tests need network. A missing or private repo surfaces as a clone failure before anything is built.
|
||||||
- `--runner=cmd:<command>` executes the named wrapper around every test binary. Wrappers come from `--runner=` or `CRAFTER_BUILD_RUNNER_*`; do not hardcode hosts, wrappers or credentials in `project.cpp` or tests.
|
- `--runner=cmd:<command>` executes the named wrapper around every test binary. Wrappers come from `--runner=` or `CRAFTER_BUILD_RUNNER_*`; do not hardcode hosts, wrappers or credentials in `project.cpp` or tests.
|
||||||
- The linter loads `libclang` at runtime by name. `CRAFTER_BUILD_LIBCLANG` pins an exact path and must fail loudly when wrong, never fall back to another library.
|
- The linter loads `libclang` at runtime by name. `CRAFTER_BUILD_LIBCLANG` pins an exact path and must fail loudly when wrong, never fall back to another library.
|
||||||
- CI force-pushes the rolling `latest` tag and publishes a prerelease on every push to `master` with the workflow's own token, and signs the Flatpak repo with the key in the `FLATPAK_GPG_PRIVATE_KEY` secret. Do not add other secrets to the workflow or echo the environment in it.
|
- CI force-pushes the rolling `latest` tag and publishes a prerelease on every push to `master` with the workflow's own token, and signs the Flatpak repo with the key mounted from the runner host. Do not add secrets to the workflow or echo the environment in it.
|
||||||
- Tests write only under `fs::temp_directory_path()` and the fixture's own `bin/` and `build/`. Keep it that way.
|
- Tests write only under `fs::temp_directory_path()` and the fixture's own `bin/` and `build/`. Keep it that way.
|
||||||
|
|
||||||
## CI and publishing
|
## CI and publishing
|
||||||
|
|
||||||
`.forgejo/workflows/ci.yaml` runs on the `flatpak` runner label: the privileged forgejo-runner on the Hetzner box (bubblewrap inside the job container needs it). Its `config.yml` allows three volumes, which the workflow mounts: `crafter-flatpak-system` (the Sdk install at `/var/lib/flatpak`), `crafter-flatpak-builder` (flatpak-builder state and ccache, so the LLVM and WASI modules are rebuilt only when their sources change) and `/var/www/flatpak`, the web root Caddy serves as `https://flatpak.catcrafts.net/`. Secrets: `FLATPAK_GPG_PRIVATE_KEY` (armored, no passphrase) and `FLATPAK_GPG_KEY_ID`. The live repo at `/var/www/flatpak/repo` is canonical; builds are promoted into it with `flatpak build-commit-from`, never built into it.
|
`.forgejo/workflows/ci.yaml` runs on the `flatpak` runner label: the privileged forgejo-runner on the Hetzner box (bubblewrap inside the job container needs it). Its `config.yml` allows three volumes, which the workflow mounts: `crafter-flatpak-system` (the Sdk install at `/var/lib/flatpak`), `crafter-flatpak-builder` (flatpak-builder state and ccache, so the LLVM and WASI modules are rebuilt only when their sources change) and `/var/www/flatpak`, the web root Caddy serves as `https://flatpak.catcrafts.net/`, and `/etc/crafter-flatpak`, which holds the repo signing key (`signing-key.asc`, no passphrase, and `key-id`), generated once on the host and mounted read-only into the job; nothing is stored in the forge. The live repo at `/var/www/flatpak/repo` is canonical; builds are promoted into it with `flatpak build-commit-from`, never built into it.
|
||||||
|
|
||||||
|
The key is made once, as root on the runner host, and the step fails with a message pointing here when it is missing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
install -d -m 700 /etc/crafter-flatpak/gnupg && export GNUPGHOME=/etc/crafter-flatpak/gnupg
|
||||||
|
gpg --batch --pinentry-mode loopback --passphrase '' --quick-generate-key 'Catcrafts Flatpak Repository <flatpak@catcrafts.net>' ed25519 sign never
|
||||||
|
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/{print $5; exit}')
|
||||||
|
gpg --batch --pinentry-mode loopback --passphrase '' --export-secret-keys --armor "$KEYID" > /etc/crafter-flatpak/signing-key.asc
|
||||||
|
printf '%s\n' "$KEYID" > /etc/crafter-flatpak/key-id && chmod 600 /etc/crafter-flatpak/signing-key.asc /etc/crafter-flatpak/key-id
|
||||||
|
```
|
||||||
|
|
||||||
To release: add a `<release>` to `packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml` (its description becomes the release notes), merge, and push the tag `X.Y.Z`. CI refuses a tag that does not match the newest entry. Local builds: `flatpak-builder --user --force-clean --ccache --repo=repo build-dir packaging/flatpak/net.catcrafts.CrafterBuild.yaml`, then `flatpak-builder --run build-dir <manifest> crafter-build --version`.
|
To release: add a `<release>` to `packaging/flatpak/net.catcrafts.CrafterBuild.metainfo.xml` (its description becomes the release notes), merge, and push the tag `X.Y.Z`. CI refuses a tag that does not match the newest entry. Local builds: `flatpak-builder --user --force-clean --ccache --repo=repo build-dir packaging/flatpak/net.catcrafts.CrafterBuild.yaml`, then `flatpak-builder --run build-dir <manifest> crafter-build --version`.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue