This repository has been archived on 2026-10-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

625 lines
22 KiB
YAML

name: WSH Windows CI
on:
push:
branches:
- master
tags:
- "**"
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: windows-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
source-quality:
name: Source quality
runs-on: windows-2022
timeout-minutes: 30
steps:
- name: Check out source and WSP
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
submodules: recursive
persist-credentials: false
- name: Use isolated WPM data directory
shell: pwsh
run: '"WPM_DATA_DIR=$env:RUNNER_TEMP\wpm-data" >> $env:GITHUB_ENV'
- name: Install WPM
shell: pwsh
run: |
$installer = Join-Path $env:RUNNER_TEMP 'install-wpm.cmd'
Invoke-WebRequest -UseBasicParsing `
'https://github.com/Thewafflication/wpm/releases/latest/download/install.cmd' `
-OutFile $installer
& $installer
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$wpmDirectory = Join-Path $env:ProgramFiles 'WPM'
$wpm = Join-Path $wpmDirectory 'wpm.exe'
if (-not (Test-Path -LiteralPath $wpm -PathType Leaf)) {
throw "WPM installation did not produce $wpm."
}
$wpmDirectory >> $env:GITHUB_PATH
- name: Install pinned TinyCC and WCRT
shell: pwsh
run: ./tools/Install-BuildDependencies.ps1 -Architecture x64
- name: Install Doxygen
shell: pwsh
run: |
choco install doxygen.install --no-progress --yes
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$doxygenDirectory = Join-Path $env:ProgramFiles 'doxygen\bin'
$doxygen = Join-Path $doxygenDirectory 'doxygen.exe'
if (-not (Test-Path -LiteralPath $doxygen -PathType Leaf)) {
throw "Doxygen installation did not produce $doxygen."
}
$doxygenDirectory >> $env:GITHUB_PATH
- name: Verify pinned WSP tools
shell: pwsh
run: |
./wsp/tools/tests/run-tests.ps1
$global:LASTEXITCODE = 0
- name: Configure source-quality build
shell: pwsh
run: |
cmake --preset x64-debug `
-B out/build/source-quality `
-DWSH_ENABLE_SOURCE_QUALITY_TESTS=ON
- name: Build with TinyCC warnings
id: quality_build
continue-on-error: true
shell: pwsh
run: |
New-Item -ItemType Directory -Force out/logs | Out-Null
cmake --build out/build/source-quality `
--target wsh wsh_shared 2>&1 |
Tee-Object out/logs/source-quality-build.log
exit $LASTEXITCODE
- name: Run traceability and source lint
if: steps.quality_build.outcome == 'success'
id: quality_tests
continue-on-error: true
shell: pwsh
run: |
$junit = Join-Path $env:GITHUB_WORKSPACE `
'out/build/source-quality/quality-results.xml'
ctest --test-dir out/build/source-quality `
-R '^((m2|m3|m4|m5|m6|m7|m8|m9)-traceability|(m2|m3|m4|m5)-source-quality)$' `
--output-on-failure `
--output-junit $junit `
2>&1 | Tee-Object out/logs/source-quality-test.log
exit $LASTEXITCODE
- name: Collect source-quality evidence
if: always()
shell: pwsh
run: |
./tools/New-CiEvidenceArchive.ps1 `
-Architecture x64 `
-Configuration Debug `
-BuildDirectory out/build/source-quality `
-SourceRevision '${{ github.sha }}' `
-BuildLog out/logs/source-quality-build.log `
-TestLog out/logs/source-quality-test.log `
-JUnitPath out/build/source-quality/quality-results.xml `
-OutputPath out/artifacts/wsh-source-quality-${{ github.sha }}.zip
- name: Upload source-quality evidence
if: always()
id: upload_quality
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: wsh-source-quality-${{ github.sha }}
path: out/artifacts/wsh-source-quality-${{ github.sha }}.zip
archive: false
if-no-files-found: error
retention-days: 90
- name: Publish source-quality summary
if: always()
shell: pwsh
run: |
./tools/Write-CiTestSummary.ps1 `
-Architecture x64 `
-Configuration 'Source quality' `
-BuildOutcome '${{ steps.quality_build.outcome }}' `
-TestOutcome '${{ steps.quality_tests.outcome }}' `
-JUnitPath out/build/source-quality/quality-results.xml `
-EvidenceArtifactUrl '${{ steps.upload_quality.outputs.artifact-url }}'
- name: Enforce source-quality gate
if: >-
always() &&
(steps.quality_build.outcome != 'success' ||
steps.quality_tests.outcome != 'success')
shell: pwsh
run: throw 'Source-quality build or lint failed.'
debug:
name: ${{ matrix.arch }} Debug
needs: source-quality
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- arch: x86
runner: windows-2022
preset: x86-debug
build_directory: out/build/x86-debug
- arch: x64
runner: windows-2022
preset: x64-debug
build_directory: out/build/x64-debug
- arch: arm64
runner: windows-11-arm
preset: arm64-debug
build_directory: out/build/arm64-debug
steps:
- name: Check out source and WSP
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
submodules: recursive
persist-credentials: false
- name: Verify native ARM64 runner
if: matrix.arch == 'arm64'
shell: pwsh
run: |
if ($env:RUNNER_ARCH -ne 'ARM64') {
throw "ARM64 tests require a native ARM64 runner; got $env:RUNNER_ARCH."
}
- name: Use isolated WPM data directory
shell: pwsh
run: '"WPM_DATA_DIR=$env:RUNNER_TEMP\wpm-data" >> $env:GITHUB_ENV'
- name: Install WPM
shell: pwsh
run: |
$installer = Join-Path $env:RUNNER_TEMP 'install-wpm.cmd'
Invoke-WebRequest -UseBasicParsing `
'https://github.com/Thewafflication/wpm/releases/latest/download/install.cmd' `
-OutFile $installer
& $installer
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$wpmDirectory = Join-Path $env:ProgramFiles 'WPM'
$wpm = Join-Path $wpmDirectory 'wpm.exe'
if (-not (Test-Path -LiteralPath $wpm -PathType Leaf)) {
throw "WPM installation did not produce $wpm."
}
$wpmDirectory >> $env:GITHUB_PATH
- name: Install pinned TinyCC and WCRT
shell: pwsh
run: ./tools/Install-BuildDependencies.ps1 -Architecture '${{ matrix.arch }}'
- name: Configure Debug
shell: pwsh
run: >-
cmake --preset '${{ matrix.preset }}'
-DWSH_ENABLE_SOURCE_QUALITY_TESTS=OFF
- name: Build Debug
id: build
continue-on-error: true
shell: pwsh
run: |
New-Item -ItemType Directory -Force out/logs | Out-Null
cmake --build --preset '${{ matrix.preset }}' 2>&1 |
Tee-Object 'out/logs/${{ matrix.arch }}-debug-build.log'
exit $LASTEXITCODE
- name: Validate native ARM64 kernel32 imports
if: matrix.arch == 'arm64' && steps.build.outcome == 'success'
shell: pwsh
run: |
$readobj = (Get-Command llvm-readobj -ErrorAction Stop).Source
$executable = (Resolve-Path `
'${{ matrix.build_directory }}/Debug/wsh.exe').Path
$symbols = @(
& $readobj --coff-imports $executable |
Select-String '^\s+Symbol:' |
ForEach-Object {
$_.Line -replace '^\s+Symbol:\s*', '' `
-replace '\s+\(\d+\)$', ''
}
)
$kernel = [Runtime.InteropServices.NativeLibrary]::Load(
'kernel32.dll')
try {
$missing = @($symbols | Where-Object {
$address = [IntPtr]::Zero
-not [Runtime.InteropServices.NativeLibrary]::TryGetExport(
$kernel, $_, [ref]$address)
})
} finally {
[Runtime.InteropServices.NativeLibrary]::Free($kernel)
}
if ($missing.Count -ne 0) {
throw "Missing kernel32 imports: $($missing -join ', ')"
}
- name: Run Debug test suite
if: steps.build.outcome == 'success'
id: tests
continue-on-error: true
shell: pwsh
run: |
$junit = Join-Path $env:GITHUB_WORKSPACE `
'${{ matrix.build_directory }}/test-results.xml'
$ctestArguments = @(
'--preset', '${{ matrix.preset }}',
'--output-on-failure',
'--output-junit', $junit,
'--timeout', '120')
if ('${{ matrix.arch }}' -eq 'arm64') {
$ctestArguments += '--stop-on-failure'
}
ctest @ctestArguments 2>&1 |
Tee-Object 'out/logs/${{ matrix.arch }}-debug-test.log'
exit $LASTEXITCODE
- name: Build Debug WPM package
if: steps.build.outcome == 'success'
id: package
continue-on-error: true
shell: pwsh
run: |
$shortRevision = '${{ github.sha }}'.Substring(0, 12)
./tools/Build-WpmPackage.ps1 `
-Architecture '${{ matrix.arch }}' `
-Configuration Debug `
-Version "1.3.3+$shortRevision" `
-BuildDirectory '${{ matrix.build_directory }}'
- name: Collect Debug build and test evidence
if: always()
shell: pwsh
run: |
./tools/New-CiEvidenceArchive.ps1 `
-Architecture '${{ matrix.arch }}' `
-Configuration Debug `
-BuildDirectory '${{ matrix.build_directory }}' `
-SourceRevision '${{ github.sha }}' `
-BuildLog 'out/logs/${{ matrix.arch }}-debug-build.log' `
-TestLog 'out/logs/${{ matrix.arch }}-debug-test.log' `
-JUnitPath '${{ matrix.build_directory }}/test-results.xml' `
-PackageDirectory out/packages `
-OutputPath 'out/artifacts/wsh-${{ matrix.arch }}-debug-${{ github.sha }}.zip'
- name: Upload Debug build and test evidence
if: always()
id: upload_evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: wsh-${{ matrix.arch }}-debug-${{ github.sha }}
path: out/artifacts/wsh-${{ matrix.arch }}-debug-${{ github.sha }}.zip
archive: false
if-no-files-found: error
retention-days: 90
- name: Publish Debug test summary
if: always()
shell: pwsh
run: |
$package = @(Get-ChildItem out/packages `
-Filter 'wsh-${{ matrix.arch }}-debug-*.zip' `
-File -ErrorAction SilentlyContinue)
$packagePath = if ($package.Count -eq 1) {
$package[0].FullName
} else {
''
}
./tools/Write-CiTestSummary.ps1 `
-Architecture '${{ matrix.arch }}' `
-Configuration Debug `
-BuildOutcome '${{ steps.build.outcome }}' `
-TestOutcome '${{ steps.tests.outcome }}' `
-PackageOutcome '${{ steps.package.outcome }}' `
-JUnitPath '${{ matrix.build_directory }}/test-results.xml' `
-PackagePath $packagePath `
-EvidenceArtifactUrl '${{ steps.upload_evidence.outputs.artifact-url }}'
- name: Enforce Debug build, test, and package results
if: >-
always() &&
(steps.build.outcome != 'success' ||
steps.tests.outcome != 'success' ||
steps.package.outcome != 'success')
shell: pwsh
run: throw '${{ matrix.arch }} Debug verification or packaging failed.'
release:
name: ${{ matrix.arch }} Release
if: startsWith(github.ref, 'refs/tags/')
needs: debug
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- arch: x86
runner: windows-2022
preset: x86-release
build_directory: out/build/x86-release
- arch: x64
runner: windows-2022
preset: x64-release
build_directory: out/build/x64-release
- arch: arm64
runner: windows-11-arm
preset: arm64-release
build_directory: out/build/arm64-release
steps:
- name: Check out tagged source and WSP
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
submodules: recursive
persist-credentials: false
- name: Verify semantic release tag
shell: pwsh
run: |
if ('${{ github.ref_name }}' -notmatch `
'^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$') {
throw 'Release tags must contain a semantic version.'
}
- name: Verify native ARM64 runner
if: matrix.arch == 'arm64'
shell: pwsh
run: |
if ($env:RUNNER_ARCH -ne 'ARM64') {
throw "ARM64 builds require a native ARM64 runner; got $env:RUNNER_ARCH."
}
- name: Use isolated WPM data directory
shell: pwsh
run: '"WPM_DATA_DIR=$env:RUNNER_TEMP\wpm-data" >> $env:GITHUB_ENV'
- name: Install WPM
shell: pwsh
run: |
$installer = Join-Path $env:RUNNER_TEMP 'install-wpm.cmd'
Invoke-WebRequest -UseBasicParsing `
'https://github.com/Thewafflication/wpm/releases/latest/download/install.cmd' `
-OutFile $installer
& $installer
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$wpmDirectory = Join-Path $env:ProgramFiles 'WPM'
$wpm = Join-Path $wpmDirectory 'wpm.exe'
if (-not (Test-Path -LiteralPath $wpm -PathType Leaf)) {
throw "WPM installation did not produce $wpm."
}
$wpmDirectory >> $env:GITHUB_PATH
- name: Install pinned TinyCC and WCRT
shell: pwsh
run: ./tools/Install-BuildDependencies.ps1 -Architecture '${{ matrix.arch }}'
- name: Configure Release
shell: pwsh
run: >-
cmake --preset '${{ matrix.preset }}'
-DWSH_ENABLE_SOURCE_QUALITY_TESTS=OFF
- name: Build Release
shell: pwsh
run: cmake --build --preset '${{ matrix.preset }}'
- name: Upload successful Release build
id: upload_build
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: wsh-${{ matrix.arch }}-release-build
path: ${{ matrix.build_directory }}/Release/wsh.exe
if-no-files-found: error
compression-level: 0
retention-days: 7
- name: Publish Release build summary
shell: pwsh
run: |
@(
'# WSH Release Build'
''
'| Architecture | Build | Build artifact |'
'| --- | --- | --- |'
'| ${{ matrix.arch }} | 🟢 **PASS** | [Download](${{ steps.upload_build.outputs.artifact-url }}) |'
) | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Encoding utf8 -Append
release-package:
name: Package successful Release matrix
if: startsWith(github.ref, 'refs/tags/')
needs: release
runs-on: windows-2022
steps:
- name: Check out tagged source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
submodules: recursive
persist-credentials: false
- name: Download successful Release builds
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
pattern: wsh-*-release-build
path: downloaded-builds
- name: Assemble Release build directories
shell: pwsh
run: |
foreach ($architecture in @('x86', 'x64', 'arm64')) {
$source = "downloaded-builds/wsh-$architecture-release-build/wsh.exe"
if (-not (Test-Path -LiteralPath $source -PathType Leaf)) {
throw "The $architecture Release build was not downloaded."
}
$destination = "out/release-builds/$architecture/Release"
New-Item -ItemType Directory -Force -Path $destination |
Out-Null
Copy-Item -LiteralPath $source -Destination $destination
}
- name: Use isolated WPM data directory
shell: pwsh
run: '"WPM_DATA_DIR=$env:RUNNER_TEMP\wpm-data" >> $env:GITHUB_ENV'
- name: Install WPM
shell: pwsh
run: |
$installer = Join-Path $env:RUNNER_TEMP 'install-wpm.cmd'
Invoke-WebRequest -UseBasicParsing `
'https://github.com/Thewafflication/wpm/releases/latest/download/install.cmd' `
-OutFile $installer
& $installer
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$wpmDirectory = Join-Path $env:ProgramFiles 'WPM'
$wpm = Join-Path $wpmDirectory 'wpm.exe'
if (-not (Test-Path -LiteralPath $wpm -PathType Leaf)) {
throw "WPM installation did not produce $wpm."
}
$wpmDirectory >> $env:GITHUB_PATH
- name: Build and verify signed Release WPM packages
shell: pwsh
env:
WPM_PRIVATE_KEY: ${{ secrets.WPM_RELEASE_PRIVATE_KEY }}
run: |
if ([string]::IsNullOrWhiteSpace($env:WPM_PRIVATE_KEY)) {
throw 'Tagged releases require the WPM_RELEASE_PRIVATE_KEY secret.'
}
$keyPath = Join-Path $env:RUNNER_TEMP 'wsh-release.private'
try {
[IO.File]::WriteAllText($keyPath, $env:WPM_PRIVATE_KEY)
foreach ($architecture in @('x86', 'x64', 'arm64')) {
./tools/Build-WpmPackage.ps1 `
-Architecture $architecture `
-Configuration Release `
-Version '${{ github.ref_name }}' `
-BuildDirectory "out/release-builds/$architecture" `
-TinyCcVersion '0.9.28-rc.1444+9a4be30f' `
-WcrtVersion '1.0.0' `
-SigningKey $keyPath
}
wpm trust add release_keys/wpm-release.public
if ($LASTEXITCODE -ne 0) {
throw 'WPM failed to trust the release public key.'
}
$packages = @(Get-ChildItem out/packages/wsh-*.zip -File)
if ($packages.Count -ne 3) {
throw "Expected three signed Release packages; found $($packages.Count)."
}
foreach ($package in $packages) {
wpm verify $package.FullName
if ($LASTEXITCODE -ne 0) {
throw "WPM signature verification failed: $($package.Name)"
}
}
} finally {
Remove-Item $keyPath -Force -ErrorAction SilentlyContinue
}
- name: Upload complete Release WPM package set
id: upload_packages
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: wsh-release-packages
path: out/packages/wsh-*.zip
if-no-files-found: error
compression-level: 0
retention-days: 14
- name: Publish Release package summary
shell: pwsh
run: |
@(
'# WSH Release Packages'
''
'All x86, x64, and ARM64 Release builds succeeded.'
''
'[Download the complete WPM package set](${{ steps.upload_packages.outputs.artifact-url }})'
) | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Encoding utf8 -Append
publish:
name: Publish tagged release
if: startsWith(github.ref, 'refs/tags/')
needs: release-package
runs-on: windows-2022
permissions:
contents: write
steps:
- name: Check out release metadata tools and WSP
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
submodules: recursive
persist-credentials: false
- name: Download Release WPM packages
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: wsh-release-packages
path: release-assets
- name: Create WPM repository assets
shell: pwsh
run: |
Copy-Item release_keys/wpm-release.public `
release-assets/wpm-release.public
./tools/New-WpmRepositoryIndex.ps1 `
-PackageDirectory release-assets `
-OutputDirectory release-assets `
-ReleaseTag '${{ github.ref_name }}' `
-Repository '${{ github.repository }}'
- name: Generate release checksums
shell: pwsh
run: |
$files = @(Get-ChildItem release-assets -File |
Select-Object -ExpandProperty FullName)
./wsp/tools/New-ArtifactChecksum.ps1 `
-Path $files `
-OutputPath release-assets/SHA256SUMS
- name: Create release and upload assets
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$tag = '${{ github.ref_name }}'
gh release view $tag --repo '${{ github.repository }}' *> $null
if ($LASTEXITCODE -ne 0) {
gh release create $tag `
--repo '${{ github.repository }}' `
--target '${{ github.sha }}' `
--verify-tag `
--generate-notes `
--title $tag
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
$assets = @(Get-ChildItem release-assets -File)
gh release upload $tag `
--repo '${{ github.repository }}' `
--clobber @($assets.FullName)
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }