Skip to content

v2026.05.18.2

v2026.05.18.2 #13

Workflow file for this run

# Sign Release Binaries
#
# This workflow signs all release artifacts with Ed25519 signatures
# using zipsign. It runs automatically when a release is created.
#
# Prerequisites:
# - ZIPSIGN_PRIVATE_KEY secret must be set in GitHub repository settings
# - The private key should be the Ed25519 private key generated via
# scripts/generate-zipsign-keypair.sh
#
# Security:
# - Private key is stored as a GitHub secret (never in code)
# - Signing happens in a clean GitHub Actions environment
# - Signed artifacts are uploaded back to the release
name: Sign Release
on:
release:
types: [created]
permissions:
contents: write # Required to upload artifacts to releases
jobs:
sign:
name: Sign Release Artifacts
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install zipsign
run: |
cargo install zipsign
zipsign --version
- name: Download release artifacts
env:
GH_TOKEN: ${{ github.token }}
run: |
# Get the tag name without 'v' prefix
VERSION="${GITHUB_REF#refs/tags/v}"
echo "Version: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
# Create directory for artifacts
mkdir -p artifacts
# Download all assets from the release
gh release view "$VERSION" --json assets --jq '.assets[].name' | \
while read -r asset_name; do
# Remove quotes from asset name
asset_name=$(echo "$asset_name" | tr -d '"')
# Only download tar.gz and tar.zst archives
if [[ "$asset_name" =~ \.(tar\.gz|tar\.zst)$ ]]; then
echo "Downloading: $asset_name"
gh release download "$VERSION" --pattern "$asset_name" --dir artifacts
fi
done
# List downloaded artifacts
echo "Downloaded artifacts:"
ls -lh artifacts/
- name: Verify artifacts exist
run: |
if [ -z "$(ls -A artifacts/)" ]; then
echo "No artifacts found to sign!"
exit 1
fi
# Count archives
ARCHIVE_COUNT=$(find artifacts/ -type f \( -name "*.tar.gz" -o -name "*.tar.zst" \) | wc -l)
echo "Found $ARCHIVE_COUNT archive(s) to sign"
if [ "$ARCHIVE_COUNT" -eq 0 ]; then
echo "No tar.gz or tar.zst archives found"
exit 1
fi
- name: Sign artifacts
env:
ZIPSIGN_PRIVATE_KEY: ${{ secrets.ZIPSIGN_PRIVATE_KEY }}
run: |
if [ -z "$ZIPSIGN_PRIVATE_KEY" ]; then
echo "Error: ZIPSIGN_PRIVATE_KEY secret not set"
echo "Please set the private key as a GitHub secret"
exit 1
fi
# Save private key to temporary file
PRIVATE_KEY_FILE=$(mktemp)
echo "$ZIPSIGN_PRIVATE_KEY" > "$PRIVATE_KEY_FILE"
chmod 600 "$PRIVATE_KEY_FILE"
# Sign each archive
SIGNED_COUNT=0
for archive in artifacts/*.{tar.gz,tar.zst}; do
if [ -f "$archive" ]; then
echo "Signing: $(basename "$archive")"
# Check if already signed
if zipsign verify tar "$archive" "$PRIVATE_KEY_FILE" >/dev/null 2>&1; then
echo " Already signed, skipping"
else
# Sign the archive
if zipsign sign tar "$archive" "$PRIVATE_KEY_FILE"; then
echo " ✓ Signed successfully"
((SIGNED_COUNT++))
else
echo " ✗ Failed to sign"
exit 1
fi
fi
fi
done
echo "Signed $SIGNED_COUNT archive(s)"
# Clean up private key file
rm -f "$PRIVATE_KEY_FILE"
- name: Verify signatures
env:
ZIPSIGN_PRIVATE_KEY: ${{ secrets.ZIPSIGN_PRIVATE_KEY }}
run: |
# Save private key to temporary file
PRIVATE_KEY_FILE=$(mktemp)
echo "$ZIPSIGN_PRIVATE_KEY" > "$PRIVATE_KEY_FILE"
chmod 600 "$PRIVATE_KEY_FILE"
# Verify each signed archive
FAILED_COUNT=0
for archive in artifacts/*.{tar.gz,tar.zst}; do
if [ -f "$archive" ]; then
echo "Verifying: $(basename "$archive")"
if zipsign verify tar "$archive" "$PRIVATE_KEY_FILE"; then
echo " ✓ Signature valid"
else
echo " ✗ Signature invalid"
((FAILED_COUNT++))
fi
fi
done
# Clean up private key file
rm -f "$PRIVATE_KEY_FILE"
if [ $FAILED_COUNT -gt 0 ]; then
echo "Error: $FAILED_COUNT signature(s) failed verification"
exit 1
fi
echo "All signatures verified successfully"
- name: Upload signed artifacts to release
env:
GH_TOKEN: ${{ github.token }}
run: |
# Upload each signed artifact back to the release
# Note: gh release upload will replace existing assets with the same name
for archive in artifacts/*.{tar.gz,tar.zst}; do
if [ -f "$archive" ]; then
echo "Uploading: $(basename "$archive")"
gh release upload "$VERSION" "$archive" --clobber
fi
done
echo "All signed artifacts uploaded"
- name: Generate signature report
if: always()
run: |
echo "# Signature Verification Report" > signature-report.md
echo "" >> signature-report.md
echo "**Release**: ${{ github.ref_name }}" >> signature-report.md
echo "**Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> signature-report.md
echo "**Workflow Run**: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> signature-report.md
echo "" >> signature-report.md
echo "## Signed Artifacts" >> signature-report.md
echo "" >> signature-report.md
echo '```' >> signature-report.md
ls -lh artifacts/
echo '```' >> signature-report.md
cat signature-report.md
- name: Upload signature report
if: always()
uses: actions/upload-artifact@v4
with:
name: signature-report
path: signature-report.md
retention-days: 90
summary:
name: Generate Summary
runs-on: ubuntu-latest
needs: sign
if: always()
steps:
- name: Download signature report
uses: actions/download-artifact@v4
with:
name: signature-report
path: .
- name: Add job summary
run: |
if [ -f signature-report.md ]; then
cat signature-report.md >> $GITHUB_STEP_SUMMARY
fi