mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 21:19:38 +00:00
42dae1b38c
The gate workflow carries its own push, pull-request and dispatch triggers next to workflow_call, so the workflow list shows it once and its page lists its runs. pester.yml, which only called it, is gone, and so are its entries in the paths-ignore of ci.yml and codeql.yml. The workflow has no concurrency group, because a workflow-level group would also bind the runs the publishers call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
128 lines
5.5 KiB
YAML
128 lines
5.5 KiB
YAML
name: Bootstrap script gates
|
|
|
|
# Quality gate for the customer-facing PowerShell scripts. Devices execute them as SYSTEM
|
|
# during enrollment, so a red suite must never ship. It runs in two roles:
|
|
# * on its own, on every pull request and push to main that changes an input of these
|
|
# checks (reports, does not block the merge button);
|
|
# * called as the gates job that BLOCKS every signing or upload of bootstrap code:
|
|
# publish-scripts.yml, build-agent.yml (stable releases), build-msi.yml. Those runs
|
|
# show under the caller.
|
|
#
|
|
# Both path lists are the checks' complete inputs and stay identical. They cover every
|
|
# push path of publish-scripts.yml, which leaves the check of a [skip publish] push to
|
|
# this workflow.
|
|
#
|
|
# No concurrency group: it would also bind the runs called by the publishers, and a
|
|
# cancel there would block a publish.
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'scripts/Bootstrap/**'
|
|
- 'scripts/CustomerSetup/**'
|
|
- '.github/scripts/Publish-BootstrapScripts.ps1'
|
|
- '.github/scripts/Get-Endpoints.ps1'
|
|
- '.github/scripts/Invoke-BootstrapSystemPass.ps1'
|
|
- '.github/scripts/Test-BootstrapAttacker.ps1'
|
|
- '.github/workflows/bootstrap-script-gates.yml'
|
|
# The integration tests run the generated OOBE script and the MSI's custom action; the
|
|
# contract tests also read the MSI build.
|
|
- 'src/Backend/AutopilotMonitor.Functions.Tests/TestData/oobe-bootstrap-golden.ps1'
|
|
- 'src/Agent/AutopilotMonitor.BootstrapMsi/Package.wxs'
|
|
- '.github/workflows/build-msi.yml'
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- 'scripts/Bootstrap/**'
|
|
- 'scripts/CustomerSetup/**'
|
|
- '.github/scripts/Publish-BootstrapScripts.ps1'
|
|
- '.github/scripts/Get-Endpoints.ps1'
|
|
- '.github/scripts/Invoke-BootstrapSystemPass.ps1'
|
|
- '.github/scripts/Test-BootstrapAttacker.ps1'
|
|
- '.github/workflows/bootstrap-script-gates.yml'
|
|
- 'src/Backend/AutopilotMonitor.Functions.Tests/TestData/oobe-bootstrap-golden.ps1'
|
|
- 'src/Agent/AutopilotMonitor.BootstrapMsi/Package.wxs'
|
|
- '.github/workflows/build-msi.yml'
|
|
workflow_dispatch:
|
|
workflow_call:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
gates:
|
|
name: Bootstrap guard tests (PS 5.1, ${{ matrix.os }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# Folder permissions and Windows PowerShell 5.1 come with the OS, so both server images.
|
|
os: [windows-2022, windows-2025]
|
|
runs-on: ${{ matrix.os }}
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# For all users: the SYSTEM pass below runs Pester as SYSTEM.
|
|
- name: Install Pester 5+
|
|
shell: powershell
|
|
run: |
|
|
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope AllUsers | Out-Null
|
|
Install-Module Pester -Scope AllUsers -Force -SkipPublisherCheck -MinimumVersion 5.5.0
|
|
|
|
# Windows PowerShell 5.1 on purpose: that is the runtime IME uses to execute
|
|
# platform scripts, so guard/relax logic is verified without resetting a device.
|
|
# The runner is elevated, so the integration tests use the real folder permissions.
|
|
- name: Run Pester
|
|
shell: powershell
|
|
run: |
|
|
Import-Module Pester -MinimumVersion 5.5.0
|
|
$c = New-PesterConfiguration
|
|
$c.Run.Path = 'scripts/Bootstrap'
|
|
$c.Run.Exit = $true
|
|
$c.Output.Verbosity = 'Detailed'
|
|
Invoke-Pester -Configuration $c
|
|
|
|
# On a device the bootstrap runs as SYSTEM: the integration tests once more as SYSTEM.
|
|
- name: Integration tests as SYSTEM
|
|
shell: powershell
|
|
run: ./.github/scripts/Invoke-BootstrapSystemPass.ps1
|
|
|
|
- name: Folder permissions against a real standard user
|
|
shell: powershell
|
|
run: ./.github/scripts/Test-BootstrapAttacker.ps1
|
|
|
|
- name: Guard scripts stay pure ASCII
|
|
shell: powershell
|
|
run: |
|
|
$failed = $false
|
|
Get-ChildItem 'scripts/Bootstrap' -Filter '*.ps1' -Recurse | ForEach-Object {
|
|
$bytes = [System.IO.File]::ReadAllBytes($_.FullName)
|
|
for ($i = 0; $i -lt $bytes.Length; $i++) {
|
|
if ($bytes[$i] -gt 127) {
|
|
Write-Host "::error file=scripts/Bootstrap/$($_.Name)::Non-ASCII byte 0x$('{0:X2}' -f $bytes[$i]) at offset $i. PS 5.1 (IME) reads BOM-less files as ANSI and corrupts multi-byte chars."
|
|
$failed = $true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if ($failed) { exit 1 }
|
|
Write-Host 'All bootstrap scripts are pure ASCII.'
|
|
|
|
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
|
|
with:
|
|
dotnet-version: '10.0.x'
|
|
|
|
# The names and log lines the scripts share with the agent, checked against the agent's
|
|
# own code.
|
|
- name: Script contracts with the agent
|
|
run: dotnet test src/Agent/AutopilotMonitor.Agent.V2.Core.Tests --configuration Release --filter "Category=BootstrapContract" --logger "console;verbosity=normal"
|
|
|
|
# The publish chain renders the -Dev variant by literal substitution and refuses to
|
|
# ship when an anchor moved. Running it dry here surfaces that (and any drift against
|
|
# the alias) on the PR instead of on the push that would have published.
|
|
- name: Publish dry run (dev-render anchors + version-bump guard + drift)
|
|
shell: pwsh
|
|
run: ./.github/scripts/Publish-BootstrapScripts.ps1 -DryRun
|