Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@
"matchStrings": [
"https://github\\.com/(?<depName>[\\w-]+/[\\w.-]+)/archive/(?<currentDigest>[a-f0-9]{40})\\.tar\\.gz"
]
},
{
"customType": "regex",
"datasourceTemplate": "nuget",
"managerFilePatterns": [
"/^powershell-modules\\.psd1$/"
],
"matchStrings": [
"(?<depName>\\w[\\w.]*)\\s*=\\s*'(?<currentValue>[^']+)'"
],
"registryUrlTemplate": "https://www.powershellgallery.com/api/v2/"
}
],
"extends": [
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ dist/

# Node.js
node_modules/

# PowerShell Gallery modules provisioned by scripts/provision-psmodules.ps1
.psmodules/
28 changes: 28 additions & 0 deletions PSScriptAnalyzerSettings.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@{
# Auto-discovered by the VS Code PowerShell extension, so the editor and the
# psscriptanalyzer hk step enforce the same rules. The step fails on any
# finding (all severities), so no Severity gate is set. Default correctness
# rules run regardless (IncludeDefaultRules only matters with CustomRulePath);
# the entries below opt into the formatting rules, which are off by default.
Rules = @{
PSAvoidLongLines = @{
Enable = $true
MaximumLineLength = 100
}
PSAvoidSemicolonsAsLineTerminators = @{ Enable = $true }
PSPlaceCloseBrace = @{
Enable = $true
NoEmptyLineBefore = $true
}
PSPlaceOpenBrace = @{ Enable = $true }
PSUseConsistentIndentation = @{
Enable = $true
IndentationSize = 2
}
PSUseConsistentWhitespace = @{
Enable = $true
CheckParameter = $true
}
PSUseCorrectCasing = @{ Enable = $true }
}
}
12 changes: 12 additions & 0 deletions hk.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ local allSteps = new Mapping<String, Step> {
glob = List("**/*.tmpl")
check = "sh scripts/lint-templates.sh {{files}}"
}

// PSScriptAnalyzer over PowerShell sources. The `**/*.ps1` glob leaves
// `.ps1.tmpl` to render-templates (rendered-output linting is deferred with
// the shell case). pwsh is aqua-managed via mise; the module is provisioned
// into .psmodules by scripts/provision-psmodules.ps1 (mise postinstall).
// Check-only: PSScriptAnalyzer's -Fix is unreliable (see lint-powershell.ps1),
// so no fix command is wired; formatting is left to the editor.
["psscriptanalyzer"] {
glob = List("**/*.ps1")
exclude = Defaults.defaultExclude
check = "pwsh -NoProfile -File scripts/lint-powershell.ps1 {{files}}"
}
}

hooks = Defaults.hooksFor(allSteps)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Symlink ~/.claude/skills to the canonical ~/.agents/skills directory so
# Claude Code (which only reads ~/.claude/skills) sees the shared skills.
$SkillsTarget = Join-Path $env:USERPROFILE ".agents" "skills"
$Link = Join-Path $env:USERPROFILE ".claude" "skills"
$SkillsTarget = Join-Path -Path $env:USERPROFILE -ChildPath ".agents" -AdditionalChildPath "skills"
$Link = Join-Path -Path $env:USERPROFILE -ChildPath ".claude" -AdditionalChildPath "skills"
if (Test-Path $Link) { Remove-Item $Link -Force }
New-Item -ItemType SymbolicLink -Path $Link -Target $SkillsTarget | Out-Null
6 changes: 6 additions & 0 deletions home/dot_config/powershell/profile.d/20-prompt.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Configure OSC 7 for Starship
# https://wezterm.org/shell-integration.html#osc-7-on-windows-with-powershell-with-starship
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSAvoidUsingInvokeExpression', '',
Justification = 'starship init emits a script string to Invoke-Expression'
)]
param()

