diff --git a/.github/workflows/pr-bash-examples-tests.yml b/.github/workflows/pr-bash-examples-tests.yml index 0787c1c..6cfc8f8 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,15 +41,17 @@ 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 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-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 diff --git a/.github/workflows/pr-powershell-examples-tests.yml b/.github/workflows/pr-powershell-examples-tests.yml index 76cf5e9..e6ca03e 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,15 +38,19 @@ 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 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/.github/workflows/release-tests.yml b/.github/workflows/release-tests.yml index df37a2e..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,12 +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: 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" = "${{ env.STAMPED_VERSION }}" release-powershell: runs-on: windows-2025 @@ -56,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" } 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 3f9a683..b0635d0 100644 --- a/README.md +++ b/README.md @@ -6,28 +6,32 @@ 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 ```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 +68,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 +78,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 +90,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/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. 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/script.ps1 b/dist/script.ps1 index 2d86319..0df4574 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, @@ -31,9 +31,10 @@ $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 +$script:DebugEnabled = $false function Test-Truthy { param([AllowNull()][object]$Value) @@ -53,22 +54,19 @@ 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' - } +# 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 +} - return $resolved - } catch { - return '0.0.0-dev' - } +if ($DebugPreference -eq 'Inquire' -or $DebugPreference -eq 'Continue') { + $script:DebugEnabled = $true } function Test-ColorEnabled { @@ -153,25 +151,8 @@ function Expand-Message { return $normalized } -function Write-StreamLine { - 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 { +function Write-Status { param( - [ValidateSet('stdout', 'stderr')] - [string]$Stream, [string]$Label, [scriptblock]$Colorizer, [AllowNull()][object]$Message = '', @@ -179,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-Information ('{0}: {1}' -f (& $Colorizer $Label), $text) -InformationAction Continue } function debug { @@ -188,43 +169,42 @@ function debug { [object[]]$MessageArgs = @() ) - if (-not $script:Resolved.Debug) { + if (-not $script:DebugEnabled) { 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 note { +function log { param( [AllowNull()][object]$Message = '', [object[]]$MessageArgs = @() ) - Write-StatusLine -Stream 'stdout' -Label 'note' -Colorizer ${function:ts} -Message $Message -MessageArgs $MessageArgs + Write-Output (Expand-Message -Message $Message -MessageArgs $MessageArgs) } -function fail { +function note { param( - [string]$Message, - [int]$ExitCode = 1 + [AllowNull()][object]$Message = '', + [object[]]$MessageArgs = @() ) - Write-StatusLine -Stream 'stderr' -Label 'error' -Colorizer ${function:red} -Message $Message - throw ([System.Exception]::new(('exit {0}: {1}' -f $ExitCode, $Message))) + Write-Status -Label 'note' -Colorizer ${function:ts} -Message $Message -MessageArgs $MessageArgs } -function Resolve-Debug { - if ($PSBoundParameters.ContainsKey('Debug')) { - return [bool]$Debug - } +function fail { + param( + [string]$Message + ) - return (Test-Truthy $env:DEBUG) -or (Test-Truthy $env:RUNNER_DEBUG) + $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 { @@ -243,30 +223,26 @@ 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-StreamLine -Stream 'stdout' -Message ($lines -join [Environment]::NewLine) + Write-Output ($lines -join [Environment]::NewLine) } 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' - 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 -if (-not $SCRIPT_VERSION) { - $SCRIPT_VERSION = Get-ScriptVersion -} - $script:Resolved = [pscustomobject]@{ - Debug = Resolve-Debug + Debug = $script:DebugEnabled Positionals = @($Positionals) } diff --git a/dist/script.sh b/dist/script.sh index ad0f05c..62ce260 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:-}}" @@ -101,7 +96,7 @@ usage() { fi cat < - https://script.example.com/script.sh - 2026-03-15T16:41:40.752Z + https://script.tanaab.sh/script.sh + 2026-03-15T20:11:15.000Z daily 0.5 - https://script.example.com/script.ps1 - 2026-03-15T16:41:40.752Z + https://script.tanaab.sh/script.ps1 + 2026-03-15T20:11:15.000Z daily 0.5 diff --git a/examples/cli-contract-bash/README.md b/examples/cli-contract-bash/README.md index 22987ee..c7dca48 100644 --- a/examples/cli-contract-bash/README.md +++ b/examples/cli-contract-bash/README.md @@ -1,14 +1,7 @@ # Script Bash CLI Contract Example -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 -``` +This example keeps coverage on the Bash-facing contract of `script.sh`: help output and version +output. ## Testing @@ -21,17 +14,4 @@ script.sh --help | grep -- '--version' # should print a version string test -n "$(script.sh --version)" - -# should fail for an unknown option -! script.sh --definitely-bogus > .tmp/invalid.log 2>&1 - -# should explain the unknown option failure -grep -F 'Unrecognized option' .tmp/invalid.log -``` - -## Destroy tests - -```bash -# should remove the example scratch directory -rm -rf .tmp ``` diff --git a/examples/cli-contract-powershell/README.md b/examples/cli-contract-powershell/README.md index daf5abf..94a102a 100644 --- a/examples/cli-contract-powershell/README.md +++ b/examples/cli-contract-powershell/README.md @@ -1,47 +1,18 @@ # 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. - -## Setup - -```powershell -# should reset the example scratch directory -Remove-Item -Recurse -Force .tmp -ErrorAction SilentlyContinue -New-Item -ItemType Directory -Force -Path .tmp | Out-Null -``` +This example keeps coverage on the PowerShell-facing contract of `script.ps1`: help output and +version output. ## Testing ```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 = Join-Path .tmp 'invalid.err.log' -$stdoutPath = Join-Path .tmp 'invalid.out.log' -$succeeded = $true -try { - script.ps1 definitely-bogus 1> $stdoutPath 2> $stderrPath -} 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' -``` - -## Destroy tests - -```powershell -# should remove the example scratch directory -Remove-Item -Recurse -Force .tmp -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..e77c703 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 | Set-Content -Path 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 ``` 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/script.ps1 b/script.ps1 index 2d86319..0df4574 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, @@ -31,9 +31,10 @@ $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 +$script:DebugEnabled = $false function Test-Truthy { param([AllowNull()][object]$Value) @@ -53,22 +54,19 @@ 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' - } +# 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 +} - return $resolved - } catch { - return '0.0.0-dev' - } +if ($DebugPreference -eq 'Inquire' -or $DebugPreference -eq 'Continue') { + $script:DebugEnabled = $true } function Test-ColorEnabled { @@ -153,25 +151,8 @@ function Expand-Message { return $normalized } -function Write-StreamLine { - 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 { +function Write-Status { param( - [ValidateSet('stdout', 'stderr')] - [string]$Stream, [string]$Label, [scriptblock]$Colorizer, [AllowNull()][object]$Message = '', @@ -179,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-Information ('{0}: {1}' -f (& $Colorizer $Label), $text) -InformationAction Continue } function debug { @@ -188,43 +169,42 @@ function debug { [object[]]$MessageArgs = @() ) - if (-not $script:Resolved.Debug) { + if (-not $script:DebugEnabled) { 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 note { +function log { param( [AllowNull()][object]$Message = '', [object[]]$MessageArgs = @() ) - Write-StatusLine -Stream 'stdout' -Label 'note' -Colorizer ${function:ts} -Message $Message -MessageArgs $MessageArgs + Write-Output (Expand-Message -Message $Message -MessageArgs $MessageArgs) } -function fail { +function note { param( - [string]$Message, - [int]$ExitCode = 1 + [AllowNull()][object]$Message = '', + [object[]]$MessageArgs = @() ) - Write-StatusLine -Stream 'stderr' -Label 'error' -Colorizer ${function:red} -Message $Message - throw ([System.Exception]::new(('exit {0}: {1}' -f $ExitCode, $Message))) + Write-Status -Label 'note' -Colorizer ${function:ts} -Message $Message -MessageArgs $MessageArgs } -function Resolve-Debug { - if ($PSBoundParameters.ContainsKey('Debug')) { - return [bool]$Debug - } +function fail { + param( + [string]$Message + ) - return (Test-Truthy $env:DEBUG) -or (Test-Truthy $env:RUNNER_DEBUG) + $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 { @@ -243,30 +223,26 @@ 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-StreamLine -Stream 'stdout' -Message ($lines -join [Environment]::NewLine) + Write-Output ($lines -join [Environment]::NewLine) } 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' - 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 -if (-not $SCRIPT_VERSION) { - $SCRIPT_VERSION = Get-ScriptVersion -} - $script:Resolved = [pscustomobject]@{ - Debug = Resolve-Debug + Debug = $script:DebugEnabled Positionals = @($Positionals) } diff --git a/script.sh b/script.sh index ad0f05c..62ce260 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:-}}" @@ -101,7 +96,7 @@ usage() { fi cat <