From 7cfedbd49de914801a816eb9843b018bbfbf17b9 Mon Sep 17 00:00:00 2001 From: DarkSky <25152247+darkskygit@users.noreply.github.com> Date: Sat, 4 Jul 2026 03:41:23 +0800 Subject: [PATCH] feat: improve ci (#15194) --- .github/workflows/release-desktop.yml | 377 ++++++++++---------------- .github/workflows/windows-signer.yml | 72 ----- scripts/check-windows-signer.mjs | 95 +------ 3 files changed, 161 insertions(+), 383 deletions(-) delete mode 100644 .github/workflows/windows-signer.yml diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 6b94fd9317..a6a05ee557 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -89,9 +89,13 @@ jobs: id: check run: node ./scripts/check-windows-signer.mjs env: + AFFINE_SIGN_CLIENT_HASH: ${{ secrets.AFFINE_SIGN_CLIENT_HASH }} + AFFINE_SIGNER_ADDR: ${{ secrets.AFFINE_SIGNER_ADDR }} + AFFINE_SIGNER_TOKEN: ${{ secrets.AFFINE_SIGNER_TOKEN }} + AFFINE_SIGNER_TS_AUTH_KEY: ${{ secrets.AFFINE_SIGNER_TS_AUTH_KEY }} BUILD_TYPE: ${{ inputs.build-type }} - GITHUB_TOKEN: ${{ github.token }} REQUIRE_SIGNER: ${{ inputs.require-windows-signing }} + WINDOWS_SIGNER_PUBLIC_CERT_BASE64: ${{ secrets.WINDOWS_SIGNER_PUBLIC_CERT_BASE64 }} make-distribution-macos: if: ${{ inputs.desktop_macos }} @@ -143,262 +147,192 @@ jobs: target: ${{ matrix.spec.target }} install_linux_deps: true - package-distribution-windows-x64: - if: ${{ inputs.desktop_windows }} - needs: before-make - uses: ./.github/workflows/release-desktop-platform.yml - secrets: inherit - with: - build_type: ${{ inputs.build-type }} - app_version: ${{ inputs.app-version }} - git_short_hash: ${{ inputs.git-short-hash }} - runner: windows-latest - platform: win32 - arch: x64 - target: x86_64-pc-windows-msvc - enable_scripts: true - - package-distribution-windows-arm64: - if: ${{ inputs.desktop_windows }} - needs: before-make - uses: ./.github/workflows/release-desktop-platform.yml - secrets: inherit - with: - build_type: ${{ inputs.build-type }} - app_version: ${{ inputs.app-version }} - git_short_hash: ${{ inputs.git-short-hash }} - runner: windows-latest - platform: win32 - arch: arm64 - target: aarch64-pc-windows-msvc - enable_scripts: true - - sign-packaged-artifacts-windows_x64: + build-and-sign-windows: if: ${{ inputs.desktop_windows && needs.windows-signer-gate.outputs.signer_available == 'true' }} needs: + - before-make - windows-signer-gate - - package-distribution-windows-x64 - uses: ./.github/workflows/windows-signer.yml - with: - files: ${{ needs.package-distribution-windows-x64.outputs.files_to_be_signed }} - artifact-name: packaged-win32-x64 - - sign-packaged-artifacts-windows_arm64: - if: ${{ inputs.desktop_windows && needs.windows-signer-gate.outputs.signer_available == 'true' }} - needs: - - windows-signer-gate - - package-distribution-windows-arm64 - uses: ./.github/workflows/windows-signer.yml - with: - files: ${{ needs.package-distribution-windows-arm64.outputs.files_to_be_signed }} - artifact-name: packaged-win32-arm64 - - make-windows-installer: - if: >- - ${{ - always() && - inputs.desktop_windows && - needs.windows-signer-gate.result == 'success' && - needs.package-distribution-windows-x64.result == 'success' && - needs.package-distribution-windows-arm64.result == 'success' && - ( - !inputs.require-windows-signing || - ( - needs.sign-packaged-artifacts-windows_x64.result == 'success' && - needs.sign-packaged-artifacts-windows_arm64.result == 'success' - ) - ) - }} - needs: - - windows-signer-gate - - package-distribution-windows-x64 - - package-distribution-windows-arm64 - - sign-packaged-artifacts-windows_x64 - - sign-packaged-artifacts-windows_arm64 strategy: fail-fast: false matrix: spec: - platform: win32 arch: x64 + target: x86_64-pc-windows-msvc - platform: win32 arch: arm64 + target: aarch64-pc-windows-msvc runs-on: windows-latest - outputs: - FILES_TO_BE_SIGNED_x64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_x64 }} - FILES_TO_BE_SIGNED_arm64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_arm64 }} + env: + AFFINE_SIGNER_ADDR: ${{ secrets.AFFINE_SIGNER_ADDR }} + AFFINE_SIGNER_TOKEN: ${{ secrets.AFFINE_SIGNER_TOKEN }} + APP_NAME: affine + BUILD_TYPE: ${{ inputs.build-type }} + DEBUG: 'affine:*,napi:*' + RELEASE_VERSION: ${{ inputs.app-version }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: 'affine' + SENTRY_RELEASE: ${{ inputs.app-version }} + SKIP_GENERATE_ASSETS: 1 + TS_RS_EXPERIMENT: this_is_unstable_software steps: - uses: actions/checkout@v6 + - name: Setup Version uses: ./.github/actions/setup-version with: app-version: ${{ inputs.app-version }} + - name: Setup Node.js timeout-minutes: 10 uses: ./.github/actions/setup-node with: - extra-flags: workspaces focus @affine/electron @affine/monorepo + extra-flags: workspaces focus @affine/electron @affine/monorepo @affine/nbstore @toeverything/infra + enableScripts: true hard-link-nm: false nmHoistingLimits: workspaces env: npm_config_arch: ${{ matrix.spec.arch }} - - name: Download packaged artifacts + + - name: Build AFFiNE native + uses: ./.github/actions/build-rust + with: + target: ${{ matrix.spec.target }} + package: '@affine/native' + + - name: Download web artifact uses: actions/download-artifact@v4 with: - name: packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} - path: packaged-unsigned - - name: unzip packaged artifacts - run: Expand-Archive -Path packaged-unsigned/archive.zip -DestinationPath packages/frontend/apps/electron/out - - name: Download signed packaged file diff - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success') }} - uses: actions/download-artifact@v4 + name: desktop-web + path: packages/frontend/apps/electron/resources/web-static + + - name: Build Desktop Layers + run: yarn affine @affine/electron build + + - name: Remove nbstore node_modules + shell: bash + run: | + rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite/affine/node_modules + rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules + + - name: Package windows app + run: yarn affine @affine/electron package --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }} + env: + HOIST_NODE_MODULES: 1 + NODE_OPTIONS: --max-old-space-size=14384 + SKIP_WEB_BUILD: 1 + + - name: Connect Tailscale + uses: tailscale/github-action@v4 with: - name: signed-packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} - path: signed-packaged-diff - - name: Apply signed packaged file diff - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success') }} + authkey: ${{ secrets.AFFINE_SIGNER_TS_AUTH_KEY }} + hostname: affine-signer-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.spec.arch }} + + - name: Check signer connectivity shell: pwsh run: | - $DiffRoot = 'signed-packaged-diff/files' - $TargetRoot = 'packages/frontend/apps/electron/out' - if (!(Test-Path -LiteralPath $DiffRoot)) { - throw "Signed diff directory not found: $DiffRoot" + $Parts = "$env:AFFINE_SIGNER_ADDR".Split(':') + if ($Parts.Count -ne 2) { + throw "AFFINE_SIGNER_ADDR must be host:port, got $env:AFFINE_SIGNER_ADDR" } - Copy-Item -Path (Join-Path $DiffRoot '*') -Destination $TargetRoot -Recurse -Force - - $ManifestPath = 'signed-packaged-diff/manifest.json' - if (Test-Path -LiteralPath $ManifestPath) { - $ManifestEntries = @(Get-Content -LiteralPath $ManifestPath | ConvertFrom-Json) - foreach ($Entry in $ManifestEntries) { - $TargetPath = Join-Path $TargetRoot $Entry.path - if (!(Test-Path -LiteralPath $TargetPath -PathType Leaf)) { - throw "Applied signed file not found: $($Entry.path)" - } - - $TargetHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $TargetPath).Hash - if ($TargetHash -ne $Entry.sha256) { - throw "Signed file hash mismatch: $($Entry.path)" - } - } + $Result = Test-NetConnection -ComputerName $Parts[0] -Port ([int]$Parts[1]) + if (!$Result.TcpTestSucceeded) { + throw "Unable to connect to signer at $env:AFFINE_SIGNER_ADDR" } + - name: Download remote signer client + shell: pwsh + run: | + if (!$env:AFFINE_SIGN_CLIENT_HASH) { + throw 'AFFINE_SIGN_CLIENT_HASH is required.' + } + Invoke-WebRequest -Uri "https://cdn.affine.pro/sign-client/$env:AFFINE_SIGN_CLIENT_HASH" -OutFile affine-sign-client.exe + env: + AFFINE_SIGN_CLIENT_HASH: ${{ secrets.AFFINE_SIGN_CLIENT_HASH }} + + - name: Prepare public signing certificate + shell: pwsh + run: | + if (!$env:WINDOWS_SIGNER_PUBLIC_CERT_BASE64) { + throw 'WINDOWS_SIGNER_PUBLIC_CERT_BASE64 is required.' + } + [IO.File]::WriteAllBytes('windows-signer-public.cer', [Convert]::FromBase64String($env:WINDOWS_SIGNER_PUBLIC_CERT_BASE64)) + env: + WINDOWS_SIGNER_PUBLIC_CERT_BASE64: ${{ secrets.WINDOWS_SIGNER_PUBLIC_CERT_BASE64 }} + + - name: Resolve signtool + shell: pwsh + run: | + $Command = Get-Command signtool.exe -ErrorAction SilentlyContinue + if ($Command) { + "SIGNTOOL=$($Command.Source)" >> $env:GITHUB_ENV + Write-Host "Using signtool: $($Command.Source)" + exit 0 + } + + $KitsRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin" + $Candidates = @() + if (Test-Path -LiteralPath $KitsRoot) { + $Candidates = Get-ChildItem -Path $KitsRoot -Recurse -Filter signtool.exe | + Where-Object { $_.FullName -match '\\x64\\signtool\.exe$' } | + Sort-Object FullName -Descending + } + + if ($Candidates.Count -eq 0) { + throw "Unable to find signtool.exe under PATH or $KitsRoot" + } + + "SIGNTOOL=$($Candidates[0].FullName)" >> $env:GITHUB_ENV + Write-Host "Using signtool: $($Candidates[0].FullName)" + + - name: Get packaged files to sign + id: packaged_files_to_sign + shell: pwsh + run: | + Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/apps/electron/out -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\apps\electron\out\', '') + '"' }) -join ' ') + "FILES_TO_BE_SIGNED=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT + echo $FILES_TO_BE_SIGNED + + - name: Sign packaged files + shell: pwsh + run: | + ./affine-sign-client.exe ` + --server "$env:AFFINE_SIGNER_ADDR" ` + --token "$env:AFFINE_SIGNER_TOKEN" ` + --workdir packages/frontend/apps/electron/out ` + --files '${{ steps.packaged_files_to_sign.outputs.FILES_TO_BE_SIGNED }}' ` + --cert windows-signer-public.cer ` + --plain-tcp + - name: Make squirrel.windows installer run: yarn affine @affine/electron make-squirrel --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }} - name: Make nsis.windows installer run: yarn affine @affine/electron make-nsis --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }} - - name: Zip artifacts for faster upload - run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make/* -DestinationPath archive.zip - - - name: get all files to be signed - id: get_files_to_be_signed - run: | - Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\apps\electron\out\${{ env.BUILD_TYPE }}\make\', '') + '"' }) -join ' ') - "FILES_TO_BE_SIGNED_${{ matrix.spec.arch }}=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT - echo $FILES_TO_BE_SIGNED - - - name: Save installer for signing - uses: actions/upload-artifact@v4 - with: - name: installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} - path: archive.zip - - sign-installer-artifacts-windows-x64: - if: ${{ inputs.desktop_windows && needs.windows-signer-gate.outputs.signer_available == 'true' }} - needs: - - windows-signer-gate - - make-windows-installer - uses: ./.github/workflows/windows-signer.yml - with: - files: ${{ needs.make-windows-installer.outputs.FILES_TO_BE_SIGNED_x64 }} - artifact-name: installer-win32-x64 - - sign-installer-artifacts-windows-arm64: - if: ${{ inputs.desktop_windows && needs.windows-signer-gate.outputs.signer_available == 'true' }} - needs: - - windows-signer-gate - - make-windows-installer - uses: ./.github/workflows/windows-signer.yml - with: - files: ${{ needs.make-windows-installer.outputs.FILES_TO_BE_SIGNED_arm64 }} - artifact-name: installer-win32-arm64 - - finalize-installer-windows: - if: >- - ${{ - always() && - inputs.desktop_windows && - needs.make-windows-installer.result == 'success' - }} - needs: - [ - windows-signer-gate, - make-windows-installer, - sign-packaged-artifacts-windows_x64, - sign-packaged-artifacts-windows_arm64, - sign-installer-artifacts-windows-x64, - sign-installer-artifacts-windows-arm64, - before-make, - ] - strategy: - fail-fast: false - matrix: - spec: - - runner: windows-latest - platform: win32 - arch: x64 - - runner: windows-latest - platform: win32 - arch: arm64 - runs-on: ${{ matrix.spec.runner }} - steps: - - name: Download installer artifacts - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success' && needs.sign-installer-artifacts-windows-x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success' && needs.sign-installer-artifacts-windows-arm64.result == 'success') }} - uses: actions/download-artifact@v4 - with: - name: installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} - path: installer-unsigned - - name: unzip installer artifacts - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success' && needs.sign-installer-artifacts-windows-x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success' && needs.sign-installer-artifacts-windows-arm64.result == 'success') }} - run: Expand-Archive -Path installer-unsigned/archive.zip -DestinationPath packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make - - name: Download signed installer file diff - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success' && needs.sign-installer-artifacts-windows-x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success' && needs.sign-installer-artifacts-windows-arm64.result == 'success') }} - uses: actions/download-artifact@v4 - with: - name: signed-installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} - path: signed-installer-diff - - name: Apply signed installer file diff - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success' && needs.sign-installer-artifacts-windows-x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success' && needs.sign-installer-artifacts-windows-arm64.result == 'success') }} + - name: Get installer files to sign + id: installer_files_to_sign shell: pwsh run: | - $DiffRoot = 'signed-installer-diff/files' - $TargetRoot = 'packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make' - if (!(Test-Path -LiteralPath $DiffRoot)) { - throw "Signed diff directory not found: $DiffRoot" - } + Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\apps\electron\out\${{ env.BUILD_TYPE }}\make\', '') + '"' }) -join ' ') + "FILES_TO_BE_SIGNED=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT + echo $FILES_TO_BE_SIGNED - Copy-Item -Path (Join-Path $DiffRoot '*') -Destination $TargetRoot -Recurse -Force - - $ManifestPath = 'signed-installer-diff/manifest.json' - if (Test-Path -LiteralPath $ManifestPath) { - $ManifestEntries = @(Get-Content -LiteralPath $ManifestPath | ConvertFrom-Json) - foreach ($Entry in $ManifestEntries) { - $TargetPath = Join-Path $TargetRoot $Entry.path - if (!(Test-Path -LiteralPath $TargetPath -PathType Leaf)) { - throw "Applied signed file not found: $($Entry.path)" - } - - $TargetHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $TargetPath).Hash - if ($TargetHash -ne $Entry.sha256) { - throw "Signed file hash mismatch: $($Entry.path)" - } - } - } + - name: Sign installer files + shell: pwsh + run: | + ./affine-sign-client.exe ` + --server "$env:AFFINE_SIGNER_ADDR" ` + --token "$env:AFFINE_SIGNER_TOKEN" ` + --workdir packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make ` + --files '${{ steps.installer_files_to_sign.outputs.FILES_TO_BE_SIGNED }}' ` + --cert windows-signer-public.cer ` + --plain-tcp - name: Save artifacts - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success' && needs.sign-installer-artifacts-windows-x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success' && needs.sign-installer-artifacts-windows-arm64.result == 'success') }} run: | mkdir -p builds mv packages/frontend/apps/electron/out/*/make/zip/win32/${{ matrix.spec.arch }}/AFFiNE*-win32-${{ matrix.spec.arch }}-*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.zip @@ -406,7 +340,6 @@ jobs: mv packages/frontend/apps/electron/out/*/make/nsis.windows/${{ matrix.spec.arch }}/*.exe ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.nsis.exe - uses: actions/attest-build-provenance@v4 - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success' && needs.sign-installer-artifacts-windows-x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success' && needs.sign-installer-artifacts-windows-arm64.result == 'success') }} with: subject-path: | ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.zip @@ -414,7 +347,6 @@ jobs: ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.nsis.exe - name: Upload Artifact - if: ${{ (matrix.spec.arch == 'x64' && needs.sign-packaged-artifacts-windows_x64.result == 'success' && needs.sign-installer-artifacts-windows-x64.result == 'success') || (matrix.spec.arch == 'arm64' && needs.sign-packaged-artifacts-windows_arm64.result == 'success' && needs.sign-installer-artifacts-windows-arm64.result == 'success') }} uses: actions/upload-artifact@v4 with: name: affine-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}-builds @@ -429,14 +361,9 @@ jobs: (!inputs.desktop_macos || needs.make-distribution-macos.result == 'success') && (!inputs.desktop_linux || needs.make-distribution-linux.result == 'success') && ( + !inputs.desktop_windows || !inputs.require-windows-signing || - ( - needs.sign-packaged-artifacts-windows_x64.result == 'success' && - needs.sign-packaged-artifacts-windows_arm64.result == 'success' && - needs.sign-installer-artifacts-windows-x64.result == 'success' && - needs.sign-installer-artifacts-windows-arm64.result == 'success' && - needs.finalize-installer-windows.result == 'success' - ) + needs.build-and-sign-windows.result == 'success' ) }} needs: @@ -444,11 +371,7 @@ jobs: before-make, make-distribution-macos, make-distribution-linux, - sign-packaged-artifacts-windows_x64, - sign-packaged-artifacts-windows_arm64, - sign-installer-artifacts-windows-x64, - sign-installer-artifacts-windows-arm64, - finalize-installer-windows, + build-and-sign-windows, ] runs-on: ubuntu-latest @@ -467,13 +390,13 @@ jobs: name: affine-darwin-arm64-builds path: ./release - name: Download Artifacts (windows-x64) - if: ${{ needs.sign-packaged-artifacts-windows_x64.result == 'success' && needs.sign-installer-artifacts-windows-x64.result == 'success' }} + if: ${{ inputs.desktop_windows && needs.build-and-sign-windows.result == 'success' }} uses: actions/download-artifact@v4 with: name: affine-win32-x64-builds path: ./release - name: Download Artifacts (windows-arm64) - if: ${{ needs.sign-packaged-artifacts-windows_arm64.result == 'success' && needs.sign-installer-artifacts-windows-arm64.result == 'success' }} + if: ${{ inputs.desktop_windows && needs.build-and-sign-windows.result == 'success' }} uses: actions/download-artifact@v4 with: name: affine-win32-arm64-builds diff --git a/.github/workflows/windows-signer.yml b/.github/workflows/windows-signer.yml deleted file mode 100644 index df6753a281..0000000000 --- a/.github/workflows/windows-signer.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Windows Signer -on: - workflow_call: - inputs: - artifact-name: - required: true - type: string - files: - required: true - type: string -jobs: - sign: - runs-on: [self-hosted, win-signer] - env: - ARCHIVE_DIR: ${{ github.run_id }}-${{ github.run_attempt }}-${{ inputs.artifact-name }} - steps: - - uses: actions/download-artifact@v4 - with: - name: ${{ inputs.artifact-name }} - path: ${{ env.ARCHIVE_DIR }} - - name: unzip file - shell: cmd - # 7za is pre-installed on the signer machine - run: | - cd ${{ env.ARCHIVE_DIR }} - md out - 7za x archive.zip -y -oout - - name: sign - shell: cmd - run: | - cd ${{ env.ARCHIVE_DIR }}/out - signtool sign /tr http://timestamp.globalsign.com/tsa/r6advanced1 /td sha256 /fd sha256 /a ${{ inputs.files }} - - name: collect signed file diff - shell: powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File {0} - run: | - $OutDir = Join-Path '${{ env.ARCHIVE_DIR }}' 'out' - $DiffDir = Join-Path '${{ env.ARCHIVE_DIR }}' 'signed-diff' - $FilesDir = Join-Path $DiffDir 'files' - New-Item -ItemType Directory -Path $FilesDir -Force | Out-Null - - $SignedFiles = [regex]::Matches('${{ inputs.files }}', '"([^"]+)"') | ForEach-Object { $_.Groups[1].Value } - if ($SignedFiles.Count -eq 0) { - throw 'No files to sign were provided.' - } - - $Manifest = @() - foreach ($RelativePath in $SignedFiles) { - $SourcePath = Join-Path $OutDir $RelativePath - if (!(Test-Path -LiteralPath $SourcePath -PathType Leaf)) { - throw "Signed file not found: $RelativePath" - } - - $TargetPath = Join-Path $FilesDir $RelativePath - $TargetDir = Split-Path -Parent $TargetPath - if ($TargetDir) { - New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null - } - - Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force - $Manifest += [PSCustomObject]@{ - path = $RelativePath - sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $TargetPath).Hash - } - } - - $Manifest | ConvertTo-Json -Depth 4 | Out-File -FilePath (Join-Path $DiffDir 'manifest.json') -Encoding utf8 - Write-Host "Collected $($SignedFiles.Count) signed files." - - name: upload - uses: actions/upload-artifact@v4 - with: - name: signed-${{ inputs.artifact-name }} - path: ${{ env.ARCHIVE_DIR }}/signed-diff diff --git a/scripts/check-windows-signer.mjs b/scripts/check-windows-signer.mjs index d01a5a1a37..28b33b0ceb 100644 --- a/scripts/check-windows-signer.mjs +++ b/scripts/check-windows-signer.mjs @@ -4,47 +4,21 @@ const buildType = process.env.BUILD_TYPE; const requireSigner = process.env.REQUIRE_SIGNER === 'true' || ['beta', 'stable'].includes(buildType); -const githubToken = process.env.GITHUB_TOKEN; -const repository = process.env.GITHUB_REPOSITORY; -const apiUrl = process.env.GITHUB_API_URL ?? 'https://api.github.com'; const outputPath = process.env.GITHUB_OUTPUT; -if (!githubToken) { - fail('Missing GITHUB_TOKEN.'); -} +const missing = [ + 'AFFINE_SIGN_CLIENT_HASH', + 'AFFINE_SIGNER_ADDR', + 'AFFINE_SIGNER_TOKEN', + 'AFFINE_SIGNER_TS_AUTH_KEY', + 'WINDOWS_SIGNER_PUBLIC_CERT_BASE64', +].filter(name => !process.env[name]); -if (!repository) { - fail('Missing GITHUB_REPOSITORY.'); -} - -const [owner, repo] = repository.split('/'); -if (!owner || !repo) { - fail(`Invalid GITHUB_REPOSITORY: ${repository}`); -} - -try { - const runners = await listAllRunners(owner, repo); - const signerAvailable = runners.some(runner => { - const labels = (runner.labels ?? []).map(label => label.name); - return runner.status === 'online' && labels.includes('win-signer'); - }); - - setOutput('signer_available', signerAvailable ? 'true' : 'false'); - - if (!signerAvailable) { - const message = - 'No online self-hosted runner with label "win-signer" is available.'; - if (requireSigner) { - fail(message); - } else { - console.warn( - `::warning::${message} Windows installer executables will be skipped.` - ); - } - } -} catch (error) { +if (missing.length === 0) { + setOutput('signer_available', 'true'); +} else { setOutput('signer_available', 'false'); - const message = `Failed to query self-hosted runner availability: ${formatError(error)}`; + const message = `Missing remote Windows signer configuration: ${missing.join(', ')}.`; if (requireSigner) { fail(message); } else { @@ -54,46 +28,6 @@ try { } } -async function listAllRunners(owner, repo) { - const runners = []; - let page = 1; - - while (true) { - const url = new URL( - `/repos/${owner}/${repo}/actions/runners`, - ensureTrailingSlash(apiUrl) - ); - url.searchParams.set('per_page', '100'); - url.searchParams.set('page', String(page)); - - const response = await fetch(url, { - headers: { - Accept: 'application/vnd.github+json', - Authorization: `Bearer ${githubToken}`, - 'X-GitHub-Api-Version': '2022-11-28', - }, - }); - - if (!response.ok) { - const body = await response.text(); - throw new Error(`GitHub API ${response.status}: ${body}`); - } - - const data = await response.json(); - runners.push(...(data.runners ?? [])); - - if ((data.runners ?? []).length < 100) { - return runners; - } - - page += 1; - } -} - -function ensureTrailingSlash(url) { - return url.endsWith('/') ? url : `${url}/`; -} - function setOutput(name, value) { if (!outputPath) return; fs.appendFileSync(outputPath, `${name}=${value}\n`); @@ -103,10 +37,3 @@ function fail(message) { console.error(`::error::${message}`); process.exit(1); } - -function formatError(error) { - if (error instanceof Error) { - return error.message; - } - return String(error); -}