scripts/New-GitQuietModule.ps1
|
#requires -Version 7.6 #requires -PSEdition Core [CmdletBinding()] param([string]$OutputPath=(Join-Path $PSScriptRoot '../../FrenchExDev.QuietCli.Git.PoSh'),[string]$ReadmePath) $ErrorActionPreference='Stop' $root=Split-Path -Parent $PSScriptRoot Import-Module (Join-Path $root 'FrenchExDev.QuietCli.PoSh.psd1') -DisableNameChecking $temporary=Join-Path ([IO.Path]::GetTempPath()) ('quietcli-git-module-'+[guid]::NewGuid().ToString('N')) [void][IO.Directory]::CreateDirectory($temporary) $previous=@{};foreach($key in @('QUIETCLI_DB_PATH','QUIETCLI_CONFIG_PATH','QUIETCLI_TRACKING','QUIETCLI_RECALL')){$previous[$key]=[Environment]::GetEnvironmentVariable($key)} $records=[Collections.Generic.List[string]]::new() function Git { param([string]$Repo,[string[]]$Words) $r=quiet -AsObject -WorkingDirectory $Repo -OnOutput {param($r,$s)} git @Words if($r.Outcome -ne 'Success'){throw "Fixture Git failure: $($Words -join ' ') / $($r.Execution.ExitCode)"} $r.Capture.Dispose() } function Repo { param([string]$Name,[switch]$Commit) $p=Join-Path $temporary $Name;[void][IO.Directory]::CreateDirectory($p) Git $p @('init','-q','--initial-branch=main');Git $p @('config','user.name','QuietCli fixture');Git $p @('config','user.email','quietcli@example.invalid') if($Commit){[IO.File]::WriteAllText((Join-Path $p 'tracked.txt'),'initial');Git $p @('add','tracked.txt');Git $p @('-c','commit.gpgsign=false','commit','-qm','initial')} $p } function Record-Case { param([string]$Repo) $r=quiet -AsObject -Record -WorkingDirectory $Repo -ContractPath (Join-Path $root 'examples/contracts/Git.Status.ps1') git status if($r.Outcome -ne 'Success' -or !$r.RecordId){throw "Unable to record fixture: $($r.Error.Message)"} $records.Add($r.RecordId) foreach($source in $r.Internal.Sources.Values){$source.Capture.Dispose()} } try{ $env:QUIETCLI_DB_PATH=Join-Path $temporary 'history.sqlite3';$env:QUIETCLI_CONFIG_PATH=Join-Path $temporary 'settings.json';$env:QUIETCLI_TRACKING='1';$env:QUIETCLI_RECALL='0' Record-Case (Repo 'unborn-clean') Record-Case (Repo 'clean' -Commit) $p=Repo 'untracked';[IO.File]::WriteAllText((Join-Path $p 'new file.txt'),'new');Record-Case $p $p=Repo 'unstaged' -Commit;[IO.File]::WriteAllText((Join-Path $p 'tracked.txt'),'modified');Record-Case $p $p=Repo 'staged' -Commit;[IO.File]::WriteAllText((Join-Path $p 'tracked.txt'),'modified');Git $p @('add','tracked.txt');Record-Case $p $p=Repo 'renamed' -Commit;Git $p @('mv','tracked.txt','renamed file.txt');Record-Case $p $p=Repo 'detached' -Commit;Git $p @('checkout','--detach','-q');Record-Case $p $p=Repo 'unicode';[IO.File]::WriteAllText((Join-Path $p ("été "+[char]10+'-file')),'unicode');Record-Case $p $p=Repo 'upstream' -Commit;Git $p @('branch','reference');Git $p @('branch','--set-upstream-to=reference','main') [IO.File]::WriteAllText((Join-Path $p 'tracked.txt'),'next');Git $p @('add','tracked.txt');Git $p @('-c','commit.gpgsign=false','commit','-qm','next');Record-Case $p $p=Repo 'conflict' -Commit;Git $p @('checkout','-qb','other') [IO.File]::WriteAllText((Join-Path $p 'tracked.txt'),'other');Git $p @('add','tracked.txt');Git $p @('-c','commit.gpgsign=false','commit','-qm','other') Git $p @('checkout','-q','main');[IO.File]::WriteAllText((Join-Path $p 'tracked.txt'),'main');Git $p @('add','tracked.txt');Git $p @('-c','commit.gpgsign=false','commit','-qm','main') $merge=Invoke-QuietCli -FilePath git -WorkingDirectory $p -ArgumentList @('merge','other') -SuccessExitCodes @(0,1) -Parse {param($c) $c.ExitCode} if($merge.Data -ne 1){throw 'Conflict fixture did not conflict.'};Record-Case $p $assertions=Join-Path $temporary 'Git.Status.Assertions.ps1' $assertionCode=@' # Expectations follow the fixture preparation, not a snapshot of the candidate. param($Cases) if($Cases.Count -ne 10){throw 'Expected all ten independently prepared Git scenarios.'} foreach($case in $Cases){ $d=$case.Actual.Data if($case.Actual.Outcome -ne 'Success' -or !$d.AnswerComplete -or !$d.GitVersion){throw 'Incomplete Git observation.'} $name=Split-Path -Leaf $case.Record.Sources[0].OriginalRequest.WorkingDirectory switch($name){ unborn-clean{if(!$d.Clean -or !$d.Unborn -or $d.Branch -ne 'main'){throw 'Unborn clean fixture.'}} clean{if(!$d.Clean -or $d.Unborn -or !$d.HeadOid){throw 'Committed clean fixture.'}} untracked{if($d.Counts.Untracked -ne 1 -or $d.Entries[0].Path -ne 'new file.txt'){throw 'Untracked fixture.'}} unstaged{if($d.Counts.Unstaged -ne 1 -or $d.Counts.Staged -ne 0){throw 'Unstaged fixture.'}} staged{if($d.Counts.Staged -ne 1 -or $d.Counts.Unstaged -ne 0){throw 'Staged fixture.'}} renamed{if($d.Counts.Renamed -ne 1 -or $d.Entries[0].OriginalPath -ne 'tracked.txt' -or $d.Entries[0].Path -ne 'renamed file.txt'){throw 'Rename fixture.'}} detached{if(!$d.Detached -or $null -ne $d.Branch){throw 'Detached fixture.'}} unicode{if($d.Entries[0].Path -ne ("été "+[char]10+'-file')){throw 'Unicode/NUL fixture.'}} upstream{if($d.Upstream -ne 'reference' -or $d.Ahead -ne 1 -or $d.Behind -ne 0){throw 'Local upstream fixture.'}} conflict{if($d.Counts.Conflicted -ne 1 -or $d.Clean){throw 'Merge conflict fixture.'}} default{throw 'Unknown fixture scenario.'} } } '@ [IO.File]::WriteAllText($assertions,$assertionCode,[Text.UTF8Encoding]::new($true)) $module=New-QuietModule -Command git -Name FrenchExDev.QuietCli.Git.PoSh -ContractPath (Join-Path $root 'examples/contracts/Git.Status.ps1') -RecordId $records.ToArray() -AssertionPath $assertions -OutputPath $OutputPath if($ReadmePath){ $readme=(Resolve-Path -LiteralPath $ReadmePath -ErrorAction Stop).ProviderPath [IO.File]::Copy($readme,(Join-Path (Split-Path $module.Path) 'README.md'),$true) } $validation=Test-QuietModule -Path $module.Path -AsObject if($validation.Outcome -ne 'Success'){throw ($validation.Data.Failures -join '; ')} @{Outcome='Success';Data=@{Module=$module.Path;Fixtures=$records.Count;Checks=$validation.Data.Checks;Hash=$validation.Data.Hash}}|ConvertTo-Json -Depth 8 -Compress }finally{ foreach($key in $previous.Keys){[Environment]::SetEnvironmentVariable($key,$previous[$key])} if($temporary.StartsWith([IO.Path]::GetTempPath(),[StringComparison]::Ordinal) -and (Split-Path -Leaf $temporary).StartsWith('quietcli-git-module-')){Remove-Item -LiteralPath $temporary -Recurse -Force} } |