ci: test every pull request and add native Windows ARM64 coverage

This commit is contained in:
kuddev committed 2026-09-16 19:40:32 +08:00
1 parent dcf7f3bb8c
commit facd48c1b2
8 files changed
+186 -34

No files matched your search

+23 -22
View File
@@ -9,27 +9,14 @@ on:
default: ""
workflow_call:
push:
# Releases call this entire workflow instead of duplicating branch-push jobs.
branches-ignore: ["release/**"]
paths:
- "**/*.rs"
- "scripts/**/*.py"
- "**/Cargo.toml"
- "Cargo.lock"
- "rust-toolchain.toml"
- ".github/workflows/linux-lua.yml"
- ".github/ci-profile.toml"
- ".github/actions/rust-cache/action.yml"
branches: [main]
# Include docs, catalogs, assets and workflow edits: every PR gets test results.
pull_request:
paths:
- "**/*.rs"
- "scripts/**/*.py"
- "**/Cargo.toml"
- "Cargo.lock"
- "rust-toolchain.toml"
- ".github/workflows/linux-lua.yml"
- ".github/ci-profile.toml"
- ".github/actions/rust-cache/action.yml"
merge_group:
concurrency:
group: native-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }}
permissions:
contents: read
@@ -48,7 +35,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-2022, macos-26, macos-26-intel]
os: [ubuntu-24.04, windows-2022, windows-11-arm, macos-26, macos-26-intel]
runs-on: ${{ matrix.os }}
timeout-minutes: 120
env:
@@ -82,6 +69,17 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.97.1"
- name: Require a native Windows ARM64 compiler
if: matrix.os == 'windows-11-arm'
shell: pwsh
run: |
if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -ne 'Arm64') {
throw 'The Windows ARM job requires an ARM64 host.'
}
$compiler = rustc -vV
if ($LASTEXITCODE -ne 0 -or $compiler -notcontains 'host: aarch64-pc-windows-msvc') {
throw 'The Windows ARM job must compile and run ARM64 tests.'
}
- uses: ./.github/actions/rust-cache
id: rust-cache
with:
@@ -94,7 +92,10 @@ jobs:
- name: Prepare pinned Windows console runtime
if: runner.os == 'Windows'
shell: pwsh
run: ./scripts/prepare-windows-runtime.ps1 -Destination assets/windows/conhost
run: |
$architecture = if ('${{ runner.arch }}' -eq 'ARM64') { 'arm64' } else { 'x64' }
./scripts/prepare-windows-runtime.ps1 -Destination assets/windows/conhost -Architecture $architecture
./scripts/tests/windows-runtime.tests.ps1 -Architecture $architecture
- name: Test complete workspace and native harness
run: python scripts/ci_native_tests.py
- name: Check source architecture
+47 -2
View File
@@ -5,10 +5,46 @@ run-name: >-
on:
pull_request:
paths: ["**/*.rs", "**/Cargo.toml", "Cargo.lock", "rust-toolchain.toml", "nebula_app/res/**", "assets/**", "packaging/**", "scripts/**", ".github/workflows/preview-packages.yml"]
# Native tests cover every PR; package only when package inputs change.
paths:
- "**/Cargo.toml"
- "Cargo.lock"
- "rust-toolchain.toml"
- "nebula_app/build.rs"
- "nebula_app/build/**"
- "nebula_app/res/**"
- "nebula_app/windows/**"
- "assets/**"
- "packaging/**"
- "scripts/package*"
- "scripts/build-*"
- "scripts/installer*"
- "scripts/prepare-windows-runtime.ps1"
- "scripts/preview_release.py"
- "scripts/conformance/**"
- "scripts/tests/test_preview_release.py"
- ".github/workflows/preview-packages.yml"
push:
branches: [main]
paths: ["**/*.rs", "**/Cargo.toml", "Cargo.lock", "rust-toolchain.toml", "nebula_app/res/**", "assets/**", "packaging/**", "scripts/**", ".github/workflows/preview-packages.yml"]
# Native tests cover every PR; package only when package inputs change.
paths:
- "**/Cargo.toml"
- "Cargo.lock"
- "rust-toolchain.toml"
- "nebula_app/build.rs"
- "nebula_app/build/**"
- "nebula_app/res/**"
- "nebula_app/windows/**"
- "assets/**"
- "packaging/**"
- "scripts/package*"
- "scripts/build-*"
- "scripts/installer*"
- "scripts/prepare-windows-runtime.ps1"
- "scripts/preview_release.py"
- "scripts/conformance/**"
- "scripts/tests/test_preview_release.py"
- ".github/workflows/preview-packages.yml"
workflow_dispatch:
inputs:
preview_id:
@@ -159,6 +195,9 @@ jobs:
with:
cache-on-failure: true
- name: Prepare pinned Windows console runtime
shell: pwsh
run: ./scripts/prepare-windows-runtime.ps1 -Destination assets/windows/conhost
- name: Test workspace natively
run: cargo test --locked --workspace
@@ -339,6 +378,9 @@ jobs:
with:
cache-on-failure: true
- name: Prepare pinned Windows console runtime
shell: pwsh
run: ./scripts/prepare-windows-runtime.ps1 -Destination assets/windows/conhost
- name: Test workspace natively
run: cargo test --locked --workspace
@@ -497,6 +539,9 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: Prepare pinned Windows console runtime
shell: pwsh
run: ./scripts/prepare-windows-runtime.ps1 -Destination assets/windows/conhost
- name: Test workspace natively
run: cargo test --locked --workspace
- name: Test SSH confirmation and password dialogs
+12
View File
@@ -101,6 +101,18 @@ Run affected behavior tests and the appropriate real product checks as well:
cargo check -p nebula --bin pebrel --features gpui-shell --tests --locked
```
`Full native tests` runs on every PR and merge-group update, and on `main` pushes.
It tests the full workspace on Linux, Windows x64 / ARM64 and both macOS
architectures. New commits cancel obsolete PR runs. Package validation has its own
input filters and can also be dispatched manually; a package job does not replace
the native test suite. These triggers do not configure required status checks.
GitHub may show **Waiting for approval** for a first-time fork contributor. A
maintainer must inspect the submitted changes and approve that workflow run from
the Actions page before tests start. Pushing more commits does not remove this
GitHub approval requirement; do not run fork code through `pull_request_target`
or give it a write token to work around the wait.
The isolated i18n test compiles production files, not copied implementations. It
does not test real window layout, every OS integration, or the full application.
Existing platform build/package checks still apply. Never turn a metadata-only
+4
View File
@@ -29,6 +29,10 @@ The bundled conpty.dll and OpenConsole.exe come from the Windows
Terminal project (github.com/microsoft/terminal), distributed under the
MIT License.
The Windows ARM64 CI runtime is pinned to the Microsoft.Windows.Console.ConPTY
NuGet package 1.24.260710001 from Microsoft. Archive and individual redistributable
hashes, architecture and source URL are verified by scripts/prepare-windows-runtime.ps1.
The GPUI interface layer is built with gpui 0.2.2, developed by Zed
Industries (github.com/zed-industries/zed), and gpui-component 0.5.1,
developed by Longbridge (github.com/longbridge/gpui-component). Both are
+42 -4
View File
@@ -1,7 +1,8 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string] $Destination,
[string] $ArchivePath
[string] $ArchivePath,
[ValidateSet('x64', 'arm64')][string] $Architecture = 'x64'
)
$ErrorActionPreference = 'Stop'
@@ -16,9 +17,44 @@ $expected = [ordered]@{
'conpty.dll' = '375BFB0479B6C53836AB307E3F9FD17BEDBD733F2E9690943D0F12E72FB80777'
'OpenConsole.exe' = '55B18996761C88C351820E82508E05AB0EC2194AEEAD20724FDFAEEDEC076EF4'
}
$entries = @{
'conpty.dll' = 'runtime\conpty.dll'
'OpenConsole.exe' = 'runtime\OpenConsole.exe'
}
$archiveName = 'pebrel-conpty-source-v1.5.0.zip'
$machine = 0x8664
if ($Architecture -eq 'arm64') {
# Microsoft MIT-licensed redistributables; the NuGet declares build 17763+.
# Keep the previously shipped x64 runtime unchanged.
$sourceUrl = 'https://api.nuget.org/v3-flatcontainer/microsoft.windows.console.conpty/1.24.260710001/microsoft.windows.console.conpty.1.24.260710001.nupkg'
$archiveName = 'pebrel-conpty-source-1.24.260710001.nupkg'
$archiveHash = '175640566A3B59C4B132070EE96C2C77E5AB7EDD2E92732A5EB3610BBF63D90E'
$expected = [ordered]@{
'conpty.dll' = 'DB3D173640B172BAFD42D5B541B638A9AEEC1C7D0E40DD636BF02822A32C912C'
'OpenConsole.exe' = 'ED7622FD0D3BEDC9AB9F122F5E58EDF0DEF9E7999224F52DD395BA9F54EDBE09'
}
$entries = @{
'conpty.dll' = 'runtimes/win-arm64/native/conpty.dll'
'OpenConsole.exe' = 'build/native/runtimes/arm64/OpenConsole.exe'
}
$machine = 0xAA64
}
function Assert-RuntimeMachine([string] $Path) {
$bytes = [IO.File]::ReadAllBytes($Path)
if ($bytes.Length -lt 64 -or $bytes[0] -ne 0x4D -or $bytes[1] -ne 0x5A) {
throw "Invalid PE runtime: $Path"
}
$offset = [BitConverter]::ToInt32($bytes, 0x3C)
if ($offset -lt 64 -or $offset -gt $bytes.Length - 6 -or
[BitConverter]::ToUInt32($bytes, $offset) -ne 0x4550 -or
[BitConverter]::ToUInt16($bytes, $offset + 4) -ne $machine) {
throw "Runtime PE architecture does not match $Architecture`: $Path"
}
}
if ([string]::IsNullOrWhiteSpace($ArchivePath)) {
$ArchivePath = Join-Path ([System.IO.Path]::GetTempPath()) 'pebrel-conpty-source-v1.5.0.zip'
$ArchivePath = Join-Path ([System.IO.Path]::GetTempPath()) $archiveName
if (-not (Test-Path -LiteralPath $ArchivePath -PathType Leaf)) {
Invoke-WebRequest -Uri $sourceUrl -OutFile $ArchivePath -UseBasicParsing -TimeoutSec 120
}
@@ -35,10 +71,11 @@ try {
$target = Join-Path $Destination $name
if ((Test-Path -LiteralPath $target -PathType Leaf) -and
(Get-FileHash -LiteralPath $target -Algorithm SHA256).Hash -eq $expected[$name]) {
Assert-RuntimeMachine $target
continue
}
$entry = $archive.GetEntry("runtime\$name")
if ($null -eq $entry) { throw "Runtime archive is missing runtime/$name" }
$entry = $archive.GetEntry($entries[$name])
if ($null -eq $entry) { throw "Runtime archive is missing $($entries[$name])" }
$temporary = "$target.$([guid]::NewGuid().ToString('N')).tmp"
try {
$inputStream = $entry.Open()
@@ -49,6 +86,7 @@ try {
if ((Get-FileHash -LiteralPath $temporary -Algorithm SHA256).Hash -ne $expected[$name]) {
throw "The pinned $name failed SHA256 verification."
}
Assert-RuntimeMachine $temporary
Move-Item -LiteralPath $temporary -Destination $target -Force
} finally {
if (Test-Path -LiteralPath $temporary) { Remove-Item -LiteralPath $temporary }
+31
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import subprocess
import re
from pathlib import Path
import tomllib
import unittest
@@ -10,6 +11,36 @@ from scripts.ci_native_tests import main, native_commands
class NativeSuiteTests(unittest.TestCase):
def test_every_pr_and_merge_group_runs_without_path_exclusions(self):
root = Path(__file__).resolve().parents[2]
workflow = (root / ".github/workflows/linux-lua.yml").read_text()
events = workflow.split("\non:\n", 1)[1].split("\nconcurrency:", 1)[0]
for event in ("pull_request", "merge_group"):
declaration = re.search(rf"^ {event}:(.*?)(?=^ [a-z_]+:|\Z)", events, re.M | re.S)
self.assertIsNotNone(declaration)
self.assertNotIn("paths", declaration.group(1))
self.assertNotIn("branches", declaration.group(1))
self.assertNotIn("types", declaration.group(1))
self.assertIn("branches: [main]", events)
self.assertNotIn("branches-ignore", events)
self.assertNotIn("pull_request_target", workflow)
self.assertNotIn("contents: write", workflow)
self.assertIn("cancel-in-progress: ${{ github.event_name == 'pull_request'", workflow)
def test_arm_job_requires_native_execution_and_matching_console_runtime(self):
root = Path(__file__).resolve().parents[2]
workflow = (root / ".github/workflows/linux-lua.yml").read_text()
self.assertIn("windows-11-arm", workflow)
self.assertIn("host: aarch64-pc-windows-msvc", workflow)
self.assertIn("OSArchitecture -ne 'Arm64'", workflow)
self.assertIn("-Architecture $architecture", workflow)
self.assertLess(workflow.index("Prepare pinned Windows console runtime"),
workflow.index("Test complete workspace"))
preview = (root / ".github/workflows/preview-packages.yml").read_text()
windows = preview.split("\n windows:\n", 1)[1].split("\n aggregate:", 1)[0]
self.assertLess(windows.index("prepare-windows-runtime.ps1"),
windows.index("cargo test --locked --workspace"))
def test_full_workspace_and_interactions_share_one_unfiltered_invocation(self):
rust = [command for command in native_commands() if command[:2] == ["cargo", "test"]]
self.assertEqual(len(rust), 1)
+1 -1
View File
@@ -74,7 +74,7 @@ class StableReleaseTests(unittest.TestCase):
native = workflow.split(" native-tests:\n", 1)[1].split("\n linux:\n", 1)[0]
self.assertIn("uses: ./.github/workflows/linux-lua.yml", native)
shared = (root / ".github/workflows/linux-lua.yml").read_text(encoding="utf-8")
for platform in ("ubuntu-24.04", "windows-2022", "macos-26", "macos-26-intel"):
for platform in ("ubuntu-24.04", "windows-2022", "windows-11-arm", "macos-26", "macos-26-intel"):
self.assertIn(platform, shared)
self.assertIn("workflow_call:", shared)
self.assertIn("run: python scripts/ci_native_tests.py", shared)
+26 -5
View File
@@ -1,5 +1,8 @@
[CmdletBinding()]
param([Parameter(Mandatory = $true)][string] $ArchivePath)
param(
[string] $ArchivePath,
[ValidateSet('x64', 'arm64')][string] $Architecture = 'x64'
)
$ErrorActionPreference = 'Stop'
$prepare = Join-Path $PSScriptRoot '../prepare-windows-runtime.ps1'
@@ -7,22 +10,40 @@ $root = Join-Path ([IO.Path]::GetTempPath()) "pebrel-runtime-test-$([guid]::NewG
New-Item -ItemType Directory -Path $root | Out-Null
try {
$destination = Join-Path $root 'runtime'
& $prepare -Destination $destination -ArchivePath $ArchivePath
& $prepare -Destination $destination -ArchivePath $ArchivePath -Architecture $Architecture
$before = @(Get-ChildItem $destination | Sort-Object Name | ForEach-Object { $_.LastWriteTimeUtc.Ticks })
& $prepare -Destination $destination -ArchivePath $ArchivePath
& $prepare -Destination $destination -ArchivePath $ArchivePath -Architecture $Architecture
$after = @(Get-ChildItem $destination | Sort-Object Name | ForEach-Object { $_.LastWriteTimeUtc.Ticks })
if (@(Compare-Object $before $after).Count -ne 0) { throw 'Verified runtime files were unnecessarily replaced.' }
if (@(Get-ChildItem $destination).Count -ne 2) { throw 'Unexpected files were extracted.' }
$dll = Join-Path $destination 'conpty.dll'
$originalHash = (Get-FileHash $dll -Algorithm SHA256).Hash
[IO.File]::WriteAllBytes($dll, [byte[]]@(0, 1, 2, 3))
& $prepare -Destination $destination -ArchivePath $ArchivePath -Architecture $Architecture
if ((Get-FileHash $dll -Algorithm SHA256).Hash -ne $originalHash) {
throw 'A corrupt or wrong-architecture installed runtime was reused.'
}
$invalid = Join-Path $root 'different.zip'
[IO.File]::WriteAllBytes($invalid, [byte[]]@(80, 75, 0, 0))
$rejected = $false
try { & $prepare -Destination (Join-Path $root 'rejected') -ArchivePath $invalid }
try { & $prepare -Destination (Join-Path $root 'rejected') -ArchivePath $invalid -Architecture $Architecture }
catch {
if ($_.Exception.Message -notlike '*SHA256 verification*') { throw }
$rejected = $true
}
if (-not $rejected) { throw 'A different archive was accepted.' }
if (Test-Path (Join-Path $root 'rejected')) { throw 'Files were created before archive verification.' }
Write-Output 'windows-runtime.tests.ps1: PASS (pinned files, incremental reuse, hash mismatch)'
if ($ArchivePath) {
$otherArchitecture = if ($Architecture -eq 'arm64') { 'x64' } else { 'arm64' }
$rejected = $false
try { & $prepare -Destination (Join-Path $root 'wrong-arch') -ArchivePath $ArchivePath -Architecture $otherArchitecture }
catch {
if ($_.Exception.Message -notlike '*SHA256 verification*') { throw }
$rejected = $true
}
if (-not $rejected) { throw 'An archive for the other architecture was accepted.' }
if (Test-Path (Join-Path $root 'wrong-arch')) { throw 'A wrong-architecture archive was extracted.' }
}
Write-Output "windows-runtime.tests.ps1: PASS ($Architecture pinned files, reuse, repair, rejected archives)"
} finally { Remove-Item -LiteralPath $root -Recurse -Force }