diff options
| author | Alex Pooley (@zuedev) <zuedev@gmail.com> | 2026-02-05 16:03:08 +0000 |
|---|---|---|
| committer | Alex Pooley (@zuedev) <zuedev@gmail.com> | 2026-02-05 16:03:08 +0000 |
| commit | 4775de64eb752b90267114c0c1c95235820521f3 (patch) | |
| tree | a24d73dd2ebe337a45bb6347c7be856bf72afad4 /validators/powershell | |
| parent | 6857b2514ac26a0731208e6f7c312cd70b606544 (diff) | |
| download | gitinfo-4775de64eb752b90267114c0c1c95235820521f3.tar gitinfo-4775de64eb752b90267114c0c1c95235820521f3.tar.gz gitinfo-4775de64eb752b90267114c0c1c95235820521f3.tar.bz2 gitinfo-4775de64eb752b90267114c0c1c95235820521f3.tar.xz gitinfo-4775de64eb752b90267114c0c1c95235820521f3.zip | |
add CLI validators in Node.js, PowerShell, and Bash
Move validate.js to validators/nodejs/ and add equivalent
validators for PowerShell and Bash. Each includes a README
with usage instructions and requirements.
Diffstat (limited to 'validators/powershell')
| -rw-r--r-- | validators/powershell/README.md | 46 | ||||
| -rw-r--r-- | validators/powershell/Validate-GitInfo.ps1 | 248 |
2 files changed, 294 insertions, 0 deletions
diff --git a/validators/powershell/README.md b/validators/powershell/README.md new file mode 100644 index 0000000..822659e --- /dev/null +++ b/validators/powershell/README.md @@ -0,0 +1,46 @@ +# PowerShell Validator + +A PowerShell script for validating `.gitinfo` files. + +## Requirements + +- PowerShell 5.1+ (Windows) or PowerShell Core 7+ (cross-platform) + +## Usage + +```powershell +# Validate .gitinfo in current directory +.\Validate-GitInfo.ps1 + +# Validate a specific file +.\Validate-GitInfo.ps1 -Path "path/to/.gitinfo" +``` + +## Features + +- Parses JSONC (strips `//` and `/* */` comments) +- Validates against the gitinfo JSON Schema +- Checks types, formats (URI, email), and patterns +- Enforces `additionalProperties: false` +- Returns exit code 0 on success, 1 on failure +- Color-coded output (green for success, red for errors) + +## Example Output + +``` +✓ .gitinfo is valid +``` + +``` +Validation failed for .gitinfo: + - .root: invalid URI "not-a-url" + - root: unknown property "invalid_field" +``` + +## Notes + +On Windows, you may need to adjust the execution policy: + +```powershell +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` diff --git a/validators/powershell/Validate-GitInfo.ps1 b/validators/powershell/Validate-GitInfo.ps1 new file mode 100644 index 0000000..e84606c --- /dev/null +++ b/validators/powershell/Validate-GitInfo.ps1 @@ -0,0 +1,248 @@ +<# +.SYNOPSIS + Validates .gitinfo files against the gitinfo JSON Schema. + +.DESCRIPTION + A PowerShell validator for .gitinfo files. Parses JSONC (strips comments) + and validates against the schema. + +.PARAMETER Path + Path to the .gitinfo file. Defaults to ".gitinfo" in the current directory. + +.EXAMPLE + .\Validate-GitInfo.ps1 + Validates .gitinfo in the current directory. + +.EXAMPLE + .\Validate-GitInfo.ps1 -Path "path/to/.gitinfo" + Validates a specific .gitinfo file. +#> + +param( + [Parameter(Position = 0)] + [string]$Path = ".gitinfo" +) + +$ErrorActionPreference = "Stop" + +# Schema path (two levels up from validators/powershell/) +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$SchemaPath = Join-Path $ScriptDir "..\..\gitinfo.schema.json" + +function Remove-JsonComments { + param([string]$Jsonc) + + $result = New-Object System.Text.StringBuilder + $i = 0 + $inString = $false + $stringChar = $null + + while ($i -lt $Jsonc.Length) { + $char = $Jsonc[$i] + $next = if ($i + 1 -lt $Jsonc.Length) { $Jsonc[$i + 1] } else { $null } + + # Track string state + if (-not $inString -and ($char -eq '"' -or $char -eq "'")) { + $inString = $true + $stringChar = $char + [void]$result.Append($char) + $i++ + continue + } + + if ($inString) { + if ($char -eq '\' -and $i + 1 -lt $Jsonc.Length) { + [void]$result.Append($char) + [void]$result.Append($Jsonc[$i + 1]) + $i += 2 + continue + } + if ($char -eq $stringChar) { + $inString = $false + $stringChar = $null + } + [void]$result.Append($char) + $i++ + continue + } + + # Single-line comment + if ($char -eq '/' -and $next -eq '/') { + while ($i -lt $Jsonc.Length -and $Jsonc[$i] -ne "`n") { + $i++ + } + continue + } + + # Multi-line comment + if ($char -eq '/' -and $next -eq '*') { + $i += 2 + while ($i -lt $Jsonc.Length - 1 -and -not ($Jsonc[$i] -eq '*' -and $Jsonc[$i + 1] -eq '/')) { + $i++ + } + $i += 2 + continue + } + + [void]$result.Append($char) + $i++ + } + + return $result.ToString() +} + +function Test-Uri { + param([string]$Value) + try { + $uri = [System.Uri]::new($Value) + return $uri.Scheme -eq "http" -or $uri.Scheme -eq "https" + } + catch { + return $false + } +} + +function Test-Email { + param([string]$Value) + return $Value -match '^[^\s@]+@[^\s@]+\.[^\s@]+$' +} + +function Test-Schema { + param( + $Data, + $Schema, + [string]$JsonPath = "" + ) + + $errors = @() + + if ($Schema.type -eq "object") { + if ($Data -isnot [System.Collections.IDictionary] -and $Data.GetType().Name -ne "PSCustomObject") { + $errors += "$(if ($JsonPath) { $JsonPath } else { 'root' }): expected object" + return $errors + } + + # Convert PSCustomObject to hashtable for easier handling + $dataHash = @{} + $Data.PSObject.Properties | ForEach-Object { $dataHash[$_.Name] = $_.Value } + + # Check additionalProperties + if ($Schema.additionalProperties -eq $false -and $Schema.properties) { + $allowed = $Schema.properties.PSObject.Properties.Name + foreach ($key in $dataHash.Keys) { + if ($key -notin $allowed) { + $errors += "$(if ($JsonPath) { $JsonPath } else { 'root' }): unknown property `"$key`"" + } + } + } + + # Validate each property + if ($Schema.properties) { + foreach ($prop in $Schema.properties.PSObject.Properties) { + $key = $prop.Name + $propSchema = $prop.Value + if ($dataHash.ContainsKey($key)) { + $errors += Test-Schema -Data $dataHash[$key] -Schema $propSchema -JsonPath "$JsonPath.$key" + } + } + } + } + elseif ($Schema.type -eq "array") { + if ($Data -isnot [System.Array]) { + $errors += "${JsonPath}: expected array" + return $errors + } + + if ($Schema.items) { + for ($i = 0; $i -lt $Data.Count; $i++) { + $itemSchema = if ($Schema.items -is [System.Array]) { $Schema.items[$i] } else { $Schema.items } + if ($itemSchema) { + $errors += Test-Schema -Data $Data[$i] -Schema $itemSchema -JsonPath "$JsonPath[$i]" + } + } + + if ($Schema.minItems -and $Data.Count -lt $Schema.minItems) { + $errors += "${JsonPath}: expected at least $($Schema.minItems) items" + } + if ($Schema.maxItems -and $Data.Count -gt $Schema.maxItems) { + $errors += "${JsonPath}: expected at most $($Schema.maxItems) items" + } + } + } + elseif ($Schema.type -eq "string") { + if ($Data -isnot [string]) { + $errors += "${JsonPath}: expected string" + return $errors + } + + if ($Schema.minLength -and $Data.Length -lt $Schema.minLength) { + $errors += "${JsonPath}: string too short (min $($Schema.minLength))" + } + + if ($Schema.format -eq "uri") { + if (-not (Test-Uri $Data)) { + $errors += "${JsonPath}: invalid URI `"$Data`"" + } + } + + if ($Schema.format -eq "email") { + if (-not (Test-Email $Data)) { + $errors += "${JsonPath}: invalid email `"$Data`"" + } + } + + if ($Schema.pattern) { + if ($Data -notmatch $Schema.pattern) { + $errors += "${JsonPath}: does not match pattern $($Schema.pattern)" + } + } + } + + return $errors +} + +# Main +if (-not (Test-Path $Path)) { + Write-Error "Error: File not found: $Path" + exit 1 +} + +if (-not (Test-Path $SchemaPath)) { + Write-Error "Error: Schema not found: $SchemaPath" + exit 1 +} + +# Read and parse schema +try { + $schemaContent = Get-Content -Path $SchemaPath -Raw + $schema = $schemaContent | ConvertFrom-Json +} +catch { + Write-Error "Error parsing schema: $_" + exit 1 +} + +# Read and parse .gitinfo file +try { + $content = Get-Content -Path $Path -Raw + $stripped = Remove-JsonComments -Jsonc $content + $data = $stripped | ConvertFrom-Json +} +catch { + Write-Error "Error parsing JSONC: $_" + exit 1 +} + +# Validate against schema +$errors = Test-Schema -Data $data -Schema $schema + +if ($errors.Count -gt 0) { + Write-Host "Validation failed for ${Path}:" -ForegroundColor Red + foreach ($error in $errors) { + Write-Host " - $error" -ForegroundColor Red + } + exit 1 +} + +Write-Host "✓ $Path is valid" -ForegroundColor Green +exit 0 |
