name: "Git-Ape: Script Lint"
on:
pull_request:
paths:
- '.github/skills/**/*.sh'
- '.github/skills/**/*.ps1'
- '.github/linters/PSScriptAnalyzerSettings.psd1'
- '.github/workflows/git-ape-script-lint.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
shell-lint:
name: Shell scripts (shellcheck + bash -n)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Ensure shellcheck is available
run: |
set -euo pipefail
if ! command -v shellcheck >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y shellcheck
fi
shellcheck --version
- name: Lint and parse-check shell scripts
run: |
set -euo pipefail
mapfile -t FILES < <(find .github/skills -type f -name '*.sh' | sort)
if [ "${#FILES[@]}" -eq 0 ]; then
echo "No skill shell scripts found."
exit 0
fi
echo "Found ${#FILES[@]} shell script(s):"
printf ' %s\n' "${FILES[@]}"
echo "::group::Syntax check (bash -n)"
syntax_rc=0
for f in "${FILES[@]}"; do
if bash -n "$f"; then
echo "ok $f"
else
echo "FAIL $f"
syntax_rc=1
fi
done
echo "::endgroup::"
if [ "$syntax_rc" -ne 0 ]; then
echo "::error::One or more shell scripts failed 'bash -n' syntax check."
exit 1
fi
echo "::group::ShellCheck (severity=warning, gating)"
shellcheck --severity=warning --color=always "${FILES[@]}"
echo "All scripts passed shellcheck at severity=warning."
echo "::endgroup::"
echo "::group::ShellCheck info/style audit (non-gating)"
shellcheck --severity=style --color=always "${FILES[@]}" || true
echo "::endgroup::"
pwsh-lint:
name: PowerShell scripts (PSScriptAnalyzer + parser)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install PSScriptAnalyzer
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
if (-not (Get-Module -ListAvailable -Name PSScriptAnalyzer)) {
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module -Name PSScriptAnalyzer -Scope CurrentUser -Force
}
$v = (Get-Module -ListAvailable -Name PSScriptAnalyzer | Select-Object -First 1).Version
Write-Host "PSScriptAnalyzer $v"
- name: Lint and parse-check PowerShell scripts
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$files = Get-ChildItem -Recurse -Path .github/skills -Filter *.ps1 | Sort-Object FullName
if (-not $files) {
Write-Host 'No skill PowerShell scripts found.'
exit 0
}
Write-Host ("Found {0} PowerShell script(s):" -f @($files).Count)
$files | ForEach-Object { Write-Host " $($_.FullName)" }
Write-Host '::group::Parser gate'
$parseFail = 0
foreach ($f in $files) {
$errs = $null
[System.Management.Automation.Language.Parser]::ParseFile($f.FullName, [ref]$null, [ref]$errs) | Out-Null
if ($errs -and $errs.Count -gt 0) {
$parseFail = 1
Write-Host "::error file=$($f.FullName)::$($errs.Count) parse error(s)"
$errs | ForEach-Object { Write-Host " $($_.Message)" }
} else {
Write-Host "ok $($f.FullName)"
}
}
Write-Host '::endgroup::'
if ($parseFail -ne 0) {
Write-Host '::error::One or more PowerShell scripts failed to parse.'
exit 1
}
Write-Host '::group::PSScriptAnalyzer (Error + Warning, gating)'
$settings = '.github/linters/PSScriptAnalyzerSettings.psd1'
$results = $files | ForEach-Object { Invoke-ScriptAnalyzer -Path $_.FullName -Settings $settings }
if ($results) {
$results |
Sort-Object Severity, ScriptName, Line |
Format-Table Severity, RuleName, @{ n = 'File'; e = { Split-Path $_.ScriptName -Leaf } }, Line, Message -AutoSize -Wrap |
Out-String -Width 200 |
Write-Host
Write-Host "::error::PSScriptAnalyzer reported $(@($results).Count) finding(s) at Error/Warning severity."
exit 1
}
Write-Host 'All scripts passed PSScriptAnalyzer at Error/Warning severity.'
Write-Host '::endgroup::'