304 lines
7.8 KiB
PowerShell
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()
|
|
}
|
|
}
|