Thanks for your interest in contributing! This guide covers the dev setup, coding conventions, and PR process.
-
Clone the repo:
git clone https://github.com/26zl/PowerShellPerfect.git cd PowerShellPerfect
-
Copy the profile to your PowerShell profile directories:
.\setprofile.ps1
-
Restart your terminal to load the profile.
All code must work under both PowerShell 5.1 and 7+. Key differences:
Remove-Aliasonly exists in PS6+; PS5 usesRemove-Item Alias:\<name>$PSStyleonly exists in PS7.2+PredictionSource/PredictionViewStyleare PowerShell Core-only PSReadLine options- Guard version-specific code with
$PSVersionTable.PSEditionor$PSVersionTable.PSVersion.Major
- No em dashes (U+2014) or non-ASCII characters - CI PS5 parse fails on them
- No BOM: Never use
Set-Content -Encoding UTF8. Use[System.IO.File]::WriteAllText()with[System.Text.UTF8Encoding]::new($false) - Avoid
<angle brackets>and|pipes inside double-quoted strings - use single quotes or-fformat operator - Use approved PowerShell verbs for function names (e.g.,
Copy-SshKeywith aliasssh-copy-key)
- Add an entry to the
$script:ProfileToolsarray inMicrosoft.PowerShell_profile.ps1withName,Id(winget),Cmd,Cache, andVerCmdAlso setUpgradeStrategy(wingetfor normal tools,preserve-directonly when a direct/MSI install must not be pushed back through winget). - Add a numbered install step in
setup.ps1(it cannot readProfileTools)
CI uses PSScriptAnalyzer and fails on both warnings and errors. Run it locally before pushing:
Install-Module -Name PSScriptAnalyzer -RequiredVersion 1.24.0 -Force -Scope CurrentUser
Invoke-ScriptAnalyzer -Path . -Recurse -ExcludeRule PSAvoidUsingWriteHost, PSAvoidUsingWMICmdlet, PSUseShouldProcessForStateChangingFunctions, PSUseBOMForUnicodeEncodedFile, PSReviewUnusedParameter, PSUseSingularNouns$env:CI = 'true'; . .\Microsoft.PowerShell_profile.ps1(Non-interactive mode is triggered by $env:CI or $env:AI_AGENT. Known agent env vars are normalized to $env:AI_AGENT.)
CI checks all .ps1 files recursively. To match locally:
Get-ChildItem -Filter *.ps1 -Recurse | ForEach-Object { powershell -NoProfile -Command "`$t = `$null; `$e = `$null; [void][System.Management.Automation.Language.Parser]::ParseFile('$($_.FullName)', [ref]`$t, [ref]`$e); if (`$e.Count -gt 0) { `$e; exit 1 }" }- Fork the repo and create a feature branch
- Make your changes following the conventions above
- Run PSScriptAnalyzer locally and fix any warnings/errors
- Test on both PS5.1 and PS7+ if possible
- Open a PR - the template will guide you through the checklist
- CI must pass before merge
CI runs on push/PR to main with three jobs:
- lint: PSScriptAnalyzer, smoke test, PS5 parse, hardcoded-path check, non-ASCII/BOM/secrets checks
- install-flow: JSON config validation, schema checks, Merge-JsonObject tests, WT merge mock, required function checks (
Test-InternetConnection,Install-NerdFonts,Install-OhMyPoshTheme,Install-WingetPackage,Merge-JsonObject,Select-PreferredEditor,Invoke-DownloadWithRetry) - functional: Runs
ci-functional.ps1(elevated): full install flow, sandbox install/execute/uninstall, and 100% command-probe coverage
All three jobs must pass. CI also fails on hardcoded user paths and embedded secrets.