<# .SYNOPSIS Automatyczne budowanie paczki aktualizacji cmsPRO na podstawie git diff miedzy tagami. .DESCRIPTION Skrypt porownuje dwa tagi git, filtruje pliki przez .updateignore, tworzy ZIP + manifest JSON i aktualizuje changelog.php oraz versions.php. .PARAMETER FromTag Tag poczatkowy (np. v1.689). Domyslnie: ostatni tag. .PARAMETER ToTag Tag docelowy (np. v1.690). Wymagany. .PARAMETER ChangelogEntry Wpis do changelogu. Wymagany (chyba ze -DryRun). .PARAMETER DryRun Tylko pokaz co zostaloby zrobione, bez tworzenia plikow. .EXAMPLE ./build-update.ps1 -ToTag v1.690 -ChangelogEntry "NEW - nowa funkcja" ./build-update.ps1 -FromTag v1.689 -ToTag v1.690 -ChangelogEntry "NEW - opis" -DryRun #> param( [string]$FromTag = "", [Parameter(Mandatory=$true)] [string]$ToTag, [string]$ChangelogEntry = "", [switch]$DryRun ) $ErrorActionPreference = "Stop" $Utf8NoBom = New-Object System.Text.UTF8Encoding $false # --- Helpers --- function Write-Step($msg) { Write-Host " [*] $msg" -ForegroundColor Cyan } function Write-Ok($msg) { Write-Host " [OK] $msg" -ForegroundColor Green } function Write-Warn($msg) { Write-Host " [!] $msg" -ForegroundColor Yellow } function Write-Err($msg) { Write-Host " [ERROR] $msg" -ForegroundColor Red } # --- 1. Walidacja tagow --- Write-Host "`n=== cmsPRO Build Update ===" -ForegroundColor White if (-not $FromTag) { $FromTag = (git describe --tags --abbrev=0 2>$null) if (-not $FromTag) { Write-Err "Nie znaleziono zadnego taga. Uzyj parametru -FromTag." exit 1 } Write-Step "Auto-detect FromTag: $FromTag" } # Sprawdz czy tagi istnieja $tagExists = git tag -l $FromTag if (-not $tagExists) { Write-Err "Tag '$FromTag' nie istnieje." exit 1 } $toTagExists = git tag -l $ToTag if (-not $toTagExists) { Write-Warn "Tag '$ToTag' nie istnieje. Uzywam HEAD jako punktu docelowego." $diffTarget = "HEAD" } else { $diffTarget = $ToTag } # --- 2. Wersja i katalog --- $versionNumber = $ToTag -replace '^v', '' $versionInt = [int]($versionNumber -replace '\.', '') # Oblicz katalog: 1.689 -> "1.6" + "0" -> "1.60" $dirStr = $versionNumber.Substring(0, $versionNumber.Length - 2) + "0" Write-Step "Wersja: $versionNumber (int: $versionInt)" Write-Step "Katalog: updates/$dirStr/" # --- 3. Git diff --- Write-Step "Porownywanie: $FromTag..$diffTarget" $diffOutput = git diff --name-status "$FromTag..$diffTarget" 2>&1 if ($LASTEXITCODE -ne 0) { Write-Err "git diff nie powiodl sie: $diffOutput" exit 1 } $addedFiles = @() $modifiedFiles = @() $deletedFiles = @() $renamedFiles = @() foreach ($line in ($diffOutput -split "`n")) { $line = $line.Trim() if (-not $line) { continue } $parts = $line -split "`t" $status = $parts[0] $filePath = if ($parts.Count -gt 1) { $parts[1] } else { "" } # Zamien backslash na slash $filePath = $filePath -replace '\\', '/' switch -Wildcard ($status) { "A" { $addedFiles += $filePath } "M" { $modifiedFiles += $filePath } "D" { $deletedFiles += $filePath } "R*" { # Rename: stary plik = deleted, nowy = added $newPath = if ($parts.Count -gt 2) { $parts[2] -replace '\\', '/' } else { "" } $deletedFiles += $filePath if ($newPath) { $addedFiles += $newPath } } } } Write-Step "Zmiany: A=$($addedFiles.Count), M=$($modifiedFiles.Count), D=$($deletedFiles.Count)" # --- 4. Filtrowanie przez .updateignore --- $ignorePatterns = @() $ignoreFile = ".updateignore" if (Test-Path $ignoreFile) { $ignorePatterns = Get-Content $ignoreFile | ForEach-Object { $_.Trim() } | Where-Object { $_ -and ($_ -notmatch '^\s*#') } Write-Step "Zaladowano $($ignorePatterns.Count) wzorcow z .updateignore" } function Test-Ignored { param([string]$FilePath) foreach ($pattern in $ignorePatterns) { # Wzorzec katalogu (konczy sie na /) if ($pattern.EndsWith('/')) { $dirPattern = $pattern.TrimEnd('/') if ($FilePath -like "$dirPattern/*" -or $FilePath -eq $dirPattern) { return $true } } # Wzorzec z wildcard elseif ($pattern.Contains('*')) { # *.md -> dopasuj nazwe pliku if ($pattern.StartsWith('*')) { $fileName = Split-Path $FilePath -Leaf if ($fileName -like $pattern) { return $true } } # Zwykly glob elseif ($FilePath -like $pattern) { return $true } } # Dokladne dopasowanie else { if ($FilePath -eq $pattern) { return $true } } } return $false } $filteredAdded = @() $filteredModified = @() $filteredDeleted = @() $ignoredCount = 0 foreach ($f in $addedFiles) { if (Test-Ignored $f) { $ignoredCount++; continue } $filteredAdded += $f } foreach ($f in $modifiedFiles) { if (Test-Ignored $f) { $ignoredCount++; continue } $filteredModified += $f } foreach ($f in $deletedFiles) { if (Test-Ignored $f) { $ignoredCount++; continue } $filteredDeleted += $f } Write-Step "Po filtrowaniu: A=$($filteredAdded.Count), M=$($filteredModified.Count), D=$($filteredDeleted.Count) (pominieto: $ignoredCount)" # Rozdziel usuniete pliki i katalogi $deletedDirs = @() $deletedFilesOnly = @() foreach ($f in $filteredDeleted) { # Git nie trackuje pustych katalogow, wiec deleted entries to zawsze pliki $deletedFilesOnly += $f } # --- 5. Odczyt migracji SQL --- $sqlQueries = @() $migrationFile = "migrations/$versionNumber.sql" if (Test-Path $migrationFile) { $sqlQueries = @(Get-Content $migrationFile | Where-Object { $_.Trim() -ne '' } | ForEach-Object { $_.ToString() }) Write-Step "Znaleziono migracje SQL: $migrationFile ($($sqlQueries.Count) zapytan)" } else { Write-Step "Brak migracji SQL ($migrationFile nie istnieje)" } # --- 6. Podsumowanie --- $filesToPack = $filteredAdded + $filteredModified if ($filesToPack.Count -eq 0 -and $filteredDeleted.Count -eq 0 -and $sqlQueries.Count -eq 0) { Write-Warn "Brak zmian do pakowania (po filtrowaniu). Przerywam." exit 0 } Write-Host "`n--- Pliki do paczki ---" -ForegroundColor White foreach ($f in $filteredAdded) { Write-Host " A $f" -ForegroundColor Green } foreach ($f in $filteredModified) { Write-Host " M $f" -ForegroundColor Yellow } foreach ($f in $filteredDeleted) { Write-Host " D $f" -ForegroundColor Red } if ($sqlQueries.Count -gt 0) { Write-Host "`n--- SQL ---" -ForegroundColor White foreach ($q in $sqlQueries) { Write-Host " $q" -ForegroundColor Gray } } # --- DryRun? --- if ($DryRun) { Write-Host "`n[DRY RUN] Zadne pliki nie zostaly utworzone.`n" -ForegroundColor Magenta exit 0 } # --- 7. Walidacja ChangelogEntry --- if (-not $ChangelogEntry) { Write-Err "Parametr -ChangelogEntry jest wymagany (chyba ze uzywasz -DryRun)." exit 1 } # --- 8. Tworzenie temp i kopiowanie plikow --- $tempDir = "temp/temp_$versionInt" if (Test-Path $tempDir) { Remove-Item -Recurse -Force $tempDir } foreach ($f in $filesToPack) { $destPath = Join-Path $tempDir $f $destDir = Split-Path $destPath -Parent if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null } if (Test-Path $f) { Copy-Item $f $destPath -Force } else { Write-Warn "Plik nie istnieje (moze zostal usuniety po TAGU): $f" } } Write-Ok "Skopiowano $($filesToPack.Count) plikow do $tempDir" # --- 9. Tworzenie ZIP --- $updatesDir = "updates/$dirStr" if (-not (Test-Path $updatesDir)) { New-Item -ItemType Directory -Path $updatesDir -Force | Out-Null } $zipPath = "$updatesDir/ver_$versionNumber.zip" if (Test-Path $zipPath) { Remove-Item $zipPath -Force } # Pakuj zawartosc temp dir (bez folderu temp/) $originalLocation = Get-Location Set-Location $tempDir Compress-Archive -Path '*' -DestinationPath "../../$zipPath" -Force Set-Location $originalLocation Write-Ok "Utworzono ZIP: $zipPath" # --- 10. Checksum SHA256 --- $hash = (Get-FileHash $zipPath -Algorithm SHA256).Hash.ToLower() Write-Ok "SHA256: $hash" # --- 11. Manifest JSON --- $manifest = @{ version = $versionNumber date = (Get-Date -Format "yyyy-MM-dd") checksum_zip = "sha256:$hash" files = @{ modified = $filteredModified added = $filteredAdded deleted = $deletedFilesOnly } directories_deleted = $deletedDirs sql = $sqlQueries changelog = $ChangelogEntry } $manifestJson = $manifest | ConvertTo-Json -Depth 4 $manifestPath = "$updatesDir/ver_${versionNumber}_manifest.json" [System.IO.File]::WriteAllText($manifestPath, $manifestJson, $Utf8NoBom) Write-Ok "Utworzono manifest: $manifestPath" # --- 12. Legacy _sql.txt i _files.txt (okres przejsciowy) --- if ($sqlQueries.Count -gt 0) { $sqlPath = "$updatesDir/ver_${versionNumber}_sql.txt" [System.IO.File]::WriteAllText($sqlPath, ($sqlQueries -join "`n"), $Utf8NoBom) Write-Ok "Utworzono legacy SQL: $sqlPath" } if ($deletedFilesOnly.Count -gt 0 -or $deletedDirs.Count -gt 0) { $filesContent = @() foreach ($f in $deletedFilesOnly) { $filesContent += "F: ../$f" } foreach ($d in $deletedDirs) { $filesContent += "D: ../$d" } $filesPath = "$updatesDir/ver_${versionNumber}_files.txt" [System.IO.File]::WriteAllText($filesPath, ($filesContent -join "`n"), $Utf8NoBom) Write-Ok "Utworzono legacy files: $filesPath" } # --- 13. Aktualizacja versions.php --- $versionsFile = "updates/versions.php" if (Test-Path $versionsFile) { $content = Get-Content $versionsFile -Raw $content = $content -replace '\$current_ver\s*=\s*\d+;', "`$current_ver = $versionInt;" [System.IO.File]::WriteAllText($versionsFile, $content, $Utf8NoBom) Write-Ok "Zaktualizowano versions.php: `$current_ver = $versionInt" } # --- 14. Cleanup --- if (Test-Path $tempDir) { Remove-Item -Recurse -Force $tempDir } # Usun pusty folder temp jesli nie ma juz w nim nic if ((Test-Path "temp") -and ((Get-ChildItem "temp" -Force).Count -eq 0)) { Remove-Item "temp" -Force } Write-Ok "Wyczyszczono pliki tymczasowe" # --- Podsumowanie --- Write-Host "`n=== Gotowe ===" -ForegroundColor Green Write-Host " ZIP: $zipPath" Write-Host " Manifest: $manifestPath" Write-Host " Wersja: $versionNumber (int: $versionInt)" Write-Host ""