diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 305daf77..95b8aea3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: name: Typecheck & test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: bun-version: latest diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f3d379d8..5e73b63b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,7 +29,7 @@ jobs: name: Build VitePress site runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: # Release events check out the release tag by default. The generated changelog is # committed to the default branch, so always build and update main. @@ -38,7 +38,7 @@ jobs: - uses: oven-sh/setup-bun@v2 with: bun-version: latest - - uses: actions/configure-pages@v5 + - uses: actions/configure-pages@v6 - run: bun install --frozen-lockfile - name: Refresh changelog env: @@ -67,7 +67,7 @@ jobs: # browser. Absent -> analytics stays a silent no-op. Same var name as # the adc.co site, so one org value can feed both. PUBLIC_POSTHOG_PROJECT_TOKEN: ${{ vars.PUBLIC_POSTHOG_PROJECT_TOKEN }} - - uses: actions/upload-pages-artifact@v3 + - uses: actions/upload-pages-artifact@v5 with: path: docs/.vitepress/dist @@ -80,4 +80,4 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 74e47dc6..fb8928ed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,27 +1,32 @@ name: Release -# Builds and (when the signing secrets are configured) signs the distributable artifacts: +# Builds and signs the distributable artifacts: # - npm: per-OS prebuilt binaries published under @agentdeploymentco/argus* # - macOS: a signed + notarized Argus.app / .dmg -# - Windows: NSIS installers (.exe) for x64 and ARM64 (Authenticode signing not wired up yet) +# - Windows: standalone CLI ZIPs plus Azure Artifact Signing-signed NSIS installers (.exe) # # Two modes, decided by what triggered the run: # - Tag push: build everything, upload to a DRAFT GitHub Release, and publish it only after # every platform leg has succeeded (the publish-release job below). # - workflow_dispatch (any branch): build-only dry run — no tag, no release; the bundles are # uploaded as workflow artifacts so they can be downloaded and tested on real machines. -# All signing is gated on secrets being present, so this workflow runs end-to-end (producing -# UNSIGNED artifacts) before any certificate exists, and starts signing the moment the secrets are -# added. No secret value ever lives in this file — only references to `secrets.*`. +# Windows signing uses GitHub Actions OIDC with Azure Artifact Signing. The Windows jobs fail +# clearly if the Azure identity or signing configuration is missing, so a tagged release cannot +# silently publish unsigned installers. No secret value ever lives in this file — only references +# to `secrets.*`. # -# Required secrets to activate signing (set in repo/org settings; never commit their values): +# Required secrets (set in repo/org settings; never commit their values): # NPM_TOKEN npm automation token (npm publish) # APPLE_CERTIFICATE base64 of the Developer ID Application .p12 # APPLE_CERTIFICATE_PASSWORD its export password # APPLE_SIGNING_IDENTITY e.g. "Developer ID Application: … (TEAMID)" # APPLE_ID / APPLE_PASSWORD / APPLE_TEAM_ID notarytool credentials (app-specific password) -# WINDOWS_CERTIFICATE / WINDOWS_CERTIFICATE_PASSWORD Authenticode signing cert (base64 + pwd; -# reserved — the Windows jobs don't sign yet) +# AZURE_CLIENT_ID / AZURE_TENANT_ID / AZURE_SUBSCRIPTION_ID +# GitHub OIDC identity used by Azure Artifact Signing +# Required repository or organization secrets: +# AZURE_ARTIFACT_SIGNING_ENDPOINT Region-specific codesigning.azure.net endpoint +# AZURE_ARTIFACT_SIGNING_ACCOUNT Artifact Signing account name +# AZURE_ARTIFACT_SIGNING_PROFILE Certificate profile name # TAURI_SIGNING_PRIVATE_KEY / TAURI_SIGNING_PRIVATE_KEY_PASSWORD # updater signing key (separate from OS certs) @@ -51,6 +56,7 @@ jobs: # Windows x64, built natively on the x64 runner. - name: Windows x64 slug: windows-x64 + archive_arch: amd64 platform: "windows-latest" rust-targets: "" stage-args: "--build" @@ -60,12 +66,14 @@ jobs: # `bun build --compile --target=bun-windows-arm64` (stage-desktop-sidecar.sh --target). - name: Windows ARM64 slug: windows-arm64 + archive_arch: arm64 platform: "windows-latest" rust-targets: "aarch64-pc-windows-msvc" stage-args: "--target aarch64-pc-windows-msvc" args: "--target aarch64-pc-windows-msvc --bundles nsis" name: ${{ matrix.name }} runs-on: ${{ matrix.platform }} + environment: production env: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} @@ -75,7 +83,7 @@ jobs: # cross-volume moves fail when `bun build --compile --target=bun-windows-arm64` populates it. BUN_INSTALL_CACHE_DIR: ${{ github.workspace }}/.bun-cache steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: bun-version: latest @@ -97,6 +105,30 @@ jobs: - name: Stage sidecar + web run: bash scripts/stage-desktop-sidecar.sh ${{ matrix.stage-args }} + - name: Azure login for Artifact Signing + if: runner.os == 'Windows' + uses: azure/login@v3 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + # Sign the CLI sidecar before Tauri embeds it in the installed application. The ARM64 + # sidecar is signed on this x64 Windows runner because Artifact Signing does not support + # Windows ARM runners. + - name: Sign Windows sidecar + if: runner.os == 'Windows' + uses: azure/artifact-signing-action@v2 + with: + endpoint: ${{ secrets.AZURE_ARTIFACT_SIGNING_ENDPOINT }} + signing-account-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_ACCOUNT }} + certificate-profile-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_PROFILE }} + files-folder: ${{ github.workspace }}\desktop\src-tauri\binaries + files-folder-filter: exe + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + - name: import Apple Developer Certificate if: runner.os == 'macOS' # Prevents keychain from locking automatically for 3600 seconds. @@ -122,7 +154,7 @@ jobs: echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV echo "Certificate imported." - - name: Build (and sign, if secrets present) the desktop app + - name: Build the desktop app uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -183,18 +215,164 @@ jobs: exit 1 fi - # Dry-run mode only: expose the bundles on the workflow run so they can be - # downloaded and installed on real machines before any tag exists. - - name: Upload bundles as workflow artifacts - if: ${{ !startsWith(github.ref, 'refs/tags/') }} - uses: actions/upload-artifact@v4 + - name: Locate Windows installers + if: runner.os == 'Windows' + id: windows_installers + shell: pwsh + run: | + $installers = @(Get-ChildItem -Path "$env:GITHUB_WORKSPACE\desktop\src-tauri\target" -Filter "*-setup.exe" -Recurse | + Select-Object -ExpandProperty FullName + ) + if ($installers.Count -eq 0) { + throw "No Windows installer was found to sign." + } + "files<&2 + exit 1 + fi + dmg="$(find desktop/src-tauri/target -path '*/bundle/dmg/*.dmg' -type f -print -quit)" + cp "$dmg" "$destination" + echo "Staged $destination" + + # Dry-run mode only: expose the desktop installers on the workflow run so they can be + # downloaded and tested on real machines before any tag exists. Standalone Windows CLI + # ZIPs are published only on tagged releases, not as workflow artifacts. + - name: Upload macOS DMG as workflow artifact + if: ${{ !startsWith(github.ref, 'refs/tags/') && runner.os == 'macOS' }} + uses: actions/upload-artifact@v7 + with: + if-no-files-found: error + path: release-artifacts/argus-macos-universal.dmg + archive: false + + - name: Upload Windows installer as workflow artifact + if: ${{ !startsWith(github.ref, 'refs/tags/') && runner.os == 'Windows' }} + uses: actions/upload-artifact@v7 with: - name: argus-${{ matrix.slug }} if-no-files-found: error - path: | - desktop/src-tauri/target/**/bundle/macos/*.app.tar.gz* - desktop/src-tauri/target/**/bundle/dmg/*.dmg - desktop/src-tauri/target/**/bundle/nsis/*-setup.exe* + path: release-artifacts/argus-${{ matrix.slug }}-setup.exe + archive: false # Tag pushes create the release as a draft; flip it public only when every platform leg # succeeded, so a failed leg can never leave a half-populated public release (and the @@ -209,7 +387,7 @@ jobs: GH_TOKEN: ${{ github.token }} HAS_UPDATER_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY != '' }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Publish the draft release run: | set -euo pipefail