zone 2
This commit is contained in:
20
.gitea/workflows/build.yml
Normal file
20
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Zone
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: site-build
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
192
scripts/gated-analysis.ps1
Normal file
192
scripts/gated-analysis.ps1
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$DiscordWebhookUrl = $env:DISCORD_WEBHOOK_URL,
|
||||||
|
[string]$Repository = $env:GITHUB_REPOSITORY,
|
||||||
|
[string]$RefName = $env:GITHUB_REF_NAME,
|
||||||
|
[string]$Sha = $env:GITHUB_SHA,
|
||||||
|
[string]$RunUrl = $env:GITHUB_SERVER_URL
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$exitCode = 0
|
||||||
|
$smellFailures = New-Object System.Collections.Generic.List[string]
|
||||||
|
$smellWarnings = New-Object System.Collections.Generic.List[string]
|
||||||
|
|
||||||
|
function Add-SmellFailure {
|
||||||
|
param([string]$Message)
|
||||||
|
$script:smellFailures.Add($Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Add-SmellWarning {
|
||||||
|
param([string]$Message)
|
||||||
|
$script:smellWarnings.Add($Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ProjectFiles {
|
||||||
|
$excludeDirs = @('.git', 'node_modules', 'dist', 'build', 'coverage')
|
||||||
|
Get-ChildItem -Path . -File -Recurse -Force |
|
||||||
|
Where-Object {
|
||||||
|
$pathParts = $_.FullName.Substring((Get-Location).Path.Length).Split([IO.Path]::DirectorySeparatorChar, [StringSplitOptions]::RemoveEmptyEntries)
|
||||||
|
-not ($pathParts | Where-Object { $excludeDirs -contains $_ })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-CodeSmells {
|
||||||
|
Write-Host 'Running gated smell checks...'
|
||||||
|
|
||||||
|
$files = @(Get-ProjectFiles)
|
||||||
|
$debtMarkerPattern = '\b(T' + 'ODO|F' + 'IXME|H' + 'ACK|X' + 'XX|W' + 'IP)\b'
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$relativePath = Resolve-Path -Relative $file.FullName
|
||||||
|
|
||||||
|
if ($file.Length -gt 1MB) {
|
||||||
|
Add-SmellWarning "$relativePath is larger than 1 MB."
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = Get-Content -Raw -LiteralPath $file.FullName
|
||||||
|
|
||||||
|
if ($content -match '(?m)^(<<<<<<<|=======|>>>>>>>)') {
|
||||||
|
Add-SmellFailure "$relativePath contains merge conflict markers."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($content -match '(?i)(discord(?:_webhook)?_url|password|api[_-]?key|secret)\s*[:=]\s*["''][^"'']{8,}["'']') {
|
||||||
|
Add-SmellFailure "$relativePath may contain a committed secret."
|
||||||
|
}
|
||||||
|
|
||||||
|
$todoMatches = [regex]::Matches($content, "(?i)$debtMarkerPattern")
|
||||||
|
if ($todoMatches.Count -gt 0) {
|
||||||
|
Add-SmellWarning "$relativePath contains $($todoMatches.Count) debt marker(s)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($smellFailures.Count -gt 0) {
|
||||||
|
Write-Host 'Smell checks failed:'
|
||||||
|
$smellFailures | ForEach-Object { Write-Host " - $_" }
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($smellWarnings.Count -gt 0) {
|
||||||
|
Write-Host 'Smell check warnings:'
|
||||||
|
$smellWarnings | ForEach-Object { Write-Host " - $_" }
|
||||||
|
} else {
|
||||||
|
Write-Host 'No smell check warnings found.'
|
||||||
|
}
|
||||||
|
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-TestSuite {
|
||||||
|
Write-Host 'Running test suite...'
|
||||||
|
$tempRoot = if ($env:RUNNER_TEMP) { $env:RUNNER_TEMP } else { [IO.Path]::GetTempPath() }
|
||||||
|
$outputFile = Join-Path $tempRoot 'zone-test-output.txt'
|
||||||
|
|
||||||
|
& npm test 2>&1 | Tee-Object -FilePath $outputFile
|
||||||
|
$testExitCode = $LASTEXITCODE
|
||||||
|
$output = Get-Content -Raw -LiteralPath $outputFile
|
||||||
|
|
||||||
|
return [pscustomobject]@{
|
||||||
|
Passed = ($testExitCode -eq 0)
|
||||||
|
ExitCode = $testExitCode
|
||||||
|
Output = $output.Trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ShortText {
|
||||||
|
param(
|
||||||
|
[string]$Text,
|
||||||
|
[int]$MaxLength = 1400
|
||||||
|
)
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($Text)) {
|
||||||
|
return 'No output captured.'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Text.Length -le $MaxLength) {
|
||||||
|
return $Text
|
||||||
|
}
|
||||||
|
|
||||||
|
return $Text.Substring($Text.Length - $MaxLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Send-DiscordReport {
|
||||||
|
param(
|
||||||
|
[bool]$SmellsPassed,
|
||||||
|
[object]$Tests
|
||||||
|
)
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($DiscordWebhookUrl)) {
|
||||||
|
Write-Host 'DISCORD_WEBHOOK_URL is not set; skipping Discord report.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = if ($SmellsPassed -and $Tests.Passed) { 'passed' } else { 'failed' }
|
||||||
|
$color = if ($status -eq 'passed') { 5763719 } else { 15548997 }
|
||||||
|
$shaShort = if ($Sha -and $Sha.Length -ge 7) { $Sha.Substring(0, 7) } else { $Sha }
|
||||||
|
$repoText = if ($Repository) { $Repository } else { 'zone' }
|
||||||
|
$refText = if ($RefName) { $RefName } else { 'unknown ref' }
|
||||||
|
$smellStatus = if ($SmellsPassed) { 'passed' } else { 'failed' }
|
||||||
|
$testStatus = if ($Tests.Passed) { 'passed' } else { "failed (exit $($Tests.ExitCode))" }
|
||||||
|
$warningText = if ($smellWarnings.Count -gt 0) { ($smellWarnings -join "`n") } else { 'None' }
|
||||||
|
$failureText = if ($smellFailures.Count -gt 0) { ($smellFailures -join "`n") } else { 'None' }
|
||||||
|
|
||||||
|
$description = @"
|
||||||
|
Repository: $repoText
|
||||||
|
Ref: $refText
|
||||||
|
Commit: $shaShort
|
||||||
|
|
||||||
|
Smell checks: $smellStatus
|
||||||
|
Tests: $testStatus
|
||||||
|
"@
|
||||||
|
|
||||||
|
$fields = @(
|
||||||
|
@{
|
||||||
|
name = 'Smell failures'
|
||||||
|
value = Get-ShortText -Text $failureText -MaxLength 900
|
||||||
|
inline = $false
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
name = 'Smell warnings'
|
||||||
|
value = Get-ShortText -Text $warningText -MaxLength 900
|
||||||
|
inline = $false
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
name = 'Test output'
|
||||||
|
value = "``````text`n$(Get-ShortText -Text $Tests.Output -MaxLength 900)`n``````"
|
||||||
|
inline = $false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
$payload = @{
|
||||||
|
username = 'Zone CI'
|
||||||
|
embeds = @(
|
||||||
|
@{
|
||||||
|
title = "Gated analysis $status"
|
||||||
|
description = $description.Trim()
|
||||||
|
color = $color
|
||||||
|
fields = $fields
|
||||||
|
timestamp = (Get-Date).ToUniversalTime().ToString('o')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($RunUrl -and $env:GITHUB_RUN_ID) {
|
||||||
|
$payload.embeds[0].url = "$RunUrl/$repoText/actions/runs/$($env:GITHUB_RUN_ID)"
|
||||||
|
}
|
||||||
|
|
||||||
|
Invoke-RestMethod -Method Post -Uri $DiscordWebhookUrl -ContentType 'application/json' -Body ($payload | ConvertTo-Json -Depth 8) | Out-Null
|
||||||
|
Write-Host 'Discord report published.'
|
||||||
|
}
|
||||||
|
|
||||||
|
$smellsPassed = Test-CodeSmells
|
||||||
|
if (-not $smellsPassed) {
|
||||||
|
$exitCode = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$tests = Invoke-TestSuite
|
||||||
|
if (-not $tests.Passed) {
|
||||||
|
$exitCode = $tests.ExitCode
|
||||||
|
}
|
||||||
|
|
||||||
|
Send-DiscordReport -SmellsPassed $smellsPassed -Tests $tests
|
||||||
|
exit $exitCode
|
||||||
Reference in New Issue
Block a user