Add PSScriptAnalyzer hook for PowerShell sources - #59
Conversation
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>
📝 WalkthroughWalkthroughThis PR adds PowerShell module provisioning infrastructure: a pinned module manifest, a provisioning script that downloads modules into a gitignored ChangesPowerShell module provisioning and linting
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
Compact metadata
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Warning Review ran into problems🔥 ProblemsLinked repositories: Your configuration references 1 linked repositories, but your current plan allows 0. Analyzed ``, skipped 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. Comment |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (2)
hk.pklis excluded by!**/*.pklmise.lockis excluded by!**/*.lock
📒 Files selected for processing (10)
.github/renovate.json.gitignorePSScriptAnalyzerSettings.psd1home/.chezmoiscripts/windows/run_onchange_after_skills-symlinks.ps1home/dot_config/powershell/profile.d/20-prompt.ps1home/dot_config/powershell/profile.d/30-modules.ps1mise.tomlpowershell-modules.psd1scripts/lint-powershell.ps1scripts/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 underhome/in the chezmoi repository, then runchezmoi applyto 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,.tmplfor Go templates,modify_for file modification scripts,run_onchange_before_andrun_onchange_after_for conditional scripts, andremove_to delete targets
Files inhome/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.ps1home/dot_config/powershell/profile.d/20-prompt.ps1
home/dot_config/**
⚙️ CodeRabbit configuration file
home/dot_config/**: # Personal Agent PreferencesCoding 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.jsonroot 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-lineovereslint-disable— scope suppressions
to the narrowest possible range.- Prefer
@ts-expect-errorover@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-erroronly 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 reasonCommunication
- 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.ps1home/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!
What
Closes the lint gap where PowerShell (
.ps1) had no dedicated linter — only generic file-hygiene. Adds an hkpsscriptanalyzerstep over**/*.ps1(leaving.ps1.tmpltorender-templates, as with shell).How
pwshis 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 runsscripts/provision-psmodules.ps1to install the pinned modules (powershell-modules.psd1) into a gitignored.psmodules. The provisioner is idempotent and data-driven.nugetdatasource 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 thenugetdatasource works against PSGallery.PSScriptAnalyzerSettings.psd1enables the opt-in formatting rules; default correctness rules run alongside them. The step fails on any finding of any severity.-Fixrewrites files and throws aNullReferenceExceptionon 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: namedJoin-Pathparameters30-modules: lowercaseifkeyword casing20-prompt: scoped suppression for the starshipInvoke-Expressioninit (no non-iexalternative)Verification
hk check --allandhk fix --allpass end-to-end.mise install --force powershellprovisionsPSScriptAnalyzerinto.psmodules.renovate-config-validator --strict.🤖 Generated with Claude Code