name: Release on: push: branches: [main] permissions: contents: write pull-requests: write concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false jobs: release-please: name: Release please runs-on: ubuntu-latest outputs: release_created: ${{ steps.rp.outputs.release_created }} tag_name: ${{ steps.rp.outputs.tag_name }} steps: - uses: googleapis/release-please-action@v5 id: rp with: config-file: release-please-config.json manifest-file: .release-please-manifest.json build-windows: name: Build Windows MSI needs: release-please if: ${{ needs.release-please.outputs.release_created == 'true' }} runs-on: windows-latest permissions: contents: write id-token: write # SLSA build-provenance attestation attestations: write steps: - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Install cargo-wix run: cargo install cargo-wix --locked - name: Build release binary run: cargo build --release # Sign the EXE before packaging so the MSI ships the signed binary. - name: Sign inlook.exe shell: pwsh env: WINDOWS_CERT_PFX_BASE64: ${{ secrets.WINDOWS_CERT_PFX_BASE64 }} WINDOWS_CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }} run: ./scripts/sign-windows.ps1 -Path target/release/inlook.exe - name: Build MSI run: cargo wix --no-build --nocapture - name: Sign MSI shell: pwsh env: WINDOWS_CERT_PFX_BASE64: ${{ secrets.WINDOWS_CERT_PFX_BASE64 }} WINDOWS_CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }} run: ./scripts/sign-windows.ps1 -Path (Get-ChildItem target/wix/inlook-*.msi).FullName # --- SignPath Foundation code signing (see packaging/signpath/README.md) --- # Active only when the SIGNPATH_ORGANIZATION_ID variable and # SIGNPATH_API_TOKEN secret are configured; otherwise the release ships # exactly as before. Each request needs manual approval in the SignPath # portal (Foundation requirement), so approve promptly or re-run this job. - name: Detect SignPath configuration id: signpath shell: bash env: SIGNPATH_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }} SIGNPATH_ORG: ${{ vars.SIGNPATH_ORGANIZATION_ID }} run: | if [ -n "$SIGNPATH_TOKEN" ] && [ -n "$SIGNPATH_ORG" ]; then echo "enabled=true" >> "$GITHUB_OUTPUT" else echo "enabled=false" >> "$GITHUB_OUTPUT" echo "SignPath not configured — shipping unsigned." fi - name: Stage unsigned artifacts for SignPath id: stage if: steps.signpath.outputs.enabled == 'true' shell: bash run: | mkdir -p unsigned cp target/release/inlook.exe unsigned/inlook.exe MSI=$(ls target/wix/inlook-*.msi) # Stable name so the SignPath artifact configuration never changes. cp "$MSI" unsigned/inlook.msi echo "msi_name=$(basename "$MSI")" >> "$GITHUB_OUTPUT" - name: Upload unsigned artifact id: unsigned if: steps.signpath.outputs.enabled == 'true' uses: actions/upload-artifact@v7 with: name: windows-unsigned path: unsigned/ - name: Submit SignPath signing request if: steps.signpath.outputs.enabled == 'true' uses: signpath/github-action-submit-signing-request@v2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }} project-slug: inlook signing-policy-slug: release-signing github-artifact-id: ${{ steps.unsigned.outputs.artifact-id }} wait-for-completion: true wait-for-completion-timeout-in-seconds: 3600 output-artifact-directory: signed - name: Replace artifacts with signed versions if: steps.signpath.outputs.enabled == 'true' shell: bash run: | cp signed/inlook.exe target/release/inlook.exe cp signed/inlook.msi "target/wix/${{ steps.stage.outputs.msi_name }}" # Attach the binaries to the release FIRST, so a provenance hiccup can # never block the actual artifacts (a release with no binaries is far # worse than one without attestation). - name: Attach Windows artifacts to GitHub Release uses: softprops/action-gh-release@v3 with: tag_name: ${{ needs.release-please.outputs.tag_name }} files: | target/wix/inlook-*.msi target/release/inlook.exe # SLSA build provenance: cryptographically bind these binaries to this # repo + workflow run. Verify with: gh attestation verify --repo StruisICT/InLook - name: Attest Windows artifacts uses: actions/attest-build-provenance@v4 with: subject-path: | target/wix/inlook-*.msi target/release/inlook.exe build-linux: name: Build Linux .deb + AppImage needs: release-please if: ${{ needs.release-please.outputs.release_created == 'true' }} runs-on: ubuntu-latest permissions: contents: write id-token: write # SLSA build-provenance attestation attestations: write steps: - uses: actions/checkout@v7 - name: Install system deps run: | sudo apt-get update sudo apt-get install -y \ libwebkit2gtk-4.1-dev \ libgtk-3-dev \ libayatana-appindicator3-dev \ librsvg2-dev \ imagemagick - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Install cargo-deb run: cargo install cargo-deb --locked - name: Build release binary run: cargo build --release - name: Build .deb run: cargo deb --no-build - name: Attach .deb to GitHub Release uses: softprops/action-gh-release@v3 with: tag_name: ${{ needs.release-please.outputs.tag_name }} files: target/debian/*.deb - name: Build AppImage run: bash scripts/build-appimage.sh ${{ needs.release-please.outputs.tag_name }} - name: Attach AppImage to GitHub Release uses: softprops/action-gh-release@v3 with: tag_name: ${{ needs.release-please.outputs.tag_name }} files: dist/*.AppImage - name: Attest Linux artifacts uses: actions/attest-build-provenance@v4 with: subject-path: | target/debian/*.deb dist/*.AppImage build-macos: name: Build macOS .dmg (universal) needs: release-please if: ${{ needs.release-please.outputs.release_created == 'true' }} runs-on: macos-latest permissions: contents: write id-token: write # SLSA build-provenance attestation attestations: write steps: - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable with: targets: x86_64-apple-darwin,aarch64-apple-darwin - uses: Swatinem/rust-cache@v2 - name: Build arm64 binary run: cargo build --release --target aarch64-apple-darwin - name: Build x86_64 binary run: cargo build --release --target x86_64-apple-darwin - name: Combine into a universal binary with lipo run: | mkdir -p dist lipo -create -output dist/inlook \ target/aarch64-apple-darwin/release/inlook \ target/x86_64-apple-darwin/release/inlook file dist/inlook # --- Apple Developer ID signing + notarization (see packaging/macos/README.md) --- # Active only when APPLE_CERT_P12_BASE64 (secret) and APPLE_SIGNING_IDENTITY # (variable) are configured; otherwise the .dmg ships unsigned exactly as # before (Gatekeeper then needs a manual bypass — see the README). - name: Detect macOS signing configuration id: macsign env: APPLE_CERT_P12_BASE64: ${{ secrets.APPLE_CERT_P12_BASE64 }} APPLE_SIGNING_IDENTITY: ${{ vars.APPLE_SIGNING_IDENTITY }} run: | if [ -n "$APPLE_CERT_P12_BASE64" ] && [ -n "$APPLE_SIGNING_IDENTITY" ]; then echo "enabled=true" >> "$GITHUB_OUTPUT" else echo "enabled=false" >> "$GITHUB_OUTPUT" echo "Apple signing not configured — shipping the .dmg unsigned." fi - name: Import Developer ID certificate if: steps.macsign.outputs.enabled == 'true' env: APPLE_CERT_P12_BASE64: ${{ secrets.APPLE_CERT_P12_BASE64 }} APPLE_CERT_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }} run: | KEYCHAIN="$RUNNER_TEMP/inlook-signing.keychain-db" KEYCHAIN_PW="$(openssl rand -base64 24)" security create-keychain -p "$KEYCHAIN_PW" "$KEYCHAIN" security set-keychain-settings -lut 21600 "$KEYCHAIN" security unlock-keychain -p "$KEYCHAIN_PW" "$KEYCHAIN" echo "$APPLE_CERT_P12_BASE64" | base64 --decode > "$RUNNER_TEMP/cert.p12" security import "$RUNNER_TEMP/cert.p12" -k "$KEYCHAIN" -P "$APPLE_CERT_PASSWORD" \ -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PW" "$KEYCHAIN" # Make our keychain searchable alongside the login keychain. security list-keychains -d user -s "$KEYCHAIN" \ $(security list-keychains -d user | sed s/\"//g) rm -f "$RUNNER_TEMP/cert.p12" - name: Build .app bundle and .dmg env: APPLE_SIGNING_IDENTITY: ${{ steps.macsign.outputs.enabled == 'true' && vars.APPLE_SIGNING_IDENTITY || '' }} run: bash scripts/build-dmg.sh ${{ needs.release-please.outputs.tag_name }} - name: Notarize and staple the .dmg if: steps.macsign.outputs.enabled == 'true' env: APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }} APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }} run: | echo "$APPLE_API_KEY_P8_BASE64" | base64 --decode > "$RUNNER_TEMP/api_key.p8" # Submit to Apple's notary service and block until it finishes; --wait # fails the step on a rejected notarization. xcrun notarytool submit dist/*.dmg \ --key "$RUNNER_TEMP/api_key.p8" \ --key-id "$APPLE_API_KEY_ID" \ --issuer "$APPLE_API_ISSUER_ID" \ --wait # Staple the ticket so the .dmg validates offline (first launch works # even without a network round-trip to Apple). xcrun stapler staple dist/*.dmg xcrun stapler validate dist/*.dmg rm -f "$RUNNER_TEMP/api_key.p8" # Attach first (see the Windows job) so a provenance hiccup can't block # the .dmg upload. - name: Attach macOS artifacts to GitHub Release uses: softprops/action-gh-release@v3 with: tag_name: ${{ needs.release-please.outputs.tag_name }} files: dist/*.dmg - name: Attest macOS artifacts uses: actions/attest-build-provenance@v4 with: subject-path: dist/*.dmg sbom: name: SBOM (CycloneDX) needs: release-please if: ${{ needs.release-please.outputs.release_created == 'true' }} runs-on: ubuntu-latest permissions: contents: write id-token: write attestations: write steps: - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable - name: Install cargo-cyclonedx run: cargo install cargo-cyclonedx --locked - name: Generate CycloneDX SBOM run: | # --target all resolves every platform's dependencies, so the SBOM # covers the Windows-only crates too, not just the host's graph. cargo cyclonedx --format json --target all --spec-version 1.5 cp inlook.cdx.json "inlook-${{ needs.release-please.outputs.tag_name }}-sbom.cdx.json" - name: Attach SBOM to GitHub Release uses: softprops/action-gh-release@v3 with: tag_name: ${{ needs.release-please.outputs.tag_name }} files: inlook-*-sbom.cdx.json - name: Attest SBOM uses: actions/attest-build-provenance@v4 with: subject-path: inlook-*-sbom.cdx.json