ci(updater): sign artifacts and consolidate updater manifest

Adds TAURI_SIGNING_PRIVATE_KEY(_PASSWORD) env to release-linux,
release-linux-aarch64, and release-macos build steps, collects the
resulting .sig files into the release artifacts, and adds a new
release-updater-manifest workflow that waits for all platform sig
files to land on the release and assembles latest.json for the
updater endpoint.

Cherry-picked from #305 by @afonsojramos.

cc
This commit is contained in:
vicrodh committed 2026-04-13 14:53:52 -06:00
1 parent 96cfb93956
commit 2f5a6aaddf
4 files changed
+187 -2

No files matched your search

@@ -71,6 +71,9 @@ jobs:
run: npm run build
- name: Build Tauri (deb + rpm + appimage)
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: npm run tauri build -- --bundles deb,rpm,appimage
- name: Collect artifacts
@@ -81,6 +84,7 @@ jobs:
cp -v src-tauri/target/release/bundle/deb/*.deb dist/ || true
cp -v src-tauri/target/release/bundle/rpm/*.rpm dist/ || true
cp -v src-tauri/target/release/bundle/appimage/*.AppImage dist/ || true
cp -v src-tauri/target/release/bundle/appimage/*.AppImage.sig dist/ || true
# Create AUR tarball
ARCH=aarch64
+4
View File
@@ -99,6 +99,9 @@ jobs:
run: npm ci
- name: Build DEB/RPM/AppImage
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: npm run tauri build -- --bundles deb,rpm,appimage
- name: Collect artifacts
@@ -110,6 +113,7 @@ jobs:
cp -v src-tauri/target/release/bundle/deb/*.deb dist/ || true
cp -v src-tauri/target/release/bundle/rpm/*.rpm dist/ || true
cp -v src-tauri/target/release/bundle/appimage/*.AppImage dist/ || true
cp -v src-tauri/target/release/bundle/appimage/*.AppImage.sig dist/ || true
# Create tarball for AUR with proper structure
TARBALL_DIR="qbz_${VERSION}_amd64"
+9 -2
View File
@@ -95,13 +95,20 @@ jobs:
- name: Install JS dependencies
run: npm ci
- name: Build DMG
run: npm run tauri build -- --target ${{ matrix.target }} --bundles dmg
- name: Build macOS bundles
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: npm run tauri build -- --target ${{ matrix.target }}
- name: Collect artifacts
run: |
mkdir -p dist
# DMG for manual distribution
cp -v src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg dist/ || true
# .app.tar.gz + .sig for auto-updater
cp -v src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.tar.gz dist/ || true
cp -v src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.tar.gz.sig dist/ || true
ls -la dist
- name: Upload artifacts
@@ -0,0 +1,170 @@
name: Generate Updater Manifest
on:
workflow_run:
workflows:
- release-macos
- release-linux
- "Linux ARM64 (aarch64) Build"
types:
- completed
concurrency:
group: updater-manifest-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true
jobs:
generate-manifest:
# Only run on successful tag-triggered builds (not manual dispatches)
if: >
github.event.workflow_run.conclusion == 'success' &&
startsWith(github.event.workflow_run.head_branch, 'v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Resolve version from tag
id: version
run: |
TAG="${{ github.event.workflow_run.head_branch }}"
VERSION="${TAG#v}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Wait for all platform builds to finish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
# Wait up to 20 minutes for .sig files from all platforms
REQUIRED_SIGS=("aarch64.app.tar.gz.sig" "x64.app.tar.gz.sig" "AppImage.sig")
MAX_ATTEMPTS=40
SLEEP_SECONDS=30
for attempt in $(seq 1 $MAX_ATTEMPTS); do
ASSETS=$(gh release view "$TAG" --repo "${{ github.repository }}" --json assets -q '.assets[].name' 2>/dev/null || echo "")
FOUND_ALL=true
for sig_suffix in "${REQUIRED_SIGS[@]}"; do
if ! echo "$ASSETS" | grep -q "$sig_suffix"; then
FOUND_ALL=false
break
fi
done
if [ "$FOUND_ALL" = true ]; then
echo "All signature files found on attempt $attempt"
break
fi
if [ "$attempt" -eq "$MAX_ATTEMPTS" ]; then
echo "Warning: Not all .sig files found after $MAX_ATTEMPTS attempts. Generating manifest with available assets."
break
fi
echo "Attempt $attempt/$MAX_ATTEMPTS: waiting for all .sig files..."
sleep $SLEEP_SECONDS
done
- name: Generate latest.json
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
VERSION: ${{ steps.version.outputs.version }}
run: |
REPO="${{ github.repository }}"
RELEASE_URL="https://github.com/${REPO}/releases/download/${TAG}"
# Get release info
RELEASE_JSON=$(gh release view "$TAG" --repo "$REPO" --json body,publishedAt)
NOTES=$(echo "$RELEASE_JSON" | jq -r '.body // ""')
PUB_DATE=$(echo "$RELEASE_JSON" | jq -r '.publishedAt // ""')
# Download all .sig files
mkdir -p sigs
ASSETS=$(gh release view "$TAG" --repo "$REPO" --json assets -q '.assets[].name')
for asset in $ASSETS; do
if [[ "$asset" == *.sig ]]; then
gh release download "$TAG" --repo "$REPO" --pattern "$asset" --dir sigs
fi
done
ls -la sigs/
# Build platform entries
# Helper: read sig content for a glob pattern, return empty string if not found
read_sig() {
local sig_file
for pattern in "$@"; do
sig_file=$(find sigs -name "$pattern" -print -quit 2>/dev/null)
if [ -n "$sig_file" ] && [ -f "$sig_file" ]; then
cat "$sig_file"
return
fi
done
echo ""
}
# Find asset names matching patterns (extended regex)
find_asset() {
local pattern="$1"
echo "$ASSETS" | grep -E "$pattern" | head -1
}
# Build platforms JSON
PLATFORMS="{}"
# macOS aarch64 (.app.tar.gz for updater)
MACOS_ARM_TAR=$(find_asset "aarch64.app.tar.gz$")
MACOS_ARM_SIG=$(read_sig "*aarch64.app.tar.gz.sig")
if [ -n "$MACOS_ARM_TAR" ] && [ -n "$MACOS_ARM_SIG" ]; then
PLATFORMS=$(echo "$PLATFORMS" | jq --arg url "${RELEASE_URL}/${MACOS_ARM_TAR}" --arg sig "$MACOS_ARM_SIG" \
'. + {"darwin-aarch64": {"signature": $sig, "url": $url}}')
fi
# macOS x86_64 (.app.tar.gz for updater)
MACOS_X64_TAR=$(find_asset "(x64|x86_64).app.tar.gz$")
MACOS_X64_SIG=$(read_sig "*x64.app.tar.gz.sig" "*x86_64.app.tar.gz.sig")
if [ -n "$MACOS_X64_TAR" ] && [ -n "$MACOS_X64_SIG" ]; then
PLATFORMS=$(echo "$PLATFORMS" | jq --arg url "${RELEASE_URL}/${MACOS_X64_TAR}" --arg sig "$MACOS_X64_SIG" \
'. + {"darwin-x86_64": {"signature": $sig, "url": $url}}')
fi
# Linux x86_64 AppImage
LINUX_X64_APPIMAGE=$(find_asset "(amd64|x86_64).AppImage$")
LINUX_X64_SIG=$(read_sig "*amd64.AppImage.sig" "*x86_64.AppImage.sig")
if [ -n "$LINUX_X64_APPIMAGE" ] && [ -n "$LINUX_X64_SIG" ]; then
PLATFORMS=$(echo "$PLATFORMS" | jq --arg url "${RELEASE_URL}/${LINUX_X64_APPIMAGE}" --arg sig "$LINUX_X64_SIG" \
'. + {"linux-x86_64": {"signature": $sig, "url": $url}}')
fi
# Linux aarch64 AppImage
LINUX_ARM_APPIMAGE=$(find_asset "aarch64.AppImage$")
LINUX_ARM_SIG=$(read_sig "*aarch64.AppImage.sig")
if [ -n "$LINUX_ARM_APPIMAGE" ] && [ -n "$LINUX_ARM_SIG" ]; then
PLATFORMS=$(echo "$PLATFORMS" | jq --arg url "${RELEASE_URL}/${LINUX_ARM_APPIMAGE}" --arg sig "$LINUX_ARM_SIG" \
'. + {"linux-aarch64": {"signature": $sig, "url": $url}}')
fi
# Assemble final latest.json
jq -n \
--arg version "$VERSION" \
--arg notes "$NOTES" \
--arg pub_date "$PUB_DATE" \
--argjson platforms "$PLATFORMS" \
'{version: $version, notes: $notes, pub_date: $pub_date, platforms: $platforms}' \
> latest.json
echo "Generated latest.json:"
cat latest.json
- name: Upload latest.json to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
# Delete existing latest.json if present (from a previous run)
gh release delete-asset "$TAG" latest.json --repo "${{ github.repository }}" --yes 2>/dev/null || true
# Upload the new one
gh release upload "$TAG" latest.json --repo "${{ github.repository }}"