Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions .github/scripts/build-fat-scripts.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#requires -Version 5.1
<#
.SYNOPSIS
Builds fat (self-contained) PowerShell scripts from modular sources in src/.

.DESCRIPTION
Walks the SourceRoot recursively, finds every .ps1 file, processes any
`# %INCLUDE <path-relative-to-repo-root>` marker lines by inlining the
referenced file's content in place of the marker, and writes the resulting
fat script to the matching relative path under OutputRoot.

Marker syntax:

# %INCLUDE oem-shared/lib/oem-manufacturer-detect.ps1
# %INCLUDE oem-dell/lib/dell-detection.ps1

Recursive includes are NOT supported in V1. Lib files cannot themselves
contain `# %INCLUDE` markers ... the build will fail loudly if it sees one.

.PARAMETER RepoRoot
Repository root directory. Used to resolve include paths.

.PARAMETER SourceRoot
Directory containing modular source .ps1 files. Typically <RepoRoot>/src.

.PARAMETER OutputRoot
Directory where fat scripts will be written. The directory is created if
it does not exist; existing files at the same path are overwritten.

.PARAMETER StripSrcPrefix
If set, the leading "src/" segment is stripped from the output path so
`src/oem-dell/dell-configure.ps1` becomes `oem-dell/dell-configure.ps1`
in the output. Default: $true.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)] [string] $RepoRoot,
[Parameter(Mandatory)] [string] $SourceRoot,
[Parameter(Mandatory)] [string] $OutputRoot,
[bool] $StripSrcPrefix = $true
)

$ErrorActionPreference = 'Stop'

function Expand-Includes {
param(
[Parameter(Mandatory)] [string] $Path,
[Parameter(Mandatory)] [string] $RepoRoot,
[switch] $IsIncluded
)

$content = Get-Content -Path $Path -Raw
if ($null -eq $content) { $content = '' }
$lines = $content -split "`r?`n"
$output = New-Object 'System.Collections.Generic.List[string]'

foreach ($line in $lines) {
if ($line -match '^\s*#\s*%INCLUDE\s+(.+?)\s*$') {
if ($IsIncluded) {
throw "Recursive %INCLUDE detected in '$Path'. Lib files cannot contain `# %INCLUDE` markers in V1."
}
$rel = $matches[1].Trim()
$includePath = Join-Path -Path $RepoRoot -ChildPath $rel
if (-not (Test-Path -LiteralPath $includePath)) {
throw "Include not found: '$rel' (resolved to '$includePath'), referenced from '$Path'."
}
$inlined = Expand-Includes -Path $includePath -RepoRoot $RepoRoot -IsIncluded
$output.Add("# === inlined from $rel ===")
foreach ($l in ($inlined -split "`r?`n")) {
$output.Add($l)
}
$output.Add("# === end inline ===")
} else {
$output.Add($line)
}
}

return ($output -join "`n")
}

if (-not (Test-Path -LiteralPath $SourceRoot)) {
throw "SourceRoot '$SourceRoot' does not exist."
}

if (-not (Test-Path -LiteralPath $OutputRoot)) {
New-Item -Path $OutputRoot -ItemType Directory -Force | Out-Null
}

$sourceFiles = Get-ChildItem -LiteralPath $SourceRoot -Recurse -Filter '*.ps1' -File
$built = 0

foreach ($file in $sourceFiles) {
$relFromRepo = $file.FullName.Substring($RepoRoot.Length).TrimStart('\','/')
$relFromRepo = $relFromRepo -replace '\\','/'

$outRel = $relFromRepo
if ($StripSrcPrefix -and $outRel -match '^src/(.+)$') {
$outRel = $matches[1]
}

$outPath = Join-Path -Path $OutputRoot -ChildPath ($outRel -replace '/','\')
$outDir = Split-Path -Path $outPath -Parent
if (-not (Test-Path -LiteralPath $outDir)) {
New-Item -Path $outDir -ItemType Directory -Force | Out-Null
}

$fat = Expand-Includes -Path $file.FullName -RepoRoot $RepoRoot
Set-Content -LiteralPath $outPath -Value $fat -Encoding UTF8 -NoNewline:$false

Write-Host "built: $relFromRepo -> $outRel"
$built++
}

Write-Host "fat-script build complete: $built file(s)."
145 changes: 145 additions & 0 deletions .github/workflows/build-fat-scripts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
name: build-fat-scripts

on:
push:
branches:
- development
- main

permissions:
contents: write

concurrency:
group: build-fat-scripts-${{ github.ref }}
cancel-in-progress: false

jobs:
build:
name: Build and publish fat scripts
runs-on: windows-latest

steps:
- name: Checkout source
uses: actions/checkout@v4
with:
fetch-depth: 0
path: source

- name: Resolve source commit metadata
id: src
shell: pwsh
run: |
$sha = git -C "${{ github.workspace }}\source" rev-parse HEAD
$shortSha = git -C "${{ github.workspace }}\source" rev-parse --short HEAD
$subject = git -C "${{ github.workspace }}\source" log -1 --pretty=%s
$branch = "${{ github.ref_name }}"
"sha=$sha" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"short_sha=$shortSha" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"subject=$subject" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"branch=$branch" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8

- name: Skip if no src/ directory
id: gate
shell: pwsh
run: |
$srcPath = "${{ github.workspace }}\source\src"
if (-not (Test-Path -LiteralPath $srcPath)) {
Write-Host "No src/ directory at source root ... nothing to build."
"has_src=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
} else {
$ps1Count = (Get-ChildItem -LiteralPath $srcPath -Recurse -Filter '*.ps1' -File -ErrorAction SilentlyContinue | Measure-Object).Count
if ($ps1Count -eq 0) {
Write-Host "src/ exists but contains no .ps1 files ... nothing to build."
"has_src=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
} else {
Write-Host "Found $ps1Count .ps1 file(s) under src/."
"has_src=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
}
}

- name: Checkout published branch (or initialize)
if: steps.gate.outputs.has_src == 'true'
shell: pwsh
run: |
$repo = "${{ github.repository }}"
$token = "${{ secrets.GITHUB_TOKEN }}"
$remoteUrl = "https://x-access-token:$token@github.com/$repo.git"

New-Item -Path "${{ github.workspace }}\published" -ItemType Directory -Force | Out-Null
Set-Location "${{ github.workspace }}\published"

git init -b published | Out-Null
git remote add origin $remoteUrl

$exists = $false
try {
git fetch --depth=1 origin published 2>$null
if ($LASTEXITCODE -eq 0) { $exists = $true }
} catch { $exists = $false }

if ($exists) {
git reset --hard FETCH_HEAD
Write-Host "Loaded existing published branch tip."
} else {
Write-Host "published branch does not exist yet ... will be created on first push."
}

# Wipe the working tree (preserve .git) so removed src files do not linger.
Get-ChildItem -Force | Where-Object { $_.Name -ne '.git' } | Remove-Item -Recurse -Force

- name: Build fat scripts
if: steps.gate.outputs.has_src == 'true'
shell: pwsh
run: |
& "${{ github.workspace }}\source\.github\scripts\build-fat-scripts.ps1" `
-RepoRoot "${{ github.workspace }}\source" `
-SourceRoot "${{ github.workspace }}\source\src" `
-OutputRoot "${{ github.workspace }}\published"

- name: Configure git identity
if: steps.gate.outputs.has_src == 'true'
shell: pwsh
run: |
Set-Location "${{ github.workspace }}\published"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Commit and push published branch
if: steps.gate.outputs.has_src == 'true'
id: publish
shell: pwsh
run: |
Set-Location "${{ github.workspace }}\published"
git add -A
$diff = git diff --cached --name-only
if ([string]::IsNullOrWhiteSpace($diff)) {
Write-Host "No changes to publish."
"published_sha=" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
return
}

$shortSha = "${{ steps.src.outputs.short_sha }}"
$subject = "${{ steps.src.outputs.subject }}"
$msg = "build: from $shortSha ""$subject"""

git commit -m "$msg"
git push origin HEAD:published

$publishedSha = git rev-parse HEAD
"published_sha=$publishedSha" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8

- name: Move release tag (push to main)
if: steps.gate.outputs.has_src == 'true' && steps.src.outputs.branch == 'main' && steps.publish.outputs.published_sha != ''
shell: pwsh
run: |
Set-Location "${{ github.workspace }}\published"
git tag -f release "${{ steps.publish.outputs.published_sha }}"
git push --force origin refs/tags/release

- name: Move dev tag (push to development)
if: steps.gate.outputs.has_src == 'true' && steps.src.outputs.branch == 'development' && steps.publish.outputs.published_sha != ''
shell: pwsh
run: |
Set-Location "${{ github.workspace }}\published"
git tag -f dev "${{ steps.publish.outputs.published_sha }}"
git push --force origin refs/tags/dev
Loading