Skip to content

Add PSScriptAnalyzer hook for PowerShell sources - #59

Merged
gtbuchanan merged 1 commit into
mainfrom
add-psscriptanalyzer-hook
Jul 3, 2026
Merged

Add PSScriptAnalyzer hook for PowerShell sources#59
gtbuchanan merged 1 commit into
mainfrom
add-psscriptanalyzer-hook

Conversation

@gtbuchanan

Copy link
Copy Markdown
Owner

What

Closes the lint gap where PowerShell (.ps1) had no dedicated linter — only generic file-hygiene. Adds an hk psscriptanalyzer step over **/*.ps1 (leaving .ps1.tmpl to render-templates, as with shell).

How

  • Toolchain: pwsh is pinned via mise (aqua) so it is reproducible on dev machines and CI. PSScriptAnalyzer is a PowerShell Gallery module with no mise backend (jdx/mise has none, and none is tracked upstream), so a per-tool postinstall runs scripts/provision-psmodules.ps1 to install the pinned modules (powershell-modules.psd1) into a gitignored .psmodules. The provisioner is idempotent and data-driven.
  • Renovate: a custom manager keeps the module pins current via the nuget datasource against the PowerShell Gallery feed. There is no native PowerShell manager in Renovate (renovatebot/renovate#15155, priority-4-low, open since 2022), but the maintainer confirms the nuget datasource works against PSGallery.
  • Rules: PSScriptAnalyzerSettings.psd1 enables the opt-in formatting rules; default correctness rules run alongside them. The step fails on any finding of any severity.
  • Check-only: PSScriptAnalyzer's -Fix rewrites files and throws a NullReferenceException on valid input (e.g. scripts with backtick line continuations), so no fix command is wired — hk falls back to the check command in fix/pre-commit mode. Formatting is left to the editor, which honors the same settings file.

Conformance

Existing PowerShell sources were brought into compliance so the tree passes:

  • skills-symlinks: named Join-Path parameters
  • 30-modules: lowercase if keyword casing
  • 20-prompt: scoped suppression for the starship Invoke-Expression init (no non-iex alternative)

Verification

  • hk check --all and hk fix --all pass end-to-end.
  • Per-tool postinstall confirmed: mise install --force powershell provisions PSScriptAnalyzer into .psmodules.
  • Renovate config validated via renovate-config-validator --strict.

🤖 Generated with Claude Code

Closes the lint gap where PowerShell (.ps1) had no dedicated linter,
only generic file-hygiene. Adds an hk `psscriptanalyzer` step over
`**/*.ps1` (leaving `.ps1.tmpl` to render-templates, as with shell).

Toolchain: pin pwsh via mise (aqua) so it is reproducible on dev
machines and CI. PSScriptAnalyzer is a PowerShell Gallery module with
no mise backend, so a per-tool postinstall runs provision-psmodules.ps1
to install the pinned modules (powershell-modules.psd1) into a
gitignored .psmodules; the provisioner is idempotent and data-driven.

Renovate keeps the module pins current via a custom manager using the
nuget datasource against the PowerShell Gallery feed (no native
PowerShell manager exists upstream).

Rules live in PSScriptAnalyzerSettings.psd1 (formatting rules enabled;
default correctness rules run too). The step fails on any finding of
any severity. It is check-only: PSScriptAnalyzer's -Fix rewrites files
and throws on valid input, so formatting is left to the editor, which
honors the same settings file.

Conform the existing PowerShell sources so the tree passes:
- skills-symlinks: named Join-Path parameters
- 30-modules: lowercase `if` keyword casing
- 20-prompt: scoped suppression for the starship Invoke-Expression init,
  which has no non-iex alternative

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds PowerShell module provisioning infrastructure: a pinned module manifest, a provisioning script that downloads modules into a gitignored .psmodules directory, a PSScriptAnalyzer-based lint script with settings, and wiring through mise.toml, Renovate, and .gitignore. It also includes minor unrelated fixes to existing PowerShell scripts.

Changes

PowerShell module provisioning and linting

Layer / File(s) Summary
Module manifest and provisioning script
powershell-modules.psd1, scripts/provision-psmodules.ps1
Adds a manifest pinning PSScriptAnalyzer to version 1.24.0 and a script that downloads pinned modules into .psmodules/<name>/<version>, skipping already-provisioned versions.
Lint script and analyzer settings
PSScriptAnalyzerSettings.psd1, scripts/lint-powershell.ps1
Adds formatting/casing rule settings and a wrapper script that runs Invoke-ScriptAnalyzer against repo-local settings and .psmodules, reporting findings to stderr with exit code 1 on findings.
Tooling wiring: mise, Renovate, gitignore
mise.toml, .github/renovate.json, .gitignore
Pins PowerShell 7.6.3 with a postinstall provisioning step, adds a Renovate custom manager to track module versions in powershell-modules.psd1, and ignores the .psmodules/ directory.
Unrelated PowerShell script fixes
home/.chezmoiscripts/windows/run_onchange_after_skills-symlinks.ps1, home/dot_config/powershell/profile.d/20-prompt.ps1, home/dot_config/powershell/profile.d/30-modules.ps1
Rewrites Join-Path calls with named parameters, suppresses a PSAvoidUsingInvokeExpression warning, and normalizes if statement casing.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Mise
  participant ProvisionScript as provision-psmodules.ps1
  participant Manifest as powershell-modules.psd1
  participant PSGallery
  participant LintScript as lint-powershell.ps1

  Mise->>ProvisionScript: run postinstall
  ProvisionScript->>Manifest: read module/version map
  ProvisionScript->>ProvisionScript: check .psmodules/<name>/<version>
  ProvisionScript->>PSGallery: Save-Module (if missing)
  PSGallery-->>ProvisionScript: module files stored

  LintScript->>LintScript: load PSScriptAnalyzerSettings.psd1
  LintScript->>LintScript: prepend .psmodules to PSModulePath
  LintScript->>LintScript: run Invoke-ScriptAnalyzer per file
  LintScript-->>LintScript: aggregate findings, exit 1 or 0
Loading

Compact metadata

  • Related issues: None found
  • Related PRs: None found
  • Suggested labels: tooling, ci
  • Suggested reviewers: gtbuchanan

Poem

A rabbit hops through PowerShell land,
Provisioning modules, version pinned by hand,
Lint scripts hum and analyzers gleam,
Renovate watches the manifest stream,
Hop, hop, hooray — a tidy dotfiles dream! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, under 50 characters, and accurately summarizes the PowerShell PSScriptAnalyzer hook changes.
Description check ✅ Passed The description clearly matches the changeset and explains the PowerShell linting, provisioning, and Renovate updates.
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.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 1 linked repositories, but your current plan allows 0. Analyzed ``, skipped gtbuchanan/tooling.


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.

@gtbuchanan
gtbuchanan marked this pull request as ready for review July 3, 2026 02:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/provision-psmodules.ps1`:
- Around line 27-33: Before the Save-Module call in the provisioning loop, make
sure PSGallery is trusted so the script won’t pause for repository confirmation
on a fresh machine. Update the logic around the foreach block in
provision-psmodules.ps1 to check the repository state and set PSGallery to
Trusted if needed, then proceed with Save-Module for each $name/$version. Keep
the fix localized to the module provisioning flow so the existing $modules
iteration and destination checks remain unchanged.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7dc21a41-7f11-4d5c-bf43-0a091d3561d0

