-
Notifications
You must be signed in to change notification settings - Fork 121
203 lines (199 loc) · 10.2 KB
/
Copy pathbuild-node-exe.yml
File metadata and controls
203 lines (199 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# Build the self-contained controlled-node executable on a per-OS matrix and
# publish the binaries as artifacts (controlled-node-remote-exec 7.3).
#
# Each OS gets its own matrix job (win/macos/linux). The macOS job combines
# official arm64 and x64 Node slices into one Universal 2 executable. The
# produced binaries are uploaded as workflow artifacts (the "hosting" for a
# deploy step that populates the server's IMCODES_NODE_EXE_DIR).
name: build-node-exe
on:
workflow_dispatch:
inputs:
build_version:
description: Runtime version advertised by the node and matched by the server
required: true
type: string
push:
tags:
- 'node-exe-v*'
permissions:
contents: read
env:
NODE_VERSION_PRIMARY: '24'
IMCODES_REQUIRE_COMPUTER_USE_HELPER: '1'
jobs:
build:
name: build (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
artifact: imcodes-node-linux
- os: macos-15
artifact: imcodes-node-macos
- os: windows-2022
artifact: imcodes-node.exe
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION_PRIMARY }}
- name: Install dependencies
run: npm ci
# Fail the build if the thin entry pulls in a native/unbundlable dep — a
# SEA binary must be self-contained (guards the same invariant the local
# `check:node-exe-deps` script enforces).
- name: Verify node-exe has no native deps
run: npm run check:node-exe-deps
- name: Resolve controlled-node runtime version
shell: bash
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ inputs.build_version }}"
else
VERSION="${GITHUB_REF_NAME#node-exe-v}"
fi
if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){1,3}(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$ ]]; then
echo "invalid controlled-node build version: $VERSION" >&2
exit 1
fi
echo "IMCODES_BUILD_VERSION=$VERSION" >> "$GITHUB_ENV"
- name: Import Windows release-signing certificate
if: runner.os == 'Windows'
shell: powershell
env:
IMCODES_WINDOWS_SIGNING_PFX_BASE64: ${{ secrets.IMCODES_WINDOWS_SIGNING_PFX_BASE64 }}
IMCODES_WINDOWS_SIGNING_PFX_PASSWORD: ${{ secrets.IMCODES_WINDOWS_SIGNING_PFX_PASSWORD }}
run: |
if ([string]::IsNullOrWhiteSpace($env:IMCODES_WINDOWS_SIGNING_PFX_BASE64) -or
[string]::IsNullOrWhiteSpace($env:IMCODES_WINDOWS_SIGNING_PFX_PASSWORD)) {
throw 'Windows release-signing secrets are required.'
}
$pfxPath = Join-Path $env:RUNNER_TEMP 'imcodes-windows-release-signing.pfx'
[IO.File]::WriteAllBytes($pfxPath, [Convert]::FromBase64String($env:IMCODES_WINDOWS_SIGNING_PFX_BASE64))
$password = ConvertTo-SecureString $env:IMCODES_WINDOWS_SIGNING_PFX_PASSWORD -AsPlainText -Force
$certificate = Import-PfxCertificate -FilePath $pfxPath -CertStoreLocation Cert:\CurrentUser\My -Password $password
if (-not $certificate -or -not $certificate.HasPrivateKey) { throw 'Imported signing certificate has no private key.' }
Remove-Item -LiteralPath $pfxPath -Force
$publicCertificatePath = Join-Path $env:RUNNER_TEMP 'imcodes-windows-release-signing.cer'
Export-Certificate -Cert $certificate -FilePath $publicCertificatePath -Force | Out-Null
Import-Certificate -FilePath $publicCertificatePath -CertStoreLocation Cert:\LocalMachine\TrustedPeople | Out-Null
Import-Certificate -FilePath $publicCertificatePath -CertStoreLocation Cert:\LocalMachine\TrustedPublisher | Out-Null
"IMCODES_WINDOWS_SIGNING_CERT_THUMBPRINT=$($certificate.Thumbprint)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
$sha256 = [System.Security.Cryptography.SHA256]::Create()
try { $signerSha256 = [BitConverter]::ToString($sha256.ComputeHash($certificate.RawData)).Replace('-', '').ToLowerInvariant() } finally { $sha256.Dispose() }
"IMCODES_WINDOWS_SIGNING_CERT_SHA256=$signerSha256" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Build controlled-node executable
run: npm run build:node-exe
- name: Resolve fixed libwebrtc SDK release
if: runner.os == 'Windows'
id: libwebrtc_sdk
run: node scripts/resolve-libwebrtc-sdk-release.mjs --wait-seconds 15000
- name: Restore fixed libwebrtc foundation SDK
if: runner.os == 'Windows'
id: libwebrtc_sdk_cache
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/imcodes-libwebrtc-sdk
key: imcodes-libwebrtc-foundation-${{ runner.os }}-${{ steps.libwebrtc_sdk.outputs.sha256 }}-v1
- name: Download fixed libwebrtc foundation SDK
if: runner.os == 'Windows' && steps.libwebrtc_sdk_cache.outputs.cache-hit != 'true'
shell: powershell
env:
GH_TOKEN: ${{ github.token }}
run: |
$download = Join-Path $env:RUNNER_TEMP 'imcodes-libwebrtc-sdk-download'
New-Item -ItemType Directory -Force -Path $download | Out-Null
gh release download '${{ steps.libwebrtc_sdk.outputs.release_tag }}' `
--repo '${{ steps.libwebrtc_sdk.outputs.repository }}' `
--pattern '${{ steps.libwebrtc_sdk.outputs.asset_name }}' `
--dir $download `
--clobber
if ($LASTEXITCODE -ne 0) { throw 'Fixed libwebrtc SDK download failed.' }
node scripts/install-libwebrtc-sdk.mjs `
(Join-Path $download '${{ steps.libwebrtc_sdk.outputs.asset_name }}') `
(Join-Path $env:RUNNER_TEMP 'imcodes-libwebrtc-sdk')
- name: Verify cached fixed libwebrtc foundation SDK
if: runner.os == 'Windows'
run: >-
node scripts/libwebrtc-sdk-artifacts.mjs verify-sdk-lock
native/windows-remote-desktop/libwebrtc-sdk.lock.json
"${{ runner.temp }}\imcodes-libwebrtc-sdk"
- name: Build and test current remote-desktop sources from SDK
if: runner.os == 'Windows'
timeout-minutes: 30
shell: powershell
run: |
$buildArguments = @{
SdkRoot = "$env:RUNNER_TEMP\imcodes-libwebrtc-sdk"
ArtifactRoot = 'dist-node-exe\remote-desktop-worker\win32-x64'
WorkerVersion = $env:IMCODES_BUILD_VERSION
CodeSigningCertificateThumbprint = $env:IMCODES_WINDOWS_SIGNING_CERT_THUMBPRINT
ExpectedSignerSha256 = $env:IMCODES_WINDOWS_SIGNING_CERT_SHA256
CodeSigningTimestampUrl = 'http://timestamp.digicert.com'
RequireAuthenticodeSignature = $true
RunNativeTests = $true
}
& .\native\windows-remote-desktop\build-worker-from-sdk.ps1 @buildArguments
- name: Verify production remote-desktop worker artifact
if: runner.os == 'Windows'
run: node scripts/remote-desktop-worker-artifacts.mjs verify dist-node-exe "${{ env.IMCODES_BUILD_VERSION }}"
- name: Verify every Windows release binary uses one publisher
if: runner.os == 'Windows'
shell: powershell
run: |
$virtualDisplayVerify = Join-Path $env:RUNNER_TEMP 'imcodes-virtual-display-signature-verification'
Remove-Item -LiteralPath $virtualDisplayVerify -Recurse -Force -ErrorAction SilentlyContinue
Expand-Archive -LiteralPath 'dist-node-exe\remote-desktop-worker\win32-x64\imcodes-virtual-display.zip' -DestinationPath $virtualDisplayVerify -Force
$artifacts = @(
'dist-node-exe\imcodes-node.exe',
'dist-node-exe\computer-use-helper\win32-x64\open-computer-use.exe',
'dist-node-exe\remote-desktop-worker\win32-x64\imcodes-remote-desktop-worker.exe',
(Join-Path $virtualDisplayVerify 'imcodes-virtual-display.dll'),
(Join-Path $virtualDisplayVerify 'imcodes-virtual-display.cat')
)
try {
foreach ($artifact in $artifacts) {
& .\scripts\windows-sign-release-artifact.ps1 -Mode Verify -ArtifactPath $artifact -ExpectedSignerSha256 $env:IMCODES_WINDOWS_SIGNING_CERT_SHA256
}
node scripts/node-exe-artifacts.mjs verify-set dist-node-exe imcodes-node.exe
} finally {
Remove-Item -LiteralPath $virtualDisplayVerify -Recurse -Force -ErrorAction SilentlyContinue
}
- name: Qualify signed Windows self-upgrade and rollback
if: runner.os == 'Windows'
run: npx tsx scripts/qualify-windows-self-upgrade.ts
- name: Upload executable artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: |
dist-node-exe/${{ matrix.artifact }}
dist-node-exe/${{ matrix.artifact }}.manifest.json
dist-node-exe/computer-use-helper/**
dist-node-exe/remote-desktop-worker/**
if-no-files-found: error
retention-days: 30
- name: Remove Windows release-signing material
if: always() && runner.os == 'Windows'
shell: powershell
run: |
$certificatePaths = @()
if ($env:IMCODES_WINDOWS_SIGNING_CERT_THUMBPRINT) {
$certificatePaths = @(
"Cert:\CurrentUser\My\$env:IMCODES_WINDOWS_SIGNING_CERT_THUMBPRINT",
"Cert:\LocalMachine\TrustedPeople\$env:IMCODES_WINDOWS_SIGNING_CERT_THUMBPRINT",
"Cert:\LocalMachine\TrustedPublisher\$env:IMCODES_WINDOWS_SIGNING_CERT_THUMBPRINT"
)
$certificatePaths | ForEach-Object { Remove-Item -LiteralPath $_ -Force -ErrorAction SilentlyContinue }
}
$materialPaths = @(
(Join-Path $env:RUNNER_TEMP 'imcodes-windows-release-signing.pfx'),
(Join-Path $env:RUNNER_TEMP 'imcodes-windows-release-signing.cer')
)
$materialPaths | ForEach-Object { Remove-Item -LiteralPath $_ -Force -ErrorAction SilentlyContinue }
$leftovers = @(($certificatePaths + $materialPaths) | Where-Object { Test-Path -LiteralPath $_ })
if ($leftovers.Count -gt 0) { throw 'Windows release-signing material cleanup was incomplete.' }