diff --git a/psscriptanalyzer/README.md b/psscriptanalyzer/README.md new file mode 100644 index 0000000..55f39b7 --- /dev/null +++ b/psscriptanalyzer/README.md @@ -0,0 +1,210 @@ +--- +title: PSScriptAnalyzer +homepage: https://github.com/PowerShell/PSScriptAnalyzer +tagline: | + PSScriptAnalyzer is Formatter & Linter for PowerShell. +--- + +To update or switch versions, run `webi psscriptanalyzer`. + +## Cheat Sheet + +> It's dangerous to go alone! Take _PSScriptAnalyzer_! \ +> (nothing crazy, just the standard fmt & lint tool you'd expect) + +You'll probably want [pwsh-essentials](../pwsh-essentials) as well. + +### Table of Contents + +- Files +- Manual Install +- Format +- Lint +- Vim Config +- Beware the BOM! +- Check Version + +### Files + +These are the files / directories that are created and/or modified with this +install: + +```text +~/.local/share/powershell/Modules/PSScriptAnalyzer/ +``` + +### How to Install PSScriptAnalyzer Manually + +It's just a one liner, but... a little harder to remember than the webi version: + +```sh +pwsh -Command "Install-Module -Name PSScriptAnalyzer -Scope CurrentUser -AllowClobber" +``` + +### How to Run the PowerShell Formatter + +**`pwsh-fmt`** from ([pwsh-essentials](../pwsh-essentials/)) is the **easiest +way** to run the formatter on a file or directory. + +```sh +pwsh-fmt ./script.ps1 +``` + +There is **no built-in one-liner** to do so. You have to do **something like +this instead**: + +```pwsh +function Format-File($Path) { + $WasDirty = $false + + $Original = Get-Content -Path $Path -Raw + $Formatted = Invoke-Formatter -ScriptDefinition $Original + + IF ($Original -eq $Formatted) { + return $WasDirty + } + $WasDirty = $true + + # By default Set-Content unconditionally adds an *extra* newline every time + # See + Set-Content -Path $Path $Formatted -Encoding utf8NoBom -NoNewline + + return $WasDirty +} +``` + +```pwsh +$WasDirty = Format-File -Path "./script.ps1" +``` + +### How to Run the PowerShell Linter + +**`pwsh-lint`** from ([pwsh-essentials](../pwsh-essentials/)) is the **easiest +way** to run the linter on a file or directory. + +```sh +pwsh-lint ./script.ps1 +``` + +However, this one does have a nice onesliner: + +```pwsh +Invoke-ScriptAnalyzer -Path "./script.ps1" -ExcludeRule PSAvoidUsingWriteHost +``` + +You can also make the output much more readable by using `Format-List`: + +```pwsh +$Diags = Invoke-ScriptAnalyzer -Path "./script.ps1" -ExcludeRule PSAvoidUsingWriteHost +Write-Host ($Diags | Format-List | Out-String) +``` + +### How to Run the PowerShell Fixer + +**`pwsh-fix`** from ([pwsh-essentials](../pwsh-essentials/)) is the **easiest +way** to run the fixer on a file or directory. + +At first blush the _Fixer_ seems simple - just at `-Fix` to the _Linter_. + +However, it's tricky because it will output a Byte-Order-Marker, which +**requires running the _Formatter_** to remove. + +That would look something like this: + +```pwsh +function Repair-File($Path) { + $WasDirty = $false + + $Original = Get-Content -Path $Path -Raw + $Formatted = Invoke-Formatter -ScriptDefinition $Original + + IF ($Original -eq $Formatted) { + return $WasDirty + } + $WasDirty = $true + + # By default Set-Content unconditionally adds an *extra* newline every time + # See + Set-Content -Path $Path $Formatted -Encoding utf8NoBom -NoNewline + + return $WasDirty +} +``` + +You'll notice this is very similar to the Formatter solution above, with just +these changes: + +```diff +- $Formatted = Invoke-Formatter -ScriptDefinition $Original ++ ++ Invoke-ScriptAnalyzer -Fix -Path $Path -ExcludeRule PSAvoidUsingWriteHost ++ $Fixed = Get-Content -Path $Path -Raw ++ $Formatted = Invoke-Formatter -ScriptDefinition $Fixed +``` + +### How to Configure PSScriptAnalyzer + +There is no standard config location for projects or globally. \ +(not at the time of this writing at least) + +Instead you'll need to change the config in your editor. + +### How to Configure PSScriptAnalyzer for Vim + +1. You'll need to install [vim-ale](../vim-ale/) + ```sh + webi vim-ale + ``` +2. For **Per-User** config, edit `~/.vimrc` and flavor to taste: `~/.vimrc:` + ```vim + " PowerShell settings + let g:ale_powershell_psscriptanalyzer_exclusions = "PSAvoidUsingWriteHost" + ``` +3. For **Per-Project** config you'll need to edit `~/.vimrc` _AND_ + `/.vimrc`: `~/.vimrc`: + ```vim + " Place these 2 lines at the very END of ~/.vimrc + " (this will enable per-directory .vimrc loading) + set secure + set exrc + ``` + `~/PROJECT-NAME/.vimrc`: + ```sh + " PowerShell settings + let g:ale_powershell_psscriptanalyzer_exclusions = "PSAvoidUsingWriteHost" + ``` + +### How to Fix Byte-Order-Marker (BOM) + +When you run the _Linter_ with `-Fix` it will sometimes output a UTF +Byte-Order-Marker to the file. + +The good news is that the _Formatter_ will remove this. + +So always run the formatter after the linter. 🤷‍♂️ + +```pwsh + $Fixed = Get-Content -Path $Path -Raw + $Formatted = Invoke-Formatter -ScriptDefinition $Fixed + + # By default Set-Content unconditionally adds an *extra* newline every time + # See + Set-Content -Path $Path $Formatted -Encoding utf8NoBom -NoNewline +``` + +## Check the Installed Version + +This will output the module and version, or `$null`. + +```pwsh +Get-InstalledModule PSScriptAnalyzer ` + | Select-Object -Property Name, Version ` + | Format-List +``` + +```text + +Name : PSScriptAnalyzer +Version : 1.21.0 + +``` diff --git a/psscriptanalyzer/install.ps1 b/psscriptanalyzer/install.ps1 new file mode 100644 index 0000000..9ae6526 --- /dev/null +++ b/psscriptanalyzer/install.ps1 @@ -0,0 +1,41 @@ +#!/usr/bin/env pwsh + +function Repair-MissingCommand { + Param( + [string]$Name, + [string]$Package, + [string]$Command + ) + + Write-Host " Checking for $Name ..." + $HasCommand = Get-Command -Name $Command -ErrorAction Silent + IF ($HasCommand) { + Return + } + + & $HOME\.local\bin\webi-pwsh.ps1 $Package + $null = Sync-EnvPath +} + +IF ($null -eq $Env:WEBI_HOST -or "" -eq $Env:WEBI_HOST) { + $Env:WEBI_HOST = "https://webinstall.dev" +} + +function Install-PSScriptAnalyzer { + # Fetch PowerShell Core + Repair-MissingCommand -Name "PowerShell Core" -Package "pwsh" -Command "pwsh" + + $NeedsTrust = pwsh -Command "Get-PSRepository -Name 'PSGallery' | Where-Object -Property InstallationPolicy -eq 'Untrusted'" + IF ($NeedsTrust) { + Write-Host " Trusting PSRepository 'PSGallery' ..." + pwsh -Command "Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted" + } + + # TODO use arguments array instead + Write-Host " Running 'Install-Module -Name PSScriptAnalyzer' ..." + pwsh -Command "Install-Module -Name PSScriptAnalyzer -Repository PSGallery -Scope CurrentUser -AllowClobber" + + pwsh -Command "Get-InstalledModule PSScriptAnalyzer | Select-Object -Property Name, Version" +} + +Install-PSScriptAnalyzer diff --git a/psscriptanalyzer/install.sh b/psscriptanalyzer/install.sh new file mode 100644 index 0000000..417441b --- /dev/null +++ b/psscriptanalyzer/install.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set -e +set -u + +__install_psscriptanalyzer() { + echo "Checking for pwsh (PowerShell Core)..." + if ! command -v pwsh > /dev/null; then + "$HOME/.local/bin/webi" pwsh + export PATH="$HOME/.local/opt/pwsh:$PATH" + pwsh -V + fi + + pwsh -Command "Install-Module -Name PSScriptAnalyzer -Scope CurrentUser -AllowClobber" + pwsh -Command 'Get-InstalledModule -Name PSScriptAnalyzer | Select-Object -Property "Name", "Version" | Format-List' +} + +__install_psscriptanalyzer