-
Notifications
You must be signed in to change notification settings - Fork 25
/
Expand-Template.ps1
executable file
·226 lines (204 loc) · 8.8 KB
/
Expand-Template.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Expands this template into an actual project, taking values for placeholders
.PARAMETER LibraryName
The name of the library. Should consist only of alphanumeric characters and periods.
.PARAMETER Author
The name to use in copyright and owner notices.
.PARAMETER CodeCovToken
A token obtained from codecov.io for your repo. If not specified, code coverage results will not be published to codecov.io,
but can be added later by editing the Azure Pipelines YAML file.
.PARAMETER CIFeed
The `/{guid}` path to the Azure Pipelines artifact feed to push your nuget package to as part of your CI.
.PARAMETER Squash
A switch that causes all of git history to be squashed to just one initial commit for the template, and one for its expansion.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')]
Param(
[Parameter(Mandatory=$true)]
[string]$LibraryName,
[Parameter(Mandatory=$true)]
[string]$Author,
[Parameter()]
[string]$CodeCovToken,
[Parameter()]
[string]$CIFeed,
[Parameter()]
[switch]$Squash
)
function Replace-Placeholders {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Path,
[Parameter(Mandatory=$true)]
$Replacements
)
$Path = Resolve-Path $Path
Write-Host "Replacing tokens in `"$Path`""
$content = Get-Content -Path $Path | Out-String
$Replacements.GetEnumerator() |% {
$modifiedContent = $content -replace $_.Key,$_.Value
if ($modifiedContent -eq $content) {
Write-Error "No $($_.Key) token found to replace."
}
$content = $modifiedContent
}
$content = $content.TrimEnd(("`r","`n"))
[System.IO.File]::WriteAllLines($Path, $content) # Don't use Set-Content because that adds a UTF8 BOM
git add $Path
}
# Try to find sn.exe if it isn't on the PATH
$sn = Get-Command sn -ErrorAction SilentlyContinue
if (-not $sn) {
if ($IsMacOS -or $IsLinux) {
Write-Error "sn command not found on PATH. Install mono and/or vote up this issue: https://github.com/dotnet/sdk/issues/13560"
exit(1)
}
$snExes = Get-ChildItem -Recurse "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows\sn.exe"
if ($snExes) {
$sn = Get-Command $snExes[0].FullName
} else {
Write-Error "sn command not found on PATH and SDK could not be found."
exit(1)
}
}
if (-not (& "$PSScriptRoot\tools\Check-DotNetSdk.ps1")) {
if ($PSCmdlet.ShouldProcess('Install .NET Core SDK?')) {
& "$PSScriptRoot\tools\Install-DotNetSdk.ps1"
} else {
Write-Error "Matching .NET Core SDK version not found. Install now?"
exit 1
}
}
# Verify all commands we use are on the PATH
('git','dotnet') |% {
if (-not (Get-Command $_ -ErrorAction SilentlyContinue)) {
Write-Error "$_ command not found on PATH."
exit(1)
}
}
Push-Location $PSScriptRoot
try {
if ($Squash) {
$originalCommitId = git rev-parse HEAD
git reset --soft $(git rev-list --max-parents=0 HEAD)
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git commit --amend -qm "Initial template from https://github.com/AArnott/Library.Template" -m "Original commit from template $originalCommitId"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
git config core.safecrlf false # Avoid warnings when adding files with mangled line endings
# Rename project directories and solution
git mv Library.sln "$LibraryName.sln"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git mv src/Library/Library.csproj "src/Library/$LibraryName.csproj"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git mv src/Library "src/$LibraryName"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git mv test/Library.Tests/Library.Tests.csproj "test/Library.Tests/$LibraryName.Tests.csproj"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git mv test/Library.Tests "test/$LibraryName.Tests"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
# Refresh solution file both to update paths and give the projects unique GUIDs
dotnet sln remove src/Library/Library.csproj
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
dotnet sln remove test/Library.Tests/Library.Tests.csproj
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
dotnet sln add "src/$LibraryName"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
dotnet sln add "test/$LibraryName.Tests"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git add "$LibraryName.sln"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
# Update project reference in test project. Add before removal to keep the same ItemGroup in place.
dotnet add "test/$LibraryName.Tests" reference "src/$LibraryName"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
dotnet remove "test/$LibraryName.Tests" reference src/Library/Library.csproj
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git add "test/$LibraryName.Tests/$LibraryName.Tests.csproj"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
# Establish a new strong-name key
& $sn.Path -k 2048 strongname.snk
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git add strongname.snk
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
# Replace placeholders in source files
Replace-Placeholders -Path "src/$LibraryName/Calculator.cs" -Replacements @{
'Library'=$LibraryName
'COMPANY-PLACEHOLDER'=$Author
}
Replace-Placeholders -Path "test/$LibraryName.Tests/CalculatorTests.cs" -Replacements @{
'Library'=$LibraryName
'COMPANY-PLACEHOLDER'=$Author
}
Replace-Placeholders -Path "src/AssemblyInfo.cs" -Replacements @{
'COMPANY-PLACEHOLDER'=$Author
}
Replace-Placeholders -Path "src/AssemblyInfo.vb" -Replacements @{
'COMPANY-PLACEHOLDER'=$Author
}
Replace-Placeholders -Path "LICENSE" -Replacements @{
'COMPANY-PLACEHOLDER'=$Author
}
Replace-Placeholders -Path "stylecop.json" -Replacements @{
'COMPANY-PLACEHOLDER'=$Author
}
Replace-Placeholders -Path "Directory.Build.props" -Replacements @{
'COMPANY-PLACEHOLDER'=$Author
}
Replace-Placeholders -Path "README.md" -Replacements @{
"(?m)^.*\[NuGet package\][^`r`n]*"="[![NuGet package](https://img.shields.io/nuget/v/$LibraryName.svg)](https://nuget.org/packages/$LibraryName)"
"(?m)^.*\[Azure Pipelines status\].*`r?`n"=""
"(?m)^.*\[GitHub Actions status\].*`r?`n"=""
"(?m)^.*\[codecov\].*`r?`n"=""
}
Replace-Placeholders -Path "docfx/docfx.json" -Replacements @{
'Library'=$LibraryName
}
Replace-Placeholders -Path "docfx/docs/getting-started.md" -Replacements @{
'Library'=$LibraryName
}
# Specially handle azure-pipelines .yml edits
Replace-Placeholders -Path "azure-pipelines/build.yml" -Replacements @{
"(?m).*expand-template\.yml(?:\r)?\n" = ""
}
$YmlReplacements = @{}
if ($CodeCovToken) {
$YmlReplacements['(codecov_token: ).*(#.*)'] = "`$1$CodeCovToken"
} else {
$YmlReplacements['(codecov_token: ).*(#.*)'] = "#`$1`$2"
}
if ($CIFeed) {
$YmlReplacements['(ci_feed: ).*(#.*)'] = "`$1$CIFeed"
} else {
$YmlReplacements['(ci_feed: ).*(#.*)'] = "#`$1`$2"
}
Replace-Placeholders -Path "azure-pipelines.yml" -Replacements $YmlReplacements
# Self destruct
git rm Expand-Template.* Apply-Template.ps1
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
git rm :/azure-pipelines/expand-template.yml
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
# Self-integrity check
Get-ChildItem -Recurse -File -Exclude bin,obj,README.md,Expand-Template.* |? { -not $_.FullName.Contains("obj") } |% {
$PLACEHOLDERS = Get-Content -Path $_.FullName |? { $_.Contains('PLACEHOLDER') }
if ($PLACEHOLDERS) {
Write-Error "PLACEHOLDER discovered in $($_.FullName)"
}
}
# Commit the changes
git commit -qm "Expanded template for $LibraryName" -m "This expansion done by the (now removed) Expand-Template.ps1 script."
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Write-Host -ForegroundColor Green "Template successfully expanded."
if ($env:PS1UnderCmd) {
# We're running under the Expand-Template.cmd script.
# Since we just deleted it from disk cmd.exe will complain. Just advise the user it's OK.
Write-Host -ForegroundColor Green 'Disregard an error you may see: "The batch file cannot be found." We just cleaned up after ourselves.'
}
} finally {
git config --local --unset core.safecrlf
Pop-Location
}
# When testing this script, all the changes can be quickly reverted with this command:
# git reset HEAD :/README.md :/LICENSE :/azure-pipelines.yml :/src :/test :/azure-pipelines; git co -- :/README.md :/LICENSE :/azure-pipelines.yml :/src :/azure-pipelines; git clean -fd :/src :/test