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:
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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 }}"
|
||||
Reference in new issue
Block a user