All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 3m23s
RUNNER_TEMP is ephemeral; copy the validated build output to C:\silvermetal\out\ so it can be retrieved out of band (e.g. for VM boot-testing). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
153 lines
6.8 KiB
YAML
153 lines
6.8 KiB
YAML
name: Build SilverMetal Enhanced - Windows ISO
|
|
|
|
# M2/M3. Builds the custom packed ISO on the self-hosted Windows runner
|
|
# (silverlabs-runner-win, labels windows-latest / windows-2025), validates the
|
|
# baked payload offline (no nested-virt needed), and on a tag attaches the ISO
|
|
# + SBOM + SHA256 to a Gitea release. Mirrors build-iso-linux.yaml's structure.
|
|
#
|
|
# Base ISO: the licensed/eval Windows 11 IoT Enterprise LTSC media is an INPUT,
|
|
# never committed. Provide it via the repo variable SILVERMETAL_BASE_ISO_URL
|
|
# (downloaded at build time) OR pre-stage it on the runner at C:\silvermetal\base.iso.
|
|
#
|
|
# Reproducibility scope (iso-builder.md §5): pinned inputs + SBOM + SHA, NOT
|
|
# bit-identical on Windows -- so this is single-build, not the Linux double-build gate.
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'windows/**'
|
|
- '.gitea/workflows/build-iso-windows.yaml'
|
|
tags:
|
|
- 'win-v*'
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- 'windows/**'
|
|
- '.gitea/workflows/build-iso-windows.yaml'
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: build-iso-windows-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: windows-latest
|
|
timeout-minutes: 120
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Ensure Windows ADK (oscdimg)
|
|
shell: pwsh
|
|
run: |
|
|
$deploy = 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools'
|
|
if ((Get-Command oscdimg.exe -EA SilentlyContinue) -or (Test-Path $deploy)) {
|
|
Write-Host 'ADK Deployment Tools already present.'; exit 0
|
|
}
|
|
Write-Host 'Installing ADK Deployment Tools...'
|
|
# Prefer winget; fall back to the ADK web installer.
|
|
$ok = $false
|
|
try { winget install --id Microsoft.WindowsADK -e --accept-source-agreements --accept-package-agreements --silent; $ok = $true } catch {}
|
|
if (-not $ok) {
|
|
# NOTE: fwlink id is ADK-version-specific; update if the channel rolls.
|
|
Invoke-WebRequest 'https://go.microsoft.com/fwlink/?linkid=2289980' -OutFile "$env:TEMP\adksetup.exe"
|
|
Start-Process "$env:TEMP\adksetup.exe" -ArgumentList '/quiet','/norestart','/features','OptionId.DeploymentTools' -Wait
|
|
}
|
|
if (-not (Test-Path $deploy)) { throw 'ADK Deployment Tools install failed.' }
|
|
|
|
- name: Acquire base ISO
|
|
id: iso
|
|
shell: pwsh
|
|
env:
|
|
ISO_SRC: ${{ vars.SILVERMETAL_BASE_ISO_URL }} # HTTP(S) URL or UNC/local path
|
|
SMB_USER: ${{ secrets.SILVERMETAL_ISO_SHARE_USER }}
|
|
SMB_PASS: ${{ secrets.SILVERMETAL_ISO_SHARE_PASS }}
|
|
run: |
|
|
$dst = "$env:RUNNER_TEMP\base.iso"
|
|
$staged = 'C:\silvermetal\base.iso'
|
|
if ($env:ISO_SRC -match '^(?i)https?://') {
|
|
Write-Host 'Downloading base ISO (HTTP)...'
|
|
Invoke-WebRequest $env:ISO_SRC -OutFile $dst
|
|
} elseif ($env:ISO_SRC) {
|
|
# UNC or local path. The runner is SYSTEM; if the share needs auth,
|
|
# provide repo secrets SILVERMETAL_ISO_SHARE_USER/_PASS and we map it.
|
|
if ($env:ISO_SRC -like '\\*' -and $env:SMB_USER) {
|
|
$root = [regex]::Match($env:ISO_SRC, '^\\\\[^\\]+\\[^\\]+').Value
|
|
Write-Host "Authenticating to $root"
|
|
cmd /c "net use `"$root`" /user:$env:SMB_USER $env:SMB_PASS" | Out-Null
|
|
}
|
|
if (-not (Test-Path -LiteralPath $env:ISO_SRC)) { throw "ISO path not reachable by the runner (SYSTEM): $env:ISO_SRC" }
|
|
Write-Host 'Copying base ISO from path...'
|
|
Copy-Item -LiteralPath $env:ISO_SRC -Destination $dst -Force
|
|
} elseif (Test-Path $staged) {
|
|
Write-Host "Using pre-staged ISO: $staged"
|
|
Copy-Item -LiteralPath $staged -Destination $dst -Force
|
|
} else {
|
|
throw "No base ISO. Set repo variable SILVERMETAL_BASE_ISO_URL (URL or UNC/local path) or stage it at $staged."
|
|
}
|
|
"path=$dst" >> $env:GITHUB_OUTPUT
|
|
|
|
- name: Build packed ISO
|
|
shell: pwsh
|
|
run: |
|
|
.\windows\installer\build.ps1 `
|
|
-SourceIso '${{ steps.iso.outputs.path }}' `
|
|
-OutputIso "$env:RUNNER_TEMP\out\SilverMetal-Enhanced-Windows.iso"
|
|
|
|
- name: Validate baked payload (offline assertions)
|
|
shell: pwsh
|
|
run: |
|
|
.\windows\tests\Assert-IsoStructure.ps1 -IsoPath "$env:RUNNER_TEMP\out\SilverMetal-Enhanced-Windows.iso"
|
|
|
|
- name: Persist build output to stable path
|
|
shell: pwsh
|
|
run: |
|
|
# RUNNER_TEMP is per-job/ephemeral. Keep the latest validated build at a
|
|
# stable path so it can be retrieved (e.g. for VM boot-testing) out of band.
|
|
New-Item -ItemType Directory -Force 'C:\silvermetal\out' | Out-Null
|
|
Copy-Item "$env:RUNNER_TEMP\out\*" 'C:\silvermetal\out\' -Force
|
|
Get-ChildItem 'C:\silvermetal\out' | ForEach-Object { Write-Host $_.Name }
|
|
|
|
- name: Upload SBOM + SHA (always)
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: silvermetal-windows-attestation-${{ github.run_id }}
|
|
path: |
|
|
${{ runner.temp }}/out/*.sbom.json
|
|
${{ runner.temp }}/out/*.sha256
|
|
if-no-files-found: warn
|
|
retention-days: 30
|
|
|
|
- name: Upload ISO (dispatch / tag only)
|
|
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/')
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: silvermetal-windows-iso-${{ github.run_id }}
|
|
path: ${{ runner.temp }}/out/*.iso
|
|
if-no-files-found: error
|
|
retention-days: 14
|
|
|
|
- name: Attach to Gitea release (tag only)
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
shell: pwsh
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
$api = "${{ github.server_url }}/api/v1"
|
|
$repo = "${{ github.repository }}"
|
|
$tag = "${{ github.ref_name }}"
|
|
$h = @{ Authorization = "token $env:GITHUB_TOKEN" }
|
|
$rel = try { Invoke-RestMethod -Headers $h "$api/repos/$repo/releases/tags/$tag" } catch { $null }
|
|
if (-not $rel) {
|
|
$body = @{ tag_name=$tag; name="SilverMetal Enhanced - Windows $tag"; body="Packed ISO + SBOM + SHA256. Reproducibility: pinned inputs + SBOM (not bit-identical)."; draft=$false; prerelease=$true } | ConvertTo-Json
|
|
$rel = Invoke-RestMethod -Method Post -Headers $h -ContentType 'application/json' -Body $body "$api/repos/$repo/releases"
|
|
}
|
|
Get-ChildItem "$env:RUNNER_TEMP\out\*" -Include *.iso,*.sbom.json,*.sha256 | ForEach-Object {
|
|
Write-Host "Attaching $($_.Name)"
|
|
Invoke-RestMethod -Method Post -Headers $h -ContentType 'application/octet-stream' `
|
|
-InFile $_.FullName "$api/repos/$repo/releases/$($rel.id)/assets?name=$($_.Name)"
|
|
}
|