mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-07 17:09:13 -10:00
ci(release): probe windows-arm64 release imports on a hosted ARM64 runner (CW-7384)
Release candidate 38867608774 ondb34bc8949failed only at the windows-arm64 static import check: codewhale, codew and codewhale-tui each carry 7 import pairs the arm64 floor never qualified (api-ms-win-crt-math _dclass/copysign/ldexp/round/trunc, kernel32 LoadLibraryExW, vcruntime140 __std_terminate). The floor was qualified on 2026-10-13 from a debug build ofcf7199b52e, on one ARM64 machine (build 26300), by an ad-hoc export probe that is not in the repository. A release build imports more, and requalifying meant waiting for that machine. This dispatch-only workflow reruns the same probe on GitHub's windows-11-arm runner against the import receipt an RC run already uploads (windows-imports-windows-arm64-<sha>), so no rebuild is needed: - it refuses to run unless the probing pwsh process is Arm64; - for every pair in the receipt's three binaries it calls LoadLibraryExW with LOAD_LIBRARY_SEARCH_SYSTEM32 (0x800) and GetProcAddress, the method the floor records; - it writes export-probe.json with the OS build, runner, source SHA and run, each binary's SHA256, resolved and unresolved counts, and each system module's path and SHA256, uploads it, and fails if any pair does not resolve. The floor itself is not changed here; it changes only from a PASS probe. Inputs reach the script through env, not interpolation. Verification: actionlint clean; YAML parses. The action pins match the ones release.yml and release-artifacts.yml already use. It has not run yet; its first dispatch is against run 38867608774 /db34bc8949. Refs CW-7384 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
This commit is contained in:
1 parent
2045e6e28a
commit
9f7134bcd1
1 file changed
+103
@@ -0,0 +1,103 @@
|
||||
name: Windows ARM64 export probe
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_id:
|
||||
description: Release-artifacts or release-candidate run whose windows-arm64 import receipt is probed
|
||||
required: true
|
||||
source_sha:
|
||||
description: Source commit of that run (names the receipt artifact)
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
probe:
|
||||
runs-on: windows-11-arm
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: windows-imports-windows-arm64-${{ inputs.source_sha }}
|
||||
path: receipt
|
||||
run-id: ${{ inputs.run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
- name: Resolve every imported pair in a native ARM64 process
|
||||
shell: pwsh
|
||||
env:
|
||||
SOURCE_SHA: ${{ inputs.source_sha }}
|
||||
SOURCE_RUN: ${{ inputs.run_id }}
|
||||
run: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$arch = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString()
|
||||
if ($arch -ne 'Arm64') { throw "probe process is $arch, not Arm64" }
|
||||
Add-Type -TypeDefinition @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
public static class ExportProbe {
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern IntPtr LoadLibraryExW(string name, IntPtr file, uint flags);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
|
||||
public static extern IntPtr GetProcAddress(IntPtr module, string name);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern uint GetModuleFileNameW(IntPtr module, StringBuilder path, uint size);
|
||||
}
|
||||
'@
|
||||
$receipt = Get-Content -Raw receipt/receipt.json | ConvertFrom-Json
|
||||
$pairs = @{}
|
||||
foreach ($binary in $receipt) {
|
||||
foreach ($pair in $binary.imports) { $pairs["$($pair[0].ToLowerInvariant())|$($pair[1])"] = $pair }
|
||||
}
|
||||
$modules = [ordered]@{}
|
||||
$unresolved = @()
|
||||
$resolved = 0
|
||||
foreach ($key in ($pairs.Keys | Sort-Object)) {
|
||||
$dll = $pairs[$key][0].ToLowerInvariant()
|
||||
$symbol = $pairs[$key][1]
|
||||
if (-not $modules.Contains($dll)) {
|
||||
$handle = [ExportProbe]::LoadLibraryExW($dll, [IntPtr]::Zero, 0x00000800)
|
||||
if ($handle -eq [IntPtr]::Zero) {
|
||||
$modules[$dll] = @{ handle = [IntPtr]::Zero; path = $null; sha256 = $null; error = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() }
|
||||
} else {
|
||||
$path = [System.Text.StringBuilder]::new(1024)
|
||||
[void][ExportProbe]::GetModuleFileNameW($handle, $path, 1024)
|
||||
$modules[$dll] = @{ handle = $handle; path = $path.ToString(); sha256 = (Get-FileHash -Algorithm SHA256 $path.ToString()).Hash.ToLowerInvariant(); error = 0 }
|
||||
}
|
||||
}
|
||||
$module = $modules[$dll]
|
||||
if ($module.handle -ne [IntPtr]::Zero -and [ExportProbe]::GetProcAddress($module.handle, $symbol) -ne [IntPtr]::Zero) { $resolved++ } else { $unresolved += ,@($dll, $symbol) }
|
||||
}
|
||||
$version = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
|
||||
$result = [ordered]@{
|
||||
result = $(if ($unresolved.Count -eq 0) { 'PASS' } else { 'FAIL' })
|
||||
kind = 'exact native system export resolution'
|
||||
time = (Get-Date).ToUniversalTime().ToString('o')
|
||||
osVersion = [System.Environment]::OSVersion.Version.ToString()
|
||||
osBuild = "$($version.CurrentBuild).$($version.UBR)"
|
||||
osProduct = $version.ProductName
|
||||
processArchitecture = 'ARM64'
|
||||
runner = 'GitHub-hosted windows-11-arm'
|
||||
source = $env:SOURCE_SHA
|
||||
sourceRun = $env:SOURCE_RUN
|
||||
binaries = @($receipt | ForEach-Object { [ordered]@{ binary = $_.binary; sha256 = $_.sha256 } })
|
||||
resolved = $resolved
|
||||
unresolved = $unresolved.Count
|
||||
unresolvedPairs = $unresolved
|
||||
exportProbeMethod = 'ARM64 process: LoadLibraryExW per imported DLL with LOAD_LIBRARY_SEARCH_SYSTEM32 (0x00000800), then GetProcAddress for every imported symbol; module path and SHA256 recorded'
|
||||
systemModules = [ordered]@{}
|
||||
}
|
||||
foreach ($dll in $modules.Keys) { $result.systemModules[$dll] = [ordered]@{ path = $modules[$dll].path; sha256 = $modules[$dll].sha256; loadError = $modules[$dll].error } }
|
||||
New-Item -ItemType Directory -Force probe | Out-Null
|
||||
$result | ConvertTo-Json -Depth 6 | Set-Content -Encoding utf8 probe/export-probe.json
|
||||
Get-Content probe/export-probe.json
|
||||
if ($unresolved.Count -ne 0) { throw "$($unresolved.Count) imported pairs did not resolve" }
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
if: always()
|
||||
with:
|
||||
name: windows-arm64-export-probe-${{ inputs.source_sha }}
|
||||
path: probe/
|
||||
if-no-files-found: warn
|
||||
Reference in new issue
Block a user