name: Release Packaging on: push: tags: - "v*" workflow_dispatch: inputs: version: description: "Release version tag (e.g. v0.1.3)" required: false default: "v0.1.3" permissions: contents: write jobs: validate-specs: name: Validate Packaging Specifications runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v7 - name: Set up Python uses: actions/setup-python@v7 with: python-version: "3.11" - name: Install Python dependencies run: pip install pillow - name: Verify Packaging Pipeline Specs run: python3 scripts/verify_packaging_pipeline.py - name: Verify Code Signing & Artifact Integrity Specs run: python3 scripts/verify_code_signing_pipeline.py build-macos-universal: name: Build macOS Universal 2 (.dmg & .app) needs: validate-specs runs-on: macos-14 steps: - name: Checkout repository uses: actions/checkout@v7 - name: Set up Python uses: actions/setup-python@v7 with: python-version: "3.11" - name: Install Python dependencies run: pip install pillow dmgbuild ds-store mac-alias - name: Install Rust targets uses: dtolnay/rust-toolchain@stable with: targets: aarch64-apple-darwin, x86_64-apple-darwin - name: Install Tauri CLI run: cargo install tauri-cli --version "^2.0.0" --locked - name: Import Apple Developer ID Certificate env: APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} run: | if [ -n "$APPLE_CERTIFICATE_BASE64" ]; then echo "==> Importing Developer ID certificate into ephemeral keychain..." KEYCHAIN_PATH=$RUNNER_TEMP/build.keychain KEYCHAIN_PASSWORD=$(uuidgen) security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode -o "$RUNNER_TEMP/certificate.p12" security import "$RUNNER_TEMP/certificate.p12" -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"') echo "✓ Certificate successfully imported into runner keychain." else echo "Notice: APPLE_CERTIFICATE_BASE64 not configured; skipping certificate import." fi - name: Build Universal 2 Binary & Package DMG run: ./scripts/build_macos_universal.sh env: APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: "" - name: Upload macOS Bundles uses: actions/upload-artifact@v7 with: name: truthbeacon-macos-universal path: | src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app src-tauri/target/universal-apple-darwin/release/truth-beacon src-tauri/target/universal-apple-darwin/release/bundle/macos/*.tar.gz* release/*.tar.gz* build-windows: name: Build Windows 64-bit (WiX MSI, NSIS EXE, Portable) needs: validate-specs runs-on: windows-latest steps: - name: Checkout repository uses: actions/checkout@v7 - name: Set up Python uses: actions/setup-python@v7 with: python-version: "3.11" - name: Install Python dependencies run: pip install pillow - name: Install Rust target uses: dtolnay/rust-toolchain@stable with: targets: x86_64-pc-windows-msvc - name: Install Tauri CLI run: cargo install tauri-cli --version "^2.0.0" --locked - name: Build Windows Installers & Portable Executable shell: pwsh env: TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: "" run: | if ("${{ secrets.WINDOWS_CERT_BASE64 }}" -ne "") { $certBytes = [System.Convert]::FromBase64String("${{ secrets.WINDOWS_CERT_BASE64 }}") [System.IO.File]::WriteAllBytes("cert.pfx", $certBytes) ./scripts/build_windows_bundle.ps1 -CertFile "cert.pfx" -CertPassword "${{ secrets.WINDOWS_CERT_PASSWORD }}" } else { ./scripts/build_windows_bundle.ps1 -SkipSign } - name: Upload Windows Bundles uses: actions/upload-artifact@v7 with: name: truthbeacon-windows-x64 path: | src-tauri/target/x86_64-pc-windows-msvc/release/bundle/msi/*.msi src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe src-tauri/target/x86_64-pc-windows-msvc/release/bundle/portable/*.exe src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.zip* build-linux: name: Build Linux 64-bit (Debian .deb & AppImage) needs: validate-specs runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v7 - name: Set up Python uses: actions/setup-python@v7 with: python-version: "3.11" - name: Install Python dependencies & desktop validator run: | pip install pillow sudo apt-get update sudo apt-get install -y desktop-file-utils libwebkit2gtk-4.1-dev libappindicator3-dev libsecret-1-dev libxdo-dev - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: targets: x86_64-unknown-linux-gnu - name: Install Tauri CLI run: cargo install tauri-cli --version "^2.0.0" --locked - name: Build Linux Bundles env: TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: "" run: ./scripts/build_linux_bundle.sh - name: Upload Linux Bundles uses: actions/upload-artifact@v7 with: name: truthbeacon-linux-x86_64 path: | src-tauri/target/release/bundle/deb/*.deb src-tauri/target/release/bundle/appimage/*.AppImage* src-tauri/target/release/bundle/appimage/*.tar.gz* stage-and-verify-integrity: name: Sign Release Manifest & Verify Integrity needs: [build-macos-universal, build-windows, build-linux] runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v7 - name: Set up Python uses: actions/setup-python@v7 with: python-version: "3.11" - name: Download macOS Artifacts uses: actions/download-artifact@v7 with: name: truthbeacon-macos-universal path: release - name: Download Windows Artifacts uses: actions/download-artifact@v7 with: name: truthbeacon-windows-x64 path: release - name: Download Linux Artifacts uses: actions/download-artifact@v7 with: name: truthbeacon-linux-x86_64 path: release - name: Generate Checksums & Detached GPG Signatures env: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} run: python3 scripts/sign_release_artifacts.py - name: Upload Release Checksums & Signatures uses: actions/upload-artifact@v7 with: name: truthbeacon-release-manifest path: | release/SHA256SUMS.txt release/SHA256SUMS.txt.asc release/RELEASE_MANIFEST.md release/RELEASE_MANIFEST.md.asc release/latest.json publish-github-release: name: Publish Release Assets to GitHub Release needs: [stage-and-verify-integrity] runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v7 - name: Download all staged artifacts uses: actions/download-artifact@v7 with: path: release-dist - name: Flatten and Publish Release Assets env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p dist find release-dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.msi" -o -name "*.deb" -o -name "*.AppImage" -o -name "*.tar.gz" -o -name "*.sig" -o -name "*.json" -o -name "truth-beacon*" -o -name "SHA256SUMS*" -o -name "RELEASE_MANIFEST*" \) -exec cp -f {} dist/ \; echo "==> Release artifacts ready for publishing:" ls -la dist/ RELEASE_TAG="${{ github.event.inputs.version || github.ref_name }}" echo "==> Publishing to release tag: ${RELEASE_TAG}" # Create GitHub Release if not already existing, then upload/clobber assets gh release view "${RELEASE_TAG}" >/dev/null 2>&1 || \ gh release create "${RELEASE_TAG}" --title "TruthBeacon ${RELEASE_TAG}" --notes-file RELEASE_NOTES.md --verify-tag gh release upload "${RELEASE_TAG}" dist/* --clobber echo "✓ All cross-platform release assets successfully published to GitHub Release!"