Public/Publish-PCRelease.ps1
|
<# .SYNOPSIS Performs a full release: bump version, commit, tag, push, and create GitHub Release. .DESCRIPTION Orchestrates the complete release workflow: 1. Validates release readiness (Test-PCReleaseReady) 2. Calculates next version (Get-PCNextVersion) 3. Updates version in project file (Set-PCProjectVersion) 4. Commits the version bump 5. Creates annotated git tag 6. Pushes commit and tag to remote 7. Optionally creates a GitHub Release via gh CLI Requires: git, optionally gh (GitHub CLI) for GitHub Release creation. .PARAMETER IncrementType Type of version increment: Major, Minor, or Patch. .PARAMETER Version Explicit version to set (overrides IncrementType calculation). .PARAMETER Path Project directory. Defaults to current directory. .PARAMETER SkipTests Skip test validation during readiness check. .PARAMETER CreateGitHubRelease Create a GitHub Release locally via gh CLI. Off by default — the CI release workflow is the intended sole authority for creating GitHub Releases after tests pass on the runner. Use only when releasing without CI. .PARAMETER DryRun Show what would happen without making changes. .PARAMETER Force Skip interactive confirmation prompts. .EXAMPLE Publish-PCRelease -IncrementType Patch # Bumps patch version and releases .EXAMPLE Publish-PCRelease -Version "2.0.0" -Force # Sets explicit version and releases without confirmation .EXAMPLE Publish-PCRelease -IncrementType Minor -DryRun # Shows what would happen without making changes #> function Publish-PCRelease { [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Increment')] param( [Parameter(Mandatory, ParameterSetName = 'Increment')] [ValidateSet('Major', 'Minor', 'Patch')] [string]$IncrementType, [Parameter(Mandatory, ParameterSetName = 'Explicit')] [string]$Version, [Parameter()] [string]$Path = (Get-Location).Path, [switch]$SkipTests, [switch]$CreateGitHubRelease, [switch]$DryRun, [switch]$Force ) Push-Location $Path try { # Step 1: Validate readiness Write-Host "Checking release readiness..." -ForegroundColor Cyan $readiness = Test-PCReleaseReady -Path $Path if (-not $readiness.Pass) { Write-Host "`nRelease validation FAILED:" -ForegroundColor Red foreach ($err in $readiness.Errors) { Write-Host " - $err" -ForegroundColor Red } throw "Release validation failed. Fix the issues above and retry." } Write-Host " All checks passed" -ForegroundColor Green # Step 2: Determine target version $currentInfo = Get-PCProjectVersion -Path $Path $currentVersion = $currentInfo.Version if ($PSCmdlet.ParameterSetName -eq 'Increment') { $newVersion = Get-PCNextVersion -IncrementType $IncrementType -CurrentVersion $currentVersion } else { $null = Get-SemanticVersion -Version $Version # validate format $newVersion = $Version -replace '^v', '' } $tagName = "v$newVersion" # Check if tag already exists $existingTag = git tag -l $tagName 2>$null if ($existingTag) { throw "Tag '$tagName' already exists. Choose a different version." } # Step 3: Confirm Write-Host "`nRelease Plan:" -ForegroundColor Cyan Write-Host " Current version: $currentVersion" Write-Host " New version: $newVersion" Write-Host " Tag: $tagName" Write-Host " Project: $($currentInfo.ProjectType.FileName)" Write-Host "" if ($DryRun) { Write-Host "[DRY RUN] Would perform:" -ForegroundColor Yellow Write-Host " 1. Update version in $($currentInfo.ProjectType.FileName) to $newVersion" Write-Host " 2. Commit: 'Release $tagName'" Write-Host " 3. Tag: $tagName" Write-Host " 4. Push commit and tag to origin" Write-Host " 5. CI release workflow runs tests and publishes to PSGallery + creates GitHub Release" if ($CreateGitHubRelease) { Write-Host " * -CreateGitHubRelease: would also create a local GitHub Release (bypasses CI gate)" } return [PSCustomObject]@{ Status = 'DryRun' Version = $newVersion Tag = $tagName } } if (-not $Force) { $confirm = Read-Host "Proceed with release $tagName? [y/N]" if ($confirm -notin @('y', 'Y', 'yes', 'Yes')) { Write-Host "Release cancelled." -ForegroundColor Yellow return } } # Step 4: Bump version Write-Host "`nUpdating version..." -ForegroundColor Cyan Set-PCProjectVersion -Version $newVersion -Path $Path Write-Host " Version set to $newVersion in $($currentInfo.ProjectType.FileName)" -ForegroundColor Green # Step 5: Commit Write-Host "Committing..." -ForegroundColor Cyan git add -A git commit -m "Release $tagName" if ($LASTEXITCODE -ne 0) { throw "Git commit failed" } Write-Host " Committed: Release $tagName" -ForegroundColor Green # Step 6: Tag Write-Host "Creating tag..." -ForegroundColor Cyan git tag -a $tagName -m "Release $newVersion" if ($LASTEXITCODE -ne 0) { throw "Git tag failed" } Write-Host " Tagged: $tagName" -ForegroundColor Green # Step 7: Push Write-Host "Pushing to remote..." -ForegroundColor Cyan git push origin HEAD if ($LASTEXITCODE -ne 0) { throw "Git push failed" } git push origin $tagName if ($LASTEXITCODE -ne 0) { throw "Git push tag failed" } Write-Host " Pushed commit and tag" -ForegroundColor Green # Step 8: GitHub Release (only when explicitly requested) $ghRelease = $null if ($CreateGitHubRelease) { Write-Host "Creating GitHub Release..." -ForegroundColor Cyan Write-Warning "Local GitHub Release creation bypasses CI stage gates. Prefer letting the release workflow create the release after tests pass on the runner." $ghAvailable = Get-Command gh -ErrorAction SilentlyContinue if ($ghAvailable) { $ghArgs = @('release', 'create', $tagName, '--title', "Release $newVersion", '--generate-notes') gh @ghArgs if ($LASTEXITCODE -eq 0) { Write-Host " GitHub Release created" -ForegroundColor Green $ghRelease = $true } else { Write-Warning "GitHub Release creation failed (exit code: $LASTEXITCODE). Tag was pushed successfully — CI workflow will create the release if tests pass." } } else { Write-Warning "gh CLI not found. Tag was pushed — CI workflow will create the release if tests pass." } } else { Write-Host "GitHub Release will be created by CI after tests pass on the runner." -ForegroundColor Cyan } # Summary Write-Host "`nRelease $tagName complete!" -ForegroundColor Green return [PSCustomObject]@{ Status = 'Released' Version = $newVersion Tag = $tagName GitHubRelease = $ghRelease ProjectFile = $currentInfo.ProjectType.FileName } } finally { Pop-Location } } |