Skip to content

Commit fafca50

Browse files
committed
adding release script
1 parent 4044684 commit fafca50

File tree

1 file changed

+271
-0
lines changed

1 file changed

+271
-0
lines changed

scripts/start-release.ps1

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
#!/usr/bin/env pwsh
2+
<#
3+
.SYNOPSIS
4+
Automate the release process for Windows App Development CLI
5+
.DESCRIPTION
6+
This script automates the release workflow:
7+
1. Verifies you are on the main branch with a clean working tree and latest changes
8+
2. Reads and confirms the version from version.json
9+
3. Creates and pushes a rel/v{version} branch to origin (triggers the release pipeline)
10+
4. Returns to main, bumps the patch version in version.json
11+
5. Creates a PR to merge the version bump back into main
12+
13+
Prerequisites:
14+
- Git must be installed and authenticated with push access to origin
15+
- GitHub CLI (gh) must be installed and authenticated (for PR creation)
16+
.PARAMETER SkipConfirmation
17+
Skip the interactive confirmation prompt before creating the release branch
18+
.PARAMETER DryRun
19+
Show what would happen without making any changes (no branches created, no pushes, no PRs)
20+
.EXAMPLE
21+
.\scripts\start-release.ps1
22+
.EXAMPLE
23+
.\scripts\start-release.ps1 -SkipConfirmation
24+
.EXAMPLE
25+
.\scripts\start-release.ps1 -DryRun
26+
#>
27+
28+
param(
29+
[switch]$SkipConfirmation = $false,
30+
[switch]$DryRun = $false
31+
)
32+
33+
$ErrorActionPreference = "Stop"
34+
$ProjectRoot = $PSScriptRoot | Split-Path -Parent
35+
$VersionFilePath = Join-Path $ProjectRoot "version.json"
36+
37+
# ─── Helpers ────────────────────────────────────────────────────────────────────
38+
39+
function Write-Step { param([string]$msg) Write-Host "`n==> $msg" -ForegroundColor Cyan }
40+
function Write-Info { param([string]$msg) Write-Host " $msg" -ForegroundColor Gray }
41+
function Write-Ok { param([string]$msg) Write-Host " $msg" -ForegroundColor Green }
42+
function Write-Warn { param([string]$msg) Write-Host " $msg" -ForegroundColor Yellow }
43+
44+
function Confirm-Step {
45+
param([string]$Prompt)
46+
if ($script:SkipConfirmation -or $script:DryRun) { return }
47+
Write-Host ""
48+
$response = Read-Host " $Prompt (y/N)"
49+
if ($response -notin @("y", "Y", "yes", "Yes")) {
50+
Write-Warn "Release cancelled by user."
51+
exit 0
52+
}
53+
}
54+
55+
function Invoke-GitOrDryRun {
56+
param([string]$Description, [string[]]$Arguments)
57+
if ($DryRun) {
58+
Write-Warn "[DRY RUN] git $($Arguments -join ' ')"
59+
} else {
60+
Write-Info "git $($Arguments -join ' ')"
61+
& git @Arguments
62+
if ($LASTEXITCODE -ne 0) {
63+
throw "git $($Arguments -join ' ') failed with exit code $LASTEXITCODE"
64+
}
65+
}
66+
}
67+
68+
function Invoke-GhOrDryRun {
69+
param([string]$Description, [string[]]$Arguments)
70+
if ($DryRun) {
71+
Write-Warn "[DRY RUN] gh $($Arguments -join ' ')"
72+
} else {
73+
Write-Info "gh $($Arguments -join ' ')"
74+
& gh @Arguments
75+
if ($LASTEXITCODE -ne 0) {
76+
throw "gh $($Arguments -join ' ') failed with exit code $LASTEXITCODE"
77+
}
78+
}
79+
}
80+
81+
# ─── Pre-flight checks ─────────────────────────────────────────────────────────
82+
83+
Push-Location $ProjectRoot
84+
try {
85+
Write-Host ""
86+
Write-Host "╔══════════════════════════════════════════════╗" -ForegroundColor Magenta
87+
Write-Host "║ Windows App Development CLI - Release ║" -ForegroundColor Magenta
88+
Write-Host "╚══════════════════════════════════════════════╝" -ForegroundColor Magenta
89+
90+
if ($DryRun) {
91+
Write-Host ""
92+
Write-Warn "DRY RUN MODE — no changes will be made"
93+
}
94+
95+
# 1. Check we are on main
96+
Write-Step "Checking current branch..."
97+
$currentBranch = (git rev-parse --abbrev-ref HEAD).Trim()
98+
if ($currentBranch -ne "main") {
99+
Write-Error "You must be on the 'main' branch to start a release. Current branch: '$currentBranch'. Please run: git checkout main"
100+
exit 1
101+
}
102+
Write-Ok "On branch: main"
103+
104+
# 2. Check for clean working tree
105+
Write-Step "Checking working tree..."
106+
$status = git status --porcelain
107+
if ($status) {
108+
Write-Error "Working tree is not clean. Please commit or stash your changes first."
109+
exit 1
110+
}
111+
Write-Ok "Working tree is clean"
112+
113+
# 3. Pull latest from origin
114+
Write-Step "Pulling latest from origin/main..."
115+
Invoke-GitOrDryRun -Description "Fetch and pull latest" -Arguments @("pull", "--ff-only", "origin", "main")
116+
Write-Ok "Up to date with origin/main"
117+
118+
# 4. Read version from version.json
119+
Write-Step "Reading version from version.json..."
120+
if (-not (Test-Path $VersionFilePath)) {
121+
Write-Error "version.json not found at: $VersionFilePath"
122+
exit 1
123+
}
124+
125+
$versionJson = Get-Content $VersionFilePath -Raw | ConvertFrom-Json
126+
$releaseVersion = $versionJson.version
127+
if (-not $releaseVersion) {
128+
Write-Error "Could not read 'version' property from version.json"
129+
exit 1
130+
}
131+
Write-Ok "Release version: $releaseVersion"
132+
133+
Confirm-Step "Is '$releaseVersion' the correct version to release?"
134+
135+
# Parse version components
136+
$versionParts = $releaseVersion -split '\.'
137+
if ($versionParts.Count -ne 3) {
138+
Write-Error "Version '$releaseVersion' is not in the expected Major.Minor.Patch format"
139+
exit 1
140+
}
141+
$major = [int]$versionParts[0]
142+
$minor = [int]$versionParts[1]
143+
$patch = [int]$versionParts[2]
144+
145+
$releaseBranch = "rel/v$releaseVersion"
146+
$nextPatch = $patch + 1
147+
$nextVersion = "$major.$minor.$nextPatch"
148+
$bumpBranch = "bump/v$nextVersion"
149+
150+
# 5. Check that the release branch doesn't already exist
151+
Write-Step "Checking for existing release branch..."
152+
$existingRemoteBranch = git ls-remote --heads origin $releaseBranch 2>$null
153+
if ($existingRemoteBranch) {
154+
Write-Error "Release branch '$releaseBranch' already exists on origin. Has this version already been released?"
155+
exit 1
156+
}
157+
Write-Ok "Branch '$releaseBranch' does not exist yet — good to go"
158+
159+
# 6. Confirm with user
160+
Write-Host ""
161+
Write-Host " ┌─────────────────────────────────────────────┐" -ForegroundColor White
162+
Write-Host " │ Release Plan │" -ForegroundColor White
163+
Write-Host " │ │" -ForegroundColor White
164+
Write-Host " │ Release version : $releaseVersion$((' ' * (25 - $releaseVersion.Length)))" -ForegroundColor White
165+
Write-Host " │ Release branch : $releaseBranch$((' ' * (25 - $releaseBranch.Length)))" -ForegroundColor White
166+
Write-Host " │ Next dev version: $nextVersion$((' ' * (25 - $nextVersion.Length)))" -ForegroundColor White
167+
Write-Host " │ Bump branch : $bumpBranch$((' ' * (25 - $bumpBranch.Length)))" -ForegroundColor White
168+
Write-Host " │ │" -ForegroundColor White
169+
Write-Host " │ Steps: │" -ForegroundColor White
170+
Write-Host " │ 1. Create & push $releaseBranch$((' ' * (18 - $releaseBranch.Length)))" -ForegroundColor White
171+
Write-Host " │ 2. Bump version.json to $nextVersion$((' ' * (12 - $nextVersion.Length)))" -ForegroundColor White
172+
Write-Host " │ 3. Create PR to merge bump into main │" -ForegroundColor White
173+
Write-Host " └─────────────────────────────────────────────┘" -ForegroundColor White
174+
175+
Confirm-Step "Does this release plan look correct? Proceed?"
176+
177+
# ─── Step 1: Create and push the release branch ─────────────────────────────
178+
179+
Write-Step "Step 1/3: Creating release branch '$releaseBranch'..."
180+
Invoke-GitOrDryRun -Description "Create release branch" -Arguments @("checkout", "-b", $releaseBranch)
181+
Write-Ok "Local branch '$releaseBranch' created"
182+
183+
Confirm-Step "Push '$releaseBranch' to origin? This will kick off the release pipeline"
184+
185+
Invoke-GitOrDryRun -Description "Push release branch" -Arguments @("push", "-u", "origin", $releaseBranch)
186+
Write-Ok "Release branch '$releaseBranch' pushed to origin"
187+
188+
# ─── Step 2: Go back to main and bump the version ───────────────────────────
189+
190+
Write-Step "Step 2/3: Bumping version to $nextVersion..."
191+
Invoke-GitOrDryRun -Description "Switch back to main" -Arguments @("checkout", "main")
192+
193+
# Create the bump branch from main
194+
Invoke-GitOrDryRun -Description "Create bump branch" -Arguments @("checkout", "-b", $bumpBranch)
195+
196+
# Update version.json
197+
if ($DryRun) {
198+
Write-Warn "[DRY RUN] Would update version.json: $releaseVersion -> $nextVersion"
199+
} else {
200+
$newVersionJson = @{ version = $nextVersion } | ConvertTo-Json
201+
Set-Content -Path $VersionFilePath -Value $newVersionJson -NoNewline
202+
Write-Info "Updated version.json: $releaseVersion -> $nextVersion"
203+
}
204+
205+
# Commit and push
206+
Invoke-GitOrDryRun -Description "Stage version.json" -Arguments @("add", "version.json")
207+
Invoke-GitOrDryRun -Description "Commit version bump" -Arguments @("commit", "-m", "Bump version to $nextVersion for development")
208+
209+
Confirm-Step "Push version bump branch '$bumpBranch' to origin and create PR?"
210+
211+
Invoke-GitOrDryRun -Description "Push bump branch" -Arguments @("push", "-u", "origin", $bumpBranch)
212+
Write-Ok "Bump branch '$bumpBranch' pushed to origin"
213+
214+
# ─── Step 3: Create a PR for the version bump ───────────────────────────────
215+
216+
Write-Step "Step 3/3: Creating pull request..."
217+
218+
# Check that gh CLI is available
219+
$ghAvailable = Get-Command gh -ErrorAction SilentlyContinue
220+
if (-not $ghAvailable -and -not $DryRun) {
221+
Write-Warn "GitHub CLI (gh) is not installed. Please create the PR manually:"
222+
Write-Warn " gh pr create --base main --head $bumpBranch --title 'Bump version to $nextVersion' --body 'Auto-generated version bump after releasing v$releaseVersion.'"
223+
} else {
224+
Invoke-GhOrDryRun -Description "Create pull request" -Arguments @(
225+
"pr", "create",
226+
"--base", "main",
227+
"--head", $bumpBranch,
228+
"--title", "Bump version to $nextVersion for development",
229+
"--body", "Auto-generated version bump after releasing v$releaseVersion.`n`nThis PR bumps the patch version in ``version.json`` from ``$releaseVersion`` to ``$nextVersion`` so that prerelease builds pick up the new version number."
230+
)
231+
Write-Ok "Pull request created"
232+
}
233+
234+
# ─── Done ───────────────────────────────────────────────────────────────────
235+
236+
# Return to main so you're in a good state
237+
Invoke-GitOrDryRun -Description "Switch back to main" -Arguments @("checkout", "main")
238+
239+
Write-Host ""
240+
Write-Host "╔══════════════════════════════════════════════╗" -ForegroundColor Green
241+
Write-Host "║ Release started successfully! ║" -ForegroundColor Green
242+
Write-Host "╠══════════════════════════════════════════════╣" -ForegroundColor Green
243+
Write-Host "║ ║" -ForegroundColor Green
244+
Write-Host "║ • Release branch '$releaseBranch' pushed$((' ' * (14 - $releaseBranch.Length)))" -ForegroundColor Green
245+
Write-Host "║ • Version bump PR created for $nextVersion$((' ' * (10 - $nextVersion.Length)))" -ForegroundColor Green
246+
Write-Host "║ ║" -ForegroundColor Green
247+
Write-Host "║ Next steps: ║" -ForegroundColor Green
248+
Write-Host "║ 1. Monitor the release pipeline ║" -ForegroundColor Green
249+
Write-Host "║ 2. Review & merge the version bump PR ║" -ForegroundColor Green
250+
Write-Host "╚══════════════════════════════════════════════╝" -ForegroundColor Green
251+
Write-Host ""
252+
253+
} catch {
254+
Write-Host ""
255+
Write-Host "ERROR: $_" -ForegroundColor Red
256+
Write-Host ""
257+
Write-Warn "The release process did not complete. You may need to manually clean up:"
258+
Write-Warn " - Check your current branch: git rev-parse --abbrev-ref HEAD"
259+
Write-Warn " - Switch back to main: git checkout main"
260+
if ($releaseBranch) {
261+
Write-Warn " - Delete local release branch: git branch -D $releaseBranch"
262+
}
263+
if ($bumpBranch) {
264+
Write-Warn " - Delete local bump branch: git branch -D $bumpBranch"
265+
Write-Warn " - Restore version.json: git checkout -- version.json"
266+
}
267+
268+
exit 1
269+
} finally {
270+
Pop-Location
271+
}

0 commit comments

Comments
 (0)