TH1/Tools/GraphifyPostCommit.ps1

304 lines
7.8 KiB
PowerShell

param(
[switch]$DryRun
)
$ErrorActionPreference = "Continue"
$script:RepoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot ".."))
$script:LogPath = Join-Path $script:RepoRoot ".git/graphify-hook.log"
function Write-HookLog {
param([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$line = "[$timestamp] $Message"
Write-Host $line
try {
Add-Content -Path $script:LogPath -Value $line -Encoding UTF8
}
catch {
Write-Host "[graphify hook] Unable to write log: $($_.Exception.Message)"
}
}
function Test-GraphifyPython {
param([string]$PythonPath)
if ([string]::IsNullOrWhiteSpace($PythonPath)) {
return $false
}
try {
& $PythonPath -c "import graphify" *> $null
return ($LASTEXITCODE -eq 0)
}
catch {
return $false
}
}
function Get-GraphifyPython {
$candidates = @()
foreach ($relative in @("Unity/graphify-out/.graphify_python", "graphify-out/.graphify_python")) {
$path = Join-Path $script:RepoRoot $relative
if (Test-Path $path) {
$value = (Get-Content -Raw -Path $path -Encoding UTF8).Trim()
if ($value) {
$candidates += $value
}
}
}
foreach ($command in @("python", "py")) {
$cmd = Get-Command $command -ErrorAction SilentlyContinue
if ($cmd) {
$candidates += $cmd.Source
}
}
foreach ($candidate in $candidates | Select-Object -Unique) {
if (Test-GraphifyPython $candidate) {
return $candidate
}
}
return $null
}
function Get-ChangedFiles {
$fromEnv = $env:GRAPHIFY_CHANGED
if (-not [string]::IsNullOrWhiteSpace($fromEnv)) {
return $fromEnv -split "`r?`n" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
}
$git = Get-Command git -ErrorAction SilentlyContinue
if (-not $git) {
return @()
}
Push-Location $script:RepoRoot
try {
$files = & git diff --name-only HEAD~1 HEAD 2>$null
if (-not $files) {
$files = & git diff --name-only HEAD 2>$null
}
return @($files | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
}
finally {
Pop-Location
}
}
function Test-GitPathLike {
param(
[string]$Path,
[string[]]$Patterns
)
foreach ($pattern in $Patterns) {
if ($Path -like $pattern) {
return $true
}
}
return $false
}
function Get-MatchingGitPaths {
param(
[string[]]$Paths,
[string[]]$Patterns
)
return @($Paths | Where-Object { Test-GitPathLike -Path $_ -Patterns $Patterns })
}
function Set-GraphifyNeedsUpdate {
param(
[string]$RelativePath,
[string]$Reason
)
$target = Join-Path $script:RepoRoot $RelativePath
$out = Join-Path $target "graphify-out"
if (-not (Test-Path $out)) {
return
}
$flag = Join-Path $out "needs_update"
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $flag -Value "[$timestamp] $Reason" -Encoding UTF8
Write-HookLog "Marked $RelativePath graph for manual semantic update: $Reason"
}
function Invoke-GraphifyRebuild {
param(
[string]$Label,
[string]$RelativePath,
[string]$PythonPath
)
$target = Join-Path $script:RepoRoot $RelativePath
$graphPath = Join-Path $target "graphify-out/graph.json"
if (-not (Test-Path $graphPath)) {
Write-HookLog "Skipping ${Label}: no graphify-out/graph.json found."
return
}
if ($DryRun) {
Write-HookLog "Dry run: would rebuild $Label graph at $target."
return
}
Write-HookLog "Rebuilding $Label graph at $target."
Push-Location $target
try {
& $PythonPath -c "from pathlib import Path; from graphify.watch import _rebuild_code; _rebuild_code(Path('.'))" *> $null
if ($LASTEXITCODE -eq 0) {
Write-HookLog "Rebuilt $Label graph."
}
else {
Write-HookLog "Rebuild failed for $Label with exit code $LASTEXITCODE."
}
}
catch {
Write-HookLog "Rebuild failed for ${Label}: $($_.Exception.Message)"
}
finally {
Pop-Location
}
}
function Invoke-HookMain {
Set-Location $script:RepoRoot
$changedFiles = @(
Get-ChangedFiles |
ForEach-Object { $_ -replace '\\', '/' } |
Where-Object {
-not [string]::IsNullOrWhiteSpace($_) -and
$_ -notlike "graphify-out/*" -and
$_ -notlike "Unity/graphify-out/*"
}
)
if ($changedFiles.Count -eq 0) {
Write-HookLog "No changed files detected; nothing to rebuild."
return 0
}
$codeExtensions = @(
".cs", ".asmdef", ".shader", ".hlsl", ".cginc",
".py", ".js", ".ts", ".ps1", ".json", ".toml", ".yaml", ".yml"
)
$changedCode = @(
$changedFiles | Where-Object {
$ext = [System.IO.Path]::GetExtension($_).ToLowerInvariant()
$codeExtensions -contains $ext
}
)
$rootCodePatterns = @(
"Unity/Assets/Scripts/*",
"Tools/*.ps1",
"Tools/*.py",
"Tools/*.js",
"Tools/OSS/*",
"Tools/Dashboard/*.py",
"ExcelExport/*"
)
$unityCodePatterns = @(
"Unity/Assets/Scripts/*"
)
$graphifyConfigPatterns = @(
".graphifyignore",
".gitignore",
"AGENTS.md",
"Unity/.graphifyignore",
"Tools/GraphifyPostCommit.ps1",
"Tools/InstallGraphifyHook.ps1"
)
$semanticDocPatterns = @(
"MD/GameMDFramework/*.md",
"MD/GameMDFramework/*/*.md"
)
$rootChangedCode = @(Get-MatchingGitPaths -Paths $changedCode -Patterns $rootCodePatterns)
$unityChangedCode = @(Get-MatchingGitPaths -Paths $changedCode -Patterns $unityCodePatterns)
$changedGraphifyConfig = @(Get-MatchingGitPaths -Paths $changedFiles -Patterns $graphifyConfigPatterns)
$changedSemanticDocs = @(Get-MatchingGitPaths -Paths $changedFiles -Patterns $semanticDocPatterns)
if ($changedSemanticDocs.Count -gt 0) {
Set-GraphifyNeedsUpdate -RelativePath "." -Reason "Architecture docs changed; run graphify update manually for semantic edges."
}
$shouldRebuildRoot = ($rootChangedCode.Count -gt 0 -or $changedGraphifyConfig.Count -gt 0)
$shouldRebuildUnity = ($unityChangedCode.Count -gt 0 -or ($changedGraphifyConfig | Where-Object { $_ -eq "Unity/.graphifyignore" }).Count -gt 0)
if (-not $shouldRebuildRoot -and -not $shouldRebuildUnity) {
Write-HookLog "$($changedFiles.Count) changed file(s), but none are in the narrowed graphify AST scopes."
return 0
}
$python = Get-GraphifyPython
if (-not $python) {
Write-HookLog "No Python interpreter with graphify installed was found."
return 0
}
Write-HookLog "$($changedFiles.Count) changed file(s); root scope: $($rootChangedCode.Count), Unity scope: $($unityChangedCode.Count), graphify config: $($changedGraphifyConfig.Count). Using Python: $python"
if ($shouldRebuildRoot) {
Invoke-GraphifyRebuild -Label "root" -RelativePath "." -PythonPath $python
}
else {
Write-HookLog "Skipping root graph: no narrowed root-scope code/config changed."
}
if ($shouldRebuildUnity) {
Invoke-GraphifyRebuild -Label "Unity" -RelativePath "Unity" -PythonPath $python
}
else {
Write-HookLog "Skipping Unity graph: no narrowed Unity-scope code/config changed."
}
return 0
}
$mutexName = "Local\TH1-GraphifyPostCommit"
$mutex = $null
$hasLock = $false
try {
$mutex = [System.Threading.Mutex]::new($false, $mutexName)
$hasLock = $mutex.WaitOne(0)
if (-not $hasLock) {
Write-HookLog "Another graphify rebuild is already running; skipping this post-commit run."
exit 0
}
$exitCode = Invoke-HookMain
exit $exitCode
}
catch {
Write-HookLog "Graphify hook failed: $($_.Exception.Message)"
exit 1
}
finally {
if ($hasLock -and $mutex) {
try {
$mutex.ReleaseMutex() | Out-Null
}
catch {
}
}
if ($mutex) {
$mutex.Dispose()
}
}