scripts/New-QuietCliArchive.ps1
|
#requires -Version 7.6 #requires -PSEdition Core [CmdletBinding(DefaultParameterSetName='Package')] param( [string]$ModuleRoot = (Join-Path $PSScriptRoot '..'), [Parameter(Mandatory,ParameterSetName='Package')][string]$OutputDirectory, [Parameter(Mandatory,ParameterSetName='Inventory')][switch]$ValidateOnly, [string]$SourceCommitBase = '', [string]$SourceRef = 'develop' ) $ErrorActionPreference = 'Stop' $root = [IO.Path]::GetFullPath($ModuleRoot) $version = (Import-PowerShellDataFile (Join-Path $root 'FrenchExDev.QuietCli.PoSh.psd1')).ModuleVersion $paths = [Collections.Generic.List[string]]::new() foreach ($name in @('FrenchExDev.QuietCli.PoSh.psd1', 'FrenchExDev.QuietCli.PoSh.psm1', 'README.md', 'LICENSE', 'THIRD-PARTY-NOTICES.md')) { if (!(Test-Path -LiteralPath (Join-Path $root $name) -PathType Leaf)) { throw "Missing package file: $name" } $paths.Add($name) } foreach ($folder in @('src', 'examples', 'fixtures', 'skills', 'test', 'scripts', 'verification', 'doc', 'resources', 'third_party')) { Get-ChildItem -LiteralPath (Join-Path $root $folder) -Recurse -File | Where-Object { $_.Extension -notin @('.pyc','.sqlite','.sqlite3','.db','.zip','.nupkg') -and $_.Name -notin @('distribution.json','v2-distribution-check.json','v2-reproducibility.json','gain.html','quietcli-gain.html') -and $_.FullName -notmatch '[\\/](?:__pycache__|\.git|downloads|artifacts|\.quietcli)[\\/]' -and $_.Name -notmatch '\.(?:sqlite3?|db)-(?:wal|shm|journal)$' } | ForEach-Object { $paths.Add([IO.Path]::GetRelativePath($root, $_.FullName).Replace([char]92, [char]47)) } } # The distribution report stays outside the ZIP to avoid a circular archive hash. $files = @($paths | Sort-Object | ForEach-Object { [ordered]@{ Path = $_; SHA256 = (Get-FileHash -LiteralPath (Join-Path $root $_) -Algorithm SHA256).Hash.ToLowerInvariant() } }) $manifest = [ordered]@{ SchemaVersion = 2 ModuleVersion = $version Repository = (Import-PowerShellDataFile (Join-Path $root 'FrenchExDev.QuietCli.PoSh.psd1')).PrivateData.PSData.ProjectUri ModulePath = '.' SourceRef = $SourceRef SourceCommitBase = $SourceCommitBase SourceKind = 'WorkingTreeFiles; identify the packaged contents by their SHA256 hashes' Files = $files } $manifestJson = $manifest | ConvertTo-Json -Depth 6 if($ValidateOnly){ [pscustomobject]@{Outcome='Success';Data=@{Mode='InventoryOnly';ModuleVersion=$version;Files=$files.Count;Manifest=$manifest}}|ConvertTo-Json -Depth 8 -Compress return } if($env:GITHUB_ACTIONS -ne 'true'){throw 'Release ZIP creation is reserved for the GitHub Actions workflow. Use -ValidateOnly for local inventory validation.'} $output = [IO.Path]::GetFullPath($OutputDirectory) [void](New-Item -ItemType Directory -Path $output -Force) $archivePath = Join-Path $output 'QuietCli.zip' $stream = [IO.File]::Open($archivePath, [IO.FileMode]::Create, [IO.FileAccess]::ReadWrite) $zip = [IO.Compression.ZipArchive]::new($stream, [IO.Compression.ZipArchiveMode]::Create, $false) try { foreach ($file in $files) { $entry = $zip.CreateEntry(('QuietCli/' + $file.Path), [IO.Compression.CompressionLevel]::Optimal) $entry.LastWriteTime = [DateTimeOffset]::new(2000, 1, 1, 0, 0, 0, [TimeSpan]::Zero) $destination = $entry.Open() $source = [IO.File]::OpenRead((Join-Path $root $file.Path)) try { $source.CopyTo($destination) } finally { $source.Dispose(); $destination.Dispose() } } $entry = $zip.CreateEntry('QuietCli/package-manifest.json', [IO.Compression.CompressionLevel]::Optimal) $entry.LastWriteTime = [DateTimeOffset]::new(2000, 1, 1, 0, 0, 0, [TimeSpan]::Zero) $destination = $entry.Open() try { $bytes = [Text.Encoding]::UTF8.GetBytes($manifestJson) $destination.Write($bytes, 0, $bytes.Length) } finally { $destination.Dispose() } } finally { $zip.Dispose(); $stream.Dispose() } $manifest['ArchiveSHA256'] = (Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash.ToLowerInvariant() $manifest | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $output 'QuietCli.manifest.json') -Encoding utf8 [pscustomobject]@{ Outcome = 'Success' Data = @{ Archive = $archivePath; Files = $files.Count; Bytes = (Get-Item -LiteralPath $archivePath).Length; SHA256 = $manifest.ArchiveSHA256 } } | ConvertTo-Json -Depth 4 -Compress |