From d3501199934b4336a19da6a989c1d18b2a8dab04 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Mon, 3 Aug 2026 18:18:45 +0300 Subject: [PATCH] livrare.ps1: roa_sync la tree curat, rebase la push respins, garda SVN - pasul 1 ruleaza roa_sync.ps1 cand tree-ul e curat (svn update + git_sync + reconciliere cu origin); cu modificari necomise ruleaza doar git_sync, ca roa_sync sa nu inglobeze munca in curs intr-un commit de sync SVN - fetch inainte de commit: raporteaza cand ramura e in urma fata de origin - push respins non-fast-forward: rebase + o singura reincercare, abort curat la conflict; fara --force - avertisment pentru fisierele comise in git dar nefacute svn add - docs: scripturi-uzuale.md - ce script se ruleaza si cand Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_014sNn6dmAimtyXkDU4frJrh --- docs/scripturi-uzuale.md | 24 +++++ utile/livrare.ps1 | 198 +++++++++++++++++++++++++++++++++++---- 2 files changed, 202 insertions(+), 20 deletions(-) create mode 100644 docs/scripturi-uzuale.md diff --git a/docs/scripturi-uzuale.md b/docs/scripturi-uzuale.md new file mode 100644 index 0000000..35365b7 --- /dev/null +++ b/docs/scripturi-uzuale.md @@ -0,0 +1,24 @@ +# Scripturi uzuale (sarcini repetitive) + +**Regula: o sarcina care se repeta se pune intr-un script si de atunci se apeleaza scriptul.** +Nu reface lantul de pasi din memorie la fiecare sesiune si nu da unui subagent aceleasi instructiuni +a doua oara — daca a doua oara e nevoie de acelasi lucru, scrie scriptul. + +| Cand | Ce rulezi | Ce face | +|---|---|---| +| inceput de sesiune, si dupa orice `svn update` | `roa_sync.bat` din radacina proiectului (→ `COMUN\scripts\roa_sync.ps1`) | `svn update` → `git_sync` (texte `.??2`) → commit `sync SVN r` → reconciliere cu `origin` (ff/rebase) → push conditionat → inchide branch-urile `claude/*` terminate | +| inainte de un commit | `COMUN\utile\curatenie.ps1` (`-DryRun` doar listeaza) | sterge artefactele de lucru netrackuite: patch-uri de review, `handoff*`/`runda*`/`propuneri_*`, backup-uri `*.pre_runda*.bak`, artefacte de test | +| la "commit / push" | `COMUN\utile\livrare.ps1 -Mesaj "..." [-Comun]` | `git_sync` → `curatenie` → verificari de livrare → status/diff pe ambele repo-uri → commit + push. `COMUN` doar cu `-Comun`; `push --force` nu exista | +| editare `.vcx`/`.scx` | `D:\ROA\UTIL\foxbin2prg\` : `git_sync.ps1`, `vfp_symbols.ps1`, `txt2vcx.ps1` | reimprospatare text in-arbore, index de simboluri, write-back cu fidelity-check | +| actualizare de baza de date blocata | `COMUN\utile\diag_actualizare.ps1 -Alias ` | `UPD_ISTORIC` → `UPD_LOG` → `script_master.log` → versiuni per schema → job → tablespace → verdict. Vezi `depanare-pack-update.md` | +| spatiu plin la un client | `COMUN\utile\diag_spatiu.ps1 -Alias [-Disc] [-SysPassword]` | tablespace, audit, ADR, FRA, disc server, top segmente. Vezi `depanare-spatiu-oracle.md` | +| teste UI VFP | harness din `COMUN\utile\Teste` | vezi `testare-ui-vfp.md` | + +Documentatia **nu** se genereaza automat si nu exista niciun hook care s-o scrie — `livrare.ps1` +doar semnaleaza ce lipseste (changelog neatins, binar VFP fara textul lui sincronizat). + +Candidati inca nescrisi: un helper pentru rulat SQL prin `sqlplus` (fisier ASCII + `@fisier.sql`, +refacut manual la fiecare interogare) si un wrapper care ruleaza `diag_spatiu.ps1` peste toti +clientii, cu un rezumat pe un rand per server. + +Fluxul SVN ↔ git din spatele lui `roa_sync`: `fluxul_svn_git.md`. diff --git a/utile/livrare.ps1 b/utile/livrare.ps1 index d98964c..7fbe75b 100644 --- a/utile/livrare.ps1 +++ b/utile/livrare.ps1 @@ -4,16 +4,33 @@ verificari de livrare, stare repo-uri, commit + push. .DESCRIPTION - 1. git_sync.ps1 -ProjectRoot - reimprospateaza .vc2/.sc2 din arbore. - Iesire nenula -> opreste tot (nu se comite cu text nesincronizat). Se sare cu -FaraSync. + 1. Sincronizare, aleasa dupa starea arborelui (git status --porcelain) in AMBELE repo-uri: + - tree curat (inceput de sesiune, nimic necomis) -> COMUN\scripts\roa_sync.ps1 -ProjectRoot + : svn update (inclusiv externalul COMUN) + git_sync (texte .??2) + commit + "sync SVN rN" + reconciliere cu origin. Prinde aici non-fast-forward-ul cu origin (nu abia + la push, pasul 5). Daca roa_sync.ps1 lipseste, fallback pe git_sync.ps1 (doar text, fara + SVN), cu avertisment. + - tree murdar (cazul normal la livrare, cu modificari necomise pe disc) -> DOAR git_sync.ps1: + roa_sync ar face `git add -A` + commit "sync SVN" pe main peste munca in curs, inglobind-o + intr-un commit etichetat gresit (fluxul_svn_git.md: "sync pe main doar din stare curata"). + Iesire nenula -> opreste tot. Se sare cu -FaraSync; doar partea SVN se sare cu -FaraSvn + (roa_sync -GitOnly, aplicabil doar cand tree-ul e curat). NU ruleaza nimic in -DryRun (roa_sync + face svn update real si poate comite/impinge) - doar anunta ce s-ar alege si de ce. 2. curatenie.ps1 -ProjectRoot - sterge artefactele de lucru (neurmarite de git). 3. Verificari de livrare (avertismente, neblocante): changelog neatins, fisiere de runda - ramase, binare VFP fara perechea text in modificari. - 4. Stare repo proiect si repo COMUN (separat, partajat de toate aplicatiile ROA): + ramase, binare VFP fara perechea text in modificari, fisiere ce urmeaza sa intre in git + dar sunt necunoscute SVN-ului ("?" la svn status - munca reala trebuie sa treaca intai + prin SVN, vezi fluxul_svn_git.md). + 4. Stare repo proiect si repo COMUN (separat, partajat de toate aplicatiile ROA): git fetch + + compara HEAD cu origin/ (semnaleaza daca ramura locala e in urma), apoi git status --short + git diff --stat. 5. Commit + push. Proiectul implicit; COMUN doar cu -Comun explicit (altfel doar raportat). - Niciodata push --force. Comite pe ramura curenta, nu creeaza alta. - 6. -DryRun: ruleaza pasii 1-4 (curatenia in -DryRun) si se opreste inainte de commit. + Push respins non-fast-forward (frecvent pe COMUN, comun tuturor aplicatiilor ROA): fetch + + rebase peste origin + reincercare o singura data. Conflicte la rebase -> rebase --abort, + mesaj clar, exit nenul (nu lasa repo-ul in rebase in curs). Niciodata push --force + (nici --force-with-lease). Comite pe ramura curenta, nu creeaza alta. + 6. -DryRun: ruleaza pasii 2-4 (curatenia in -DryRun; pasul 1 doar anuntat, nu executat) si se + opreste inainte de commit. .PARAMETER ProjectRoot Radacina proiectului (implicit dedus, ca la curatenie.ps1 - doua nivele deasupra acestui script). @@ -25,13 +42,21 @@ Comite si impinge si repo-ul COMUN (implicit doar proiectul). .PARAMETER FaraSync - Sare peste git_sync.ps1 (util cand nu s-a atins niciun binar VFP). + Sare peste tot pasul 1 (roa_sync/git_sync) - util cand nu s-a atins niciun binar VFP si nici + SVN-ul nu trebuie adus la zi. + +.PARAMETER FaraSvn + Cand tree-ul e curat (altfel se ruleaza oricum doar git_sync): ruleaza roa_sync.ps1 -GitOnly, + fara svn update si fara regenerarea textelor, doar commit sync + reconciliere origin + + inchidere branch-uri claude/*. Util cand SVN nu e disponibil sau nu s-a atins nimic in SVN. + Ignorat daca roa_sync.ps1 lipseste (fallback-ul pe git_sync.ps1 nu are oricum pas SVN). .PARAMETER FaraPush Doar commit local, fara push. .PARAMETER DryRun - Nu modifica nimic - arata ce s-ar comite si unde. + Nu modifica nimic - arata ce s-ar comite si unde. Pasul 1 nu se executa (roa_sync ar face svn + update real si poate comite/impinge) - doar anunta ce s-ar alege (roa_sync sau git_sync) si de ce. .EXAMPLE powershell -File COMUN\utile\livrare.ps1 -DryRun @@ -44,12 +69,16 @@ .EXAMPLE powershell -File COMUN\utile\livrare.ps1 -Mesaj "doar .prg, fara binare" -FaraSync -FaraPush + +.EXAMPLE + powershell -File COMUN\utile\livrare.ps1 -Mesaj "SVN indisponibil acum" -FaraSvn #> param( [string]$ProjectRoot, [string]$Mesaj, [switch]$Comun, [switch]$FaraSync, + [switch]$FaraSvn, [switch]$FaraPush, [switch]$DryRun ) @@ -58,6 +87,7 @@ $ErrorActionPreference = 'Stop' if (-not $ProjectRoot) { $ProjectRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent } $comunPath = Join-Path $ProjectRoot 'COMUN' +$roaSync = Join-Path $ProjectRoot 'COMUN\scripts\roa_sync.ps1' $gitSync = 'D:\ROA\UTIL\foxbin2prg\git_sync.ps1' $curatenie = Join-Path $PSScriptRoot 'curatenie.ps1' @@ -68,10 +98,15 @@ if (-not $DryRun -and -not $Mesaj) { Write-Output ('livrare: ProjectRoot=' + $ProjectRoot) -# --- pas 1: git_sync --- -if ($FaraSync) { - Write-Output '[1] git_sync: sarit (-FaraSync)' -} else { +# --- tree curat in ambele repo-uri? roa_sync e unealta de inceput de sesiune (stare curata), +# nu de livrare cu modificari necomise pe disc (fluxul_svn_git.md) --- +function Test-TreeCurat($repoPath) { + if (-not (Test-Path (Join-Path $repoPath '.git'))) { return $true } + return (@(& git -C $repoPath status --porcelain).Count -eq 0) +} +$treeCurat = (Test-TreeCurat $ProjectRoot) -and (Test-TreeCurat $comunPath) + +function Ruleaza-GitSync { if (-not (Test-Path $gitSync)) { Write-Output ('[1] git_sync: NU s-a gasit ' + $gitSync) exit 2 @@ -84,6 +119,36 @@ if ($FaraSync) { Write-Output '[1] git_sync: OK' } +# --- pas 1: tree curat -> roa_sync (fallback git_sync daca lipseste); tree murdar -> doar git_sync --- +if ($FaraSync) { + Write-Output '[1] sincronizare: sarita (-FaraSync)' +} elseif ($DryRun) { + # roa_sync face svn update real si poate comite/impinge (chiar si cu -GitOnly) - nu ruleaza in -DryRun. + if (-not $treeCurat) { + Write-Output ('[1] sincronizare: sarita (-DryRun) - modificari necomise, ar rula doar ' + $gitSync + ' (roa_sync ar ingloba munca in curs intr-un commit "sync SVN")') + } elseif (Test-Path $roaSync) { + $variantaGO = if ($FaraSvn) { ' -GitOnly' } else { '' } + Write-Output ('[1] sincronizare: sarita (-DryRun) - tree curat, ar rula ' + $roaSync + $variantaGO) + } else { + Write-Output ('[1] sincronizare: sarita (-DryRun) - tree curat, roa_sync.ps1 lipseste, ar rula fallback ' + $gitSync) + } +} elseif (-not $treeCurat) { + Write-Output '[1] sincronizare: modificari necomise -> rulez doar git_sync (roa_sync ar ingloba munca in curs intr-un commit "sync SVN")' + Ruleaza-GitSync +} elseif (Test-Path $roaSync) { + $rsArgs = @('-ProjectRoot', $ProjectRoot) + if ($FaraSvn) { $rsArgs += '-GitOnly' } + & powershell -ExecutionPolicy Bypass -File $roaSync @rsArgs + if ($LASTEXITCODE -ne 0) { + Write-Output ('[1] roa_sync: ESUAT (cod ' + $LASTEXITCODE + ') - opresc tot, nu se comite') + exit 2 + } + Write-Output '[1] roa_sync: OK' +} else { + Write-Output ('[1] roa_sync: NU s-a gasit (' + $roaSync + ') - fallback pe git_sync.ps1') + Ruleaza-GitSync +} + # --- pas 2: curatenie --- $curArgs = @('-ProjectRoot', $ProjectRoot) if ($DryRun) { $curArgs += '-DryRun' } @@ -113,6 +178,27 @@ $modifComun = Get-ChangedFiles $comunPath # --- pas 3: verificari de livrare (avertismente, neblocante) --- $avertismente = New-Object System.Collections.ArrayList +# fisiere din $modif care apar cu "?" (necunoscut) la svn status - munca reala trebuie sa treaca +# intai prin SVN (fluxul_svn_git.md). Fara svn sau folder ce nu e working copy -> tacere. +function Get-SvnNeurmarite($repoPath, $modif) { + if ($modif.Count -eq 0) { return @() } + if (-not (Get-Command svn -ErrorAction SilentlyContinue)) { return @() } + $ErrorActionPreference = 'Continue' + $iesire = & svn status $repoPath 2>$null + if ($LASTEXITCODE -ne 0) { return @() } + $necunoscute = New-Object System.Collections.Generic.HashSet[string] ([StringComparer]::OrdinalIgnoreCase) + foreach ($linie in $iesire) { + if ($linie -match '^\?\s+(.+)$') { + $cale = $Matches[1].Trim() + if ($cale.StartsWith($repoPath, [System.StringComparison]::OrdinalIgnoreCase)) { + $cale = $cale.Substring($repoPath.Length).TrimStart('\', '/') + } + [void]$necunoscute.Add(($cale -replace '\\', '/')) + } + } + return @($modif | Where-Object { $necunoscute.Contains($_) }) +} + function Verifica-Livrare($modif, $repoPath, $eticheta) { $codExt = @('.prg', '.vc2', '.sc2', '.ps1') $areCod = $modif | Where-Object { $ext = [System.IO.Path]::GetExtension($_).ToLower(); $codExt -contains $ext } @@ -138,6 +224,11 @@ function Verifica-Livrare($modif, $repoPath, $eticheta) { [void]$script:avertismente.Add('fisier de runda ramas (' + $eticheta + '): docs\' + $r.Name) } } + + $neurmarite = Get-SvnNeurmarite $repoPath $modif + foreach ($f in $neurmarite) { + [void]$script:avertismente.Add('fisier necunoscut SVN (' + $eticheta + '), fa svn add + svn commit intai: ' + $f) + } } Verifica-Livrare $modifProiect $ProjectRoot 'proiect' @@ -150,13 +241,45 @@ if ($avertismente.Count -eq 0) { foreach ($a in $avertismente) { Write-Output (' ATENTIE: ' + $a) } } +# --- ramura locala vs origin (fetch + numarare ahead/behind) --- +function Get-StareFetch($repoPath) { + # ErrorActionPreference local 'Continue': altfel 2>$null pe un exe extern (chiar la iesire 0, + # git scrie mesaje normale pe stderr) devine NativeCommandError terminal sub preferinta 'Stop'. + $ErrorActionPreference = 'Continue' + if (-not (Test-Path (Join-Path $repoPath '.git'))) { return $null } + $remote = & git -C $repoPath remote + if (-not $remote) { return @{ AreRemote = $false } } + & git -C $repoPath fetch --quiet 2>$null | Out-Null + $fetchOk = ($LASTEXITCODE -eq 0) + $upstream = (& git -C $repoPath rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>$null) + if ($LASTEXITCODE -ne 0 -or -not $upstream) { + return @{ AreRemote = $true; FetchOk = $fetchOk; AreUpstream = $false } + } + $numere = (& git -C $repoPath rev-list --left-right --count "$upstream...HEAD" 2>$null) -split '\s+' | Where-Object { $_ -ne '' } + return @{ AreRemote = $true; FetchOk = $fetchOk; AreUpstream = $true; Upstream = $upstream; Behind = [int]$numere[0]; Ahead = [int]$numere[1] } +} + +function Scrie-StareFetch($stare) { + if (-not $stare) { return } + if (-not $stare.AreRemote) { return } + if (-not $stare.FetchOk) { Write-Output ' (fetch esuat, sar verificarea fata de origin)'; return } + if (-not $stare.AreUpstream) { return } + if ($stare.Behind -gt 0) { + Write-Output (' ramura locala e in urma cu ' + $stare.Behind + ' commit-uri fata de ' + $stare.Upstream) + } +} + # --- pas 4: stare repo-uri --- Write-Output '[4] stare repo proiect:' +$stareProiect = Get-StareFetch $ProjectRoot +Scrie-StareFetch $stareProiect & git -C $ProjectRoot status --short | ForEach-Object { Write-Output (' ' + $_) } & git -C $ProjectRoot diff --stat HEAD | ForEach-Object { Write-Output (' ' + $_) } Write-Output '[4] stare repo COMUN:' if (Test-Path (Join-Path $comunPath '.git')) { + $stareComun = Get-StareFetch $comunPath + Scrie-StareFetch $stareComun & git -C $comunPath status --short | ForEach-Object { Write-Output (' ' + $_) } & git -C $comunPath diff --stat HEAD | ForEach-Object { Write-Output (' ' + $_) } } else { @@ -185,6 +308,47 @@ if ($DryRun) { # --- pas 5: commit + push --- $exitCode = 0 +# push, si daca e respins non-fast-forward: fetch + rebase peste upstream + o singura reincercare. +# Conflicte la rebase -> rebase --abort (nu lasa repo-ul in rebase in curs) + esec, fara push --force. +function Invoke-PushCuRebaseRetry($repoPath, $eticheta) { + # ErrorActionPreference local 'Continue': vezi motivul in Get-StareFetch. + $ErrorActionPreference = 'Continue' + # | Out-Null pe fiecare apel git: stdout-ul lui n-ar trebui sa ajunga in valoarea + # intoarsa de functie (rezultatul e capturat de apelant) - vezi Comite-Si-Impinge. + & git -C $repoPath push | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host ('[5] ' + $eticheta + ': push OK') + return 0 + } + & git -C $repoPath fetch --quiet 2>$null | Out-Null + $upstream = (& git -C $repoPath rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>$null) + if ($LASTEXITCODE -ne 0 -or -not $upstream) { + Write-Host ('[5] ' + $eticheta + ': PUSH ESUAT (fara upstream de comparat)') + return 1 + } + $numere = (& git -C $repoPath rev-list --left-right --count "$upstream...HEAD" 2>$null) -split '\s+' | Where-Object { $_ -ne '' } + $behind = [int]$numere[0] + if ($behind -eq 0) { + Write-Host ('[5] ' + $eticheta + ': PUSH ESUAT (nu e in urma fata de ' + $upstream + ', alta cauza)') + return 1 + } + Write-Host ('[5] ' + $eticheta + ': push respins, ramura in urma cu ' + $behind + ' fata de ' + $upstream + ' - rebase') + & git -C $repoPath rebase $upstream | Out-Null + if ($LASTEXITCODE -ne 0) { + & git -C $repoPath rebase --abort 2>$null | Out-Null + Write-Host ('[5] ' + $eticheta + ': REBASE CU CONFLICTE - abort facut, e nevoie de interventie manuala') + return 1 + } + Write-Host ('[5] ' + $eticheta + ': rebase OK, reincerc push') + & git -C $repoPath push | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Host ('[5] ' + $eticheta + ': PUSH ESUAT si dupa rebase') + return 1 + } + Write-Host ('[5] ' + $eticheta + ': push OK dupa rebase') + return 0 +} + function Comite-Si-Impinge($repoPath, $eticheta, $faraPush) { # mesajele folosesc Write-Host (nu Write-Output): functia e apelata cu rezultatul # capturat intr-o variabila, iar Write-Output ar ajunge in valoarea returnata. @@ -192,7 +356,7 @@ function Comite-Si-Impinge($repoPath, $eticheta, $faraPush) { Write-Host ('[5] ' + $eticheta + ': fara repo git, sarit') return 0 } - & git -C $repoPath add -A + & git -C $repoPath add -A | Out-Null & git -C $repoPath diff --cached --quiet if ($LASTEXITCODE -eq 0) { Write-Host ('[5] ' + $eticheta + ': nimic de comis') @@ -209,13 +373,7 @@ function Comite-Si-Impinge($repoPath, $eticheta, $faraPush) { Write-Host ('[5] ' + $eticheta + ': push sarit (-FaraPush)') return 0 } - & git -C $repoPath push - if ($LASTEXITCODE -ne 0) { - Write-Host ('[5] ' + $eticheta + ': PUSH ESUAT') - return 1 - } - Write-Host ('[5] ' + $eticheta + ': push OK') - return 0 + return (Invoke-PushCuRebaseRetry $repoPath $eticheta) } $exitCode += (Comite-Si-Impinge $ProjectRoot 'proiect' $FaraPush)