feat: add PSScriptAnalyzer (pwsh fmt, fix, lint)

This commit is contained in:
AJ ONeal
2023-11-11 23:08:05 -07:00
parent 693e91d275
commit 62c9dc597c
3 changed files with 268 additions and 0 deletions
+210
View File
@@ -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 <https://stackoverflow.com/a/45266681/151312>
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 <https://stackoverflow.com/a/45266681/151312>
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_
`<PROJECT-DIR>/.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 <https://stackoverflow.com/a/45266681/151312>
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
```
+41
View File
@@ -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
+17
View File
@@ -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