name: Apple release (macOS DMG) # Builds, signs, notarizes, and uploads the direct-download macOS build: # - a Developer ID–signed, notarized VniDrop-.dmg, # - a Sparkle appcast.xml. # # The central release workflow publishes these artifacts and updates Homebrew. # # The App Store / TestFlight build is NOT produced here — that goes through Xcode # Organizer / App Store Connect. This workflow only covers direct distribution. # # Called by the central tag-release workflow, or run manually to validate the # signed/notarized direct-download artifact. on: workflow_call: workflow_dispatch: permissions: contents: read concurrency: group: apple-release-${{ github.ref }} cancel-in-progress: false defaults: run: shell: bash jobs: build: name: Build & notarize DMG runs-on: macos-latest timeout-minutes: 90 outputs: version: ${{ steps.version.outputs.app }} steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false - name: Verify tag is on master if: github.event_name == 'push' run: | if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/master; then echo "Release tags must point to a commit on master" >&2 exit 1 fi - name: Resolve canonical version id: version run: | packaging/version/resolve-version.sh verify >/dev/null version="$(packaging/version/resolve-version.sh product)" echo "app=$version" >> "$GITHUB_OUTPUT" - name: Select Xcode run: sudo xcode-select -s /Applications/Xcode.app - name: Install Rust toolchain uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # v1 with: toolchain: stable targets: aarch64-apple-darwin - name: Cache Cargo uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.cargo/registry ~/.cargo/git target key: apple-release-cargo-${{ hashFiles('Cargo.lock') }} restore-keys: apple-release-cargo- - name: Install tooling run: brew install xcodegen swiftlint create-dmg - name: Install Bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - name: Download Sparkle tools # generate_appcast + sign_update ship in the Sparkle release tarball. run: | set -euo pipefail ver="2.9.4" curl -fsSL -o /tmp/sparkle.tar.xz \ "https://github.com/sparkle-project/Sparkle/releases/download/${ver}/Sparkle-${ver}.tar.xz" mkdir -p /tmp/sparkle && tar -xJf /tmp/sparkle.tar.xz -C /tmp/sparkle echo "SPARKLE_BIN=/tmp/sparkle/bin" >> "$GITHUB_ENV" - name: Import Developer ID certificate env: CERT_P12_BASE64: ${{ secrets.DEVELOPER_ID_CERT_P12 }} CERT_PASSWORD: ${{ secrets.DEVELOPER_ID_CERT_PASSWORD }} run: | set -euo pipefail keychain="$RUNNER_TEMP/signing.keychain-db" kpw="$(openssl rand -hex 20)" security create-keychain -p "$kpw" "$keychain" security set-keychain-settings -lut 21600 "$keychain" security unlock-keychain -p "$kpw" "$keychain" echo "$CERT_P12_BASE64" | base64 --decode > "$RUNNER_TEMP/cert.p12" security import "$RUNNER_TEMP/cert.p12" -k "$keychain" -P "$CERT_PASSWORD" \ -T /usr/bin/codesign -T /usr/bin/security security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$kpw" "$keychain" # Prepend our keychain so codesign/xcodebuild can find the identity. security list-keychains -d user -s "$keychain" $(security list-keychains -d user | tr -d '"') rm -f "$RUNNER_TEMP/cert.p12" - name: Store notarytool credentials env: NOTARY_KEY_P8: ${{ secrets.NOTARY_API_KEY }} NOTARY_KEY_ID: ${{ secrets.NOTARY_KEY_ID }} NOTARY_ISSUER: ${{ secrets.NOTARY_ISSUER }} run: | set -euo pipefail echo "$NOTARY_KEY_P8" | base64 --decode > "$RUNNER_TEMP/notary.p8" xcrun notarytool store-credentials vnidrop-notary \ --key "$RUNNER_TEMP/notary.p8" \ --key-id "$NOTARY_KEY_ID" \ --issuer "$NOTARY_ISSUER" echo "NOTARY_PROFILE=vnidrop-notary" >> "$GITHUB_ENV" - name: Write Sparkle signing key env: SPARKLE_ED_PRIVATE_KEY: ${{ secrets.SPARKLE_ED_PRIVATE_KEY }} run: | printf '%s' "$SPARKLE_ED_PRIVATE_KEY" > "$RUNNER_TEMP/sparkle_ed_private_key" echo "SPARKLE_ED_KEY_FILE=$RUNNER_TEMP/sparkle_ed_private_key" >> "$GITHUB_ENV" - name: Build, sign & notarize DMG run: apple/scripts/build-dmg.sh - name: Generate appcast env: RELEASE_REPO: ${{ github.repository }} run: apple/scripts/generate-appcast.sh - name: Upload artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: vnidrop-${{ steps.version.outputs.app }}-macos-dmg path: | apple/dist/VniDrop-*.dmg apple/dist/VniDrop-*.build-info.json apple/dist/appcast.xml if-no-files-found: error retention-days: 14