feat: GitHub Pages apt repo with per-major-version dist tracks (#230)

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 <noreply@anthropic.com>
This commit is contained in:
Kevin Adams 2026-06-02 16:03:57 -04:00
parent 1d0cbca537
commit dfbb4f6c31
3 changed files with 211 additions and 17 deletions

View File

@ -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

View File

@ -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
```
---

View File

@ -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 <<EOF
%echo Generating truenas-proxmox apt signing key
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: ${KEY_NAME}
Name-Comment: ${KEY_COMMENT}
Name-Email: ${KEY_EMAIL}
Expire-Date: 0
$([ -n "$PASSPHRASE" ] && echo "Passphrase: ${PASSPHRASE}" || echo "%no-protection")
%commit
%echo Done
EOF
KEY_ID="$(gpg --list-secret-keys --with-colons 2>/dev/null \
| awk -F: '/^sec/{print $5; exit}')"
echo "Generated key: $KEY_ID"
fi
# ── Export public key ─────────────────────────────────────────────────────────
echo
echo "==> 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 <<INSTRUCTIONS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NEXT STEPS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Add APT_SIGNING_KEY to GitHub Actions secrets:
gh secret set APT_SIGNING_KEY --repo ${REPO} --body "${PRIVATE_KEY_B64}"
2. Add APT_SIGNING_KEY_PASSPHRASE to GitHub Actions secrets:
gh secret set APT_SIGNING_KEY_PASSPHRASE --repo ${REPO} --body "YOUR_PASSPHRASE"
(use empty string if you chose no passphrase:)
gh secret set APT_SIGNING_KEY_PASSPHRASE --repo ${REPO} --body ""
3. Commit public.gpg.key to the gh-pages branch:
git checkout gh-pages
cp /tmp/truenas-proxmox-public.gpg.key public.gpg.key
git add public.gpg.key
git commit -m "chore: add apt repo GPG public key"
git push origin gh-pages
git checkout release/3.x
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
INSTRUCTIONS