name: Release
on:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
release-please:
name: Release Please
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- id: release
uses: googleapis/release-please-action@v4
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
publish-crate:
name: Publish wattop to crates.io
needs: release-please
if: needs.release-please.outputs.release_created
runs-on: macos-latest
environment: release
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Get crates.io token via trusted publishing
id: auth
uses: rust-lang/crates-io-auth-action@v1.0.6
- name: Publish
run: cargo publish -p wattop
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
app-bundle:
name: Build macOS app
needs: release-please
if: needs.release-please.outputs.release_created
runs-on: macos-latest
environment: release
permissions:
contents: write
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.release-please.outputs.tag_name }}
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Install tauri-cli
run: cargo install tauri-cli --version "^2" --locked
- name: Bundle the CLI as an app sidecar
run: |
cargo build --release -p wattop
mkdir -p app/binaries
cp target/release/wattop app/binaries/wattop-aarch64-apple-darwin
- name: Check for signing secrets
id: signing
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
run: |
set -euo pipefail
present=0
missing=""
for name in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY \
APPLE_API_ISSUER APPLE_API_KEY APPLE_API_KEY_P8; do
if [ -n "${!name}" ]; then
present=$((present + 1))
else
missing="$missing $name"
fi
done
if [ "$present" -eq 0 ]; then
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "No signing secrets. Building ad-hoc signed app."
elif [ -z "$missing" ]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
echo "Signing secrets found. Building Developer ID app."
else
echo "Missing signing secrets:$missing" >&2
exit 1
fi
- name: Export signing settings
if: steps.signing.outputs.enabled == 'true'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
run: |
set -euo pipefail
printf '%s' "$APPLE_API_KEY_P8" > "$RUNNER_TEMP/AuthKey.p8"
chmod 600 "$RUNNER_TEMP/AuthKey.p8"
{
printf 'APPLE_CERTIFICATE=%s\n' "$APPLE_CERTIFICATE"
printf 'APPLE_CERTIFICATE_PASSWORD=%s\n' "$APPLE_CERTIFICATE_PASSWORD"
printf 'APPLE_SIGNING_IDENTITY=%s\n' "$APPLE_SIGNING_IDENTITY"
printf 'APPLE_API_ISSUER=%s\n' "$APPLE_API_ISSUER"
printf 'APPLE_API_KEY=%s\n' "$APPLE_API_KEY"
printf 'APPLE_API_KEY_PATH=%s\n' "$RUNNER_TEMP/AuthKey.p8"
} >> "$GITHUB_ENV"
- name: Build app
working-directory: app
run: cargo tauri build --bundles app
- name: Verify the app signature
run: codesign --verify --deep --strict --verbose=2 target/release/bundle/macos/Wattop.app
- name: Verify notarization and stapling of the app
if: steps.signing.outputs.enabled == 'true'
run: |
set -euo pipefail
APP=target/release/bundle/macos/Wattop.app
info=$(codesign -dvv "$APP" 2>&1)
grep -q '^Timestamp=' <<<"$info"
xcrun stapler validate "$APP"
out=$(spctl -a -vv -t exec "$APP" 2>&1)
echo "$out"
grep -q 'accepted' <<<"$out"
grep -q 'source=Notarized Developer ID' <<<"$out"
- name: Install dmgbuild
run: |
python3 -m venv "$RUNNER_TEMP/dmgvenv"
"$RUNNER_TEMP/dmgvenv/bin/pip" install dmgbuild==1.6.5
- name: Package the app zip and the DMG
run: |
APP=$(ls -d target/release/bundle/macos/*.app)
ditto -c -k --keepParent "$APP" "Wattop-$TAG-aarch64.app.zip"
"$RUNNER_TEMP/dmgvenv/bin/python" app/dmg/build_dmg.py -s app/dmg/settings.py -D app="$APP" "Wattop" "Wattop-$TAG-aarch64.dmg"
- name: Import Developer ID certificate for the DMG
if: steps.signing.outputs.enabled == 'true'
run: |
set -euo pipefail
KC="$RUNNER_TEMP/dmg-signing.keychain-db"
KC_PASS=$(openssl rand -hex 24)
printf '%s' "$APPLE_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/cert.p12"
security create-keychain -p "$KC_PASS" "$KC"
security set-keychain-settings -lut 21600 "$KC"
security unlock-keychain -p "$KC_PASS" "$KC"
security import "$RUNNER_TEMP/cert.p12" -k "$KC" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -f pkcs12
security set-key-partition-list -S apple-tool:,apple: -s -k "$KC_PASS" "$KC" > /dev/null
rm -f "$RUNNER_TEMP/cert.p12"
echo "DMG_KEYCHAIN=$KC" >> "$GITHUB_ENV"
- name: Sign, notarize and staple the DMG
if: steps.signing.outputs.enabled == 'true'
run: |
set -euo pipefail
DMG="Wattop-$TAG-aarch64.dmg"
codesign --sign "$APPLE_SIGNING_IDENTITY" --timestamp --keychain "$DMG_KEYCHAIN" "$DMG"
xcrun notarytool submit "$DMG" --key "$RUNNER_TEMP/AuthKey.p8" --key-id "$APPLE_API_KEY" --issuer "$APPLE_API_ISSUER" --wait
xcrun stapler staple "$DMG"
- name: Verify notarization and stapling of the DMG
if: steps.signing.outputs.enabled == 'true'
run: |
set -euo pipefail
DMG="Wattop-$TAG-aarch64.dmg"
xcrun stapler validate "$DMG"
out=$(spctl -a -vv -t open --context context:primary-signature "$DMG" 2>&1)
echo "$out"
grep -q 'accepted' <<<"$out"
grep -q 'source=Notarized Developer ID' <<<"$out"
- name: Upload to GitHub release
run: gh release upload "$TAG" "Wattop-$TAG-aarch64.app.zip" "Wattop-$TAG-aarch64.dmg" --clobber
- name: Remove signing material
if: always() && steps.signing.outputs.enabled == 'true'
run: |
security delete-keychain "$RUNNER_TEMP/dmg-signing.keychain-db" || true
rm -f "$RUNNER_TEMP/AuthKey.p8" "$RUNNER_TEMP/cert.p12"