-
Notifications
You must be signed in to change notification settings - Fork 0
518 lines (465 loc) · 22.1 KB
/
Copy pathrelease-beta.yml
File metadata and controls
518 lines (465 loc) · 22.1 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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
name: Build signed Matrix beta candidate
# Candidate factory only. This workflow may create a signed draft prerelease
# for external acceptance, but it must never publish or promote that draft.
# Public promotion remains a separate owner-controlled lifecycle decision.
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
release_version:
description: Exact non-placeholder application version to release
required: true
type: string
release_tag:
description: Optional existing v* tag for a signed draft candidate; leave empty for validation-only
required: false
default: ""
type: string
permissions:
contents: read
concurrency:
group: matrix-beta-${{ github.ref }}
cancel-in-progress: false
env:
MESH_RELEASE_VERSION: ${{ github.event_name == 'workflow_dispatch' && inputs.release_version || github.ref_name }}
MESH_RELEASE_TAG: ${{ inputs.release_tag }}
# Public desktop OAuth registrations are build inputs, not credentials.
# Each entry is bound to one exact discovered issuer and the fixed loopback callback.
MESH_OAUTH_CLIENT_REGISTRATIONS_JSON: ${{ vars.MESH_OAUTH_CLIENT_REGISTRATIONS_JSON }}
jobs:
quality-gate:
name: Matrix beta quality gate
runs-on: ubuntu-latest
timeout-minutes: 45
outputs:
create_candidate: ${{ steps.release-context.outputs.create_candidate }}
release_tag: ${{ steps.release-context.outputs.release_tag }}
source_sha: ${{ steps.release-context.outputs.source_sha }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref }}
- name: Validate release source and protected-main ancestry
id: release-context
shell: bash
run: |
set -euo pipefail
source_sha="$(git rev-parse HEAD)"
release_tag=""
create_candidate=false
if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
if [[ "$GITHUB_REF" != refs/tags/v* ]]; then
echo "Tagged candidate runs require a v* tag." >&2
exit 1
fi
release_tag="$GITHUB_REF_NAME"
create_candidate=true
elif [[ -n "$MESH_RELEASE_TAG" ]]; then
release_tag="$MESH_RELEASE_TAG"
create_candidate=true
fi
git fetch --no-tags origin \
+refs/heads/main:refs/remotes/origin/main
if [[ "$create_candidate" == "true" ]]; then
if [[ ! "$release_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
echo "Release tag must be an explicit v-prefixed semantic version." >&2
exit 1
fi
git fetch --force origin \
"refs/tags/$release_tag:refs/tags/$release_tag"
tag_sha="$(git rev-parse "$release_tag^{commit}")"
if [[ "$tag_sha" != "$source_sha" ]]; then
echo "Checked-out source $source_sha does not match tag $release_tag at $tag_sha." >&2
exit 1
fi
if ! git merge-base --is-ancestor \
"$tag_sha" refs/remotes/origin/main; then
echo "Release tag $release_tag is not contained in protected origin/main." >&2
exit 1
fi
fi
echo "create_candidate=$create_candidate" >> "$GITHUB_OUTPUT"
echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT"
echo "source_sha=$source_sha" >> "$GITHUB_OUTPUT"
echo "MESH_CREATE_CANDIDATE=$create_candidate" >> "$GITHUB_ENV"
echo "MESH_RELEASE_TAG=$release_tag" >> "$GITHUB_ENV"
echo "MESH_SOURCE_SHA=$source_sha" >> "$GITHUB_ENV"
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
cache: npm
cache-dependency-path: mesh/package-lock.json
- name: Install Rust 1.93 with Clippy
uses: dtolnay/rust-toolchain@d0befba8b9ddf874327619e84c39b094edd58b66 # 1.93.0
with:
components: clippy
- name: Install Tauri system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev libsqlcipher-dev
- name: Install frontend and browser dependencies
working-directory: mesh
run: |
npm ci
npx playwright install --with-deps chromium
- name: Run frontend and browser gates
working-directory: mesh
run: |
npm audit --audit-level=high
if [[ "$MESH_CREATE_CANDIDATE" == "true" ]]; then
pwsh -NoProfile -File ./scripts/beta-release-preflight.ps1 \
-Tag "$MESH_RELEASE_TAG" \
-ReleaseVersion "$MESH_RELEASE_VERSION" \
-ExpectedSourceSha "$MESH_SOURCE_SHA" \
-RequireProtectedMainAncestry
else
pwsh -NoProfile -File ./scripts/beta-release-preflight.ps1 \
-ReleaseVersion "$MESH_RELEASE_VERSION"
fi
pwsh -NoProfile -File ./infra/matrixrtc/test-evidence-validation.ps1
pwsh -NoProfile -File ./scripts/matrixrtc-preflight.ps1
node --test ./scripts/check-external-acceptance.test.mjs
node ./scripts/check-external-acceptance.mjs
pwsh -NoProfile -File ./scripts/operator-smoke.ps1
npm run check:public-services
npm run check:beta-contract
npm run check:operations-contract
npm run check:third-party-notices
# Evidence is collected on the tested source commit. A final release
# metadata-only commit may update readiness.json afterward; the
# validator allows that one-file delta and rejects source drift.
# The signed candidate must exist before R2 clean-device and manual
# acceptance can be collected. Candidate creation therefore requires
# exact-SHA R0 readiness; R2 stays blocked until genuine live evidence
# is collected from this signed draft.
npm run check:readiness-ledger -- --milestone R0 --require-live --commit-sha "$MESH_SOURCE_SHA" --allow-ledger-only-commit
npm run check:public-site
npm test -- --maxWorkers=4
npm run build
npm run check:bundle-size -- --report release/bundle-report.json
pwsh -NoProfile -File ./scripts/beta-release-preflight.ps1 -VerifyFrontendBundle
npm run e2e -- --workers=1
- name: Run Matrix backend gates
working-directory: mesh/src-tauri
run: |
cargo test --no-default-features --features matrix-backend --locked --jobs 1
cargo clippy --no-default-features --features matrix-backend --all-targets --locked --jobs 1 -- -D warnings
- name: Install pinned Rust advisory scanner
run: |
cargo install cargo-audit --version 0.22.2 --locked
- name: Report raw Rust advisory status
continue-on-error: true
run: |
cargo audit --file mesh/src-tauri/Cargo.lock
- name: Enforce Matrix shipping and legacy visibility policy
working-directory: mesh
shell: pwsh
run: |
./scripts/check-matrix-release-dependencies.ps1 `
-ReportPath release/rust-dependency-report.json
- name: Upload exact-source quality evidence
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: matrix-beta-quality-${{ steps.release-context.outputs.source_sha }}
path: |
mesh/release/bundle-report.json
mesh/release/rust-dependency-report.json
if-no-files-found: error
retention-days: 90
windows:
name: Signed Windows Matrix beta candidate
needs: quality-gate
if: ${{ needs.quality-gate.outputs.create_candidate == 'true' }}
runs-on: windows-latest
timeout-minutes: 90
environment: matrix-beta
env:
MESH_RELEASE_TAG: ${{ needs.quality-gate.outputs.release_tag }}
MESH_SOURCE_SHA: ${{ needs.quality-gate.outputs.source_sha }}
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
ref: ${{ needs.quality-gate.outputs.source_sha }}
- name: Download exact-source quality evidence
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: matrix-beta-quality-${{ needs.quality-gate.outputs.source_sha }}
# Keep downloaded evidence outside the checkout until the clean-source
# gate has run. Import only the verified reports afterward.
path: ${{ runner.temp }}/matrix-beta-quality
- name: Validate release tag and signing configuration
shell: pwsh
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: |
$version = (Get-Content mesh/src-tauri/tauri.conf.json | ConvertFrom-Json).version
if ($env:MESH_RELEASE_TAG -ne "v$version") {
throw "Tag $env:MESH_RELEASE_TAG does not match Tauri version v$version."
}
$missing = @(
"WINDOWS_CERTIFICATE",
"WINDOWS_CERTIFICATE_PASSWORD",
"WINDOWS_CERTIFICATE_THUMBPRINT"
) | Where-Object {
[string]::IsNullOrWhiteSpace([Environment]::GetEnvironmentVariable($_))
}
if ($missing.Count -gt 0) {
throw "Missing required GitHub release secrets: $($missing -join ', ')."
}
& ./mesh/scripts/beta-release-preflight.ps1 `
-Tag $env:MESH_RELEASE_TAG `
-ReleaseVersion $env:MESH_RELEASE_VERSION `
-RequireCleanSource `
-ExpectedSourceSha $env:MESH_SOURCE_SHA `
-RequireProtectedMainAncestry `
-RequireSigningEnvironment
- name: Verify and import exact-source quality evidence
shell: pwsh
run: |
$evidenceRoot = Join-Path $env:RUNNER_TEMP "matrix-beta-quality"
$bundlePath = Join-Path $evidenceRoot "bundle-report.json"
$dependencyPath = Join-Path $evidenceRoot "rust-dependency-report.json"
$bundle = Get-Content -LiteralPath $bundlePath -Raw | ConvertFrom-Json
$dependency = Get-Content -LiteralPath $dependencyPath -Raw | ConvertFrom-Json
if ($bundle.sourceSha -ne $env:MESH_SOURCE_SHA -or $bundle.status -ne "pass") {
throw "Bundle evidence is not a passing report for the validated source SHA."
}
if ($dependency.sourceSha -ne $env:MESH_SOURCE_SHA -or
$dependency.matrixReleaseVulnerabilityCount -ne 0 -or
$dependency.rawAuditVulnerabilityCount -ne $dependency.excludedNonShippingVulnerabilityCount) {
throw "Rust dependency evidence is not the reviewed Matrix shipping policy for the validated source SHA."
}
$lockSha = (Get-FileHash -LiteralPath mesh/src-tauri/Cargo.lock -Algorithm SHA256).Hash.ToLowerInvariant()
$policySha = (Get-FileHash -LiteralPath mesh/scripts/rust-dependency-policy.json -Algorithm SHA256).Hash.ToLowerInvariant()
if ($dependency.cargoLockSha256 -ne $lockSha -or $dependency.policySha256 -ne $policySha) {
throw "Rust dependency evidence does not match the checked-out lockfile and policy."
}
$policy = Get-Content -LiteralPath mesh/scripts/rust-dependency-policy.json -Raw | ConvertFrom-Json
$windowsBuildWarnings = @($policy.nonRuntimeBuildWarnings | Where-Object {
-not ($_.PSObject.Properties.Name -contains "platforms") -or
@($_.platforms) -contains "windows"
})
if ($dependency.matrixRuntimeWarningCount -ne @($policy.shippingRuntimeWarnings).Count -or
$dependency.hostPlatform -ne "windows" -or
$dependency.nonRuntimeBuildWarningCount -ne $windowsBuildWarnings.Count -or
$dependency.excludedNonShippingVulnerabilityCount -ne @($policy.nonShippingVulnerabilities).Count) {
throw "Rust dependency evidence counts do not match the checked-out policy."
}
foreach ($expectedWarningCount in $policy.expectedRawWarningCounts.PSObject.Properties) {
if ($dependency.rawAuditWarningCounts.($expectedWarningCount.Name) -ne $expectedWarningCount.Value) {
throw "Rust dependency raw warning counts do not match the checked-out policy."
}
}
New-Item -ItemType Directory -Force -Path mesh/release-evidence | Out-Null
Copy-Item -LiteralPath $bundlePath, $dependencyPath -Destination mesh/release-evidence
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
cache: npm
cache-dependency-path: mesh/package-lock.json
- name: Install Rust 1.93
uses: dtolnay/rust-toolchain@d0befba8b9ddf874327619e84c39b094edd58b66 # 1.93.0
- name: Install frontend dependencies
working-directory: mesh
run: npm ci
- name: Import Windows signing certificate
shell: pwsh
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: |
$certificatePath = Join-Path $env:RUNNER_TEMP "mesh-release.pfx"
try {
[IO.File]::WriteAllBytes(
$certificatePath,
[Convert]::FromBase64String($env:WINDOWS_CERTIFICATE)
)
$password = ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force
Import-PfxCertificate `
-FilePath $certificatePath `
-CertStoreLocation Cert:\CurrentUser\My `
-Password $password | Out-Null
} finally {
if (Test-Path -LiteralPath $certificatePath) {
Remove-Item -LiteralPath $certificatePath -Force
}
}
$thumbprint = $env:WINDOWS_CERTIFICATE_THUMBPRINT.Replace(" ", "").ToUpperInvariant()
$certificate = Get-Item "Cert:\CurrentUser\My\$thumbprint" -ErrorAction Stop
if (-not $certificate.HasPrivateKey) {
throw "The configured signing certificate does not include its private key."
}
$tauriConfig = @{
bundle = @{
windows = @{
certificateThumbprint = $thumbprint
digestAlgorithm = "sha256"
timestampUrl = "http://timestamp.digicert.com"
}
}
} | ConvertTo-Json -Depth 4 -Compress
"TAURI_CONFIG=$tauriConfig" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
- name: Verify Matrix production dependency compile
shell: pwsh
run: |
cargo check --manifest-path mesh/src-tauri/Cargo.toml --no-default-features --features matrix-backend --locked --jobs 1
- name: Generate release SBOMs
shell: pwsh
run: |
Push-Location mesh
npm sbom --sbom-format=cyclonedx | Out-File mesh-node.cdx.json -Encoding utf8
Pop-Location
cargo install cargo-cyclonedx --version 0.5.9 --locked
cargo cyclonedx --manifest-path mesh/src-tauri/Cargo.toml --no-default-features --features matrix-backend --format json
- name: Record clean-source build provenance
shell: pwsh
run: |
$sourceSha = (& git rev-parse HEAD).Trim()
if ($sourceSha -ne $env:MESH_SOURCE_SHA) {
throw "Build source SHA does not match the validated release source SHA."
}
$provenance = [ordered]@{
schemaVersion = 1
sourceSha = $sourceSha
releaseVersion = $env:MESH_RELEASE_VERSION.TrimStart("v")
matrixBackendOnly = $true
legacyP2pIncluded = $false
workflowRepository = $env:GITHUB_REPOSITORY
workflowRunId = $env:GITHUB_RUN_ID
workflowRunAttempt = $env:GITHUB_RUN_ATTEMPT
generatedAt = [DateTimeOffset]::UtcNow.ToString("o")
}
$provenance |
ConvertTo-Json -Depth 4 |
Out-File mesh/build-provenance.json -Encoding utf8
- name: Build signed Matrix installers
working-directory: mesh
run: npm run tauri -- build --features matrix-backend -- --no-default-features --locked --jobs 1
- name: Verify installer signatures and timestamps
shell: pwsh
env:
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: |
& ./mesh/scripts/beta-release-preflight.ps1 `
-Tag $env:MESH_RELEASE_TAG `
-VerifyFrontendBundle `
-VerifyArtifacts `
-BundleRoot mesh/src-tauri/target/release/bundle
- name: Record release bundle budget evidence
working-directory: mesh
shell: pwsh
run: npm run check:bundle-size -- --report bundle-report.json
- name: Generate release checksums
shell: pwsh
run: |
$bundleRoot = "mesh/src-tauri/target/release/bundle"
$installers = Get-ChildItem -LiteralPath $bundleRoot -Recurse -File |
Where-Object { $_.Extension -in @(".msi", ".exe") }
if ($installers.Count -eq 0) {
throw "No signed Windows installers were produced."
}
$sboms = @(
Get-Item mesh/mesh-node.cdx.json
Get-Item mesh/src-tauri/mesh.cdx.json
Get-Item mesh/build-provenance.json
Get-Item mesh/bundle-report.json
Get-Item mesh/release-evidence/rust-dependency-report.json
)
$evidence = @($installers) + $sboms
$checksumPath = Join-Path $PWD "mesh/SHA256SUMS.txt"
$evidence |
ForEach-Object {
$hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
"$hash $($_.Name)"
} |
Sort-Object |
Out-File $checksumPath -Encoding ascii
"MESH_CHECKSUM_PATH=$checksumPath" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
- name: Attest signed release provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: |
mesh/src-tauri/target/release/bundle/**/*.msi
mesh/src-tauri/target/release/bundle/**/*.exe
mesh/mesh-node.cdx.json
mesh/src-tauri/mesh.cdx.json
mesh/build-provenance.json
mesh/bundle-report.json
mesh/release-evidence/rust-dependency-report.json
mesh/SHA256SUMS.txt
- name: Scan release artifacts for secrets
shell: pwsh
env:
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: |
& ./mesh/scripts/scan-release-artifacts.ps1 -Path @(
"mesh/src-tauri/target/release/bundle",
"mesh/mesh-node.cdx.json",
"mesh/src-tauri/mesh.cdx.json",
"mesh/build-provenance.json",
"mesh/bundle-report.json",
"mesh/release-evidence/rust-dependency-report.json",
$env:MESH_CHECKSUM_PATH
)
- name: Create reviewed candidate as a draft prerelease
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release view $env:MESH_RELEASE_TAG --json tagName *> $null
if ($LASTEXITCODE -eq 0) {
throw "A release already exists for $env:MESH_RELEASE_TAG. Refusing to mutate an existing version."
}
$bundleRoot = "mesh/src-tauri/target/release/bundle"
$installers = @(
Get-ChildItem -LiteralPath $bundleRoot -Recurse -File |
Where-Object { $_.Extension -in @(".msi", ".exe") } |
Sort-Object FullName
)
$assets = @(
$installers.FullName
$env:MESH_CHECKSUM_PATH
(Resolve-Path mesh/mesh-node.cdx.json).Path
(Resolve-Path mesh/src-tauri/mesh.cdx.json).Path
(Resolve-Path mesh/build-provenance.json).Path
(Resolve-Path mesh/bundle-report.json).Path
(Resolve-Path mesh/release-evidence/rust-dependency-report.json).Path
)
$notesPath = Join-Path $env:RUNNER_TEMP "release-notes.txt"
@"
Signed Matrix-only beta candidate for external acceptance. Legacy libp2p is not included.
This draft is not a public release and must not be promoted until the owner-controlled R2 acceptance and publication lifecycle is reviewed and complete.
Verify downloads against ``SHA256SUMS.txt`` and inspect the attached CycloneDX SBOMs.
Automatic updates are intentionally disabled until Mesh has a provisioned signed-update endpoint and updater public key. Install this beta only in a controlled test environment.
"@ | Out-File $notesPath -Encoding utf8
gh release create $env:MESH_RELEASE_TAG @assets `
--draft `
--prerelease `
--title "Mesh $env:MESH_RELEASE_TAG signed beta candidate" `
--notes-file $notesPath
- name: Remove imported signing certificate
if: always()
shell: pwsh
env:
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: |
if (-not [string]::IsNullOrWhiteSpace($env:WINDOWS_CERTIFICATE_THUMBPRINT)) {
$thumbprint = $env:WINDOWS_CERTIFICATE_THUMBPRINT.Replace(" ", "").ToUpperInvariant()
$certificatePath = "Cert:\CurrentUser\My\$thumbprint"
if (Test-Path -LiteralPath $certificatePath) {
Remove-Item -LiteralPath $certificatePath -Force
}
}