name: Build App (macOS) on: workflow_dispatch: inputs: tag: description: 'Release tag to build (e.g. v1.4.5)' required: true jobs: build-macos: name: Build App (macOS) runs-on: macos-latest env: NODE_VERSION: "20" RELEASE_TAG: "${{ inputs.tag }}" steps: - name: Show tag run: | echo "Building for tag: ${RELEASE_TAG}" - uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Install Rust stable run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Install system dependencies run: brew install --quiet create-dmg || true - name: Install npm dependencies run: npm ci - name: Setup code signing env: APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }} run: | if [ -n "${APPLE_API_KEY_CONTENT}" ]; then echo "Setting up notarization API key..." mkdir -p ~/private_keys echo "${APPLE_API_KEY_CONTENT}" > ~/private_keys/AuthKey_${APPLE_API_KEY}.p8 else echo "No signing secrets configured, skipping code signing setup" fi - name: Build Tauri app env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} APPLE_API_KEY_PATH: ~/private_keys/AuthKey_${{ secrets.APPLE_API_KEY }}.p8 run: npm run tauri build - name: Upload to release env: BUILD_TOKEN: ${{ secrets.BUILD_TOKEN }} run: | which jq || brew install jq REPO_API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" TAG="${RELEASE_TAG}" echo "Release tag: ${TAG}" echo "Waiting for release ${TAG} to be available..." RELEASE_ID="" for i in $(seq 1 30); do RELEASE_JSON=$(curl -s -H "Authorization: token ${BUILD_TOKEN}" \ "${REPO_API}/releases/tags/${TAG}") RELEASE_ID=$(echo "$RELEASE_JSON" | jq -r '.id // empty') if [ -n "${RELEASE_ID}" ] && [ "${RELEASE_ID}" != "null" ]; then echo "Found release: ${TAG} (ID: ${RELEASE_ID})" break fi echo "Attempt ${i}/30: Release not ready yet, retrying in 10s..." sleep 10 done if [ -z "${RELEASE_ID}" ] || [ "${RELEASE_ID}" = "null" ]; then echo "ERROR: Failed to find release for tag ${TAG} after 30 attempts." exit 1 fi find src-tauri/target/release/bundle -type f -name "*.dmg" | while IFS= read -r file; do filename=$(basename "$file") encoded_name=$(echo "$filename" | sed 's/ /%20/g') echo "Uploading ${filename} ($(du -h "$file" | cut -f1))..." ASSET_ID=$(curl -s -H "Authorization: token ${BUILD_TOKEN}" \ "${REPO_API}/releases/${RELEASE_ID}/assets" | jq -r ".[] | select(.name == \"${filename}\") | .id // empty") if [ -n "${ASSET_ID}" ]; then curl -s -X DELETE -H "Authorization: token ${BUILD_TOKEN}" \ "${REPO_API}/releases/${RELEASE_ID}/assets/${ASSET_ID}" fi HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ -H "Authorization: token ${BUILD_TOKEN}" \ -H "Content-Type: application/octet-stream" \ -T "$file" \ "${REPO_API}/releases/${RELEASE_ID}/assets?name=${encoded_name}") echo "Upload response: HTTP ${HTTP_CODE}" done - name: Cleanup signing artifacts run: rm -rf ~/private_keys