$prompt = ""
function Invoke-Starship-PreCommand {
$current_location = $executionContext.SessionState.Path.CurrentLocation
Expand Down
2 changes: 1 addition & 1 deletion home/dot_config/powershell/profile.d/30-modules.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Set-PSReadLineKeyHandler `
-Description 'Autocomplete commands via fzf' `
-ScriptBlock { Invoke-FzfTabCompletion }
function local:Write-AtCursor ([Parameter(ValueFromPipeline)]$Result) {
If ($Result.Length -gt 0) {
if ($Result.Length -gt 0) {
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($Result -join "")
}
[Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
Expand Down
24 changes: 24 additions & 0 deletions mise.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,12 @@ node = "24.18.0"
"npm:renovate" = "43.231.2"
pkl = "0.31.1"
pnpm = "11.9.0"
# Backs the hk psscriptanalyzer step (and lint-powershell.ps1) so pwsh is pinned
# and reproducible on dev machines and CI rather than relying on an ambient one.
# The per-tool postinstall provisions the pinned PowerShell Gallery modules (see
# powershell-modules.psd1) once pwsh is installed; the script is idempotent, so
# it re-runs cheaply on reinstall. Bumping a module version in the manifest
# needs a `mise install --force powershell` (or a manual run) to re-provision.
powershell = { version = "7.6.3", postinstall = "pwsh -NoProfile -File scripts/provision-psmodules.ps1" }
shellcheck = "0.11.0"
shfmt = "3.13.1"
12 changes: 12 additions & 0 deletions powershell-modules.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@{
# PowerShell Gallery modules the dev toolchain provisions into a repo-local,
# gitignored .psmodules via scripts/provision-psmodules.ps1 (run from the mise
# postinstall hook). Add a module by listing its pinned version here; the
# provisioner is generic and installs whatever this manifest declares.
#
# Renovate keeps these current via the nuget datasource pointed at the
# PowerShell Gallery feed (a custom manager in .github/renovate.json), so a
# bump lands like any other dependency PR. Keep the mapping alphabetical to
# reduce merge conflicts.
PSScriptAnalyzer = '1.24.0'
}
46 changes: 46 additions & 0 deletions scripts/lint-powershell.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env pwsh
# Lint PowerShell sources with PSScriptAnalyzer, mirroring
# scripts/lint-templates.sh: hk passes the changed files as positional args and
# this wraps the module so the hk step stays a one-liner. Rules live in
# PSScriptAnalyzerSettings.psd1 at the repo root (also honored by the VS Code
# PowerShell extension).
#
# Check-only: PSScriptAnalyzer's -Fix rewrites files in place and throws on some
# valid inputs (e.g. NullReferenceException on scripts with backtick line
# continuations), so autofix is left to the editor, which applies the same psd1.
[CmdletBinding()]
param(
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$Path = @()
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

if ($Path.Count -eq 0) { exit 0 }

# Prefer the repo-local, pinned module provisioned by provision-psmodules.ps1;
# fall back to whatever PSScriptAnalyzer is already on PSModulePath.
$root = Join-Path $PSScriptRoot '..'
$repoModules = Join-Path $root '.psmodules'
if (Test-Path $repoModules) {
$env:PSModulePath = $repoModules + [IO.Path]::PathSeparator + $env:PSModulePath
}

$settings = Join-Path $root 'PSScriptAnalyzerSettings.psd1'

# Invoke-ScriptAnalyzer -Path is single-valued, so analyze each file in turn.
$findings = foreach ($file in $Path) {
Invoke-ScriptAnalyzer -Path $file -Settings $settings
}

if ($findings) {
$report = $findings |
Format-Table -AutoSize -Wrap -Property Severity, RuleName,
@{ Label = 'Location'; Expression = { '{0}:{1}' -f $_.ScriptPath, $_.Line } },
Message |
Out-String
[Console]::Error.WriteLine($report)
exit 1
}
exit 0
33 changes: 33 additions & 0 deletions scripts/provision-psmodules.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env pwsh
# Provision pinned PowerShell Gallery modules into a repo-local, gitignored
# .psmodules so PowerShell-backed hk steps (e.g. psscriptanalyzer) are
# reproducible across dev machines and CI. mise has no PowerShell Gallery
# backend (and none is tracked upstream), so this bootstrap is the durable way
# to pin PS modules alongside the aqua-managed pwsh in mise.toml.
#
# Generic and data-driven: the module -> version map lives in
# powershell-modules.psd1 at the repo root. Idempotent -- a module already
# present at its pinned version is skipped, so re-running the postinstall hook
# is cheap.
[CmdletBinding()]
param()

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$root = Join-Path $PSScriptRoot '..'
$manifest = Join-Path $root 'powershell-modules.psd1'
$dest = Join-Path $root '.psmodules'

$modules = Import-PowerShellDataFile -Path $manifest
if ($modules.Count -eq 0) { exit 0 }

New-Item -ItemType Directory -Force -Path $dest | Out-Null

foreach ($name in $modules.Keys) {
$version = $modules[$name]
if (Test-Path (Join-Path $dest "$name/$version")) { continue }

[Console]::Error.WriteLine("Provisioning $name $version into .psmodules")
Save-Module -Name $name -RequiredVersion $version -Repository PSGallery -Path $dest
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.