Skip to content
Merged
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
17 changes: 12 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,16 @@ jobs:
nfpm --version

- name: Build Linux packages (.deb / .rpm)
# CHANNEL derives from the version so develop snapshots produce the
# `wheels-be` package (which the Phase 2 bleeding-edge receiver
# downloads) instead of `wheels`. Without this, the snapshot release
# ships `wheels_<v>_amd64.deb` and the apt-wheels-dev / yum-wheels-dev
# receiver workflow 404s on its `wheels-be_<v>_amd64.deb` fetch.
# RC builds keep CHANNEL=stable — RCs aren't fanned out to apt/yum
# (see release.yml § "Dispatch Linux repo buckets").
env:
WHEELS_VERSION: ${{ env.WHEELS_VERSION }}
CHANNEL: stable
CHANNEL: ${{ contains(env.WHEELS_VERSION, '-snapshot') && 'bleeding-edge' || 'stable' }}
OUT_DIR: artifacts/wheels/${{ env.WHEELS_VERSION }}
run: bash tools/distribution-drafts/linux-packages/build-linux-packages.sh

Expand Down Expand Up @@ -428,8 +435,8 @@ jobs:
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip.md5
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip.sha512
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels_*_amd64.deb
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-*.x86_64.rpm
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*_amd64.deb
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*.x86_64.rpm

#############################################
# Create Snapshot Pre-Release (develop only)
Expand Down Expand Up @@ -487,8 +494,8 @@ jobs:
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip.md5
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-module-*.zip.sha512
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels_*_amd64.deb
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels-*.x86_64.rpm
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*_amd64.deb
artifacts/wheels/${{ env.WHEELS_VERSION }}/wheels*.x86_64.rpm

#############################################
# Dispatch downstream package managers
Expand Down
3 changes: 2 additions & 1 deletion tools/distribution-drafts/apt-repo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ Before this bucket repo will function:

