Desktop Signed Preview Candidates #34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Desktop Signed Preview Candidates | |
| on: | |
| workflow_dispatch: | |
| push: | |
| tags: | |
| - "desktop-v*" | |
| concurrency: | |
| group: desktop-signed-preview-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| actions: read | |
| contents: read | |
| jobs: | |
| validate: | |
| name: Validate desktop candidate source | |
| runs-on: ubuntu-latest | |
| outputs: | |
| candidate_artifacts: ${{ steps.resolve-candidate.outputs.candidate_artifacts }} | |
| candidate_run_id: ${{ steps.resolve-candidate.outputs.candidate_run_id }} | |
| candidate_run_attempt: ${{ steps.resolve-candidate.outputs.candidate_run_attempt }} | |
| steps: | |
| - name: Checkout immutable source | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 | |
| with: | |
| node-version-file: .node-version | |
| cache: npm | |
| - name: Verify Node toolchain | |
| run: node scripts/check-node-version.mjs | |
| - name: Require main or a matching desktop release tag | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version="$(node -p 'require("./desktop/package.json").version')" | |
| if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then | |
| [[ "$GITHUB_REF" == refs/tags/desktop-v* ]] || { | |
| echo "Desktop release publication requires a desktop-v* tag push" >&2 | |
| exit 1 | |
| } | |
| [[ "$GITHUB_REF_NAME" == "desktop-v${version}" ]] || { | |
| echo "Tag $GITHUB_REF_NAME does not match desktop-v${version}" >&2 | |
| exit 1 | |
| } | |
| git merge-base --is-ancestor "$GITHUB_SHA" refs/remotes/origin/main || { | |
| echo "Desktop release tag must point to a commit on origin/main" >&2 | |
| exit 1 | |
| } | |
| else | |
| [[ "$GITHUB_REF" == "refs/heads/main" ]] || { | |
| echo "Manual signed candidates may only be built from main" >&2 | |
| exit 1 | |
| } | |
| fi | |
| - name: Resolve the pinned manual candidate run | |
| id: resolve-candidate | |
| if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/desktop-v') }} | |
| shell: bash | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| RELAYER_DESKTOP_VERSION: ${{ github.ref_name }} | |
| run: | | |
| set -euo pipefail | |
| export RELAYER_DESKTOP_VERSION="${RELAYER_DESKTOP_VERSION#desktop-v}" | |
| export RELAYER_DESKTOP_TAG_OBJECT_TYPE="$(git cat-file -t "$GITHUB_REF")" | |
| export RELAYER_DESKTOP_TAG_MESSAGE="$(git for-each-ref --format='%(contents)' "$GITHUB_REF")" | |
| node desktop/release/preview-candidate-run.mjs | |
| - name: Install locked dependencies | |
| if: ${{ github.event_name == 'workflow_dispatch' }} | |
| run: npm ci | |
| - name: Run repository checks | |
| if: ${{ github.event_name == 'workflow_dispatch' }} | |
| run: npm run check | |
| package-macos: | |
| name: Sign and notarize ${{ matrix.target }} Preview | |
| needs: validate | |
| if: ${{ github.event_name == 'workflow_dispatch' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Apple Silicon is the only signed Preview target. Intel macOS is | |
| # disabled alongside the blocked Windows job; restore this entry and | |
| # the macos-x64 publisher target together to resume Intel Previews. | |
| - target: macos-arm64 | |
| runner: macos-15 | |
| update_base_url: https://updates.relayerlabs.ai/desktop/macos/arm64 | |
| runs-on: ${{ matrix.runner }} | |
| environment: | |
| name: desktop-production | |
| steps: | |
| - name: Checkout immutable source | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 | |
| with: | |
| node-version-file: .node-version | |
| cache: npm | |
| - name: Verify Node toolchain | |
| run: node scripts/check-node-version.mjs | |
| - name: Install locked dependencies | |
| run: npm ci | |
| - name: Build signed and notarized Preview candidate | |
| shell: bash | |
| env: | |
| RELAYER_DESKTOP_TARGET: ${{ matrix.target }} | |
| RELAYER_DESKTOP_SIGN_IDENTITY: ${{ secrets.RELAYER_DESKTOP_SIGN_IDENTITY }} | |
| RELAYER_DESKTOP_APPLE_API_KEY_P8: ${{ secrets.RELAYER_DESKTOP_APPLE_API_KEY }} | |
| RELAYER_DESKTOP_CANDIDATE_RUN_ID: ${{ github.run_id }} | |
| RELAYER_DESKTOP_CANDIDATE_RUN_ATTEMPT: ${{ github.run_attempt }} | |
| APPLE_API_KEY_ID: ${{ secrets.RELAYER_DESKTOP_APPLE_API_KEY_ID }} | |
| APPLE_API_ISSUER: ${{ secrets.RELAYER_DESKTOP_APPLE_API_ISSUER }} | |
| CSC_LINK: ${{ secrets.RELAYER_DESKTOP_CSC_LINK }} | |
| CSC_KEY_PASSWORD: ${{ secrets.RELAYER_DESKTOP_CSC_KEY_PASSWORD }} | |
| RELAYER_DESKTOP_SOURCE_COMMIT: ${{ github.sha }} | |
| RELAYER_DESKTOP_UPDATE_BASE_URL: ${{ matrix.update_base_url }} | |
| run: | | |
| set -euo pipefail | |
| required=( | |
| RELAYER_DESKTOP_SIGN_IDENTITY | |
| RELAYER_DESKTOP_APPLE_API_KEY_P8 | |
| APPLE_API_KEY_ID | |
| APPLE_API_ISSUER | |
| CSC_LINK | |
| CSC_KEY_PASSWORD | |
| ) | |
| for name in "${required[@]}"; do | |
| [[ -n "${!name:-}" ]] || { echo "Missing required desktop release secret: $name" >&2; exit 1; } | |
| done | |
| api_key_path="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8" | |
| cleanup() { rm -f "$api_key_path"; } | |
| trap cleanup EXIT | |
| umask 077 | |
| printf '%s' "$RELAYER_DESKTOP_APPLE_API_KEY_P8" > "$api_key_path" | |
| unset RELAYER_DESKTOP_APPLE_API_KEY_P8 | |
| export APPLE_API_KEY="$api_key_path" | |
| npm run desktop:dist:preview | |
| - name: Install pinned telemetry upload client without credentials | |
| run: npm install --no-save --package-lock=false @sentry/cli@3.7.0 | |
| - name: Upload version-matched telemetry artifacts | |
| shell: bash | |
| env: | |
| RELAYER_DESKTOP_RELEASE: "1" | |
| RELAYER_DESKTOP_CHANNEL: preview | |
| RELAYER_DESKTOP_TARGET: ${{ matrix.target }} | |
| RELAYER_DESKTOP_SOURCE_COMMIT: ${{ github.sha }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_CLI_BINARY: ${{ github.workspace }}/node_modules/.bin/sentry-cli | |
| SENTRY_ORG: relayer-labs-llc | |
| SENTRY_PROJECT: graphcomplete-desktop | |
| run: node desktop/release/telemetry-artifacts.mjs | |
| - name: Upload sealed candidate artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: relayer-desktop-preview-${{ matrix.target }}-${{ github.sha }} | |
| if-no-files-found: error | |
| retention-days: 90 | |
| path: | | |
| desktop/dist/Relayer-*.dmg | |
| desktop/dist/Relayer-*.zip | |
| desktop/dist/Relayer-*.blockmap | |
| desktop/dist/beta-mac.yml | |
| desktop/dist/Relayer-*-SHA256SUMS.txt | |
| desktop/dist/Relayer-*-RELEASE.json | |
| desktop/dist/telemetry/** | |
| package-windows: | |
| # Enable candidate execution after the exact publisher is configured. Add | |
| # Windows publication only after Azure signing succeeds. Stable additionally | |
| # requires the interactive AVD updater canary. | |
| if: ${{ false }} | |
| name: Windows x64 Preview (blocked) | |
| needs: validate | |
| runs-on: windows-2025 | |
| environment: | |
| name: desktop-production-windows | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - name: Checkout immutable source | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 | |
| with: | |
| node-version-file: .node-version | |
| cache: npm | |
| - name: Verify Node toolchain | |
| run: node scripts/check-node-version.mjs | |
| - name: Install locked dependencies | |
| run: npm ci | |
| - name: Require Azure Artifact Signing configuration | |
| shell: pwsh | |
| env: | |
| AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} | |
| AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} | |
| AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} | |
| RELAYER_WINDOWS_CERTIFICATE_PROFILE: ${{ vars.RELAYER_WINDOWS_CERTIFICATE_PROFILE }} | |
| RELAYER_WINDOWS_PUBLISHER_NAME: ${{ vars.RELAYER_WINDOWS_PUBLISHER_NAME }} | |
| run: | | |
| $required = @( | |
| "AZURE_CLIENT_ID", | |
| "AZURE_TENANT_ID", | |
| "AZURE_SUBSCRIPTION_ID", | |
| "RELAYER_WINDOWS_CERTIFICATE_PROFILE", | |
| "RELAYER_WINDOWS_PUBLISHER_NAME" | |
| ) | |
| foreach ($name in $required) { | |
| if ([string]::IsNullOrWhiteSpace([Environment]::GetEnvironmentVariable($name))) { | |
| throw "Missing required Windows signing variable: $name" | |
| } | |
| } | |
| - name: Exchange GitHub OIDC identity for Azure access | |
| uses: azure/login@f5d393ae46f8fde4be8b75f32e3fc50e654ad0ca | |
| with: | |
| client-id: ${{ vars.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ vars.AZURE_TENANT_ID }} | |
| subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} | |
| - name: Build Artifact Signing signed Preview candidate | |
| shell: pwsh | |
| env: | |
| # azure/login establishes the short-lived Azure CLI session. Keep the | |
| # service-principal identity explicit for Artifact Signing's | |
| # DefaultAzureCredential chain; no client secret is used. | |
| AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} | |
| AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} | |
| AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} | |
| RELAYER_DESKTOP_TARGET: windows-x64 | |
| RELAYER_DESKTOP_SOURCE_COMMIT: ${{ github.sha }} | |
| RELAYER_DESKTOP_CANDIDATE_RUN_ID: ${{ github.run_id }} | |
| RELAYER_DESKTOP_CANDIDATE_RUN_ATTEMPT: ${{ github.run_attempt }} | |
| RELAYER_DESKTOP_UPDATE_BASE_URL: https://updates.relayerlabs.ai/desktop/windows/x64 | |
| RELAYER_WINDOWS_SIGNING_ENDPOINT: https://eus.codesigning.azure.net/ | |
| RELAYER_WINDOWS_SIGNING_ACCOUNT: relayercodesigning | |
| RELAYER_WINDOWS_CERTIFICATE_PROFILE: ${{ vars.RELAYER_WINDOWS_CERTIFICATE_PROFILE }} | |
| RELAYER_WINDOWS_PUBLISHER_NAME: ${{ vars.RELAYER_WINDOWS_PUBLISHER_NAME }} | |
| run: npm run desktop:dist:preview | |
| - name: Install pinned telemetry upload client without credentials | |
| run: npm install --no-save --package-lock=false @sentry/cli@3.7.0 | |
| - name: Upload version-matched telemetry artifacts | |
| shell: pwsh | |
| env: | |
| RELAYER_DESKTOP_RELEASE: "1" | |
| RELAYER_DESKTOP_CHANNEL: preview | |
| RELAYER_DESKTOP_TARGET: windows-x64 | |
| RELAYER_DESKTOP_SOURCE_COMMIT: ${{ github.sha }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_CLI_BINARY: ${{ github.workspace }}\node_modules\.bin\sentry-cli.cmd | |
| SENTRY_ORG: relayer-labs-llc | |
| SENTRY_PROJECT: graphcomplete-desktop | |
| run: node desktop/release/telemetry-artifacts.mjs | |
| - name: Upload sealed candidate artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: relayer-desktop-preview-windows-x64-${{ github.sha }} | |
| if-no-files-found: error | |
| retention-days: 90 | |
| path: | | |
| desktop/dist/Relayer-*.exe | |
| desktop/dist/Relayer-*.exe.blockmap | |
| desktop/dist/beta.yml | |
| desktop/dist/Relayer-*-SHA256SUMS.txt | |
| desktop/dist/Relayer-*-RELEASE.json | |
| desktop/dist/telemetry/** | |
| publish-preview-macos: | |
| name: Publish ${{ matrix.target }} Preview update | |
| # A tag publishes only the manual candidate run pinned by its annotation. | |
| # The skipped tag packaging job remains a declared dependency so a future | |
| # target matrix cannot accidentally rebuild before publication. | |
| if: ${{ always() && github.event_name == 'push' && needs.validate.result == 'success' && startsWith(github.ref, 'refs/tags/desktop-v') }} | |
| runs-on: ubuntu-latest | |
| needs: [validate, package-macos] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: [macos-arm64] | |
| environment: | |
| name: desktop-update-preview | |
| permissions: | |
| actions: read | |
| contents: read | |
| id-token: write | |
| steps: | |
| - name: Checkout immutable source | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 | |
| with: | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 | |
| with: | |
| node-version-file: .node-version | |
| cache: npm | |
| - name: Verify Node toolchain | |
| run: node scripts/check-node-version.mjs | |
| - name: Install publication dependencies only | |
| run: npm ci --ignore-scripts | |
| - name: Download sealed candidate | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| with: | |
| artifact-ids: ${{ fromJSON(needs.validate.outputs.candidate_artifacts)[matrix.target].id }} | |
| path: desktop/dist | |
| merge-multiple: true | |
| run-id: ${{ needs.validate.outputs.candidate_run_id }} | |
| github-token: ${{ github.token }} | |
| - name: Assume Preview publication role | |
| uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a | |
| with: | |
| role-to-assume: ${{ vars.DESKTOP_UPDATE_PREVIEW_ROLE_ARN }} | |
| aws-region: us-east-1 | |
| - name: Publish immutable artifacts and move Preview pointer | |
| env: | |
| DESKTOP_UPDATE_BUCKET: ${{ vars.DESKTOP_UPDATE_BUCKET }} | |
| RELAYER_DESKTOP_CANDIDATE_ARTIFACT_DIGEST: ${{ fromJSON(needs.validate.outputs.candidate_artifacts)[matrix.target].digest }} | |
| RELAYER_DESKTOP_CANDIDATE_ARTIFACT_ID: ${{ fromJSON(needs.validate.outputs.candidate_artifacts)[matrix.target].id }} | |
| RELAYER_DESKTOP_CANDIDATE_RUN_ID: ${{ needs.validate.outputs.candidate_run_id }} | |
| RELAYER_DESKTOP_CANDIDATE_RUN_ATTEMPT: ${{ needs.validate.outputs.candidate_run_attempt }} | |
| RELAYER_DESKTOP_TARGET: ${{ matrix.target }} | |
| run: npm run desktop:release:publish-preview -- --bucket "$DESKTOP_UPDATE_BUCKET" | |
| - name: Preserve publication receipt | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: relayer-desktop-preview-publication-${{ matrix.target }}-${{ github.sha }} | |
| if-no-files-found: error | |
| retention-days: 90 | |
| path: desktop/dist/preview-publication-${{ matrix.target }}-*.json |