fix(void): wire signed xbps repo into release, README, and release process
Add build-xbps tag job to release.yml: verifies the tag against src/vocalinux/version.py, fails closed when XBPS_SIGNING_KEY is unset, builds and signs via podman-only repo.sh, and attaches packages, signatures, repodata, and client key files to the release. Fold the xbps-repo artifact into publish-checksums (manifest + SHA256SUMS). Document the Void install in the root README (glibc x86_64 scope, fingerprint check, third-party repo caveat) and the post-tag template version/checksum bump as a pull request in docs/RELEASE_PROCESS.md. Proof: local podman run with a throwaway RSA4096 key built all four templates, signed packages + repodata, and verified voculinux installs from the signed repo in a clean rootdir (fingerprint eb:29:5d:9e:5c:94:c1:63:ae:35:cd:35:db:7a:28:da).
This commit is contained in:
@@ -289,6 +289,21 @@ jobs:
|
||||
`--dangerous` is required because this file is not a Store revision. It will not
|
||||
refresh from the Store. amd64 only.
|
||||
|
||||
### Void Linux (glibc x86_64, signed xbps repo)
|
||||
Also attached below: the signed repository files (`*.x86_64.xbps`
|
||||
with signatures, `x86_64-repodata`, and the client key
|
||||
`FINGERPRINT` plus the fingerprint-named `.plist`). This is a
|
||||
third-party repository published by this project, not upstream
|
||||
Void Linux:
|
||||
```bash
|
||||
# as root (the files come from this release's assets):
|
||||
cp <fingerprint>.plist /var/db/xbps/keys/
|
||||
echo 'repository=<repo-url-from-release-notes>' > /etc/xbps.d/voculinux.conf
|
||||
sudo xbps-install -Sy voculinux
|
||||
```
|
||||
Answer `Y` at the fingerprint prompt only after checking it
|
||||
matches the attached `FINGERPRINT` file.
|
||||
|
||||
### Verifying what you downloaded
|
||||
`SHA256SUMS` covers every file here and is attached once the aarch64 AppImage,
|
||||
both Flatpak builds, and the snap finish, a few minutes after this release appears.
|
||||
@@ -789,7 +804,7 @@ jobs:
|
||||
# missing one looks like the tampered one.
|
||||
publish-checksums:
|
||||
name: Checksums and Provenance
|
||||
needs: [build-and-release, build-appimage-arm64, build-native, build-native-arm64, build-flatpak-amd64, build-flatpak-arm64, attach-flatpak, attach-snap]
|
||||
needs: [build-and-release, build-appimage-arm64, build-native, build-native-arm64, build-flatpak-amd64, build-flatpak-arm64, attach-flatpak, attach-snap, build-xbps]
|
||||
runs-on: ubuntu-latest
|
||||
# An attach-job failure must not skip checksums: this job hashes the build
|
||||
# artifacts, which exist regardless of whether an upload to the release
|
||||
@@ -805,7 +820,7 @@ jobs:
|
||||
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
with:
|
||||
# Verification-only artifacts must never enter the release manifest.
|
||||
pattern: '@(python-dist|appimage-*|native-*|flatpak-*|snap-amd64)'
|
||||
pattern: '@(python-dist|appimage-*|native-*|flatpak-*|snap-amd64|xbps-repo)'
|
||||
merge-multiple: true
|
||||
path: dist
|
||||
|
||||
@@ -816,6 +831,9 @@ jobs:
|
||||
for pat in '*.whl' '*.tar.gz' '*.AppImage' '*.deb' '*.rpm' '*.flatpak' '*.snap'; do
|
||||
compgen -G "$pat" > /dev/null || { echo "missing artifact: $pat"; exit 1; }
|
||||
done
|
||||
for pat in 'repo/*.x86_64.xbps' 'repo/*.sig2' 'repo/*-repodata*' 'keys/FINGERPRINT' 'keys/*.plist'; do
|
||||
compgen -G "$pat" > /dev/null || { echo "missing artifact: $pat"; exit 1; }
|
||||
done
|
||||
test "$(compgen -G '*.AppImage' | wc -l)" -eq 2
|
||||
test "$(compgen -G '*.deb' | wc -l)" -eq 2
|
||||
test "$(compgen -G '*.rpm' | wc -l)" -eq 2
|
||||
@@ -827,8 +845,12 @@ jobs:
|
||||
set -euo pipefail
|
||||
ls -la
|
||||
# Bare names, so `sha256sum -c` works in whatever directory the user
|
||||
# downloaded the release into.
|
||||
# downloaded the release into. The xbps repo files hash from their
|
||||
# own directories for the same reason: the release attaches them
|
||||
# flat, and RSA signatures stay the primary trust root.
|
||||
sha256sum -- *.whl *.tar.gz *.AppImage *.deb *.rpm *.flatpak *.snap | tee SHA256SUMS
|
||||
(cd repo && sha256sum -- *.x86_64.xbps *.sig2 *-repodata*) >> SHA256SUMS
|
||||
(cd keys && sha256sum -- FINGERPRINT *.plist) >> SHA256SUMS
|
||||
|
||||
# Attesting from the manifest rather than a second glob: the file we
|
||||
# publish and the set we attest cannot drift apart.
|
||||
@@ -1021,6 +1043,94 @@ jobs:
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: 'Release ${{ github.ref_name }}'
|
||||
|
||||
# The signed Void xbps repository (ticket #29): every template builds in
|
||||
# smoke-gate order in the pinned Void container via podman, the full
|
||||
# repository index is signed with the RSA4096 repository key, and the
|
||||
# packages, signatures, repodata, and client key land on this release.
|
||||
# XBPS_SIGNING_KEY is required — an unsigned repository can never
|
||||
# publish, so a missing key fails the job instead of skipping it (unlike
|
||||
# the opt-in Flatpak remote and AUR publishes). The voculinux template
|
||||
# builds the canonical release sdist, so this waits for build-and-release,
|
||||
# and the tag must match src/vocalinux/version.py or the job aborts.
|
||||
build-xbps:
|
||||
name: Build signed xbps repository
|
||||
needs: build-and-release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Verify tag matches the version source of truth
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="${GITHUB_REF#refs/tags/v}"
|
||||
PACKAGE_VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' src/vocalinux/version.py)
|
||||
echo "Tag version: $TAG"
|
||||
echo "Package version: $PACKAGE_VERSION"
|
||||
if [[ "$PACKAGE_VERSION" != *"$TAG"* ]] && [[ "$TAG" != *"$PACKAGE_VERSION"* ]]; then
|
||||
echo "::error::Tag v$TAG does not match src/vocalinux/version.py ($PACKAGE_VERSION); refusing to ship the signed repo"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fail-closed: no key, no repo, no publish — never a silent skip.
|
||||
- name: Require the repository signing key
|
||||
env:
|
||||
XBPS_SIGNING_KEY: ${{ secrets.XBPS_SIGNING_KEY }}
|
||||
run: |
|
||||
if [ -z "$XBPS_SIGNING_KEY" ]; then
|
||||
echo "::error::XBPS_SIGNING_KEY is unset; refusing to publish an unsigned repository (packaging/void/README.md)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Install podman
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends podman
|
||||
|
||||
- name: Build and sign the repository
|
||||
env:
|
||||
XBPS_SIGNING_KEY: ${{ secrets.XBPS_SIGNING_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
printf '%s' "$XBPS_SIGNING_KEY" > /tmp/xbps-sign-key.pem
|
||||
chmod 600 /tmp/xbps-sign-key.pem
|
||||
XBPS_SIGNING_KEY_FILE=/tmp/xbps-sign-key.pem bash packaging/void/repo.sh
|
||||
rm -f /tmp/xbps-sign-key.pem
|
||||
ls -la publish/xbps/repo publish/xbps/keys
|
||||
|
||||
- name: Upload the signed repository
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: xbps-repo
|
||||
path: |
|
||||
publish/xbps/repo/*
|
||||
publish/xbps/keys/*
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
- name: Attach the signed repository to the release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd publish/xbps
|
||||
# Full signed repo or nothing: binary packages (*.x86_64.xbps)
|
||||
# with their signatures, the signed repository metadata
|
||||
# (*-repodata), and the client key files (FINGERPRINT plus the
|
||||
# fingerprint-named .plist). Each class is required below, so a
|
||||
# partial repo can never attach.
|
||||
compgen -G 'repo/*.x86_64.xbps' > /dev/null || { echo "missing packages"; exit 1; }
|
||||
compgen -G 'repo/*.sig2' > /dev/null || { echo "missing signatures"; exit 1; }
|
||||
compgen -G 'repo/*-repodata*' > /dev/null || { echo "missing repodata"; exit 1; }
|
||||
compgen -G 'keys/FINGERPRINT' > /dev/null || { echo "missing FINGERPRINT"; exit 1; }
|
||||
compgen -G 'keys/*.plist' > /dev/null || { echo "missing client key"; exit 1; }
|
||||
gh release upload "${{ github.ref_name }}" \
|
||||
repo/*.x86_64.xbps repo/*.sig2 repo/*-repodata* keys/FINGERPRINT keys/*.plist \
|
||||
--repo ${{ github.repository }} \
|
||||
--clobber
|
||||
|
||||
# GitHub Release first, Store second. A super-privileged plug can still
|
||||
# queue a human review and refuse edge/candidate; the .snap still
|
||||
# has to land on the tag for sideload. grade: stable allows candidate; do
|
||||
|
||||
Reference in New Issue
Block a user