Skip to content

Skip template-incompatible CI/Publish jobs on un-initialized template#16

Merged
tablackburn merged 13 commits into
mainfrom
chore/workflow-template-guards
Apr 30, 2026
Merged

Skip template-incompatible CI/Publish jobs on un-initialized template#16
tablackburn merged 13 commits into
mainfrom
chore/workflow-template-guards

Conversation

@tablackburn
Copy link
Copy Markdown
Owner

@tablackburn tablackburn commented Apr 29, 2026

Summary

Adds a template-state guard to the GitHub Actions workflow jobs that don't make sense on the un-initialized template, so Actions can be re-enabled at the repo level without producing broken-CI noise on every PR.

  • CI.yaml unit-tests job — currently fails on the template because ./build.ps1 -Task Build,Test -Bootstrap calls Build's GENERATEMARKDOWN, which imports {{ModuleName}}/{{ModuleName}}.psd1 and chokes on the literal {{GUID}} placeholder (Cannot convert value "{{GUID}}" to type "System.Guid"). Now skipped on the template.
  • CI.yaml lint jobInvoke-ScriptAnalyzer -Path ./{{ModuleName}} fails because PowerShell's parser splits {{ModuleName}} into mismatched script-block delimiters (A positional parameter cannot be found that accepts argument '{ModuleName}'). Now skipped on the template.
  • PublishModuleToPowerShellGallery.yaml publish job — would, if it ever fired against main with placeholder content, try to publish a literal {{ModuleName}} module to PSGallery. Now skipped on the template.

The guard is a Detect template state step that writes a marker to $GITHUB_OUTPUT, plus per-step if: gates on the downstream work-doing steps:

- name: Detect template state
  id: template_guard
  shell: bash
  run: |
    if [ -f CHANGELOG.template.md ]; then
      echo "is_template=true" >> "$GITHUB_OUTPUT"
    else
      echo "is_template=false" >> "$GITHUB_OUTPUT"
    fi

- name: <work-doing step>
  if: steps.template_guard.outputs.is_template == 'false'
  ...

The original draft used a job-level if: hashFiles('CHANGELOG.template.md') == '', but hashFiles() is restricted to step-level contexts in GitHub Actions (caught by CodeRabbit and actionlint during review). The step-output pattern is the supported equivalent.

CHANGELOG.template.md exists only pre-init — Initialize-Template.ps1 moves it onto CHANGELOG.md during init, so the marker flips off for downstream repos. The path contains no {{Placeholder}} token, so init's substitution loop leaves the guard intact when it copies the workflow files into a new module.

Same marker pattern PR #15 uses in tests/Manifest.tests.ps1's -Skip:$isTemplate logic.

What's NOT changed

  • auto-merge-bots.yml — universal (handles Dependabot / pre-commit.ci PRs). No guard needed.
  • ggshield.yaml — universal (secret scanning). No guard needed.
  • Initialize-Template.ps1, tests, init flow — no changes.

Note on base branch

