name: Desktop Release Candidate on: workflow_dispatch: inputs: artifact_base_url: description: "Versioned HTTPS prefix for immutable desktop artifacts, for example https://ctms.example.com/desktop-updates/stable/v0.1.0/" required: true type: string push: tags: - "v*" permissions: contents: read jobs: macos-release-candidate: name: Signed macOS release candidate runs-on: macos-latest defaults: run: working-directory: frontend env: VITE_BUILD_CHANNEL: release VITE_BUILD_COMMIT: ${{ github.sha }} RELEASE_BUILD: "true" REQUIRE_DESKTOP_SIGNING: "true" DESKTOP_UPDATE_BASE_URL: ${{ github.event_name == 'workflow_dispatch' && inputs.artifact_base_url || vars.DESKTOP_UPDATE_BASE_URL }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} steps: - name: Checkout release source uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: "22.13" cache: npm cache-dependency-path: frontend/package-lock.json - name: Enforce release tag context shell: bash run: | if [[ "${GITHUB_REF_TYPE}" != "tag" ]]; then echo "Signed desktop release candidates must run from a vX.Y.Z tag." exit 1 fi expected_tag="v$(node -p "require('./package.json').version")" if [[ "${GITHUB_REF_NAME}" != "${expected_tag}" ]]; then echo "Release tag ${GITHUB_REF_NAME} does not match package version ${expected_tag}." exit 1 fi - name: Setup Rust uses: dtolnay/rust-toolchain@stable with: targets: aarch64-apple-darwin,x86_64-apple-darwin - name: Install dependencies run: npm ci - name: Check release build metadata and signing environment run: npm run release:env:check - name: Check signed desktop release readiness run: npm run desktop:release-readiness:check - name: Check synchronized client version run: npm run version:check - name: Check runtime boundary run: npm run runtime:check - name: Check desktop release and security gate run: npm run desktop:release:check - name: Check UI contract run: npm run ui:contract - name: Type check run: npm run type-check - name: Unit tests run: npm run test:unit - name: Build Web artifact run: npm run build - name: Build signed Universal macOS artifacts run: npm run desktop:build:macos-release -- --ci - name: Create desktop update feed shell: bash run: | if [[ -z "${DESKTOP_UPDATE_BASE_URL}" ]]; then echo "DESKTOP_UPDATE_BASE_URL or workflow input artifact_base_url is required." exit 1 fi artifact="$(find src-tauri/target -path '*/release/bundle/macos/*.app.tar.gz' -print -quit)" if [[ -z "${artifact}" ]]; then echo "No macOS updater artifact was produced." exit 1 fi include_args=() dmg="$(find src-tauri/target -path '*/release/bundle/dmg/*.dmg' -print -quit)" if [[ -n "${dmg}" ]]; then include_args+=(--include "${dmg}") fi npm run desktop:update-feed:create -- \ --artifact "${artifact}" \ "${include_args[@]}" \ --output-dir src-tauri/target/desktop-release-feed \ --base-url "${DESKTOP_UPDATE_BASE_URL}" - name: Verify desktop update feed run: npm run desktop:update-feed:check -- --feed src-tauri/target/desktop-release-feed/latest.json --artifacts-dir src-tauri/target/desktop-release-feed --base-url "${DESKTOP_UPDATE_BASE_URL}" - name: Upload verified desktop release directory uses: actions/upload-artifact@v4 with: name: ctms-desktop-release-${{ github.ref_name }} path: frontend/src-tauri/target/desktop-release-feed/* if-no-files-found: error