From dfbb4f6c315f577d2f095f995e53e03127b042de Mon Sep 17 00:00:00 2001 From: Kevin Adams Date: Tue, 2 Jun 2026 16:03:57 -0400 Subject: [PATCH] feat: GitHub Pages apt repo with per-major-version dist tracks (#230) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements ADR-010: stable releases are now published to https://thegrandwazoo.github.io/freenas-proxmox/ in addition to Cloudsmith. Dist tracks: v3 main — v3.x releases (new installs) main main — v2.x only (backward-compat, never auto-promoted to v3) v2 main — v2.x alias (explicit pin) CI: new "Publish to GitHub Pages APT repo" step in the publish job runs on tagged stable releases. Downloads the built .deb, places it in pool/v{major}, regenerates Packages.gz and a GPG-signed InRelease for the relevant dist(s), and pushes to the gh-pages branch. Setup: scripts/setup-apt-signing-key.sh generates the GPG key pair and prints the exact `gh secret set` commands to run. Requires APT_SIGNING_KEY and APT_SIGNING_KEY_PASSPHRASE secrets to be added to the repo before the first tagged release. README updated to point new installs at GitHub Pages; Cloudsmith testing channel retained for beta builds. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/build.yml | 83 ++++++++++++++++++++++++++++++ README.md | 59 +++++++++++++++------- scripts/setup-apt-signing-key.sh | 86 ++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 17 deletions(-) create mode 100644 scripts/setup-apt-signing-key.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 71c77a0..0ae73b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -292,6 +292,89 @@ jobs: release: any-version file: ${{ needs.build.outputs.transitional_deb_file }} + - name: Publish to GitHub Pages APT repo + if: needs.build.outputs.is_release == 'true' + env: + APT_SIGNING_KEY: ${{ secrets.APT_SIGNING_KEY }} + APT_SIGNING_KEY_PASSPHRASE: ${{ secrets.APT_SIGNING_KEY_PASSPHRASE }} + GH_TOKEN: ${{ secrets.ACCESS_TOKEN }} + run: | + # Abort clearly if signing key not configured + if [[ -z "$APT_SIGNING_KEY" ]]; then + echo "::error::APT_SIGNING_KEY secret is not set — run scripts/setup-apt-signing-key.sh and add the secret" + exit 1 + fi + + # Determine major version from package version (e.g. 3.0.0-1 → 3) + VERSION="${{ needs.build.outputs.version }}" + MAJOR="${VERSION%%.*}" + + # Import GPG signing key + echo "$APT_SIGNING_KEY" | base64 -d | gpg --batch --import + GPG_KEY_ID="$(gpg --list-secret-keys --with-colons 2>/dev/null \ + | awk -F: '/^sec/{print $5; exit}')" + + # Checkout gh-pages branch into a temp directory + PAGES_DIR="$(mktemp -d)" + git clone --branch gh-pages \ + "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git" \ + "$PAGES_DIR" + + # Add .deb files to the versioned pool directory + mkdir -p "${PAGES_DIR}/pool/v${MAJOR}" + cp "${{ needs.build.outputs.deb_file }}" "${PAGES_DIR}/pool/v${MAJOR}/" + cp "${{ needs.build.outputs.transitional_deb_file }}" "${PAGES_DIR}/pool/v${MAJOR}/" + + # Install apt tooling + sudo apt-get install -y --no-install-recommends dpkg-dev apt-utils + + # Determine which dists to update + # v2.x: update v2, main (alias), and v2 (explicit) + # v3.x and above: update only the versioned dist + if [[ "$MAJOR" == "2" ]]; then + DISTS="v2 main" + else + DISTS="v${MAJOR}" + fi + + for DIST in $DISTS; do + mkdir -p "${PAGES_DIR}/dists/${DIST}/main/binary-all" + + # Generate Packages file (paths relative to repo root) + dpkg-scanpackages --multiversion "${PAGES_DIR}/pool/v${MAJOR}" \ + | sed "s|^Filename: ${PAGES_DIR}/||" \ + > "${PAGES_DIR}/dists/${DIST}/main/binary-all/Packages" + gzip -kf "${PAGES_DIR}/dists/${DIST}/main/binary-all/Packages" + + # Generate Release file + apt-ftparchive \ + -o "APT::FTPArchive::Release::Origin=truenas-proxmox" \ + -o "APT::FTPArchive::Release::Label=truenas-proxmox" \ + -o "APT::FTPArchive::Release::Suite=${DIST}" \ + -o "APT::FTPArchive::Release::Codename=${DIST}" \ + -o "APT::FTPArchive::Release::Components=main" \ + -o "APT::FTPArchive::Release::Architectures=all" \ + release "${PAGES_DIR}/dists/${DIST}" \ + > "${PAGES_DIR}/dists/${DIST}/Release" + + # Sign → InRelease + gpg --batch --yes \ + --passphrase "${APT_SIGNING_KEY_PASSPHRASE}" \ + --default-key "${GPG_KEY_ID}" \ + --clearsign \ + -o "${PAGES_DIR}/dists/${DIST}/InRelease" \ + "${PAGES_DIR}/dists/${DIST}/Release" + done + + # Commit and push + cd "$PAGES_DIR" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add . + git diff --staged --quiet || \ + git commit -m "apt: publish ${VERSION} to dist(s): ${DISTS}" + git push + - name: Create draft GitHub Release if: needs.build.outputs.is_release == 'true' uses: softprops/action-gh-release@v3 diff --git a/README.md b/README.md index 54752af..e267b9f 100644 --- a/README.md +++ b/README.md @@ -120,24 +120,51 @@ v3.0 is a fully API-driven custom storage plugin. No SSH keys required. ## Installation -### Stable Release - -Add the repository and install: +### Stable Release (v3.x) ```bash # Import the GPG key -curl -fsSL https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox/gpg.284C106104A8CE6D.key \ +curl -fsSL https://thegrandwazoo.github.io/freenas-proxmox/public.gpg.key \ | gpg --dearmor \ - | tee /usr/share/keyrings/ksatechnologies-truenas-proxmox-keyring.gpg > /dev/null + | sudo tee /etc/apt/keyrings/truenas-proxmox.gpg > /dev/null -# Add the repository -cat > /etc/apt/sources.list.d/ksatechnologies-repo.list << 'EOF' -deb [signed-by=/usr/share/keyrings/ksatechnologies-truenas-proxmox-keyring.gpg] \ - https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox/deb/debian any-version main -EOF +# Add the v3 repository track +echo "deb [signed-by=/etc/apt/keyrings/truenas-proxmox.gpg] \ +https://thegrandwazoo.github.io/freenas-proxmox v3 main" \ + | sudo tee /etc/apt/sources.list.d/truenas-proxmox.list # Install -apt update && apt install truenas-proxmox +sudo apt update && sudo apt install truenas-proxmox +``` + +`apt upgrade` will deliver v3.x point releases automatically. You will never be +automatically promoted to a future v4 — that requires changing the dist track in +your `sources.list` to `v4`. + +### Upgrading from v2.x + +**v3.0 is a breaking change.** Do not run `apt upgrade` until you have read the +migration guide — your storage configuration will need to be updated. + +See [docs/migrating-from-v2.md](docs/migrating-from-v2.md). + +### Staying on v2.x + +If you are on v2.x and want to continue receiving v2.x point releases without +risking a v3 upgrade, switch to the `main` dist track (v2.x only): + +```bash +# Import the GPG key (if not already done) +curl -fsSL https://thegrandwazoo.github.io/freenas-proxmox/public.gpg.key \ + | gpg --dearmor \ + | sudo tee /etc/apt/keyrings/truenas-proxmox.gpg > /dev/null + +# Add the v2 repository track +echo "deb [signed-by=/etc/apt/keyrings/truenas-proxmox.gpg] \ +https://thegrandwazoo.github.io/freenas-proxmox main main" \ + | sudo tee /etc/apt/sources.list.d/truenas-proxmox.list + +sudo apt update ``` ### Testing / Beta Release @@ -145,19 +172,17 @@ apt update && apt install truenas-proxmox For early access to new features (may be unstable): ```bash -# Import the GPG key +# Cloudsmith testing channel (beta builds from release/3.x branch) curl -fsSL https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox-testing/gpg.CACC9EE03F2DFFCC.key \ | gpg --dearmor \ - | tee /usr/share/keyrings/ksatechnologies-truenas-proxmox-testing-keyring.gpg > /dev/null + | sudo tee /usr/share/keyrings/ksatechnologies-truenas-proxmox-testing-keyring.gpg > /dev/null -# Add the repository -cat > /etc/apt/sources.list.d/ksatechnologies-testing-repo.list << 'EOF' +cat > /etc/apt/sources.list.d/truenas-proxmox-testing.list << 'EOF' deb [signed-by=/usr/share/keyrings/ksatechnologies-truenas-proxmox-testing-keyring.gpg] \ https://dl.cloudsmith.io/public/ksatechnologies/truenas-proxmox-testing/deb/debian any-version main EOF -# Install -apt update && apt install truenas-proxmox +sudo apt update && sudo apt install truenas-proxmox ``` --- diff --git a/scripts/setup-apt-signing-key.sh b/scripts/setup-apt-signing-key.sh new file mode 100644 index 0000000..f5eed74 --- /dev/null +++ b/scripts/setup-apt-signing-key.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# One-time setup: generate the GPG key used to sign the GitHub Pages apt repo. +# +# Run this locally once. After running: +# 1. Commit public.gpg.key to the gh-pages branch (instructions printed below) +# 2. Add APT_SIGNING_KEY to GitHub Actions secrets +# 3. Add APT_SIGNING_KEY_PASSPHRASE to GitHub Actions secrets (empty string if no passphrase) +# +# Re-running is safe — it checks for an existing key first. +set -euo pipefail + +REPO="TheGrandWazoo/freenas-proxmox" +KEY_NAME="truenas-proxmox" +KEY_EMAIL="packages@ksatechnologies.com" +KEY_COMMENT="truenas-proxmox apt repo signing key" + +# ── Check for existing key ──────────────────────────────────────────────────── +EXISTING="$(gpg --list-secret-keys --with-colons 2>/dev/null \ + | awk -F: -v name="$KEY_NAME" '$1=="uid" && $10 ~ name {found=1} END {print found+0}')" + +if [[ "$EXISTING" == "1" ]]; then + echo "Key '$KEY_NAME' already exists in your keyring — skipping generation." + KEY_ID="$(gpg --list-secret-keys --with-colons 2>/dev/null \ + | awk -F: '/^sec/{print $5; exit}')" +else + # ── Generate key ───────────────────────────────────────────────────────────── + echo "Generating GPG key for apt repo signing..." + read -r -s -p "Enter a passphrase (leave empty for no passphrase): " PASSPHRASE + echo + + gpg --batch --gen-key < Exporting public key..." +gpg --export --armor "$KEY_ID" > /tmp/truenas-proxmox-public.gpg.key +echo "Public key written to /tmp/truenas-proxmox-public.gpg.key" + +# ── Export private key (base64) ─────────────────────────────────────────────── +echo +echo "==> Exporting private key (base64) for GitHub Actions secret..." +PRIVATE_KEY_B64="$(gpg --export-secret-keys --armor "$KEY_ID" | base64 -w 0)" + +# ── Instructions ───────────────────────────────────────────────────────────── +cat <