1. **GPG signing key** — generate a 4096-bit RSA key (or Ed25519 if you prefer)
for `Wheels Distribution <hello@wheels.dev>`. Private key + passphrase go
into 1Password under `op://Infrastructure/wheels-linux-repo-signing/`.
into 1Password under `op://Wheels/wheels-linux-repo-signing/` (Wheels
project vault on `my.1password.com`).
Public key (ASCII-armored) overwrites `wheels.gpg` at the bucket-repo root.
2. **Cloudflare Pages** — create a Pages project pointing at this repo, bind
the apex domain `apt.wheels.dev`. The build command is empty (the repo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ for DIST in $DISTRIBUTIONS; do
echo "── Regenerating dists/${DIST}/ ──"
DIST_DIR="dists/${DIST}"
mkdir -p "$DIST_DIR"
# First publish for a brand-new channel: the pool dir may not exist yet.
# apt-ftparchive aborts on a missing scan path, so create an empty pool
# for now — it'll be backfilled by the first publish dispatch on that channel.
mkdir -p "pool/${DIST}"

for COMPONENT in $COMPONENTS; do
for ARCH in $ARCHITECTURES; do
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
This file is a placeholder. The real `wheels.gpg` at the bucket-repo root must
be the ASCII-armored *public* half of the signing key whose private half lives
in 1Password at `op://Infrastructure/wheels-linux-repo-signing/private-key`.
in 1Password at `op://Wheels/wheels-linux-repo-signing/private-key`.

Once the key is minted:

op read 'op://Infrastructure/wheels-linux-repo-signing/public-key' > wheels.gpg
op read 'op://Wheels/wheels-linux-repo-signing/public-key' > wheels.gpg
# or, from a host that has the secret key imported:
gpg --armor --export <KEY_ID> > wheels.gpg

Expand Down
10 changes: 8 additions & 2 deletions tools/distribution-drafts/apt-repo/workflows/wheels-released.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,16 @@ jobs:
fi
echo "$GPG_PRIVATE_KEY" | gpg --batch --yes --import
# Trust the imported key ultimately so subsequent sign operations don't prompt.
# `import-ownertrust` expects the 40-char fingerprint (`fpr:` line, field 10),
# NOT the 16-char key_id (`sec:` line, field 5). Using the key_id form emits
# a "gpg: error in '[stdin]': invalid fingerprint" warning that's non-fatal
# but noisy in CI logs.
FINGERPRINT=$(gpg --list-secret-keys --with-colons \
| awk -F: '/^fpr:/ { print $10; exit }')
KEY_ID=$(gpg --list-secret-keys --keyid-format=long --with-colons \
| awk -F: '/^sec:/ { print $5; exit }')
echo "Imported signing key: $KEY_ID"
echo "$KEY_ID:6:" | gpg --batch --yes --import-ownertrust
echo "Imported signing key: $KEY_ID (fpr $FINGERPRINT)"
echo "$FINGERPRINT:6:" | gpg --batch --yes --import-ownertrust
echo "GPG_KEY_ID=$KEY_ID" >> "$GITHUB_ENV"

- name: Download .deb from upstream Release
Expand Down
14 changes: 8 additions & 6 deletions tools/distribution-drafts/linux-packages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,14 @@ Bucket-repo templates are now drafted in:

The remaining work is operational:

1. **Mint a GPG signing key** for the Wheels project (one key signs both
`Release` / `InRelease` and `repomd.xml.asc`). Store the private key +
passphrase in 1Password under
`op://Infrastructure/wheels-linux-repo-signing/`. Commit the public half
to the root of *each* bucket repo as `wheels.gpg` (template placeholders
live at `<bucket>/templates/wheels.gpg.placeholder`).
1. **Mint a GPG signing key** for the Wheels project (one key signs the apt
`Release`/`InRelease`, the yum `repomd.xml.asc`, AND each individual `.rpm`
via `rpm --addsign`). Store the private key + passphrase in 1Password under
`op://Wheels/wheels-linux-repo-signing/` (the Wheels project vault on the
personal `my.1password.com` tenant — NOT the PAI work `op://Infrastructure/`
vault). Commit the public half to the root of *each* bucket repo as
`wheels.gpg` (template placeholders live at
`<bucket>/templates/wheels.gpg.placeholder`).
2. **Create the two bucket repos** under `wheels-dev`:
- `wheels-dev/apt-wheels-dev` — copy contents of `apt-repo/` template
- `wheels-dev/yum-wheels-dev` — copy contents of `yum-repo/` template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ fi

CHANNELS="stable bleeding-edge"

# nfpm produces unsigned .rpm files. The wheels.repo / wheels-be.repo files
# served by this bucket set gpgcheck=1, so dnf REJECTS unsigned packages with
# "Package is not signed: GPG check FAILED". Sign every .rpm in the channel's
# packages/ dir before regenerating metadata — rpm --addsign embeds the
# signature in the .rpm header, which createrepo_c then records in the
# primary.xml.gz hash chain. Re-runs are idempotent (rpm --addsign replaces
# any existing signature).
#
# Requires rpm-sign (Fedora/RHEL) for the rpm command itself.
cat > ~/.rpmmacros <<RPMMACROS
%_signature gpg
%_gpg_name ${GPG_KEY_ID}
%_gpg_path ${GNUPGHOME:-${HOME}/.gnupg}
%__gpg $(command -v gpg)
RPMMACROS

# rpm --addsign drives gpg via the agent; force loopback so the passphrase
# can come from the env var without a TTY.
mkdir -p "${GNUPGHOME:-${HOME}/.gnupg}"
cat > "${GNUPGHOME:-${HOME}/.gnupg}/gpg-agent.conf" <<GPGAGENT
allow-loopback-pinentry
GPGAGENT
cat > "${GNUPGHOME:-${HOME}/.gnupg}/gpg.conf" <<GPGCONF
use-agent
pinentry-mode loopback
GPGCONF
gpg-connect-agent reloadagent /bye >/dev/null 2>&1 || true

for CHANNEL in $CHANNELS; do
CHANNEL_DIR="$CHANNEL"
PKG_DIR="${CHANNEL_DIR}/packages"
Expand All @@ -30,6 +58,15 @@ for CHANNEL in $CHANNELS; do
continue
fi

echo "── Signing .rpm files in ${PKG_DIR}/ ──"
for rpm_file in "${PKG_DIR}"/*.rpm; do
[ -f "$rpm_file" ] || continue
# --addsign with the macro setup above. Passphrase via env (rpm reads
# $GNUPGHOME/gpg.conf which sets pinentry-mode loopback).
rpm --addsign "$rpm_file" >/dev/null
echo " ✓ signed $(basename "$rpm_file")"
done

echo "── Regenerating ${CHANNEL_DIR}/repodata/ ──"

# createrepo_c scans <channel>/packages/ and writes <channel>/repodata/.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ distributions.

Once the key is minted:

op read 'op://Infrastructure/wheels-linux-repo-signing/public-key' > wheels.gpg
op read 'op://Wheels/wheels-linux-repo-signing/public-key' > wheels.gpg

Commit `wheels.gpg` to the **root** of the bucket repo (not in `templates/`).
Cloudflare Pages will serve it at `https://yum.wheels.dev/wheels.gpg`, which
Expand Down
18 changes: 14 additions & 4 deletions tools/distribution-drafts/yum-repo/workflows/wheels-released.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,17 @@ jobs:
fetch-depth: 1
persist-credentials: true

- name: Install createrepo_c + gpg
- name: Install createrepo_c + gpg + rpm
# `rpm` brings the `rpm --addsign` tool, used by
# regenerate-yum-metadata.sh to sign each .rpm before createrepo_c
# records the hash chain. Without it, dnf clients refuse to install
# because the wheels.repo files set gpgcheck=1.
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends createrepo-c gnupg
sudo apt-get install -y --no-install-recommends createrepo-c gnupg rpm
createrepo_c --version
gpg --version | head -1
rpm --version

- name: Import GPG signing key
env:
Expand All @@ -102,10 +107,15 @@ jobs:
exit 1
fi
echo "$GPG_PRIVATE_KEY" | gpg --batch --yes --import
# See apt-repo/workflows/wheels-released.yml for the fingerprint-vs-key_id
# rationale — import-ownertrust needs the 40-char fingerprint, not the
# 16-char key_id (otherwise: "gpg: error in '[stdin]': invalid fingerprint").
FINGERPRINT=$(gpg --list-secret-keys --with-colons \
| awk -F: '/^fpr:/ { print $10; exit }')
KEY_ID=$(gpg --list-secret-keys --keyid-format=long --with-colons \
| awk -F: '/^sec:/ { print $5; exit }')
echo "Imported signing key: $KEY_ID"
echo "$KEY_ID:6:" | gpg --batch --yes --import-ownertrust
echo "Imported signing key: $KEY_ID (fpr $FINGERPRINT)"
echo "$FINGERPRINT:6:" | gpg --batch --yes --import-ownertrust
echo "GPG_KEY_ID=$KEY_ID" >> "$GITHUB_ENV"

- name: Download .rpm from upstream Release
Expand Down
Loading