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 SkipGitHubRelease Skip GitHub Release creation (only tag and push). .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]$SkipGitHubRelease, [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" if (-not $SkipGitHubRelease) { Write-Host " 5. Create GitHub Release" } 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 (optional) $ghRelease = $null if (-not $SkipGitHubRelease) { Write-Host "Creating GitHub Release..." -ForegroundColor Cyan $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 — you can create the release manually." } } else { Write-Warning "gh CLI not found. Tag was pushed — GitHub Actions workflow or manual release creation required." } } # 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 } } |