📥 Commits

Reviewing files that changed from the base of the PR and between 65ec479 and 7b448ca.

⛔ Files ignored due to path filters (2)
  • hk.pkl is excluded by !**/*.pkl
  • mise.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .github/renovate.json
  • .gitignore
  • PSScriptAnalyzerSettings.psd1
  • home/.chezmoiscripts/windows/run_onchange_after_skills-symlinks.ps1
  • home/dot_config/powershell/profile.d/20-prompt.ps1
  • home/dot_config/powershell/profile.d/30-modules.ps1
  • mise.toml
  • powershell-modules.psd1
  • scripts/lint-powershell.ps1
  • scripts/provision-psmodules.ps1
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
home/**/*

📄 CodeRabbit inference engine (AGENTS.md)

home/**/*: Never edit deployed files directly (e.g., ~/.gitconfig, ~/.config/AGENTS.md, ~/.claude/settings.json); always edit the corresponding source under home/ in the chezmoi repository, then run chezmoi apply to deploy
Use filename prefixes and suffixes to control chezmoi deployment behavior: dot_ for leading dot, private_ for restricted permissions, readonly_ for read-only, executable_ for execute bit, .tmpl for Go templates, modify_ for file modification scripts, run_onchange_before_ and run_onchange_after_ for conditional scripts, and remove_ to delete targets
Files in home/ should use UTF-8 encoding, 2-space indentation, LF line endings, include a final newline, and trim trailing whitespace, with the exception that *gitconfig* files must use tab indentation (required by Git)

