From 64d7c8198d790cd3315b0f764cffda34ee975238 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 13:09:01 -0400 Subject: [PATCH 01/16] Trigger CI From 7359df22f33c7fbb8fa1c4942a65b944dcc69318 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 13:25:47 -0400 Subject: [PATCH 02/16] Fix release version stamping contract --- dist/script.ps1 | 36 ++++++++++-------------------------- dist/script.sh | 7 +------ dist/sitemap.xml | 4 ++-- script.ps1 | 36 ++++++++++-------------------------- script.sh | 7 +------ 5 files changed, 24 insertions(+), 66 deletions(-) diff --git a/dist/script.ps1 b/dist/script.ps1 index 2d86319..e006d23 100644 --- a/dist/script.ps1 +++ b/dist/script.ps1 @@ -31,7 +31,7 @@ $ErrorActionPreference = 'Stop' $CLI_NAME = if ($PSCommandPath) { Split-Path -Leaf $PSCommandPath } else { $MyInvocation.MyCommand.Name } # Keep a single top-level assignment so release automation can stamp the entrypoint in place. -$SCRIPT_VERSION = $null +$SCRIPT_VERSION = if (-not [string]::IsNullOrWhiteSpace($env:SCRIPT_VERSION)) { $env:SCRIPT_VERSION } else { try { $resolved = (& git describe --tags --always --abbrev=1 2>$null | Out-String).Trim(); if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($resolved)) { $resolved } else { '0.0.0-dev' } } catch { '0.0.0-dev' } } $ESCAPE = [char]27 $USE_COLOR = $false @@ -53,24 +53,6 @@ function Test-Truthy { } } -function Get-ScriptVersion { - try { - $output = & git describe --tags --always --abbrev=1 2>$null - if ($LASTEXITCODE -ne 0) { - return '0.0.0-dev' - } - - $resolved = ($output | Out-String).Trim() - if ([string]::IsNullOrWhiteSpace($resolved)) { - return '0.0.0-dev' - } - - return $resolved - } catch { - return '0.0.0-dev' - } -} - function Test-ColorEnabled { if (-not [string]::IsNullOrWhiteSpace($env:NO_COLOR)) { return $false @@ -216,8 +198,13 @@ function fail { } function Resolve-Debug { - if ($PSBoundParameters.ContainsKey('Debug')) { - return [bool]$Debug + param( + [bool]$CliDebug, + [bool]$DebugProvided + ) + + if ($DebugProvided) { + return $CliDebug } return (Test-Truthy $env:DEBUG) -or (Test-Truthy $env:RUNNER_DEBUG) @@ -260,13 +247,10 @@ function Invoke-RunCli { } $script:USE_COLOR = Test-ColorEnabled - -if (-not $SCRIPT_VERSION) { - $SCRIPT_VERSION = Get-ScriptVersion -} +$rawDebug = [bool]$Debug $script:Resolved = [pscustomobject]@{ - Debug = Resolve-Debug + Debug = Resolve-Debug -CliDebug $rawDebug -DebugProvided $PSBoundParameters.ContainsKey('Debug') Positionals = @($Positionals) } diff --git a/dist/script.sh b/dist/script.sh index ad0f05c..af7462a 100755 --- a/dist/script.sh +++ b/dist/script.sh @@ -42,12 +42,7 @@ tty_ts="$(tty_escape '38;2;219;39;119')" CLI_NAME="${0##*/}" # Keep a single top-level assignment so release automation can stamp the entrypoint in place. -SCRIPT_VERSION="" -if [[ -z "${SCRIPT_VERSION-}" ]]; then - if ! SCRIPT_VERSION="$(git describe --tags --always --abbrev=1 2>/dev/null)"; then - SCRIPT_VERSION="0.0.0-dev" - fi -fi +SCRIPT_VERSION="${SCRIPT_VERSION:-$(git describe --tags --always --abbrev=1 2>/dev/null || printf '%s' '0.0.0-dev')}" DEBUG="${DEBUG:-${RUNNER_DEBUG:-}}" diff --git a/dist/sitemap.xml b/dist/sitemap.xml index 9fc46c3..4880d4b 100644 --- a/dist/sitemap.xml +++ b/dist/sitemap.xml @@ -2,13 +2,13 @@ https://script.example.com/script.sh - 2026-03-15T16:41:40.752Z + 2026-03-15T17:07:46.000Z daily 0.5 https://script.example.com/script.ps1 - 2026-03-15T16:41:40.752Z + 2026-03-15T17:07:46.000Z daily 0.5 diff --git a/script.ps1 b/script.ps1 index 2d86319..e006d23 100644 --- a/script.ps1 +++ b/script.ps1 @@ -31,7 +31,7 @@ $ErrorActionPreference = 'Stop' $CLI_NAME = if ($PSCommandPath) { Split-Path -Leaf $PSCommandPath } else { $MyInvocation.MyCommand.Name } # Keep a single top-level assignment so release automation can stamp the entrypoint in place. -$SCRIPT_VERSION = $null +$SCRIPT_VERSION = if (-not [string]::IsNullOrWhiteSpace($env:SCRIPT_VERSION)) { $env:SCRIPT_VERSION } else { try { $resolved = (& git describe --tags --always --abbrev=1 2>$null | Out-String).Trim(); if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($resolved)) { $resolved } else { '0.0.0-dev' } } catch { '0.0.0-dev' } } $ESCAPE = [char]27 $USE_COLOR = $false @@ -53,24 +53,6 @@ function Test-Truthy { } } -function Get-ScriptVersion { - try { - $output = & git describe --tags --always --abbrev=1 2>$null - if ($LASTEXITCODE -ne 0) { - return '0.0.0-dev' - } - - $resolved = ($output | Out-String).Trim() - if ([string]::IsNullOrWhiteSpace($resolved)) { - return '0.0.0-dev' - } - - return $resolved - } catch { - return '0.0.0-dev' - } -} - function Test-ColorEnabled { if (-not [string]::IsNullOrWhiteSpace($env:NO_COLOR)) { return $false @@ -216,8 +198,13 @@ function fail { } function Resolve-Debug { - if ($PSBoundParameters.ContainsKey('Debug')) { - return [bool]$Debug + param( + [bool]$CliDebug, + [bool]$DebugProvided + ) + + if ($DebugProvided) { + return $CliDebug } return (Test-Truthy $env:DEBUG) -or (Test-Truthy $env:RUNNER_DEBUG) @@ -260,13 +247,10 @@ function Invoke-RunCli { } $script:USE_COLOR = Test-ColorEnabled - -if (-not $SCRIPT_VERSION) { - $SCRIPT_VERSION = Get-ScriptVersion -} +$rawDebug = [bool]$Debug $script:Resolved = [pscustomobject]@{ - Debug = Resolve-Debug + Debug = Resolve-Debug -CliDebug $rawDebug -DebugProvided $PSBoundParameters.ContainsKey('Debug') Positionals = @($Positionals) } diff --git a/script.sh b/script.sh index ad0f05c..af7462a 100755 --- a/script.sh +++ b/script.sh @@ -42,12 +42,7 @@ tty_ts="$(tty_escape '38;2;219;39;119')" CLI_NAME="${0##*/}" # Keep a single top-level assignment so release automation can stamp the entrypoint in place. -SCRIPT_VERSION="" -if [[ -z "${SCRIPT_VERSION-}" ]]; then - if ! SCRIPT_VERSION="$(git describe --tags --always --abbrev=1 2>/dev/null)"; then - SCRIPT_VERSION="0.0.0-dev" - fi -fi +SCRIPT_VERSION="${SCRIPT_VERSION:-$(git describe --tags --always --abbrev=1 2>/dev/null || printf '%s' '0.0.0-dev')}" DEBUG="${DEBUG:-${RUNNER_DEBUG:-}}" From 02190d9a793de3d654005a4804987a947f6a6e44 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 14:01:19 -0400 Subject: [PATCH 03/16] Normalize PowerShell debug handling --- .github/workflows/release-tests.yml | 5 +++- dist/script.ps1 | 36 +++++++++++++++-------------- dist/sitemap.xml | 4 ++-- script.ps1 | 36 +++++++++++++++-------------- 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/.github/workflows/release-tests.yml b/.github/workflows/release-tests.yml index df37a2e..0317f7b 100644 --- a/.github/workflows/release-tests.yml +++ b/.github/workflows/release-tests.yml @@ -37,7 +37,10 @@ jobs: - name: Verify stamped release entrypoint shell: bash - run: test "$(./dist/script.sh --version)" = "${{ steps.prepare-release.outputs.resolved-version }}" + run: | + actual="$(bash ./dist/script.sh --version)" + printf 'actual=%s\n' "$actual" + test "$actual" = "${{ steps.prepare-release.outputs.resolved-version }}" release-powershell: runs-on: windows-2025 diff --git a/dist/script.ps1 b/dist/script.ps1 index e006d23..90d376f 100644 --- a/dist/script.ps1 +++ b/dist/script.ps1 @@ -17,8 +17,8 @@ $env:DEBUG = '1' Run `./script.ps1 -Help` for more advanced usage. #> +[CmdletBinding()] param( - [switch]$Debug, [switch]$Help, [switch]$Version, @@ -34,6 +34,7 @@ $CLI_NAME = if ($PSCommandPath) { Split-Path -Leaf $PSCommandPath } else { $MyIn $SCRIPT_VERSION = if (-not [string]::IsNullOrWhiteSpace($env:SCRIPT_VERSION)) { $env:SCRIPT_VERSION } else { try { $resolved = (& git describe --tags --always --abbrev=1 2>$null | Out-String).Trim(); if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($resolved)) { $resolved } else { '0.0.0-dev' } } catch { '0.0.0-dev' } } $ESCAPE = [char]27 $USE_COLOR = $false +$script:DebugEnabled = $false function Test-Truthy { param([AllowNull()][object]$Value) @@ -53,6 +54,21 @@ function Test-Truthy { } } +# Normalize debug preference using the common -Debug parameter plus env fallbacks. +$DebugPreference = if ( + $PSBoundParameters.ContainsKey('Debug') -or + (Test-Truthy $env:DEBUG) -or + (Test-Truthy $env:RUNNER_DEBUG) +) { + 'Continue' +} else { + $DebugPreference +} + +if ($DebugPreference -eq 'Inquire' -or $DebugPreference -eq 'Continue') { + $script:DebugEnabled = $true +} + function Test-ColorEnabled { if (-not [string]::IsNullOrWhiteSpace($env:NO_COLOR)) { return $false @@ -170,7 +186,7 @@ function debug { [object[]]$MessageArgs = @() ) - if (-not $script:Resolved.Debug) { + if (-not $script:DebugEnabled) { return } @@ -197,19 +213,6 @@ function fail { throw ([System.Exception]::new(('exit {0}: {1}' -f $ExitCode, $Message))) } -function Resolve-Debug { - param( - [bool]$CliDebug, - [bool]$DebugProvided - ) - - if ($DebugProvided) { - return $CliDebug - } - - return (Test-Truthy $env:DEBUG) -or (Test-Truthy $env:RUNNER_DEBUG) -} - function Show-Version { Write-StreamLine -Stream 'stdout' -Message $script:SCRIPT_VERSION } @@ -247,10 +250,9 @@ function Invoke-RunCli { } $script:USE_COLOR = Test-ColorEnabled -$rawDebug = [bool]$Debug $script:Resolved = [pscustomobject]@{ - Debug = Resolve-Debug -CliDebug $rawDebug -DebugProvided $PSBoundParameters.ContainsKey('Debug') + Debug = $script:DebugEnabled Positionals = @($Positionals) } diff --git a/dist/sitemap.xml b/dist/sitemap.xml index 4880d4b..d0a5506 100644 --- a/dist/sitemap.xml +++ b/dist/sitemap.xml @@ -2,13 +2,13 @@ https://script.example.com/script.sh - 2026-03-15T17:07:46.000Z + 2026-03-15T17:25:47.000Z daily 0.5 https://script.example.com/script.ps1 - 2026-03-15T17:07:46.000Z + 2026-03-15T17:25:47.000Z daily 0.5 diff --git a/script.ps1 b/script.ps1 index e006d23..90d376f 100644 --- a/script.ps1 +++ b/script.ps1 @@ -17,8 +17,8 @@ $env:DEBUG = '1' Run `./script.ps1 -Help` for more advanced usage. #> +[CmdletBinding()] param( - [switch]$Debug, [switch]$Help, [switch]$Version, @@ -34,6 +34,7 @@ $CLI_NAME = if ($PSCommandPath) { Split-Path -Leaf $PSCommandPath } else { $MyIn $SCRIPT_VERSION = if (-not [string]::IsNullOrWhiteSpace($env:SCRIPT_VERSION)) { $env:SCRIPT_VERSION } else { try { $resolved = (& git describe --tags --always --abbrev=1 2>$null | Out-String).Trim(); if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($resolved)) { $resolved } else { '0.0.0-dev' } } catch { '0.0.0-dev' } } $ESCAPE = [char]27 $USE_COLOR = $false +$script:DebugEnabled = $false function Test-Truthy { param([AllowNull()][object]$Value) @@ -53,6 +54,21 @@ function Test-Truthy { } } +# Normalize debug preference using the common -Debug parameter plus env fallbacks. +$DebugPreference = if ( + $PSBoundParameters.ContainsKey('Debug') -or + (Test-Truthy $env:DEBUG) -or + (Test-Truthy $env:RUNNER_DEBUG) +) { + 'Continue' +} else { + $DebugPreference +} + +if ($DebugPreference -eq 'Inquire' -or $DebugPreference -eq 'Continue') { + $script:DebugEnabled = $true +} + function Test-ColorEnabled { if (-not [string]::IsNullOrWhiteSpace($env:NO_COLOR)) { return $false @@ -170,7 +186,7 @@ function debug { [object[]]$MessageArgs = @() ) - if (-not $script:Resolved.Debug) { + if (-not $script:DebugEnabled) { return } @@ -197,19 +213,6 @@ function fail { throw ([System.Exception]::new(('exit {0}: {1}' -f $ExitCode, $Message))) } -function Resolve-Debug { - param( - [bool]$CliDebug, - [bool]$DebugProvided - ) - - if ($DebugProvided) { - return $CliDebug - } - - return (Test-Truthy $env:DEBUG) -or (Test-Truthy $env:RUNNER_DEBUG) -} - function Show-Version { Write-StreamLine -Stream 'stdout' -Message $script:SCRIPT_VERSION } @@ -247,10 +250,9 @@ function Invoke-RunCli { } $script:USE_COLOR = Test-ColorEnabled -$rawDebug = [bool]$Debug $script:Resolved = [pscustomobject]@{ - Debug = Resolve-Debug -CliDebug $rawDebug -DebugProvided $PSBoundParameters.ContainsKey('Debug') + Debug = $script:DebugEnabled Positionals = @($Positionals) } From 4f5aaf0154482becd377dd7248e895d7abc4f2a0 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 14:31:10 -0400 Subject: [PATCH 04/16] Use stamped version in PR workflows --- .github/workflows/pr-bash-examples-tests.yml | 7 +++++-- .../pr-powershell-examples-tests.yml | 7 +++++-- .github/workflows/release-tests.yml | 19 ++++++++++--------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr-bash-examples-tests.yml b/.github/workflows/pr-bash-examples-tests.yml index 0787c1c..45ff207 100644 --- a/.github/workflows/pr-bash-examples-tests.yml +++ b/.github/workflows/pr-bash-examples-tests.yml @@ -6,6 +6,9 @@ on: permissions: contents: read +env: + STAMPED_VERSION: v9.9.9-ci.${{ github.event.pull_request.head.sha }} + jobs: examples: name: ${{ matrix.runs-on }} / ${{ matrix.example }} @@ -38,8 +41,8 @@ jobs: sync: false commands: | bun run build - version-injector dist/script.sh --style sh --version "$PREPARE_RELEASE_VERSION" - version-injector dist/script.ps1 --style ps1 --version "$PREPARE_RELEASE_VERSION" + version-injector dist/script.sh --style sh --version "${{ env.STAMPED_VERSION }}" + version-injector dist/script.ps1 --style ps1 --version "${{ env.STAMPED_VERSION }}" - name: Put prepared script on PATH shell: bash diff --git a/.github/workflows/pr-powershell-examples-tests.yml b/.github/workflows/pr-powershell-examples-tests.yml index 76cf5e9..3f072fd 100644 --- a/.github/workflows/pr-powershell-examples-tests.yml +++ b/.github/workflows/pr-powershell-examples-tests.yml @@ -6,6 +6,9 @@ on: permissions: contents: read +env: + STAMPED_VERSION: v9.9.9-ci.${{ github.event.pull_request.head.sha }} + jobs: examples: name: windows-2025 / ${{ matrix.example }} @@ -35,8 +38,8 @@ jobs: sync: false commands: | bun run build - version-injector dist/script.sh --style sh --version "$PREPARE_RELEASE_VERSION" - version-injector dist/script.ps1 --style ps1 --version "$PREPARE_RELEASE_VERSION" + version-injector dist/script.sh --style sh --version "${{ env.STAMPED_VERSION }}" + version-injector dist/script.ps1 --style ps1 --version "${{ env.STAMPED_VERSION }}" - name: Put prepared scripts on PATH shell: pwsh diff --git a/.github/workflows/release-tests.yml b/.github/workflows/release-tests.yml index 0317f7b..4671fb1 100644 --- a/.github/workflows/release-tests.yml +++ b/.github/workflows/release-tests.yml @@ -6,6 +6,9 @@ on: permissions: contents: read +env: + STAMPED_VERSION: v9.9.9-ci.${{ github.event.pull_request.head.sha }} + jobs: release-bash: runs-on: ubuntu-24.04 @@ -24,7 +27,6 @@ jobs: run: bun install --frozen-lockfile --ignore-scripts - name: Prepare release-shaped build - id: prepare-release uses: tanaabased/prepare-release-action@v1 with: version: dev @@ -32,15 +34,15 @@ jobs: commands: | bun run lint bun run build - version-injector dist/script.sh --style sh --version "$PREPARE_RELEASE_VERSION" - version-injector dist/script.ps1 --style ps1 --version "$PREPARE_RELEASE_VERSION" + version-injector dist/script.sh --style sh --version "${{ env.STAMPED_VERSION }}" + version-injector dist/script.ps1 --style ps1 --version "${{ env.STAMPED_VERSION }}" - name: Verify stamped release entrypoint shell: bash run: | actual="$(bash ./dist/script.sh --version)" printf 'actual=%s\n' "$actual" - test "$actual" = "${{ steps.prepare-release.outputs.resolved-version }}" + test "$actual" = "${{ env.STAMPED_VERSION }}" release-powershell: runs-on: windows-2025 @@ -59,20 +61,19 @@ jobs: run: bun install --frozen-lockfile --ignore-scripts - name: Prepare release-shaped build - id: prepare-release uses: tanaabased/prepare-release-action@v1 with: version: dev sync: false commands: | bun run build - version-injector dist/script.sh --style sh --version "$PREPARE_RELEASE_VERSION" - version-injector dist/script.ps1 --style ps1 --version "$PREPARE_RELEASE_VERSION" + version-injector dist/script.sh --style sh --version "${{ env.STAMPED_VERSION }}" + version-injector dist/script.ps1 --style ps1 --version "${{ env.STAMPED_VERSION }}" - name: Verify stamped PowerShell release entrypoint shell: pwsh run: | $version = & ./dist/script.ps1 -Version - if ($version -ne "${{ steps.prepare-release.outputs.resolved-version }}") { - throw "expected version ${{ steps.prepare-release.outputs.resolved-version }}, got $version" + if ($version -ne "${{ env.STAMPED_VERSION }}") { + throw "expected version ${{ env.STAMPED_VERSION }}, got $version" } From 133babaf12aec8ff2afda672c0e945194c2e49e6 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 14:31:48 -0400 Subject: [PATCH 05/16] update test name so we know whether bash or pwsh --- .github/workflows/pr-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-linter.yml b/.github/workflows/pr-linter.yml index f711dce..166dbf7 100644 --- a/.github/workflows/pr-linter.yml +++ b/.github/workflows/pr-linter.yml @@ -7,7 +7,7 @@ permissions: contents: read jobs: - lint: + bash-lint: runs-on: ubuntu-24.04 steps: - name: Checkout code From 4e1014dd4879fa184d0563210d1136c3362e00df Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 15:21:51 -0400 Subject: [PATCH 06/16] Use native PowerShell streams in starter --- dist/script.ps1 | 45 ++++++++++++++++++--------------------------- script.ps1 | 45 ++++++++++++++++++--------------------------- 2 files changed, 36 insertions(+), 54 deletions(-) diff --git a/dist/script.ps1 b/dist/script.ps1 index 90d376f..1091100 100644 --- a/dist/script.ps1 +++ b/dist/script.ps1 @@ -151,25 +151,8 @@ function Expand-Message { return $normalized } -function Write-StreamLine { +function Write-Status { param( - [ValidateSet('stdout', 'stderr')] - [string]$Stream = 'stdout', - [string]$Message = '' - ) - - if ($Stream -eq 'stderr') { - [Console]::Error.WriteLine($Message) - return - } - - [Console]::Out.WriteLine($Message) -} - -function Write-StatusLine { - param( - [ValidateSet('stdout', 'stderr')] - [string]$Stream, [string]$Label, [scriptblock]$Colorizer, [AllowNull()][object]$Message = '', @@ -177,7 +160,7 @@ function Write-StatusLine { ) $text = Expand-Message -Message $Message -MessageArgs $MessageArgs - Write-StreamLine -Stream $Stream -Message ('{0}: {1}' -f (& $Colorizer $Label), $text) + Write-Host ('{0}: {1}' -f (& $Colorizer $Label), $text) } function debug { @@ -190,8 +173,16 @@ function debug { return } - $text = Expand-Message -Message $Message -MessageArgs $MessageArgs - Write-StreamLine -Stream 'stderr' -Message ('{0} {1}' -f (dim 'debug'), $text) + Write-Debug (Expand-Message -Message $Message -MessageArgs $MessageArgs) +} + +function log { + param( + [AllowNull()][object]$Message = '', + [object[]]$MessageArgs = @() + ) + + Write-Output (Expand-Message -Message $Message -MessageArgs $MessageArgs) } function note { @@ -200,7 +191,7 @@ function note { [object[]]$MessageArgs = @() ) - Write-StatusLine -Stream 'stdout' -Label 'note' -Colorizer ${function:ts} -Message $Message -MessageArgs $MessageArgs + Write-Status -Label 'note' -Colorizer ${function:ts} -Message $Message -MessageArgs $MessageArgs } function fail { @@ -209,12 +200,12 @@ function fail { [int]$ExitCode = 1 ) - Write-StatusLine -Stream 'stderr' -Label 'error' -Colorizer ${function:red} -Message $Message - throw ([System.Exception]::new(('exit {0}: {1}' -f $ExitCode, $Message))) + $text = Expand-Message -Message $Message + Write-Error -Message ('error: {0}' -f $text) -ErrorAction Stop } function Show-Version { - Write-StreamLine -Stream 'stdout' -Message $script:SCRIPT_VERSION + Write-Output $script:SCRIPT_VERSION } function Show-Usage { @@ -236,7 +227,7 @@ function Show-Usage { ('Replace the body of {0} with your project behavior.' -f $script:CLI_NAME) ) - Write-StreamLine -Stream 'stdout' -Message ($lines -join [Environment]::NewLine) + Write-Output ($lines -join [Environment]::NewLine) } function Invoke-RunCli { @@ -246,7 +237,7 @@ function Invoke-RunCli { } debug 'running placeholder command body' - note 'Replace the body of {0} with your project logic.' $script:CLI_NAME + log 'Replace the body of {0} with your project logic.' $script:CLI_NAME } $script:USE_COLOR = Test-ColorEnabled diff --git a/script.ps1 b/script.ps1 index 90d376f..1091100 100644 --- a/script.ps1 +++ b/script.ps1 @@ -151,25 +151,8 @@ function Expand-Message { return $normalized } -function Write-StreamLine { +function Write-Status { param( - [ValidateSet('stdout', 'stderr')] - [string]$Stream = 'stdout', - [string]$Message = '' - ) - - if ($Stream -eq 'stderr') { - [Console]::Error.WriteLine($Message) - return - } - - [Console]::Out.WriteLine($Message) -} - -function Write-StatusLine { - param( - [ValidateSet('stdout', 'stderr')] - [string]$Stream, [string]$Label, [scriptblock]$Colorizer, [AllowNull()][object]$Message = '', @@ -177,7 +160,7 @@ function Write-StatusLine { ) $text = Expand-Message -Message $Message -MessageArgs $MessageArgs - Write-StreamLine -Stream $Stream -Message ('{0}: {1}' -f (& $Colorizer $Label), $text) + Write-Host ('{0}: {1}' -f (& $Colorizer $Label), $text) } function debug { @@ -190,8 +173,16 @@ function debug { return } - $text = Expand-Message -Message $Message -MessageArgs $MessageArgs - Write-StreamLine -Stream 'stderr' -Message ('{0} {1}' -f (dim 'debug'), $text) + Write-Debug (Expand-Message -Message $Message -MessageArgs $MessageArgs) +} + +function log { + param( + [AllowNull()][object]$Message = '', + [object[]]$MessageArgs = @() + ) + + Write-Output (Expand-Message -Message $Message -MessageArgs $MessageArgs) } function note { @@ -200,7 +191,7 @@ function note { [object[]]$MessageArgs = @() ) - Write-StatusLine -Stream 'stdout' -Label 'note' -Colorizer ${function:ts} -Message $Message -MessageArgs $MessageArgs + Write-Status -Label 'note' -Colorizer ${function:ts} -Message $Message -MessageArgs $MessageArgs } function fail { @@ -209,12 +200,12 @@ function fail { [int]$ExitCode = 1 ) - Write-StatusLine -Stream 'stderr' -Label 'error' -Colorizer ${function:red} -Message $Message - throw ([System.Exception]::new(('exit {0}: {1}' -f $ExitCode, $Message))) + $text = Expand-Message -Message $Message + Write-Error -Message ('error: {0}' -f $text) -ErrorAction Stop } function Show-Version { - Write-StreamLine -Stream 'stdout' -Message $script:SCRIPT_VERSION + Write-Output $script:SCRIPT_VERSION } function Show-Usage { @@ -236,7 +227,7 @@ function Show-Usage { ('Replace the body of {0} with your project behavior.' -f $script:CLI_NAME) ) - Write-StreamLine -Stream 'stdout' -Message ($lines -join [Environment]::NewLine) + Write-Output ($lines -join [Environment]::NewLine) } function Invoke-RunCli { @@ -246,7 +237,7 @@ function Invoke-RunCli { } debug 'running placeholder command body' - note 'Replace the body of {0} with your project logic.' $script:CLI_NAME + log 'Replace the body of {0} with your project logic.' $script:CLI_NAME } $script:USE_COLOR = Test-ColorEnabled From e7aeeb1ca90be96d68fa188062af21e3705b153b Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 15:30:01 -0400 Subject: [PATCH 07/16] Refine PowerShell starter and Leia workflows --- .github/workflows/pr-bash-examples-tests.yml | 8 +++++--- .github/workflows/pr-powershell-examples-tests.yml | 10 +++++++--- dist/script.ps1 | 5 ++--- script.ps1 | 5 ++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pr-bash-examples-tests.yml b/.github/workflows/pr-bash-examples-tests.yml index 45ff207..6cfc8f8 100644 --- a/.github/workflows/pr-bash-examples-tests.yml +++ b/.github/workflows/pr-bash-examples-tests.yml @@ -48,8 +48,10 @@ jobs: shell: bash run: echo "$PWD/dist" >> "$GITHUB_PATH" - - name: Run Leia-backed example + - name: Verify example ${{ matrix.example }} with Leia shell: bash + env: + TMPDIR: ${{ github.workspace }}/examples/${{ matrix.example }}/.tmp run: | - mkdir -p examples/.tmp - TMPDIR="$PWD/examples/.tmp" ./node_modules/.bin/leia "examples/${{ matrix.example }}/README.md" -c "Destroy tests" --stdin + mkdir -p "$TMPDIR" + bun run leia "examples/${{ matrix.example }}/README.md" -c "Destroy tests" --stdin diff --git a/.github/workflows/pr-powershell-examples-tests.yml b/.github/workflows/pr-powershell-examples-tests.yml index 3f072fd..e6ca03e 100644 --- a/.github/workflows/pr-powershell-examples-tests.yml +++ b/.github/workflows/pr-powershell-examples-tests.yml @@ -45,8 +45,12 @@ jobs: shell: pwsh run: '"{0}\dist" -f $PWD | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append' - - name: Run Leia-backed example + - name: Verify example ${{ matrix.example }} with Leia shell: pwsh + env: + TMPDIR: ${{ github.workspace }}\examples\${{ matrix.example }}\.tmp + TMP: ${{ github.workspace }}\examples\${{ matrix.example }}\.tmp + TEMP: ${{ github.workspace }}\examples\${{ matrix.example }}\.tmp run: | - New-Item -ItemType Directory -Force -Path 'examples/.tmp' | Out-Null - & "$PWD\node_modules\.bin\leia.cmd" "examples/${{ matrix.example }}/README.md" --shell pwsh -c "Destroy tests" --stdin + New-Item -ItemType Directory -Force -Path $env:TMPDIR | Out-Null + bun run leia "examples/${{ matrix.example }}/README.md" --shell pwsh -c "Destroy tests" --stdin diff --git a/dist/script.ps1 b/dist/script.ps1 index 1091100..1c6ebe3 100644 --- a/dist/script.ps1 +++ b/dist/script.ps1 @@ -160,7 +160,7 @@ function Write-Status { ) $text = Expand-Message -Message $Message -MessageArgs $MessageArgs - Write-Host ('{0}: {1}' -f (& $Colorizer $Label), $text) + Write-Information ('{0}: {1}' -f (& $Colorizer $Label), $text) -InformationAction Continue } function debug { @@ -196,8 +196,7 @@ function note { function fail { param( - [string]$Message, - [int]$ExitCode = 1 + [string]$Message ) $text = Expand-Message -Message $Message diff --git a/script.ps1 b/script.ps1 index 1091100..1c6ebe3 100644 --- a/script.ps1 +++ b/script.ps1 @@ -160,7 +160,7 @@ function Write-Status { ) $text = Expand-Message -Message $Message -MessageArgs $MessageArgs - Write-Host ('{0}: {1}' -f (& $Colorizer $Label), $text) + Write-Information ('{0}: {1}' -f (& $Colorizer $Label), $text) -InformationAction Continue } function debug { @@ -196,8 +196,7 @@ function note { function fail { param( - [string]$Message, - [int]$ExitCode = 1 + [string]$Message ) $text = Expand-Message -Message $Message From c906d185e54a1837a174bc04330746c955c4b221 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 15:39:12 -0400 Subject: [PATCH 08/16] Simplify Leia example scratch usage --- examples/cli-contract-bash/README.md | 15 ++++----------- examples/cli-contract-powershell/README.md | 16 ++++------------ examples/minimal-bash/README.md | 11 ++++------- examples/minimal-powershell/README.md | 12 ++++-------- 4 files changed, 16 insertions(+), 38 deletions(-) diff --git a/examples/cli-contract-bash/README.md b/examples/cli-contract-bash/README.md index 22987ee..3d1e9a6 100644 --- a/examples/cli-contract-bash/README.md +++ b/examples/cli-contract-bash/README.md @@ -3,13 +3,6 @@ This example keeps coverage on the Bash-facing contract of `script.sh`: help output, version output, and a clear failure for unknown options. -## Setup - -```bash -# should reset the example scratch directory -rm -rf .tmp && mkdir -p .tmp -``` - ## Testing ```bash @@ -23,15 +16,15 @@ script.sh --help | grep -- '--version' test -n "$(script.sh --version)" # should fail for an unknown option -! script.sh --definitely-bogus > .tmp/invalid.log 2>&1 +! script.sh --definitely-bogus > invalid.log 2>&1 # should explain the unknown option failure -grep -F 'Unrecognized option' .tmp/invalid.log +grep -F 'Unrecognized option' invalid.log ``` ## Destroy tests ```bash -# should remove the example scratch directory -rm -rf .tmp +# should remove the cli-contract failure log +rm -f invalid.log ``` diff --git a/examples/cli-contract-powershell/README.md b/examples/cli-contract-powershell/README.md index daf5abf..08a3371 100644 --- a/examples/cli-contract-powershell/README.md +++ b/examples/cli-contract-powershell/README.md @@ -3,14 +3,6 @@ This example keeps coverage on the PowerShell-facing contract of `script.ps1`: help output, version output, and a clear failure for unexpected positional arguments. -## Setup - -```powershell -# should reset the example scratch directory -Remove-Item -Recurse -Force .tmp -ErrorAction SilentlyContinue -New-Item -ItemType Directory -Force -Path .tmp | Out-Null -``` - ## Testing ```powershell @@ -25,8 +17,8 @@ $version = script.ps1 -Version if ([string]::IsNullOrWhiteSpace($version)) { throw 'expected version output' } # should fail for an unexpected positional argument -$stderrPath = Join-Path .tmp 'invalid.err.log' -$stdoutPath = Join-Path .tmp 'invalid.out.log' +$stderrPath = 'invalid.err.log' +$stdoutPath = 'invalid.out.log' $succeeded = $true try { script.ps1 definitely-bogus 1> $stdoutPath 2> $stderrPath @@ -42,6 +34,6 @@ Get-Content $stderrPath | Select-String -Pattern 'does not accept positional arg ## Destroy tests ```powershell -# should remove the example scratch directory -Remove-Item -Recurse -Force .tmp -ErrorAction SilentlyContinue +# should remove the cli-contract failure logs +Remove-Item invalid.out.log,invalid.err.log -Force -ErrorAction SilentlyContinue ``` diff --git a/examples/minimal-bash/README.md b/examples/minimal-bash/README.md index 4df5c35..41d246e 100644 --- a/examples/minimal-bash/README.md +++ b/examples/minimal-bash/README.md @@ -6,23 +6,20 @@ no-argument flow and verifies the placeholder output from the prepared `dist/scr ## Setup ```bash -# should reset the example scratch directory -rm -rf .tmp && mkdir -p .tmp - # should run the default placeholder flow -script.sh > .tmp/run.log 2>&1 +script.sh > run.log 2>&1 ``` ## Testing ```bash # should print the placeholder execution message -grep -F 'Replace the body of script.sh with your project logic.' .tmp/run.log +grep -F 'Replace the body of script.sh with your project logic.' run.log ``` ## Destroy tests ```bash -# should remove the example scratch directory -rm -rf .tmp +# should remove the placeholder run log +rm -f run.log ``` diff --git a/examples/minimal-powershell/README.md b/examples/minimal-powershell/README.md index d8c9723..4af9333 100644 --- a/examples/minimal-powershell/README.md +++ b/examples/minimal-powershell/README.md @@ -6,24 +6,20 @@ no-argument flow and verifies the placeholder output from the prepared `dist/scr ## Setup ```powershell -# should reset the example scratch directory -Remove-Item -Recurse -Force .tmp -ErrorAction SilentlyContinue -New-Item -ItemType Directory -Force -Path .tmp | Out-Null - # should run the default placeholder flow -script.ps1 *> .tmp/run.log +script.ps1 *> run.log ``` ## Testing ```powershell # should print the placeholder execution message -Get-Content .tmp/run.log | Select-String -Pattern 'Replace the body of script.ps1 with your project logic.' +Get-Content run.log | Select-String -Pattern 'Replace the body of script.ps1 with your project logic.' ``` ## Destroy tests ```powershell -# should remove the example scratch directory -Remove-Item -Recurse -Force .tmp -ErrorAction SilentlyContinue +# should remove the placeholder run log +Remove-Item run.log -Force -ErrorAction SilentlyContinue ``` From a5674e33352ee280e57bf380dae966c29e82bfb1 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 15:44:03 -0400 Subject: [PATCH 09/16] Fix PowerShell Leia examples --- examples/cli-contract-powershell/README.md | 12 +++++------- examples/minimal-powershell/README.md | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/cli-contract-powershell/README.md b/examples/cli-contract-powershell/README.md index 08a3371..bdc08fa 100644 --- a/examples/cli-contract-powershell/README.md +++ b/examples/cli-contract-powershell/README.md @@ -7,28 +7,26 @@ output, and a clear failure for unexpected positional arguments. ```powershell # should show the debug flag in help output -script.ps1 -Help | Select-String -Pattern '-Debug' +& script.ps1 -Help | Select-String -Pattern '-Debug' # should show the version flag in help output -script.ps1 -Help | Select-String -Pattern '-Version' +& script.ps1 -Help | Select-String -Pattern '-Version' # should print a version string -$version = script.ps1 -Version +$version = & script.ps1 -Version if ([string]::IsNullOrWhiteSpace($version)) { throw 'expected version output' } # should fail for an unexpected positional argument -$stderrPath = 'invalid.err.log' -$stdoutPath = 'invalid.out.log' $succeeded = $true try { - script.ps1 definitely-bogus 1> $stdoutPath 2> $stderrPath + & script.ps1 definitely-bogus 1> invalid.out.log 2> invalid.err.log } catch { $succeeded = $false } if ($succeeded) { throw 'expected script.ps1 definitely-bogus to fail' } # should explain the positional argument failure -Get-Content $stderrPath | Select-String -Pattern 'does not accept positional arguments yet' +Get-Content invalid.err.log | Select-String -Pattern 'does not accept positional arguments yet' ``` ## Destroy tests diff --git a/examples/minimal-powershell/README.md b/examples/minimal-powershell/README.md index 4af9333..d52f7a7 100644 --- a/examples/minimal-powershell/README.md +++ b/examples/minimal-powershell/README.md @@ -7,7 +7,7 @@ no-argument flow and verifies the placeholder output from the prepared `dist/scr ```powershell # should run the default placeholder flow -script.ps1 *> run.log +& script.ps1 *> run.log ``` ## Testing From 0c0de656f84d60e9fca7721cd6b2c99d1ab6caf5 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 15:46:53 -0400 Subject: [PATCH 10/16] Fix minimal PowerShell Leia capture --- examples/minimal-powershell/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/minimal-powershell/README.md b/examples/minimal-powershell/README.md index d52f7a7..eccb363 100644 --- a/examples/minimal-powershell/README.md +++ b/examples/minimal-powershell/README.md @@ -7,7 +7,7 @@ no-argument flow and verifies the placeholder output from the prepared `dist/scr ```powershell # should run the default placeholder flow -& script.ps1 *> run.log +& script.ps1 | Set-Content -Path run.log ``` ## Testing From 49a60031783521839e629068b24d7fe88e14d30a Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 15:52:41 -0400 Subject: [PATCH 11/16] Fix final PowerShell minimal example --- examples/minimal-powershell/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/minimal-powershell/README.md b/examples/minimal-powershell/README.md index eccb363..c6b6dde 100644 --- a/examples/minimal-powershell/README.md +++ b/examples/minimal-powershell/README.md @@ -7,7 +7,8 @@ no-argument flow and verifies the placeholder output from the prepared `dist/scr ```powershell # should run the default placeholder flow -& script.ps1 | Set-Content -Path run.log +$output = & script.ps1 +$output | Set-Content -Path run.log ``` ## Testing From 86299ad47b292e189479429323a53ea32a2f9ce0 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 16:04:57 -0400 Subject: [PATCH 12/16] Allow bare PowerShell starter invocation --- dist/script.ps1 | 16 +++++++++++++++- examples/minimal-powershell/README.md | 3 +-- script.ps1 | 16 +++++++++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/dist/script.ps1 b/dist/script.ps1 index 1c6ebe3..5449eef 100644 --- a/dist/script.ps1 +++ b/dist/script.ps1 @@ -229,6 +229,20 @@ function Show-Usage { Write-Output ($lines -join [Environment]::NewLine) } +function Normalize-Positionals { + param( + [string[]]$Arguments = @() + ) + + $resolved = @($Arguments) + if ($resolved.Count -eq 1 -and $resolved[0] -eq '.') { + debug 'ignoring lone PowerShell wrapper positional "."' + return @() + } + + return $resolved +} + function Invoke-RunCli { if ($script:Resolved.Positionals.Count -gt 0) { $joined = ($script:Resolved.Positionals -join ' ') @@ -243,7 +257,7 @@ $script:USE_COLOR = Test-ColorEnabled $script:Resolved = [pscustomobject]@{ Debug = $script:DebugEnabled - Positionals = @($Positionals) + Positionals = @(Normalize-Positionals -Arguments $Positionals) } if ($Help) { diff --git a/examples/minimal-powershell/README.md b/examples/minimal-powershell/README.md index c6b6dde..e77c703 100644 --- a/examples/minimal-powershell/README.md +++ b/examples/minimal-powershell/README.md @@ -7,8 +7,7 @@ no-argument flow and verifies the placeholder output from the prepared `dist/scr ```powershell # should run the default placeholder flow -$output = & script.ps1 -$output | Set-Content -Path run.log +script.ps1 | Set-Content -Path run.log ``` ## Testing diff --git a/script.ps1 b/script.ps1 index 1c6ebe3..5449eef 100644 --- a/script.ps1 +++ b/script.ps1 @@ -229,6 +229,20 @@ function Show-Usage { Write-Output ($lines -join [Environment]::NewLine) } +function Normalize-Positionals { + param( + [string[]]$Arguments = @() + ) + + $resolved = @($Arguments) + if ($resolved.Count -eq 1 -and $resolved[0] -eq '.') { + debug 'ignoring lone PowerShell wrapper positional "."' + return @() + } + + return $resolved +} + function Invoke-RunCli { if ($script:Resolved.Positionals.Count -gt 0) { $joined = ($script:Resolved.Positionals -join ' ') @@ -243,7 +257,7 @@ $script:USE_COLOR = Test-ColorEnabled $script:Resolved = [pscustomobject]@{ Debug = $script:DebugEnabled - Positionals = @($Positionals) + Positionals = @(Normalize-Positionals -Arguments $Positionals) } if ($Help) { From 53e8710269244a26f8bc1cd0ba9182f2343268bf Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 16:11:15 -0400 Subject: [PATCH 13/16] Simplify starter argument handling --- dist/script.ps1 | 20 +++---------------- dist/script.sh | 7 ++++--- examples/cli-contract-bash/README.md | 17 ++-------------- examples/cli-contract-powershell/README.md | 23 ++-------------------- script.ps1 | 20 +++---------------- script.sh | 7 ++++--- 6 files changed, 18 insertions(+), 76 deletions(-) diff --git a/dist/script.ps1 b/dist/script.ps1 index 5449eef..0df4574 100644 --- a/dist/script.ps1 +++ b/dist/script.ps1 @@ -223,30 +223,16 @@ function Show-Usage { ' -Help show this help message' '' 'This starter intentionally has no product logic yet.' + 'Additional arguments are ignored by the starter until you replace the placeholder logic.' ('Replace the body of {0} with your project behavior.' -f $script:CLI_NAME) ) Write-Output ($lines -join [Environment]::NewLine) } -function Normalize-Positionals { - param( - [string[]]$Arguments = @() - ) - - $resolved = @($Arguments) - if ($resolved.Count -eq 1 -and $resolved[0] -eq '.') { - debug 'ignoring lone PowerShell wrapper positional "."' - return @() - } - - return $resolved -} - function Invoke-RunCli { if ($script:Resolved.Positionals.Count -gt 0) { - $joined = ($script:Resolved.Positionals -join ' ') - fail ('This starter does not accept positional arguments yet: {0}.' -f (bold $joined)) + debug 'ignoring starter positional arguments: {0}' ($script:Resolved.Positionals -join ' ') } debug 'running placeholder command body' @@ -257,7 +243,7 @@ $script:USE_COLOR = Test-ColorEnabled $script:Resolved = [pscustomobject]@{ Debug = $script:DebugEnabled - Positionals = @(Normalize-Positionals -Arguments $Positionals) + Positionals = @($Positionals) } if ($Help) { diff --git a/dist/script.sh b/dist/script.sh index af7462a..62ce260 100755 --- a/dist/script.sh +++ b/dist/script.sh @@ -96,7 +96,7 @@ usage() { fi cat < invalid.log 2>&1 - -# should explain the unknown option failure -grep -F 'Unrecognized option' invalid.log -``` - -## Destroy tests - -```bash -# should remove the cli-contract failure log -rm -f invalid.log ``` diff --git a/examples/cli-contract-powershell/README.md b/examples/cli-contract-powershell/README.md index bdc08fa..94a102a 100644 --- a/examples/cli-contract-powershell/README.md +++ b/examples/cli-contract-powershell/README.md @@ -1,7 +1,7 @@ # Script PowerShell CLI Contract Example -This example keeps coverage on the PowerShell-facing contract of `script.ps1`: help output, version -output, and a clear failure for unexpected positional arguments. +This example keeps coverage on the PowerShell-facing contract of `script.ps1`: help output and +version output. ## Testing @@ -15,23 +15,4 @@ output, and a clear failure for unexpected positional arguments. # should print a version string $version = & script.ps1 -Version if ([string]::IsNullOrWhiteSpace($version)) { throw 'expected version output' } - -# should fail for an unexpected positional argument -$succeeded = $true -try { - & script.ps1 definitely-bogus 1> invalid.out.log 2> invalid.err.log -} catch { - $succeeded = $false -} -if ($succeeded) { throw 'expected script.ps1 definitely-bogus to fail' } - -# should explain the positional argument failure -Get-Content invalid.err.log | Select-String -Pattern 'does not accept positional arguments yet' -``` - -## Destroy tests - -```powershell -# should remove the cli-contract failure logs -Remove-Item invalid.out.log,invalid.err.log -Force -ErrorAction SilentlyContinue ``` diff --git a/script.ps1 b/script.ps1 index 5449eef..0df4574 100644 --- a/script.ps1 +++ b/script.ps1 @@ -223,30 +223,16 @@ function Show-Usage { ' -Help show this help message' '' 'This starter intentionally has no product logic yet.' + 'Additional arguments are ignored by the starter until you replace the placeholder logic.' ('Replace the body of {0} with your project behavior.' -f $script:CLI_NAME) ) Write-Output ($lines -join [Environment]::NewLine) } -function Normalize-Positionals { - param( - [string[]]$Arguments = @() - ) - - $resolved = @($Arguments) - if ($resolved.Count -eq 1 -and $resolved[0] -eq '.') { - debug 'ignoring lone PowerShell wrapper positional "."' - return @() - } - - return $resolved -} - function Invoke-RunCli { if ($script:Resolved.Positionals.Count -gt 0) { - $joined = ($script:Resolved.Positionals -join ' ') - fail ('This starter does not accept positional arguments yet: {0}.' -f (bold $joined)) + debug 'ignoring starter positional arguments: {0}' ($script:Resolved.Positionals -join ' ') } debug 'running placeholder command body' @@ -257,7 +243,7 @@ $script:USE_COLOR = Test-ColorEnabled $script:Resolved = [pscustomobject]@{ Debug = $script:DebugEnabled - Positionals = @(Normalize-Positionals -Arguments $Positionals) + Positionals = @($Positionals) } if ($Help) { diff --git a/script.sh b/script.sh index af7462a..62ce260 100755 --- a/script.sh +++ b/script.sh @@ -96,7 +96,7 @@ usage() { fi cat < Date: Sun, 15 Mar 2026 16:22:10 -0400 Subject: [PATCH 14/16] update script urls --- README.md | 16 ++++++++-------- dist/robots.txt | 4 ++-- dist/sitemap.xml | 8 ++++---- netlify.toml | 5 ----- scripts/build-dist.js | 2 +- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 3f9a683..2fb0a70 100644 --- a/README.md +++ b/README.md @@ -10,24 +10,24 @@ Netlify serves. ## Quickstart ```sh -curl -fsSL https://script.example.com/script.sh | bash +curl -fsSL https://script.tanaab.sh/script.sh | bash ``` ```powershell -irm https://script.example.com/script.ps1 | iex +irm https://script.tanaab.sh/script.ps1 | iex ``` ## Installation -`template-netscript` is designed around hosted raw scripts at `https://script.example.com/script.sh` -and `https://script.example.com/script.ps1`. +`template-netscript` is designed around hosted raw scripts at `https://script.tanaab.sh/script.sh` +and `https://script.tanaab.sh/script.ps1`. - The Bash path requires Bash and cURL. - The PowerShell path requires PowerShell. - The hosted URLs serve the generated `dist/script.sh` and `dist/script.ps1` entrypoints used for release-shaped validation and Netlify publishing. - In a repository created from this template, replace `script.sh`, `script.ps1`, and - `https://script.example.com` before publishing. + `https://script.tanaab.sh` before publishing. ## Usage @@ -64,7 +64,7 @@ a directory that is already in your `PATH` or one you manage yourself. ```sh mkdir -p "$HOME/.local/bin" -curl -fsSL https://script.example.com/script.sh -o "$HOME/.local/bin/script" +curl -fsSL https://script.tanaab.sh/script.sh -o "$HOME/.local/bin/script" chmod +x "$HOME/.local/bin/script" script --help @@ -74,7 +74,7 @@ script --version ```powershell $target = Join-Path $HOME 'bin\script.ps1' New-Item -ItemType Directory -Force -Path (Split-Path -Parent $target) | Out-Null -Invoke-WebRequest https://script.example.com/script.ps1 -OutFile $target +Invoke-WebRequest https://script.tanaab.sh/script.ps1 -OutFile $target & $target -Help & $target -Version @@ -86,7 +86,7 @@ If you are using this repository as a GitHub template, the adoption flow is: 2. Rename `script.sh` and `script.ps1`, then update the same paths in `README.md`, `scripts/build-dist.js`, `site/index.html`, `netlify.toml`, `examples/`, and `.github/workflows/`. 3. Replace the placeholder command bodies in `script.sh` and `script.ps1`. -4. Replace the placeholder origin `https://script.example.com`. +4. Replace the placeholder origin `https://script.tanaab.sh`. 5. Run the local checks before pushing. ## Development diff --git a/dist/robots.txt b/dist/robots.txt index 0dd7f3b..1ba6498 100644 --- a/dist/robots.txt +++ b/dist/robots.txt @@ -1,7 +1,7 @@ User-agent: * Disallow: / -Sitemap: https://script.example.com/sitemap.xml -Host: https://script.example.com +Sitemap: https://script.tanaab.sh/sitemap.xml +Host: https://script.tanaab.sh Allow: /script.sh Allow: /script.ps1 Allow: /sitemap.xml diff --git a/dist/sitemap.xml b/dist/sitemap.xml index d0a5506..7454e0f 100644 --- a/dist/sitemap.xml +++ b/dist/sitemap.xml @@ -1,14 +1,14 @@ - https://script.example.com/script.sh - 2026-03-15T17:25:47.000Z + https://script.tanaab.sh/script.sh + 2026-03-15T20:11:15.000Z daily 0.5 - https://script.example.com/script.ps1 - 2026-03-15T17:25:47.000Z + https://script.tanaab.sh/script.ps1 + 2026-03-15T20:11:15.000Z daily 0.5 diff --git a/netlify.toml b/netlify.toml index 63877da..5cd79ed 100644 --- a/netlify.toml +++ b/netlify.toml @@ -12,8 +12,3 @@ [headers.values] Cache-Control = "public, max-age=31536000" Content-Type = "text/plain; charset=utf-8" - -[[plugins]] - package = "@netlify/plugin-lighthouse" - [plugins.inputs.audits] - output_path = "reports/lighthouse.html" diff --git a/scripts/build-dist.js b/scripts/build-dist.js index a60d5ce..8f997af 100644 --- a/scripts/build-dist.js +++ b/scripts/build-dist.js @@ -7,7 +7,7 @@ const execFileAsync = promisify(execFile); const DIST_URL = new URL('../dist/', import.meta.url); const REPO_URL = new URL('../', import.meta.url); const REPO_ROOT = fileURLToPath(REPO_URL); -const PUBLIC_ORIGIN = 'https://script.example.com'; +const PUBLIC_ORIGIN = 'https://script.tanaab.sh'; const PUBLISHED_SCRIPTS = [ { sourcePath: 'script.sh', From 38995d6f58a9bc312915434826db1eba814ffbc7 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 16:38:48 -0400 Subject: [PATCH 15/16] add agent driven setup doc --- README.md | 3 + TEMPLATE-SETUP.md | 246 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 TEMPLATE-SETUP.md diff --git a/README.md b/README.md index 2fb0a70..6af090b 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,9 @@ If you are using this repository as a GitHub template, the adoption flow is: 4. Replace the placeholder origin `https://script.tanaab.sh`. 5. Run the local checks before pushing. +See [`TEMPLATE-SETUP.md`](./TEMPLATE-SETUP.md) for the fuller agent-driven post-fork setup flow and +manual adoption checklist. + ## Development `template-netscript` uses Bun for repo-local tooling and treats `dist/` as a tracked, Netlify-ready diff --git a/TEMPLATE-SETUP.md b/TEMPLATE-SETUP.md new file mode 100644 index 0000000..984eba3 --- /dev/null +++ b/TEMPLATE-SETUP.md @@ -0,0 +1,246 @@ +# Template Setup + +`template-netscript` is meant to be copied, renamed, and simplified into a repo whose main product is +one or two hosted scripts served from Netlify. + +This document has two jobs: + +- describe the manual steps to turn this template into a real project +- act as a human-readable spec for AI-agent-driven setup + +## Agent-Driven Setup + +This document is also meant to work as an AI-agent setup spec. You can hand this file plus a small +set of inputs to an agent such as Codex and let it perform the repo adoption directly. + +That is the preferred path while the template is still evolving. + +## Agent Inputs + +If you use an AI agent to adopt this template, give it these inputs up front: + +- `project-slug` +- `github-owner` +- `public-origin` +- `script-basename` +- `runtime-support`: `bash`, `powershell`, or `both` +- `package-name`: optional, only if package publication is planned +- `description`: optional but useful + +Everything else should be derived where possible. + +## Copy-Paste Agent Prompt + +Use this prompt as the default setup handoff for an AI agent: + +```md +Use `TEMPLATE-SETUP.md` in this repository as the source of truth for adopting the template. + +Inputs: + +- project-slug: +- github-owner: +- public-origin: +- script-basename: +- runtime-support: +- package-name: +- description: + +Tasks: + +1. Follow `TEMPLATE-SETUP.md` in order. +2. Prune unsupported runtimes and remove their files, examples, workflows, and release logic. +3. Rename the kept script entrypoints and update all repo metadata, docs, workflow references, redirects, and build metadata to match the chosen name and origin. +4. Replace the placeholder script logic with project-specific logic only if requested; otherwise preserve the starter logic and just rename and re-scope it. +5. Reconfigure the hosted origin and Netlify-facing files to match the provided domain. +6. Intentionally refresh `dist` after source changes so tracked publish artifacts match the new repo. +7. Perform template cleanup after the setup is complete: + - remove `TEMPLATE-SETUP.md` + - remove the `TEMPLATE-SETUP.md` reference from `README.md` + - reset `CHANGELOG.md` so it is immediately usable by `prepare-release-action` + - keep the standard top header `## {{ UNRELEASED_VERSION }} - [{{ UNRELEASED_DATE }}]({{ UNRELEASED_LINK }})` + - keep a single bullet under that header recording that the project was initialized from this template + - remove or rewrite template-specific README sections that only make sense in the source template repo + - remove any leftover `template-netscript`, `script.tanaab.sh`, `script.sh`, or `script.ps1` placeholders that should no longer exist + - remove unused workflow files, examples, and runtime-specific config for dropped platforms +8. Run the relevant validation commands and report what was run and what could not be run. +9. Summarize: + - runtime support kept + - files removed + - files renamed + - final hosted URLs + - any follow-up work still recommended + +Use the smallest defensible diff that fully completes the adoption. +``` + +## Setup Order + +Follow these steps in order. The later steps assume the earlier decisions are already made. + +## 1. Choose Runtime Support + +Decide which script surfaces the new repo will support: + +- Bash on macOS and Linux via `script.sh` +- PowerShell on Windows via `script.ps1` +- both + +Then prune the repo to match that decision. + +If you keep Bash only: + +- keep [script.sh](/Users/pirog/tanaab/template-netscript/script.sh) +- remove [script.ps1](/Users/pirog/tanaab/template-netscript/script.ps1) +- remove [examples/cli-contract-powershell/README.md](/Users/pirog/tanaab/template-netscript/examples/cli-contract-powershell/README.md) +- remove [examples/minimal-powershell/README.md](/Users/pirog/tanaab/template-netscript/examples/minimal-powershell/README.md) +- remove [.github/workflows/pr-powershell-examples-tests.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/pr-powershell-examples-tests.yml) +- remove the `powershell-lint` job from [.github/workflows/pr-linter.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/pr-linter.yml) +- remove the Windows release job from [.github/workflows/release-tests.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/release-tests.yml) +- remove PowerShell references from [netlify.toml](/Users/pirog/tanaab/template-netscript/netlify.toml), [site/index.html](/Users/pirog/tanaab/template-netscript/site/index.html), and [scripts/build-dist.js](/Users/pirog/tanaab/template-netscript/scripts/build-dist.js) + +If you keep PowerShell only: + +- keep [script.ps1](/Users/pirog/tanaab/template-netscript/script.ps1) +- remove [script.sh](/Users/pirog/tanaab/template-netscript/script.sh) +- remove [examples/cli-contract-bash/README.md](/Users/pirog/tanaab/template-netscript/examples/cli-contract-bash/README.md) +- remove [examples/minimal-bash/README.md](/Users/pirog/tanaab/template-netscript/examples/minimal-bash/README.md) +- remove [.github/workflows/pr-bash-examples-tests.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/pr-bash-examples-tests.yml) +- remove the `bash-lint` job from [.github/workflows/pr-linter.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/pr-linter.yml) +- remove the Bash release job from [.github/workflows/release-tests.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/release-tests.yml) +- remove Bash references from [netlify.toml](/Users/pirog/tanaab/template-netscript/netlify.toml), [site/index.html](/Users/pirog/tanaab/template-netscript/site/index.html), and [scripts/build-dist.js](/Users/pirog/tanaab/template-netscript/scripts/build-dist.js) + +If you keep both: + +- keep the current runtime split +- only rename and replace placeholders in later steps + +## 2. Rename Scripts And Replace Repo Identity + +Pick the final script basename first. The template assumes `script`, but a real repo should replace +that with the actual project name. + +At minimum, update: + +- [script.sh](/Users/pirog/tanaab/template-netscript/script.sh) and or [script.ps1](/Users/pirog/tanaab/template-netscript/script.ps1) +- [dist/script.sh](/Users/pirog/tanaab/template-netscript/dist/script.sh) and or [dist/script.ps1](/Users/pirog/tanaab/template-netscript/dist/script.ps1) after rebuilding +- [README.md](/Users/pirog/tanaab/template-netscript/README.md) +- [scripts/build-dist.js](/Users/pirog/tanaab/template-netscript/scripts/build-dist.js) +- [site/index.html](/Users/pirog/tanaab/template-netscript/site/index.html) +- [package.json](/Users/pirog/tanaab/template-netscript/package.json) +- [CHANGELOG.md](/Users/pirog/tanaab/template-netscript/CHANGELOG.md) +- [`.github/workflows/`](/Users/pirog/tanaab/template-netscript/.github/workflows/pr-bash-examples-tests.yml) +- the example README files under [examples/](/Users/pirog/tanaab/template-netscript/examples/cli-contract-bash/README.md) + +Replace all template identity placeholders too: + +- `template-netscript` +- `script.tanaab.sh` +- `script.sh` +- `script.ps1` +- `@tanaabased/template-netscript` +- `tanaabased/template-netscript` + +If the new repo will publish a real package later, also update the package name, description, +repository URL, bugs URL, and any org or maintainer metadata in [package.json](/Users/pirog/tanaab/template-netscript/package.json). + +## 3. Replace The Placeholder Script Logic + +Both starter entrypoints intentionally ship with minimal behavior: + +- help output +- version output +- debug toggles +- one placeholder execution path + +The next real customization step is to replace the placeholder command body in: + +- [script.sh](/Users/pirog/tanaab/template-netscript/script.sh) +- [script.ps1](/Users/pirog/tanaab/template-netscript/script.ps1) + +Keep the top-level `SCRIPT_VERSION` assignment intact so release stamping continues to work with +`prepare-release-action`. + +If the real project eventually needs strict positional handling, subcommands, or richer option +parsing, introduce that after the repo has been renamed and the hosted path is working. + +## 4. Configure Hosting And Netlify + +This template assumes Netlify publishes the tracked [dist/](/Users/pirog/tanaab/template-netscript/dist) directory. + +Set up the new repo so that: + +- Netlify publishes `dist` +- the custom domain points at the repo's real hosted origin +- the final origin replaces `https://script.tanaab.sh` everywhere +- the raw script headers in [netlify.toml](/Users/pirog/tanaab/template-netscript/netlify.toml) still match the kept script types +- the landing page in [site/index.html](/Users/pirog/tanaab/template-netscript/site/index.html) redirects to the correct default script + +After the custom domain is known, update the canonical origin in: + +- [README.md](/Users/pirog/tanaab/template-netscript/README.md) +- [scripts/build-dist.js](/Users/pirog/tanaab/template-netscript/scripts/build-dist.js) + +Then intentionally rebuild `dist` so [dist/robots.txt](/Users/pirog/tanaab/template-netscript/dist/robots.txt) and [dist/sitemap.xml](/Users/pirog/tanaab/template-netscript/dist/sitemap.xml) match the real domain. + +## 5. Review CI And Release Scope + +Runtime pruning changes the CI and release contract, not just the file tree. + +Before shipping the new repo, make sure: + +- the example workflows only test the runtimes you kept +- [.github/workflows/pr-linter.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/pr-linter.yml) only lints the maintained surfaces +- [.github/workflows/release-tests.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/release-tests.yml) only verifies the artifacts you still publish +- [.github/workflows/release.yml](/Users/pirog/tanaab/template-netscript/.github/workflows/release.yml) only stamps the entrypoints you still ship +- example names, runner names, and docs still match the real runtime support + +If the repo no longer ships one of the two script types, remove its `version-injector` step instead +of leaving dead release logic behind. + +## 6. Template Cleanup + +Once the new repo identity is in place, remove template-only traces so the adopted repository looks +native rather than “generated from a template”. + +At minimum, clean up: + +- [TEMPLATE-SETUP.md](/Users/pirog/tanaab/template-netscript/TEMPLATE-SETUP.md) +- the `TEMPLATE-SETUP.md` reference in [README.md](/Users/pirog/tanaab/template-netscript/README.md) +- [CHANGELOG.md](/Users/pirog/tanaab/template-netscript/CHANGELOG.md), resetting it so it is immediately useful for `prepare-release-action` +- any README wording that only makes sense in the source template repo, especially the “If you are using this repository as a GitHub template” framing +- leftover `template-netscript` naming in docs and metadata +- leftover `script.tanaab.sh` placeholder references once the real domain is known +- workflow files, examples, and runtime-specific config for any dropped runtime + +For `CHANGELOG.md`, do not keep this template repo's release history in the adopted project. + +Reset it to this exact starting shape: + +```md +## {{ UNRELEASED_VERSION }} - [{{ UNRELEASED_DATE }}]({{ UNRELEASED_LINK }}) + +- Initialized this project from `tanaabased/template-netscript`. +``` + +That gives `prepare-release-action` a clean file to update on the first real release instead of +carrying forward irrelevant template history. + +Do not leave both the adopted project identity and template instructions in the same repo unless that +is an explicit product choice. + +## 7. Refresh Dist And Verify The Hosted Surface + +Once naming, hosting, and runtime support are settled: + +1. Run `bun install` +2. Run `bun run lint` +3. Intentionally run `bun run build` +4. Review the contents of `dist` +5. Verify the local release-shaped entrypoints +6. Verify the actual hosted install commands from the README + +The important check is not just whether source files look right. The important check is whether the +generated `dist` surface matches the docs and the real hosted URLs. + +That is the reason this document is checklist-shaped rather than purely narrative. From 63d036c00f3c62a97c46be8c038c1eba7d8da4c5 Mon Sep 17 00:00:00 2001 From: Mike Pirog Date: Sun, 15 Mar 2026 16:43:10 -0400 Subject: [PATCH 16/16] add agent driven setup doc part 2 --- CHANGELOG.md | 5 +++-- README.md | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68de225..934a243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ ## {{ UNRELEASED_VERSION }} - [{{ UNRELEASED_DATE }}]({{ UNRELEASED_LINK }}) +- Added [TEMPLATE-SETUP.md](./TEMPLATE-SETUP.md) as the agent-driven template adoption and seeding spec, including a copy-paste prompt plus cleanup and changelog-reset rules. - Added Bash and PowerShell starter entrypoints at repo root for hosted script repositories on macOS, Linux, and Windows. - Added CI linting for the maintained shell surfaces with `shellcheck` for Bash and `PSScriptAnalyzer` for PowerShell. -- Added Netlify-ready `dist/` publishing for both `script.sh` and `script.ps1`, plus landing-page, robots, and sitemap metadata. +- Added Netlify-ready tracked `dist/` publishing for both `script.sh` and `script.ps1`, plus landing-page, robots, and sitemap metadata. - Added release workflows that stamp both distributed entrypoints with `prepare-release-action` and verify the shipped artifacts before release. - Added runtime-specific Leia example suites for Bash and PowerShell, with Bash coverage on Ubuntu and macOS plus PowerShell coverage on Windows. -- Documented the starter's hosted install, local usage, and template-adoption flow for both runtime paths. +- Documented the starter's hosted install, local usage, and agent-driven post-fork setup flow for both runtime paths. diff --git a/README.md b/README.md index 6af090b..b0635d0 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ Bash and PowerShell scripts. The primary entrypoints live at repo root, support Netlify serves. > Supports Bash on macOS and Linux plus PowerShell on Windows. +> +> If you are adopting this repository as a template, start with +> [`TEMPLATE-SETUP.md`](./TEMPLATE-SETUP.md). It is the agent-driven setup spec and manual +> post-fork checklist. ## Quickstart @@ -89,9 +93,6 @@ If you are using this repository as a GitHub template, the adoption flow is: 4. Replace the placeholder origin `https://script.tanaab.sh`. 5. Run the local checks before pushing. -See [`TEMPLATE-SETUP.md`](./TEMPLATE-SETUP.md) for the fuller agent-driven post-fork setup flow and -manual adoption checklist. - ## Development `template-netscript` uses Bun for repo-local tooling and treats `dist/` as a tracked, Netlify-ready