|
| 1 | +#Requires -Version 7.0 |
| 2 | +<# |
| 3 | +.SYNOPSIS |
| 4 | + Initializes this template with your project name via interactive wizard. |
| 5 | +.DESCRIPTION |
| 6 | + Run without parameters for an interactive guided setup. |
| 7 | + Supply -ProjectName and -Force for non-interactive (CI/script) usage. |
| 8 | +.EXAMPLE |
| 9 | + ./init.ps1 |
| 10 | +.EXAMPLE |
| 11 | + ./init.ps1 -ProjectName Acme.Payments -Author "Acme Corp" -Force |
| 12 | +#> |
| 13 | +param( |
| 14 | + [ValidatePattern('^[A-Za-z][A-Za-z0-9.]*$')] |
| 15 | + [string]$ProjectName, |
| 16 | + |
| 17 | + [string]$Author, |
| 18 | + |
| 19 | + [switch]$ResetGit, |
| 20 | + |
| 21 | + [switch]$Force |
| 22 | +) |
| 23 | + |
| 24 | +$ErrorActionPreference = 'Stop' |
| 25 | + |
| 26 | +$OldSample = 'Dotnet.CI.Template.Sample' |
| 27 | +$OldSln = 'Dotnet.CI.Template' |
| 28 | +$ScriptDir = $PSScriptRoot |
| 29 | + |
| 30 | +# ── Interactive wizard ─────────────────────────────────────────────── |
| 31 | + |
| 32 | +if (-not $Force) { |
| 33 | + Write-Host "" |
| 34 | + Write-Host " dotnet.CI.template - Project Setup" -ForegroundColor Cyan |
| 35 | + Write-Host " ===================================" -ForegroundColor Cyan |
| 36 | + Write-Host "" |
| 37 | + Write-Host " This wizard will customize the template for your project." |
| 38 | + Write-Host "" |
| 39 | + |
| 40 | + # Project name |
| 41 | + if (-not $ProjectName) { |
| 42 | + do { |
| 43 | + $ProjectName = Read-Host " ? Project name (e.g. Acme.Payments)" |
| 44 | + if ($ProjectName -notmatch '^[A-Za-z][A-Za-z0-9.]*$') { |
| 45 | + Write-Host " Must start with a letter and contain only letters, digits, and dots." -ForegroundColor Red |
| 46 | + $ProjectName = '' |
| 47 | + } |
| 48 | + } while (-not $ProjectName) |
| 49 | + } |
| 50 | + Write-Host " Project name: $ProjectName" -ForegroundColor Green |
| 51 | + |
| 52 | + # Author |
| 53 | + if (-not $Author) { |
| 54 | + $Author = Read-Host " ? Author / organization (leave blank to skip)" |
| 55 | + } |
| 56 | + if ($Author) { |
| 57 | + Write-Host " Author: $Author" -ForegroundColor Green |
| 58 | + } |
| 59 | + |
| 60 | + # Reset git |
| 61 | + if (-not $ResetGit) { |
| 62 | + $resetAnswer = Read-Host " ? Reset git history to a fresh commit? [y/N]" |
| 63 | + $ResetGit = $resetAnswer -match '^[Yy]' |
| 64 | + } |
| 65 | + |
| 66 | + # Preview |
| 67 | + $affectedFiles = Get-ChildItem -Path $ScriptDir -Recurse -File | |
| 68 | + Where-Object { |
| 69 | + $path = $_.FullName |
| 70 | + $skip = $false |
| 71 | + foreach ($d in @('.git', 'node_modules', 'artifacts', 'dist', 'bin', 'obj')) { |
| 72 | + if ($path -match [regex]::Escape([IO.Path]::DirectorySeparatorChar + $d + [IO.Path]::DirectorySeparatorChar)) { |
| 73 | + $skip = $true; break |
| 74 | + } |
| 75 | + } |
| 76 | + if (-not $skip -and $_.Name -notin @('init.sh', 'init.ps1')) { |
| 77 | + $content = Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue |
| 78 | + $content -and ($content -match [regex]::Escape($OldSample) -or $content -match [regex]::Escape($OldSln)) |
| 79 | + } |
| 80 | + } |
| 81 | + $affectedDirs = Get-ChildItem -Path $ScriptDir -Recurse -Directory | |
| 82 | + Where-Object { $_.Name -match [regex]::Escape($OldSample) -and $_.FullName -notmatch '\.git' } |
| 83 | + |
| 84 | + $currentVersion = '(unknown)' |
| 85 | + $propsPath = Join-Path $ScriptDir 'Directory.Build.props' |
| 86 | + if (Test-Path $propsPath) { |
| 87 | + $propsContent = Get-Content $propsPath -Raw |
| 88 | + if ($propsContent -match '<VersionPrefix>([^<]+)</VersionPrefix>') { |
| 89 | + $currentVersion = $Matches[1] |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + Write-Host "" |
| 94 | + Write-Host " ──────────────────────────────────────" -ForegroundColor DarkGray |
| 95 | + Write-Host "" |
| 96 | + Write-Host " The following changes will be applied:" -ForegroundColor Yellow |
| 97 | + Write-Host "" |
| 98 | + Write-Host " Rename $OldSample -> $ProjectName" |
| 99 | + Write-Host " Rename $OldSln.slnx -> $ProjectName.slnx" |
| 100 | + if ($Author) { |
| 101 | + Write-Host " Update Authors -> $Author" |
| 102 | + } |
| 103 | + Write-Host " Reset VersionPrefix $currentVersion -> 0.1.0" |
| 104 | + Write-Host " Git $(if ($ResetGit) { 'reset to fresh commit' } else { 'preserved' })" |
| 105 | + Write-Host " Affect $($affectedFiles.Count) files, $($affectedDirs.Count) directories" |
| 106 | + Write-Host "" |
| 107 | + Write-Host " WARNING: This operation is IRREVERSIBLE." -ForegroundColor Red |
| 108 | + Write-Host "" |
| 109 | + |
| 110 | + $confirm = Read-Host " ? Proceed? [y/N]" |
| 111 | + if ($confirm -notmatch '^[Yy]') { |
| 112 | + Write-Host "" |
| 113 | + Write-Host " Cancelled." -ForegroundColor Yellow |
| 114 | + exit 0 |
| 115 | + } |
| 116 | + Write-Host "" |
| 117 | +} |
| 118 | +else { |
| 119 | + if (-not $ProjectName) { |
| 120 | + Write-Error "ProjectName is required when using -Force." |
| 121 | + exit 1 |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +# ── Execute ────────────────────────────────────────────────────────── |
| 126 | + |
| 127 | +Write-Host "[1/6] Replacing file contents..." |
| 128 | +$excludeDirs = @('.git', 'node_modules', 'artifacts', 'dist', 'bin', 'obj') |
| 129 | +Get-ChildItem -Path $ScriptDir -Recurse -File | |
| 130 | + Where-Object { |
| 131 | + $path = $_.FullName |
| 132 | + $skip = $false |
| 133 | + foreach ($d in $excludeDirs) { |
| 134 | + if ($path -match [regex]::Escape([IO.Path]::DirectorySeparatorChar + $d + [IO.Path]::DirectorySeparatorChar)) { |
| 135 | + $skip = $true; break |
| 136 | + } |
| 137 | + } |
| 138 | + -not $skip -and $_.Name -notin @('init.sh', 'init.ps1') |
| 139 | + } | |
| 140 | + ForEach-Object { |
| 141 | + $content = Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue |
| 142 | + if ($content -and ($content -match [regex]::Escape($OldSample) -or $content -match [regex]::Escape($OldSln))) { |
| 143 | + $content = $content -replace [regex]::Escape($OldSample), $ProjectName |
| 144 | + $content = $content -replace [regex]::Escape($OldSln), $ProjectName |
| 145 | + Set-Content $_.FullName -Value $content -NoNewline |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | +# Author update in csproj |
| 150 | +if ($Author) { |
| 151 | + Write-Host "[2/6] Updating author..." |
| 152 | + Get-ChildItem -Path $ScriptDir -Recurse -File -Filter '*.csproj' | |
| 153 | + Where-Object { $_.FullName -notmatch '[\\/](_build|build)[\\/]' } | |
| 154 | + ForEach-Object { |
| 155 | + $content = Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue |
| 156 | + if ($content -and $content -match '<Authors>[^<]*</Authors>') { |
| 157 | + $content = $content -replace '<Authors>[^<]*</Authors>', "<Authors>$Author</Authors>" |
| 158 | + Set-Content $_.FullName -Value $content -NoNewline |
| 159 | + } |
| 160 | + } |
| 161 | +} |
| 162 | +else { |
| 163 | + Write-Host "[2/6] Skipping author update..." |
| 164 | +} |
| 165 | + |
| 166 | +# Rename directories (deepest first) |
| 167 | +Write-Host "[3/6] Renaming directories..." |
| 168 | +Get-ChildItem -Path $ScriptDir -Recurse -Directory | |
| 169 | + Where-Object { $_.Name -match [regex]::Escape($OldSample) -and $_.FullName -notmatch '\.git' } | |
| 170 | + Sort-Object { $_.FullName.Length } -Descending | |
| 171 | + ForEach-Object { |
| 172 | + $newName = $_.Name -replace [regex]::Escape($OldSample), $ProjectName |
| 173 | + $newPath = Join-Path $_.Parent.FullName $newName |
| 174 | + Write-Host " $($_.FullName) -> $newPath" |
| 175 | + Rename-Item $_.FullName $newPath |
| 176 | + } |
| 177 | + |
| 178 | +# Rename files |
| 179 | +Write-Host "[4/6] Renaming files..." |
| 180 | +Get-ChildItem -Path $ScriptDir -Recurse -File | |
| 181 | + Where-Object { $_.Name -match [regex]::Escape($OldSample) -and $_.FullName -notmatch '\.git' } | |
| 182 | + ForEach-Object { |
| 183 | + $newName = $_.Name -replace [regex]::Escape($OldSample), $ProjectName |
| 184 | + $newPath = Join-Path $_.Directory.FullName $newName |
| 185 | + Write-Host " $($_.FullName) -> $newPath" |
| 186 | + Rename-Item $_.FullName $newPath |
| 187 | + } |
| 188 | + |
| 189 | +$slnFile = Join-Path $ScriptDir "$OldSln.slnx" |
| 190 | +if (Test-Path $slnFile) { |
| 191 | + $newSlnFile = Join-Path $ScriptDir "$ProjectName.slnx" |
| 192 | + Rename-Item $slnFile $newSlnFile |
| 193 | + Write-Host " $OldSln.slnx -> $ProjectName.slnx" |
| 194 | +} |
| 195 | + |
| 196 | +# Reset version |
| 197 | +Write-Host "[5/6] Resetting version to 0.1.0..." |
| 198 | +$propsFile = Join-Path $ScriptDir 'Directory.Build.props' |
| 199 | +if (Test-Path $propsFile) { |
| 200 | + $xml = Get-Content $propsFile -Raw |
| 201 | + $xml = $xml -replace '<VersionPrefix>[^<]*</VersionPrefix>', '<VersionPrefix>0.1.0</VersionPrefix>' |
| 202 | + Set-Content $propsFile -Value $xml -NoNewline |
| 203 | +} |
| 204 | + |
| 205 | +# Update build paths |
| 206 | +Write-Host "[6/6] Updating build configuration..." |
| 207 | +$paramsFile = Join-Path $ScriptDir 'build/BuildTask.Parameters.cs' |
| 208 | +if (Test-Path $paramsFile) { |
| 209 | + $cs = Get-Content $paramsFile -Raw |
| 210 | + $cs = $cs -replace [regex]::Escape("$OldSln.slnx"), "$ProjectName.slnx" |
| 211 | + Set-Content $paramsFile -Value $cs -NoNewline |
| 212 | +} |
| 213 | + |
| 214 | +# Optional: reset git history |
| 215 | +if ($ResetGit) { |
| 216 | + Write-Host "" |
| 217 | + Write-Host "Resetting git history..." |
| 218 | + Remove-Item (Join-Path $ScriptDir '.git') -Recurse -Force |
| 219 | + git -C $ScriptDir init |
| 220 | + git -C $ScriptDir add . |
| 221 | + git -C $ScriptDir commit -m "Initial commit from dotnet.CI.template" |
| 222 | +} |
| 223 | + |
| 224 | +# Clean up init scripts |
| 225 | +Remove-Item (Join-Path $ScriptDir 'init.sh') -ErrorAction SilentlyContinue |
| 226 | +Remove-Item (Join-Path $ScriptDir 'init.ps1') -ErrorAction SilentlyContinue |
| 227 | + |
| 228 | +Write-Host "" |
| 229 | +Write-Host "Done! Your project '$ProjectName' is ready." -ForegroundColor Green |
| 230 | +Write-Host "" |
| 231 | +Write-Host "Next steps:" |
| 232 | +Write-Host " 1. Update GitHub URL in docs/.vitepress/config.ts" |
| 233 | +Write-Host " 2. Run: dotnet restore --force-evaluate" |
| 234 | +Write-Host " 3. Run: dotnet build" |
0 commit comments