Files:

  • home/dot_config/powershell/profile.d/30-modules.ps1
  • home/dot_config/powershell/profile.d/20-prompt.ps1
home/dot_config/**

⚙️ CodeRabbit configuration file

home/dot_config/**: # Personal Agent Preferences

Coding Style

  • Prefer functional programming style over procedural
  • Prefer pure functions for testability. Push side effects to app boundaries
  • Prefer discriminated unions over optional/nullable properties when modeling
    distinct states — they make invalid states unrepresentable
  • Prefer alphabetical sorting for unordered collections (object keys, YAML
    mappings, keyed lists, etc.) to reduce merge conflicts. Exception:
    package.json root keys have their own conventional order — sort nested
    objects (dependencies, scripts, etc.) but not top-level keys.
  • Prefer multiline comment style for comments spanning multiple lines

Static Analysis

  • Avoid diagnostic suppressions. They usually indicate poor code quality — fix
    the underlying issue instead. Only suppress when there is no reasonable alternative.
  • Prefer eslint-disable-next-line over eslint-disable — scope suppressions
    to the narrowest possible range.
  • Prefer @ts-expect-error over @ts-ignore — it fails when the error is
    resolved, preventing stale suppressions.
  • Every eslint/oxlint suppression comment MUST include a reason explaining why
    the rule is suppressed. Use the multiline format:
    /* eslint-disable-next-line rule-name-1, rule-name-2 --
       This is my reason */
    
  • @ts-expect-error only supports single-line comments — do NOT use block
    comments (/* */). Keep the reason concise to fit the line length limit.
    If extended reasoning is needed, put it in a separate comment above with
    a blank line between:
    // Extended reasoning here...
    
    // `@ts-expect-error` -- concise reason

Communication

  • I'm a senior engineer - don't explain basics
  • When presenting multiple questions or items that expect feedback, label each
    with a short identifier (e.g., [A], [B], [C]) so I can respond
    by label instead of restating the item.

Markdown

  • Only use numbered lists for sequential steps....

Files:

  • home/dot_config/powershell/profile.d/30-modules.ps1
  • home/dot_config/powershell/profile.d/20-prompt.ps1
🔇 Additional comments (10)
home/.chezmoiscripts/windows/run_onchange_after_skills-symlinks.ps1 (1)

3-4: LGTM!

home/dot_config/powershell/profile.d/20-prompt.ps1 (1)

3-8: LGTM!

home/dot_config/powershell/profile.d/30-modules.ps1 (1)

16-16: LGTM!

powershell-modules.psd1 (1)

1-13: LGTM!

scripts/provision-psmodules.ps1 (1)

18-25: LGTM!

PSScriptAnalyzerSettings.psd1 (1)

1-29: LGTM!

scripts/lint-powershell.ps1 (1)

1-47: LGTM!

mise.toml (1)

47-53: LGTM!

.github/renovate.json (1)

34-44: LGTM!

.gitignore (1)

12-14: LGTM!

Comment thread scripts/provision-psmodules.ps1
@gtbuchanan
gtbuchanan merged commit 34b5b20 into main Jul 3, 2026
2 checks passed
@gtbuchanan
gtbuchanan deleted the add-psscriptanalyzer-hook branch July 3, 2026 02:26
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.

1 participant