218 строки
8.5 KiB
PowerShell
Executable File
218 строки
8.5 KiB
PowerShell
Executable File
#!/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 "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"=""
|
|
}
|
|
|
|
# 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
|