mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-24 05:07:06 +08:00
feat: improve ci (#15194)
This commit is contained in:
@@ -89,9 +89,13 @@ jobs:
|
|||||||
id: check
|
id: check
|
||||||
run: node ./scripts/check-windows-signer.mjs
|
run: node ./scripts/check-windows-signer.mjs
|
||||||
env:
|
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 }}
|
BUILD_TYPE: ${{ inputs.build-type }}
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
|
||||||
REQUIRE_SIGNER: ${{ inputs.require-windows-signing }}
|
REQUIRE_SIGNER: ${{ inputs.require-windows-signing }}
|
||||||
|
WINDOWS_SIGNER_PUBLIC_CERT_BASE64: ${{ secrets.WINDOWS_SIGNER_PUBLIC_CERT_BASE64 }}
|
||||||
|
|
||||||
make-distribution-macos:
|
make-distribution-macos:
|
||||||
if: ${{ inputs.desktop_macos }}
|
if: ${{ inputs.desktop_macos }}
|
||||||
@@ -143,262 +147,192 @@ jobs:
|
|||||||
target: ${{ matrix.spec.target }}
|
target: ${{ matrix.spec.target }}
|
||||||
install_linux_deps: true
|
install_linux_deps: true
|
||||||
|
|
||||||
package-distribution-windows-x64:
|
build-and-sign-windows:
|
||||||
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:
|
|
||||||
if: ${{ inputs.desktop_windows && needs.windows-signer-gate.outputs.signer_available == 'true' }}
|
if: ${{ inputs.desktop_windows && needs.windows-signer-gate.outputs.signer_available == 'true' }}
|
||||||
needs:
|
needs:
|
||||||
|
- before-make
|
||||||
- windows-signer-gate
|
- 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:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
spec:
|
spec:
|
||||||
- platform: win32
|
- platform: win32
|
||||||
arch: x64
|
arch: x64
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
- platform: win32
|
- platform: win32
|
||||||
arch: arm64
|
arch: arm64
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
outputs:
|
env:
|
||||||
FILES_TO_BE_SIGNED_x64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_x64 }}
|
AFFINE_SIGNER_ADDR: ${{ secrets.AFFINE_SIGNER_ADDR }}
|
||||||
FILES_TO_BE_SIGNED_arm64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_arm64 }}
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Version
|
- name: Setup Version
|
||||||
uses: ./.github/actions/setup-version
|
uses: ./.github/actions/setup-version
|
||||||
with:
|
with:
|
||||||
app-version: ${{ inputs.app-version }}
|
app-version: ${{ inputs.app-version }}
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
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
|
hard-link-nm: false
|
||||||
nmHoistingLimits: workspaces
|
nmHoistingLimits: workspaces
|
||||||
env:
|
env:
|
||||||
npm_config_arch: ${{ matrix.spec.arch }}
|
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
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
name: desktop-web
|
||||||
path: packaged-unsigned
|
path: packages/frontend/apps/electron/resources/web-static
|
||||||
- name: unzip packaged artifacts
|
|
||||||
run: Expand-Archive -Path packaged-unsigned/archive.zip -DestinationPath packages/frontend/apps/electron/out
|
- name: Build Desktop Layers
|
||||||
- name: Download signed packaged file diff
|
run: yarn affine @affine/electron build
|
||||||
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: 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:
|
with:
|
||||||
name: signed-packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
authkey: ${{ secrets.AFFINE_SIGNER_TS_AUTH_KEY }}
|
||||||
path: signed-packaged-diff
|
hostname: affine-signer-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.spec.arch }}
|
||||||
- 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') }}
|
- name: Check signer connectivity
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
$DiffRoot = 'signed-packaged-diff/files'
|
$Parts = "$env:AFFINE_SIGNER_ADDR".Split(':')
|
||||||
$TargetRoot = 'packages/frontend/apps/electron/out'
|
if ($Parts.Count -ne 2) {
|
||||||
if (!(Test-Path -LiteralPath $DiffRoot)) {
|
throw "AFFINE_SIGNER_ADDR must be host:port, got $env:AFFINE_SIGNER_ADDR"
|
||||||
throw "Signed diff directory not found: $DiffRoot"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Copy-Item -Path (Join-Path $DiffRoot '*') -Destination $TargetRoot -Recurse -Force
|
$Result = Test-NetConnection -ComputerName $Parts[0] -Port ([int]$Parts[1])
|
||||||
|
if (!$Result.TcpTestSucceeded) {
|
||||||
$ManifestPath = 'signed-packaged-diff/manifest.json'
|
throw "Unable to connect to signer at $env:AFFINE_SIGNER_ADDR"
|
||||||
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: 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
|
- name: Make squirrel.windows installer
|
||||||
run: yarn affine @affine/electron make-squirrel --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
run: yarn affine @affine/electron make-squirrel --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
||||||
|
|
||||||
- name: Make nsis.windows installer
|
- name: Make nsis.windows installer
|
||||||
run: yarn affine @affine/electron make-nsis --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
run: yarn affine @affine/electron make-nsis --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
|
||||||
|
|
||||||
- name: Zip artifacts for faster upload
|
- name: Get installer files to sign
|
||||||
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make/* -DestinationPath archive.zip
|
id: installer_files_to_sign
|
||||||
|
|
||||||
- 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') }}
|
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
$DiffRoot = 'signed-installer-diff/files'
|
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 ' ')
|
||||||
$TargetRoot = 'packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make'
|
"FILES_TO_BE_SIGNED=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
|
||||||
if (!(Test-Path -LiteralPath $DiffRoot)) {
|
echo $FILES_TO_BE_SIGNED
|
||||||
throw "Signed diff directory not found: $DiffRoot"
|
|
||||||
}
|
|
||||||
|
|
||||||
Copy-Item -Path (Join-Path $DiffRoot '*') -Destination $TargetRoot -Recurse -Force
|
- name: Sign installer files
|
||||||
|
shell: pwsh
|
||||||
$ManifestPath = 'signed-installer-diff/manifest.json'
|
run: |
|
||||||
if (Test-Path -LiteralPath $ManifestPath) {
|
./affine-sign-client.exe `
|
||||||
$ManifestEntries = @(Get-Content -LiteralPath $ManifestPath | ConvertFrom-Json)
|
--server "$env:AFFINE_SIGNER_ADDR" `
|
||||||
foreach ($Entry in $ManifestEntries) {
|
--token "$env:AFFINE_SIGNER_TOKEN" `
|
||||||
$TargetPath = Join-Path $TargetRoot $Entry.path
|
--workdir packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make `
|
||||||
if (!(Test-Path -LiteralPath $TargetPath -PathType Leaf)) {
|
--files '${{ steps.installer_files_to_sign.outputs.FILES_TO_BE_SIGNED }}' `
|
||||||
throw "Applied signed file not found: $($Entry.path)"
|
--cert windows-signer-public.cer `
|
||||||
}
|
--plain-tcp
|
||||||
|
|
||||||
$TargetHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $TargetPath).Hash
|
|
||||||
if ($TargetHash -ne $Entry.sha256) {
|
|
||||||
throw "Signed file hash mismatch: $($Entry.path)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Save artifacts
|
- 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: |
|
run: |
|
||||||
mkdir -p builds
|
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
|
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
|
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
|
- 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:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.zip
|
./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
|
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-${{ matrix.spec.arch }}.nsis.exe
|
||||||
|
|
||||||
- name: Upload Artifact
|
- 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
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: affine-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}-builds
|
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_macos || needs.make-distribution-macos.result == 'success') &&
|
||||||
(!inputs.desktop_linux || needs.make-distribution-linux.result == 'success') &&
|
(!inputs.desktop_linux || needs.make-distribution-linux.result == 'success') &&
|
||||||
(
|
(
|
||||||
|
!inputs.desktop_windows ||
|
||||||
!inputs.require-windows-signing ||
|
!inputs.require-windows-signing ||
|
||||||
(
|
needs.build-and-sign-windows.result == 'success'
|
||||||
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:
|
needs:
|
||||||
@@ -444,11 +371,7 @@ jobs:
|
|||||||
before-make,
|
before-make,
|
||||||
make-distribution-macos,
|
make-distribution-macos,
|
||||||
make-distribution-linux,
|
make-distribution-linux,
|
||||||
sign-packaged-artifacts-windows_x64,
|
build-and-sign-windows,
|
||||||
sign-packaged-artifacts-windows_arm64,
|
|
||||||
sign-installer-artifacts-windows-x64,
|
|
||||||
sign-installer-artifacts-windows-arm64,
|
|
||||||
finalize-installer-windows,
|
|
||||||
]
|
]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
@@ -467,13 +390,13 @@ jobs:
|
|||||||
name: affine-darwin-arm64-builds
|
name: affine-darwin-arm64-builds
|
||||||
path: ./release
|
path: ./release
|
||||||
- name: Download Artifacts (windows-x64)
|
- 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
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: affine-win32-x64-builds
|
name: affine-win32-x64-builds
|
||||||
path: ./release
|
path: ./release
|
||||||
- name: Download Artifacts (windows-arm64)
|
- 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
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: affine-win32-arm64-builds
|
name: affine-win32-arm64-builds
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -4,47 +4,21 @@ const buildType = process.env.BUILD_TYPE;
|
|||||||
const requireSigner =
|
const requireSigner =
|
||||||
process.env.REQUIRE_SIGNER === 'true' ||
|
process.env.REQUIRE_SIGNER === 'true' ||
|
||||||
['beta', 'stable'].includes(buildType);
|
['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;
|
const outputPath = process.env.GITHUB_OUTPUT;
|
||||||
|
|
||||||
if (!githubToken) {
|
const missing = [
|
||||||
fail('Missing GITHUB_TOKEN.');
|
'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) {
|
if (missing.length === 0) {
|
||||||
fail('Missing GITHUB_REPOSITORY.');
|
setOutput('signer_available', 'true');
|
||||||
}
|
} else {
|
||||||
|
|
||||||
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) {
|
|
||||||
setOutput('signer_available', 'false');
|
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) {
|
if (requireSigner) {
|
||||||
fail(message);
|
fail(message);
|
||||||
} else {
|
} 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) {
|
function setOutput(name, value) {
|
||||||
if (!outputPath) return;
|
if (!outputPath) return;
|
||||||
fs.appendFileSync(outputPath, `${name}=${value}\n`);
|
fs.appendFileSync(outputPath, `${name}=${value}\n`);
|
||||||
@@ -103,10 +37,3 @@ function fail(message) {
|
|||||||
console.error(`::error::${message}`);
|
console.error(`::error::${message}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatError(error) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
return error.message;
|
|
||||||
}
|
|
||||||
return String(error);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user