This branch stacked on chore/template-changelog (PR #15) because the marker file (CHANGELOG.template.md) only exists once #15's rename lands. PRs #14 and #15 are now merged, so the diff against main is clean.

Merge order: PR #14 → PR #15 → this PR. ✅

Operational follow-ups

Three items from the original "After this PR merges" list, status as of merge:

  1. Re-enable Actions — done pre-merge: gh api -X PUT repos/tablackburn/PowerShellModuleTemplate/actions/permissions --field enabled=true --field allowed_actions=all.
  2. Cancel the stuck Apr 9 queued run — both cancel and force-cancel return HTTP 500 from GitHub's side; the run is orphaned. Cosmetic only — leaving as-is.
  3. CI verification — done on this branch via workflow_dispatch. Run 25145990527 shows all four jobs succeeding with the expected pattern: checkout + Detect template state run, then all work-doing steps skipped.

Test plan

  • Visual diff review: guard placed at correct level, correct YAML indentation, comment explains the why.
  • Marker survives substitution: Initialize-Template.ps1's file-processing loop targets {{Placeholder}} patterns. The string CHANGELOG.template.md contains no such token, so the guard step is copied verbatim into a downstream's workflow files. (Verified by inspection of Initialize-Template.ps1:178-184 and the substitution rules.)
  • Marker direction is correct: [ -f CHANGELOG.template.md ] returns is_template=true on the template (so subsequent steps skip); on a downstream the file is absent (so is_template=false ⇒ subsequent steps run).
  • CI behavior verified post-Actions-re-enable on run 25145990527: lint=success (skipped past Detect step), all 3 unit-tests matrix entries=success (skipped past Detect step).
  • Downstream init smoke test (re-run the verification step from PR Add template-level CHANGELOG.md (CalVer) #15) — workflow files post-init have the same guard step and no CHANGELOG.template.md, so all jobs run.

Verified 2026-05-10: copied template HEAD to a scratch dir and ran Initialize-Template.ps1 -ModuleName SmokeTestPr16 -NoGitInit -NoBootstrap. Post-init: CHANGELOG.template.md removed, {{ModuleName}}/ renamed to SmokeTestPr16/, CHANGELOG.md generated from template. Both .github/workflows/CI.yaml and PublishModuleToPowerShellGallery.yaml retain the full "Detect template state" step + all downstream is_template == 'false' gates verbatim. Inside the init'd repo [ -f CHANGELOG.template.md ] returns false, so jobs would run.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Template-driven initialization with automated placeholder substitution and README/changelog templates
    • SemVer-aware dependency/version validation for manifest checks
  • Bug Fixes

    • Fixed help parameter name test
    • Improved module load error reporting to preserve original error details
  • Documentation

    • Expanded README and added README/CHANGELOG templates; added localized help template
  • Chores

    • Bumped PSScriptAnalyzer to 1.25.0
    • CI/publishing workflows now skip template repositories via conditional guards

tablackburn and others added 10 commits April 29, 2026 16:03
…ception

- tests/Help.tests.ps1: rename undefined $parameterNames to $commandParameterNames
  in the help-vs-code parameter existence check. The variable was never assigned
  in scope, silently making the assertion always evaluate against $null.
- {{ModuleName}}/{{ModuleName}}.psm1: re-throw original exception object in the
  dot-source catch block instead of throwing a new string, preserving stack
  traces and inner-exception details for debugging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the single-shape Manifest.tests.ps1 dependency check with constraint-
specific assertions and a SemVer comparison helper module.

- tests/ManifestHelpers.psm1: new module exporting Test-VersionConstraint, with
  SemVer 2.0.0 prerelease ordering, .NET Version normalization, and Equal /
  GreaterOrEqual / LessOrEqual constraint modes.
- tests/Manifest.tests.ps1: differentiate ModuleVersion (minimum), RequiredVersion
  (exact), and MaximumVersion (maximum) checks; accept both string and hashtable
  shapes for entries in requirements.psd1; detect duplicate RequiredModules
  entries; skip with a reason for plain-string dependencies. Preserves the
  existing BHBuildOutput override that points at Output/<Module>/<Version>/.

Validated end-to-end against an initialized SmokeTest module: 30 passed,
0 failed, 2 -Skip'd.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- .gitattributes: mark docs/en-US/* as linguist-generated so platyPS-generated
  help files don't skew GitHub language stats.
- .markdownlint-cli2.jsonc: relax MD013 in tables and code blocks, allow MD024
  duplicate headings under different parents, and ignore AGENTS.md, the
  generated docs/en-US/**, and the instructions/** AI-agent guides.
- docs/en-US/about_{{ModuleName}}.help.md: stub for Get-Help about_<Module>.
  Initialize-Template.ps1 already renames {{ModuleName}} files in docs/en-US.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PSScriptAnalyzerSettings.psd1: replace the single-line @{ IncludeRules = @('*') }
  with the standard structured form (IncludeDefaultRules + Include/Exclude/Rules
  blocks). Includes a commented-out PSUseCompatibleSyntax/PSUseCompatibleCmdlets
  scaffold for projects that want cross-version compatibility checks.
- build.depend.psd1: bump PSScriptAnalyzer from 1.24.0 to 1.25.0 to pick up
  newer rule fixes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The repo's README.md was a placeholder-laden module README, which made it look
like docs for a hypothetical {{ModuleName}} when viewed on GitHub. Split into
two files so the GitHub template page sells the template itself.

- README.md: rewritten as template documentation — what's included (build,
  CI, devcontainer, instructions, test infra), quick-start with Initialize-
  Template.ps1, placeholder reference table, and post-init project structure.
- README.template.md: holds the original placeholder-based module README. The
  file-processing loop in Initialize-Template.ps1 still substitutes its
  placeholders, then the script moves it over README.md as the final step.
- Initialize-Template.ps1:
  - Rename files in docs/en-US/ that contain {{ModuleName}} (e.g.,
    about_{{ModuleName}}.help.md), parallel to the Public/Private/test
    Prefix-rename loop.
  - After renames, replace template-facing README.md with the now-substituted
    README.template.md.
  - Adjust the "Next steps" message to match the new flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ManifestHelpers: strip SemVer build metadata in Split-SemVerString;
  use [bigint] (not [long]) for numeric prerelease comparison so large
  identifiers don't overflow.
- Initialize-Template: replace 'docs\en-US' and 'tests\Unit\{Public,Private}'
  with forward-slash child paths so docs/test renaming works on PS7+
  Linux/macOS.
- Manifest.tests: skip with a clear reason when requirements.psd1 has an
  empty/whitespace Version, instead of throwing inside Test-VersionConstraint.
- {{ModuleName}}.psm1: bare 'throw' in the dot-source catch to preserve
  the original ErrorRecord (was 'throw $_').
- about_{{ModuleName}}.help.md: drop trailing lone '-' that rendered as
  an empty bullet under KEYWORDS.
- README.md / README.template.md: label project-tree fence as 'text'
  (markdownlint MD040).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The existing CHANGELOG.md is the downstream-module starter (contains
{{Date}} and {{Prefix}}Example placeholders). Rename it to follow the
README.template.md convention so a separate CHANGELOG.md can hold the
template's own version history. Initialize-Template.ps1 will swap
CHANGELOG.template.md over CHANGELOG.md during init in a follow-up
commit.

Pure rename — no content change — so git log --follow stays clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track changes to the PowerShell module template itself with its own
changelog, separate from the downstream-module starter changelog.

CHANGELOG.md (new) — template's own history. Uses Calendar Versioning
(YYYY.MM.DD) since the template has no API contract for SemVer to
describe; date-based answers the question downstream consumers actually
ask ("how stale is my init?"). First entry [2026.04.29] captures the
five chunks landed in the chore/template-sync branch.

Initialize-Template.ps1:
- Add CHANGELOG.md to the substitution-loop exclusion list. Without
  this, init would rewrite literal {{ModuleName}} mentions in the
  template's history to the user's chosen module name.
- After the README.template.md → README.md swap, add a parallel
  CHANGELOG.template.md → CHANGELOG.md swap so downstream modules
  receive the placeholder-laden starter changelog (the existing
  CHANGELOG.template.md, formerly CHANGELOG.md), not the template's
  own history.

tests/Manifest.tests.ps1:
- Skip the "Changelog and manifest versions are the same" assertion
  when running on the un-initialized template, where CHANGELOG.md
  holds the template's CalVer version which intentionally diverges
  from the {{ModuleName}}.psd1 ModuleVersion. Marker: presence of
  CHANGELOG.template.md (exists only pre-init; survives init's
  substitution loop because nothing in the path matches a placeholder
  token). The "valid version in changelog" assertion stays — PowerShell
  parses [Version]'2026.4.29' fine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a Changelog section to the template-facing README so visitors of
the template repo on GitHub can discover the version history.
Inserted between Requirements and License, with a one-line note that
the template uses CalVer rather than SemVer (so reviewers don't read
date-shaped numbers as broken SemVer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI.yaml's unit-tests job runs ./build.ps1 -Task Build,Test, which fails
on the template because Build's GENERATEMARKDOWN task imports
{{ModuleName}}/{{ModuleName}}.psd1 and {{GUID}} can't be parsed as a
valid Guid. PublishModuleToPowerShellGallery.yaml would, if it ever
fired against main with placeholder content, try to publish a literal
"{{ModuleName}}" module to PSGallery.

Add a job-level guard to both:

    if: hashFiles('CHANGELOG.template.md') == ''

CHANGELOG.template.md exists only pre-init (Initialize-Template.ps1
moves it onto CHANGELOG.md during init), so the marker flips on the
template / off downstream. The path contains no {{Placeholder}} token,
so init's substitution loop leaves the if-clause untouched in the
copied workflow files — downstream repos run all jobs normally.

The lint job in CI.yaml is left unchanged: Invoke-ScriptAnalyzer runs
fine on the template's placeholder folder. ggshield.yaml and
auto-merge-bots.yml are universal and need no guard.

Companion to PR #15 (template-level CHANGELOG.md / CalVer); reuses the
same marker the Manifest.tests.ps1 -Skip:$isTemplate logic uses there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 29, 2026 23:59
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

Adds template-focused documentation and initialization, conditional CI/CD workflow guards for uninitialized templates, markdown lint configuration, a SemVer-aware version comparison helper for tests, expanded manifest dependency validation in tests, and minor module/test fixes.

Changes

Cohort / File(s) Summary
Workflows & Attributes
.github/workflows/CI.yaml, .github/workflows/PublishModuleToPowerShellGallery.yaml, .gitattributes
Introduce a template-detection guard (checks CHANGELOG.template.md) to skip workflow steps when the repo is an uninitialized template; mark docs/en-US/ as linguist-generated.
Markdown/Lint Config
.markdownlint-cli2.jsonc
Add markdownlint-cli2 configuration, disable select rules, and ignore docs and instruction paths (including docs/en-US/**).
Template Docs & Changelog
README.md, README.template.md, CHANGELOG.md, CHANGELOG.template.md, docs/en-US/about_{{ModuleName}}.help.md
Add comprehensive template README and README template, introduce changelog template, update changelog content to template-focused framing, and add templated help doc.
Initializer & Build Config
Initialize-Template.ps1, PSScriptAnalyzerSettings.psd1, build.depend.psd1
Improve placeholder replacement (skip CHANGELOG.md), normalize path handling and final artifact generation, restructure PSScriptAnalyzer settings (IncludeDefaultRules/PS*), and bump PSScriptAnalyzer to 1.25.0.
Testing: Manifest & Helpers
tests/Manifest.tests.ps1, tests/ManifestHelpers.psm1, tests/Help.tests.ps1
Add ManifestHelpers.psm1 exporting Test-VersionConstraint with SemVer-compliant prerelease handling; expand manifest tests to validate dependency constraints (Equal/GreaterOrEqual/LessOrEqual) and skip checks when running the template; fix help test parameter source.
Module Error Handling
{{ModuleName}}/{{ModuleName}}.psm1
Change dot-source catch to log the failing file path and rethrow the original exception rather than throwing a new string message.
Changelog Template File
CHANGELOG.template.md
Add new changelog template used by initializer when generating project changelog.

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer / CI
  participant Pester as Test Runner (Pester)
  participant ManifestTest as tests/Manifest.tests.ps1
  participant Helpers as tests/ManifestHelpers.psm1
  participant Manifest as ModuleManifest (`*.psd1` / manifest)
  participant Req as requirements.psd1

  Dev->>Pester: run tests
  Pester->>ManifestTest: execute manifest tests
  ManifestTest->>Manifest: read RequiredModules
  ManifestTest->>Req: load declared requirements
  ManifestTest->>Helpers: call Test-VersionConstraint(manifestVersion, requirement, constraint)
  Helpers->>Helpers: parse semver, compare core & prerelease
  Helpers-->>ManifestTest: return pass/fail or throw on bad input
  ManifestTest-->>Pester: report test result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hopped through templates, tidy and bright,
Version-wise checks in the soft moonlight.
Docs and tests lined up in a row,
SemVer whispers where prereleases go.
Initialize, run — watch the carrots grow!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title directly and accurately summarizes the primary change: adding template-state guards to CI/Publish jobs to skip them on un-initialized templates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/workflow-template-guards

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
Initialize-Template.ps1 (1)

245-246: 💤 Low value

Consider using Join-Path for path consistency.

Lines 243-244 use Join-Path for constructing paths, but lines 245-246 use hardcoded forward-slash strings. While PowerShell handles both, using Join-Path would be more consistent and idiomatic.

♻️ Suggested refactor for consistency
-    $testPublicFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests/Unit/Public'
-    $testPrivateFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests/Unit/Private'
+    $testsFolder = Join-Path -Path $PSScriptRoot -ChildPath 'tests'
+    $testUnitFolder = Join-Path -Path $testsFolder -ChildPath 'Unit'
+    $testPublicFolder = Join-Path -Path $testUnitFolder -ChildPath 'Public'
+    $testPrivateFolder = Join-Path -Path $testUnitFolder -ChildPath 'Private'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Initialize-Template.ps1` around lines 245 - 246, The two variables
$testPublicFolder and $testPrivateFolder are constructed with hardcoded
forward-slash paths; change them to use Join-Path like the earlier lines to keep
path construction consistent and platform-idiomatic — update the assignments
that set $testPublicFolder and $testPrivateFolder to call Join-Path with
$PSScriptRoot and the respective child folder names (e.g.,
'tests','Unit','Public' and 'tests','Unit','Private') so they match the style
used elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/CI.yaml:
- Around line 59-65: The job-level use of hashFiles() is invalid; replace it by
adding an initial step (e.g., a step named check_template) that detects the
presence of CHANGELOG.template.md (using a shell test or hashFiles() inside that
step), emits a step output like has_template via the GITHUB_OUTPUT mechanism
(e.g., echo "has_template=true" >> $GITHUB_OUTPUT), and then guard subsequent
steps with step-level if conditions referencing
steps.check_template.outputs.has_template (or its negation) instead of using
hashFiles() at the job level.

In `@tests/Manifest.tests.ps1`:
- Around line 276-291: The MaximumVersion test branch fails to stop execution
after calling Set-ItResult -Skipped, causing the subsequent
Test-VersionConstraint run to execute incorrectly; update the block that checks
$dependencyRawData and the block that checks $requirementsVersionSkipReason to
add an immediate return after each Set-ItResult -Skipped so the function exits
early (referencing Set-ItResult, $dependencyRawData.MaximumVersion,
$requirementsVersionSkipReason, and Test-VersionConstraint) to prevent running
Test-VersionConstraint when the test was skipped.
- Around line 293-308: The test block for "It '<_.Name> has a minimum
version..." can continue executing after a skip because there is no return after
Set-ItResult -Skipped; to fix, add explicit returns immediately after each
Set-ItResult -Skipped call (the branch checking $dependencyRawData -or missing
'ModuleVersion' and the branch checking $requirementsVersionSkipReason) so the
remainder that builds $constraintParameters and calls Test-VersionConstraint is
not run when skipped; locate the test case containing Set-ItResult,
$dependencyRawData.ModuleVersion, $requirementsVersion,
$requirementsVersionSkipReason, and Test-VersionConstraint and insert the return
statements directly after the Set-ItResult -Skipped invocations.
- Around line 259-274: The test may continue after Set-ItResult -Skipped and
attempt to access $dependencyRawData.RequiredVersion when $dependencyRawData is
$null; update the 'It '<_.Name> has a matching required version in
requirements.psd1'' block to exit early after each skip by returning immediately
after Set-ItResult -Skipped calls (both the branch where $dependencyRawData is
null/missing 'RequiredVersion' and the branch that checks
$requirementsVersionSkipReason) so the subsequent building of
$constraintParameters and call to Test-VersionConstraint only runs when
$dependencyRawData and its RequiredVersion are present.
- Around line 244-257: The test continues after calling Set-ItResult -Skipped
when $dependencyRawData is $null, causing a null reference when accessing
$dependencyRawData.Keys; update the It block to return immediately after
Set-ItResult -Skipped (i.e., add a return after the Set-ItResult -Skipped call)
so the subsequent code that computes $dependencyKeysUsed and checks
dependencyKeysUsed.Count only runs when $dependencyRawData is non-null.

---

Nitpick comments:
In `@Initialize-Template.ps1`:
- Around line 245-246: The two variables $testPublicFolder and
$testPrivateFolder are constructed with hardcoded forward-slash paths; change
them to use Join-Path like the earlier lines to keep path construction
consistent and platform-idiomatic — update the assignments that set
$testPublicFolder and $testPrivateFolder to call Join-Path with $PSScriptRoot
and the respective child folder names (e.g., 'tests','Unit','Public' and
'tests','Unit','Private') so they match the style used elsewhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b7915ecd-4083-4039-8bad-d99844e0c332

📥 Commits

Reviewing files that changed from the base of the PR and between c1bab46 and 83ea2b3.

📒 Files selected for processing (16)
  • .gitattributes
  • .github/workflows/CI.yaml
  • .github/workflows/PublishModuleToPowerShellGallery.yaml
  • .markdownlint-cli2.jsonc
  • CHANGELOG.md
  • CHANGELOG.template.md
  • Initialize-Template.ps1
  • PSScriptAnalyzerSettings.psd1
  • README.md
  • README.template.md
  • build.depend.psd1
  • docs/en-US/about_{{ModuleName}}.help.md
  • tests/Help.tests.ps1
  • tests/Manifest.tests.ps1
  • tests/ManifestHelpers.psm1
  • {{ModuleName}}/{{ModuleName}}.psm1

Comment thread .github/workflows/CI.yaml Outdated
Comment thread tests/Manifest.tests.ps1
Comment thread tests/Manifest.tests.ps1
Comment thread tests/Manifest.tests.ps1
Comment thread tests/Manifest.tests.ps1
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a template-state guard to skip CI/publish jobs that are incompatible with the un-initialized template repo, allowing GitHub Actions to be enabled without generating failing runs on every PR. The branch also includes stacked template improvements around manifest validation, initialization flow, and template documentation.

Changes:

  • Add job-level GitHub Actions guards (based on CHANGELOG.template.md) to skip template-incompatible CI and publish jobs.
  • Expand manifest dependency validation with SemVer-aware constraint checks and additional parsing/validation logic.
  • Update template initialization and documentation (README/CHANGELOG split, docs stubs, analyzer config).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
.github/workflows/CI.yaml Skip unit-tests job on un-initialized template using hashFiles('CHANGELOG.template.md').
.github/workflows/PublishModuleToPowerShellGallery.yaml Skip publish job on un-initialized template to avoid publishing placeholder module.
tests/ManifestHelpers.psm1 Adds SemVer comparison + constraint helper used by dependency tests.
tests/Manifest.tests.ps1 Adds template-state skip for changelog/version assertion and richer dependency/version-constraint validation.
tests/Help.tests.ps1 Fixes help-vs-code parameter validation variable usage.
{{ModuleName}}/{{ModuleName}}.psm1 Adjusts dot-sourcing error handling to rethrow original error after logging.
Initialize-Template.ps1 Excludes template CHANGELOG.md from substitution; adds docs rename + README/CHANGELOG swap logic.
README.md Replaces module-facing placeholder README with template-facing repository README.
README.template.md Adds module-facing README used post-init.
CHANGELOG.md Converts template changelog to CalVer + template-focused entries.
CHANGELOG.template.md Adds module-facing starter changelog used post-init.
PSScriptAnalyzerSettings.psd1 Switches to structured analyzer settings format.
build.depend.psd1 Bumps PSScriptAnalyzer dependency version.
.markdownlint-cli2.jsonc Adds markdownlint-cli2 configuration and ignore patterns.
.gitattributes Marks generated help docs as linguist-generated.
docs/en-US/about_{{ModuleName}}.help.md Adds about_ help stub for initialized modules.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/ManifestHelpers.psm1 Outdated
Comment thread tests/Manifest.tests.ps1
Comment thread tests/Manifest.tests.ps1
Comment thread tests/Manifest.tests.ps1
Comment thread tests/Manifest.tests.ps1
- ci: hashFiles() is not allowed in jobs.<job_id>.if (actionlint and the
  GitHub Actions docs both flag it). Replace the job-level guard with a
  Detect template state step that writes is_template to $GITHUB_OUTPUT,
  then gate downstream steps on that output. CI.yaml guards Cache,
  Build/Test, and the upload steps; PublishModuleToPowerShellGallery.yaml
  guards Get Module Version and Check if Release Exists, with the
  remaining publish steps cascading via their existing dependencies on
  steps.check_release.outputs.exists.
- tests: [bigint] is a PS 7+ accelerator only; Windows PowerShell 5.1
  needs the full type. Spell it [System.Numerics.BigInteger] in
  Compare-SemVerPrerelease so the SemVer numeric-identifier comparison
  works on the 5.1 runner.
- init: switch the two hardcoded forward-slash test paths to Join-Path
  for consistency with the rest of Initialize-Template.ps1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Initialize-Template.ps1`:
- Around line 283-299: The current Move-Item calls for $readmeTemplate ->
$readmePath and $changelogTemplate -> $changelogPath use -Force and will
unconditionally overwrite existing README.md/CHANGELOG.md; change the logic in
that block to first check if the destination file exists (Test-Path on
$readmePath / $changelogPath) and only perform Move-Item if the destination does
not exist, or prompt the user for confirmation before overwriting, ensuring you
reference the existing variables ($readmeTemplate, $readmePath,
$changelogTemplate, $changelogPath) and replace the unconditional Move-Item
-Force behavior with a guarded or interactive move.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: da714c07-8dda-4f7f-9607-a83785274ec2

📥 Commits

Reviewing files that changed from the base of the PR and between 83ea2b3 and 997c47f.

📒 Files selected for processing (4)
  • .github/workflows/CI.yaml
  • .github/workflows/PublishModuleToPowerShellGallery.yaml
  • Initialize-Template.ps1
  • tests/ManifestHelpers.psm1
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/ManifestHelpers.psm1
  • .github/workflows/CI.yaml

Comment thread Initialize-Template.ps1
@tablackburn
Copy link
Copy Markdown
Owner Author

Re: the Join-Path nitpick on Initialize-Template.ps1:245-246 (from CodeRabbit's review summary) — applied in 997c47f. Introduced a $testUnitFolder intermediate and built $testPublicFolder / $testPrivateFolder from it via Join-Path.

tablackburn and others added 2 commits April 29, 2026 23:18
The README and CHANGELOG generation steps used Move-Item -Force, which
silently overwrites the destination. The top-of-script "already
initialized" check is just a warning the user can dismiss, so a re-run
after customization (e.g., user re-runs init to redo something, or
template files reappear via git checkout) could clobber their work.

Guard each move on the destination not existing. If both source and
destination are present, warn and leave both in place — manual
resolution is safer than picking a side automatically.

Addresses CodeRabbit feedback on PR #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PowerShell's parser splits the literal './{{ModuleName}}' into mismatched
script-block delimiters, so Invoke-ScriptAnalyzer fails before it can
read the folder:

  A positional parameter cannot be found that accepts argument
  '{ModuleName}'.

The original PR claimed lint worked on the template; testing post-
Actions-re-enable proves otherwise. Apply the same Detect template state
guard the unit-tests job uses, gated on Cache PowerShell modules,
Install PSScriptAnalyzer, and Run PSScriptAnalyzer. Downstream init's
substitution turns './{{ModuleName}}' into a real path so the guard
flips off and lint runs normally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tablackburn tablackburn merged commit c8f5586 into main Apr 30, 2026
15 checks passed
@tablackburn tablackburn deleted the chore/workflow-template-guards branch April 30, 2026 03:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants