From 7230ea7f7e54cbcf3f80f4949f768e38cae97afa Mon Sep 17 00:00:00 2001 From: PBNZ Date: Tue, 7 Jul 2026 00:27:11 +1200 Subject: [PATCH] test: prove the powershell-module templates work on Linux pwsh The plugin ships no runnable scripts of its own (skills are prose using git/gh; validation is Python), and the powershell-module templates already target pwsh 7 / PSEdition Core with ubuntu-latest CI - but nothing in this repo ever executed the stamped templates on any OS. - Add scripts/smoke_test_powershell_module.ps1: stamps the core-tier templates with dummy values, then verifies Test-ModuleManifest, Import-Module, PSScriptAnalyzer (Error), and the Pester scaffold. - Run it in validate.yml on ubuntu-latest with shell: pwsh, so the templates are exercised on Linux for every push/PR. - Make the {{Guid}} placeholder instruction shell-agnostic (uuidgen / pwsh / python) instead of assuming PowerShell on the scaffolding host. - Record the cross-platform posture as ADR-0005. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01156rgoYreVp88JDZFtYL6X --- .github/workflows/validate.yml | 19 +++++ CHANGELOG.md | 13 +++ docs/adr/0005-cross-platform-pwsh.md | 38 +++++++++ .../new-repo/references/placeholders.md | 2 +- scripts/smoke_test_powershell_module.ps1 | 80 +++++++++++++++++++ 5 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 docs/adr/0005-cross-platform-pwsh.md create mode 100644 scripts/smoke_test_powershell_module.ps1 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 14aceff..205376a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -39,3 +39,22 @@ jobs: - name: Scan SKILL.md bodies for safety issues run: python scripts/check_skill_safety.py + + smoke-powershell-module: + name: Smoke-test powershell-module templates (pwsh on Linux) + runs-on: ubuntu-latest + defaults: + run: + shell: pwsh + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install PSScriptAnalyzer and Pester + run: | + Set-PSRepository PSGallery -InstallationPolicy Trusted + Install-Module PSScriptAnalyzer -Force -Scope CurrentUser + Install-Module Pester -Force -Scope CurrentUser + + - name: Stamp templates and verify the module works + run: ./scripts/smoke_test_powershell_module.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 804b552..5cf1413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- A cross-platform smoke test for the `powershell-module` templates + (`scripts/smoke_test_powershell_module.ps1`): stamps the core-tier templates with dummy values + and verifies the result parses (`Test-ModuleManifest`), imports, lints clean (PSScriptAnalyzer), + and passes its Pester scaffold. CI runs it with pwsh on `ubuntu-latest`, so the templates are now + exercised on Linux for every push/PR (ADR-0005). + +### Changed + +- The `{{Guid}}` placeholder instruction in `/new-repo` is now shell-agnostic (`uuidgen`, pwsh, or + python) instead of assuming PowerShell is available on the scaffolding host. + ## [0.2.0] - 2026-07-06 ### Added diff --git a/docs/adr/0005-cross-platform-pwsh.md b/docs/adr/0005-cross-platform-pwsh.md new file mode 100644 index 0000000..822ba23 --- /dev/null +++ b/docs/adr/0005-cross-platform-pwsh.md @@ -0,0 +1,38 @@ +# ADR-0005: RepoKit runs from any host OS; PowerShell templates are Linux-tested + +- **Status:** accepted +- **Date:** 2026-07-07 + +## Context + +RepoKit's plugin is prose and templates — it ships no scripts that run on the user's machine. +A scaffolding session shells out in whatever shell the host provides, so on Windows the author's +sessions ran PowerShell, which raised the question: does any of this work on Linux/macOS? + +An audit found the moving parts were already cross-platform by design: skill instructions use +`git`, `gh`, and `rg`; the repo's own validation is Python on `ubuntu-latest`; and the +`powershell-module` templates target pwsh 7 (`PowerShellVersion = '7.0'`, +`CompatiblePSEditions = @('Core')`, forward-slash paths) with CI on `ubuntu-latest` + +`shell: pwsh`. But two gaps remained: nothing in *this* repo ever executed the stamped templates +(only a scaffolded repo's own CI would, after the fact), and one skill instruction assumed +PowerShell on the scaffolding host (`{{Guid}}` via `[guid]::NewGuid()`). + +## Decision + +1. **Skill instructions must not assume a host shell.** Use cross-platform CLIs (`git`, `gh`, + `rg`) or list per-OS equivalents (as the `{{Guid}}` row now does). +2. **PowerShell templates target pwsh 7+ / PSEdition Core only** — no Windows PowerShell 5.1 + compatibility, no Windows-only cmdlets or path assumptions. +3. **This repo's CI proves it on Linux:** `scripts/smoke_test_powershell_module.ps1` stamps the + `powershell-module` core templates with dummy values and verifies the result parses, imports, + lints clean, and passes its Pester scaffold. `validate.yml` runs it with pwsh on + `ubuntu-latest` for every push/PR. + +## Consequences + +- A template regression that breaks on Linux now fails PR CI here, instead of surfacing in the + first repo someone scaffolds. +- macOS is not in the CI matrix; pwsh Core is the same engine there, and the Linux run already + catches case-sensitivity and path-separator mistakes — acceptable until proven otherwise. +- A future repo type that ships *executable* templates should add a matching smoke job; the + script's stamp-then-verify shape is the pattern to copy. diff --git a/plugins/repokit/skills/new-repo/references/placeholders.md b/plugins/repokit/skills/new-repo/references/placeholders.md index cbcbf25..435fd82 100644 --- a/plugins/repokit/skills/new-repo/references/placeholders.md +++ b/plugins/repokit/skills/new-repo/references/placeholders.md @@ -19,7 +19,7 @@ | Token | How to compute | |-------|----------------| | `{{year}}` | the current year | -| `{{Guid}}` | a **fresh** GUID per repo — run `[guid]::NewGuid()` (PowerShell) or equivalent. Never reuse a literal GUID; a hardcoded one would collide across every scaffolded module. | +| `{{Guid}}` | a **fresh** GUID per repo — use whatever the host offers: `uuidgen` (Linux/macOS), `[guid]::NewGuid()` (pwsh), or `python3 -c "import uuid; print(uuid.uuid4())"`. Never reuse a literal GUID; a hardcoded one would collide across every scaffolded module. | | `{{START_HERE_MAP}}` | the where-things-live table you build in step 4 from the resolved file set | ## Post-scaffold self-check diff --git a/scripts/smoke_test_powershell_module.ps1 b/scripts/smoke_test_powershell_module.ps1 new file mode 100644 index 0000000..1c0a07f --- /dev/null +++ b/scripts/smoke_test_powershell_module.ps1 @@ -0,0 +1,80 @@ +#!/usr/bin/env pwsh +#Requires -Version 7.0 +# Smoke-test for the powershell-module type templates. Stamps the core-tier +# templates with dummy values into a temp directory, then proves the result is a +# working module: manifest parses, module imports, PSScriptAnalyzer is clean, and +# the scaffolded Pester test passes. Runs on any pwsh 7 platform; CI runs it on +# ubuntu-latest so the templates are exercised on Linux for every push/PR. + +$ErrorActionPreference = 'Stop' + +$repoRoot = Split-Path -Parent $PSScriptRoot +$templateRoot = Join-Path $repoRoot 'plugins/repokit/skills/new-repo/templates/types/powershell-module/core' +$moduleName = 'RepoKitSmoke' +$stageDir = Join-Path ([IO.Path]::GetTempPath()) "repokit-template-smoke/$moduleName" + +if (-not (Test-Path $templateRoot)) { throw "Template root not found: $templateRoot" } +if (Test-Path $stageDir) { Remove-Item $stageDir -Recurse -Force } +New-Item -ItemType Directory -Path $stageDir -Force | Out-Null + +# Dummy values for every placeholder the templates can contain +# (see skills/new-repo/references/placeholders.md). +$values = @{ + ModuleName = $moduleName + Guid = [guid]::NewGuid().ToString() + name = 'repokit-smoke' + description = 'RepoKit powershell-module template smoke test.' + author = 'RepoKit CI' + license = 'Apache-2.0' + type = 'powershell-module' + tier = 'Core' + year = (Get-Date).Year +} + +Write-Host "Stamping templates from $templateRoot" +Get-ChildItem -Path $templateRoot -Recurse -File | ForEach-Object { + $rel = [IO.Path]::GetRelativePath($templateRoot, $_.FullName) -replace '\.tmpl$', '' + foreach ($k in $values.Keys) { $rel = $rel.Replace("{{$k}}", [string]$values[$k]) } + $target = Join-Path $stageDir $rel + New-Item -ItemType Directory -Path (Split-Path -Parent $target) -Force | Out-Null + $content = Get-Content -Path $_.FullName -Raw + if ($null -eq $content) { $content = '' } + foreach ($k in $values.Keys) { $content = $content.Replace("{{$k}}", [string]$values[$k]) } + Set-Content -Path $target -Value $content -NoNewline + Write-Host " $rel" +} + +Write-Host "`nCheck 1: no leftover placeholder tokens" +$tokenPattern = '\{\{(name|description|author|year|license|type|tier|ModuleName|Guid|START_HERE_MAP)\}\}' +$leftovers = Get-ChildItem -Path $stageDir -Recurse -File | Select-String -Pattern $tokenPattern +if ($leftovers) { + $leftovers | ForEach-Object { Write-Host " LEFTOVER: $_" } + throw "Placeholder tokens survived stamping." +} + +Write-Host "Check 2: Test-ModuleManifest" +$manifest = Join-Path $stageDir "$moduleName.psd1" +Test-ModuleManifest -Path $manifest | Out-Null + +Write-Host "Check 3: Import-Module" +Import-Module $manifest -Force +Remove-Module $moduleName -Force + +Write-Host "Check 4: PSScriptAnalyzer (Error severity)" +if (Get-Module -ListAvailable PSScriptAnalyzer) { + $issues = Invoke-ScriptAnalyzer -Path $stageDir -Recurse -Severity Error + if ($issues) { + $issues | Format-Table -AutoSize | Out-String | Write-Host + throw "PSScriptAnalyzer found $($issues.Count) error-severity issue(s)." + } +} else { + Write-Warning 'PSScriptAnalyzer not installed - lint check skipped (CI installs it).' +} + +Write-Host "Check 5: Pester scaffold test" +Import-Module Pester -MinimumVersion 5.0 -Force +$result = Invoke-Pester -Path (Join-Path $stageDir 'Tests') -PassThru +if ($result.TotalCount -eq 0) { throw 'No Pester tests were discovered in the stamped Tests/ directory.' } +if ($result.FailedCount -gt 0) { throw "$($result.FailedCount) Pester test(s) failed." } + +Write-Host "`nSmoke test passed: templates stamp into a working module on $($PSVersionTable.Platform ?? 'Windows') / pwsh $($PSVersionTable.PSVersion)."