(chore): migrate to .slnx and refine release scripts/docs
This commit is contained in:
parent
91adc78690
commit
6b72b83487
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024 - 2025 Maksym Sadovnychyy (MAKS-IT)
|
Copyright (c) 2024 - 2026 Maksym Sadovnychyy (MAKS-IT)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@ -108,6 +108,7 @@ If you have any questions or need further assistance, feel free to reach out:
|
|||||||
|
|
||||||
- **Email**: [maksym.sadovnychyy@gmail.com](mailto:maksym.sadovnychyy@gmail.com)
|
- **Email**: [maksym.sadovnychyy@gmail.com](mailto:maksym.sadovnychyy@gmail.com)
|
||||||
- **Reddit**: [MaksIT.Results: Streamline Your ASP.NET Core API Response Handling](https://www.reddit.com/r/MaksIT/comments/1f89ifn/maksitresults_streamline_your_aspnet_core_api/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button)
|
- **Reddit**: [MaksIT.Results: Streamline Your ASP.NET Core API Response Handling](https://www.reddit.com/r/MaksIT/comments/1f89ifn/maksitresults_streamline_your_aspnet_core_api/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
See `LICENSE.md`.
|
See `LICENSE.md`.
|
||||||
@ -1,30 +0,0 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 18
|
|
||||||
VisualStudioVersion = 18.0.11222.15
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MaksIT.Results", "MaksIT.Results\MaksIT.Results.csproj", "{E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaksIT.Results.Tests", "MaksIT.Results.Tests\MaksIT.Results.Tests.csproj", "{68D2F460-1550-5219-355F-BEDA6C1557AA}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{E947F5FC-8FD9-4F1E-AA5F-29FED95B5A2D}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{68D2F460-1550-5219-355F-BEDA6C1557AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{68D2F460-1550-5219-355F-BEDA6C1557AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{68D2F460-1550-5219-355F-BEDA6C1557AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{68D2F460-1550-5219-355F-BEDA6C1557AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {C3627A51-0642-40DB-96BC-07C627FF8ACC}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
4
src/MaksIT.Results.slnx
Normal file
4
src/MaksIT.Results.slnx
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="MaksIT.Results.Tests/MaksIT.Results.Tests.csproj" />
|
||||||
|
<Project Path="MaksIT.Results/MaksIT.Results.csproj" />
|
||||||
|
</Solution>
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Amends the latest tagged commit and force-pushes updated branch and tag.
|
Amends the latest commit, recreates its associated tag, and force pushes both to remote.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
This script performs the following operations:
|
This script performs the following operations:
|
||||||
@ -66,6 +66,29 @@ Import-Module $gitToolsModulePath -Force
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
|
||||||
|
function Select-PreferredHeadTag {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$Tags
|
||||||
|
)
|
||||||
|
|
||||||
|
# Pick the latest tag on HEAD by git's own ordering (no tag-name parsing assumptions).
|
||||||
|
$ordered = (& git tag --points-at HEAD --sort=-creatordate 2>$null)
|
||||||
|
if ($LASTEXITCODE -eq 0 -and $ordered) {
|
||||||
|
$orderedTags = @($ordered | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim() })
|
||||||
|
if ($orderedTags.Count -gt 0) {
|
||||||
|
return $orderedTags[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fallback: keep script functional even if sorting is unavailable.
|
||||||
|
return $Tags[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Load Settings
|
#region Load Settings
|
||||||
|
|
||||||
$settings = Get-ScriptSettings -ScriptDir $scriptDir
|
$settings = Get-ScriptSettings -ScriptDir $scriptDir
|
||||||
@ -110,14 +133,17 @@ Write-Log -Level "INFO" -Message "Commit: $CommitHash - $CommitMessage"
|
|||||||
|
|
||||||
# 3. Ensure HEAD has at least one tag
|
# 3. Ensure HEAD has at least one tag
|
||||||
Write-LogStep "Finding tag on last commit..."
|
Write-LogStep "Finding tag on last commit..."
|
||||||
$tags = @(Get-HeadTags)
|
$tags = Get-HeadTags
|
||||||
if ($tags.Count -eq 0) {
|
if ($tags.Count -eq 0) {
|
||||||
Write-Error "No tag found on the last commit ($CommitHash). This script requires the last commit to have an associated tag."
|
Write-Error "No tag found on the last commit ($CommitHash). This script requires the last commit to have an associated tag."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# If multiple tags exist, use the first one returned by git.
|
# If multiple tags exist, choose the latest one on HEAD by git ordering.
|
||||||
$TagName = $tags[0]
|
if ($tags.Count -gt 1) {
|
||||||
|
Write-Log -Level "WARN" -Message "Multiple tags found on HEAD: $($tags -join ', ')"
|
||||||
|
}
|
||||||
|
$TagName = Select-PreferredHeadTag -Tags $tags
|
||||||
Write-Log -Level "OK" -Message "Found tag: $TagName"
|
Write-Log -Level "OK" -Message "Found tag: $TagName"
|
||||||
|
|
||||||
# 4. Inspect pending changes before amend
|
# 4. Inspect pending changes before amend
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Builds, tests, packs, and publishes MaksIT.Core to NuGet and GitHub releases.
|
Release script for MaksIT.Core NuGet package and GitHub release.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
This script automates the release process for MaksIT.Core library.
|
This script automates the release process for MaksIT.Core library.
|
||||||
The script is IDEMPOTENT - you can safely re-run it if any step fails.
|
The script is IDEMPOTENT - you can safely re-run it if any step fails.
|
||||||
It will skip already-completed steps (NuGet and GitHub) and only create what's missing.
|
It will skip already-completed steps (NuGet and GitHub) and only create what's missing.
|
||||||
GitHub repository target can be configured explicitly in scriptsettings.json.
|
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Validates environment and prerequisites
|
- Validates environment and prerequisites
|
||||||
@ -18,7 +17,7 @@
|
|||||||
- Generates test result artifacts (TRX format) and coverage reports
|
- Generates test result artifacts (TRX format) and coverage reports
|
||||||
- Displays test results with pass/fail counts and coverage percentage
|
- Displays test results with pass/fail counts and coverage percentage
|
||||||
- Publishes to NuGet.org
|
- Publishes to NuGet.org
|
||||||
- Creates a GitHub release with changelog and package assets
|
- Creates a GitHub release with changelog and NuGet package assets
|
||||||
- Shows timing summary for all steps
|
- Shows timing summary for all steps
|
||||||
|
|
||||||
.REQUIREMENTS
|
.REQUIREMENTS
|
||||||
@ -105,6 +104,7 @@
|
|||||||
|
|
||||||
.CONFIGURATION
|
.CONFIGURATION
|
||||||
All settings are stored in scriptsettings.json:
|
All settings are stored in scriptsettings.json:
|
||||||
|
- qualityGates: Coverage threshold, vulnerability checks
|
||||||
- packageSigning: Code signing certificate configuration
|
- packageSigning: Code signing certificate configuration
|
||||||
- emailNotification: SMTP settings for release notifications
|
- emailNotification: SMTP settings for release notifications
|
||||||
|
|
||||||
@ -174,7 +174,6 @@ $settings = Get-ScriptSettings -ScriptDir $scriptDir
|
|||||||
$githubReleseEnabled = $settings.github.enabled
|
$githubReleseEnabled = $settings.github.enabled
|
||||||
$githubTokenEnvVar = $settings.github.githubToken
|
$githubTokenEnvVar = $settings.github.githubToken
|
||||||
$githubToken = [System.Environment]::GetEnvironmentVariable($githubTokenEnvVar)
|
$githubToken = [System.Environment]::GetEnvironmentVariable($githubTokenEnvVar)
|
||||||
$githubRepositorySetting = $settings.github.repository
|
|
||||||
|
|
||||||
# NuGet configuration
|
# NuGet configuration
|
||||||
$nugetReleseEnabled = $settings.nuget.enabled
|
$nugetReleseEnabled = $settings.nuget.enabled
|
||||||
@ -214,12 +213,11 @@ if ($csprojPaths.Count -eq 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$testResultsDir = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.testResultsDir))
|
$testResultsDir = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.testResultsDir))
|
||||||
$stagingDir = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.stagingDir))
|
|
||||||
$releaseDir = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.releaseDir))
|
$releaseDir = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.releaseDir))
|
||||||
$changelogPath = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.changelogPath))
|
$changelogPath = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.changelogPath))
|
||||||
$testProjectPath = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.testProject))
|
$testProjectPath = [System.IO.Path]::GetFullPath((Join-Path $scriptDir $settings.paths.testProject))
|
||||||
|
|
||||||
# Release naming patterns
|
# Release naming pattern
|
||||||
$zipNamePattern = $settings.release.zipNamePattern
|
$zipNamePattern = $settings.release.zipNamePattern
|
||||||
$releaseTitlePattern = $settings.release.releaseTitlePattern
|
$releaseTitlePattern = $settings.release.releaseTitlePattern
|
||||||
|
|
||||||
@ -249,21 +247,6 @@ function Get-CsprojPropertyValue {
|
|||||||
return $null
|
return $null
|
||||||
}
|
}
|
||||||
|
|
||||||
# Helper: resolve output assembly name for published exe
|
|
||||||
function Resolve-ProjectExeName {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory=$true)][string]$projPath
|
|
||||||
)
|
|
||||||
|
|
||||||
[xml]$csproj = Get-Content $projPath
|
|
||||||
$assemblyName = Get-CsprojPropertyValue -csproj $csproj -propertyName "AssemblyName"
|
|
||||||
if ($assemblyName) {
|
|
||||||
return $assemblyName
|
|
||||||
}
|
|
||||||
|
|
||||||
return [System.IO.Path]::GetFileNameWithoutExtension($projPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Helper: check for uncommitted changes
|
# Helper: check for uncommitted changes
|
||||||
function Assert-WorkingTreeClean {
|
function Assert-WorkingTreeClean {
|
||||||
param(
|
param(
|
||||||
@ -324,42 +307,6 @@ function Get-CsprojVersions {
|
|||||||
return $projectVersions
|
return $projectVersions
|
||||||
}
|
}
|
||||||
|
|
||||||
# Helper: resolve GitHub repository (owner/repo) from settings override or remote URL
|
|
||||||
function Resolve-GitHubRepository {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory = $false)]
|
|
||||||
[string]$RepositorySetting
|
|
||||||
)
|
|
||||||
|
|
||||||
if (-not [string]::IsNullOrWhiteSpace($RepositorySetting)) {
|
|
||||||
$value = $RepositorySetting.Trim()
|
|
||||||
|
|
||||||
if ($value -match '^https?://github\.com/(?<owner>[^/]+)/(?<repo>[^/]+?)(?:\.git)?/?$') {
|
|
||||||
return "$($Matches['owner'])/$($Matches['repo'])"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($value -match '^(?<owner>[^/]+)/(?<repo>[^/]+)$') {
|
|
||||||
return "$($Matches['owner'])/$($Matches['repo'])"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Error "Invalid github.repository format '$value'. Use 'owner/repo' or 'https://github.com/owner/repo'."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$remoteUrl = git config --get remote.origin.url
|
|
||||||
if ($LASTEXITCODE -ne 0 -or -not $remoteUrl) {
|
|
||||||
Write-Error "Could not determine git remote origin URL. Configure github.repository in scriptsettings.json."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($remoteUrl -match "[:/](?<owner>[^/]+)/(?<repo>[^/.]+)(\.git)?$") {
|
|
||||||
return "$($Matches['owner'])/$($Matches['repo'])"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Error "Could not parse repository from remote URL: $remoteUrl. Configure github.repository in scriptsettings.json."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Validate CLI Dependencies
|
#region Validate CLI Dependencies
|
||||||
@ -480,78 +427,18 @@ Write-Log -Level "INFO" -Message " Method Coverage: $($testResult.MethodRate)%"
|
|||||||
|
|
||||||
#region Build And Publish
|
#region Build And Publish
|
||||||
|
|
||||||
# 7. Prepare staging directory
|
# 7. Prepare release directory
|
||||||
Write-Log -Level "STEP" -Message "Preparing staging directory..."
|
|
||||||
if (Test-Path $stagingDir) {
|
|
||||||
Remove-Item $stagingDir -Recurse -Force
|
|
||||||
}
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path $stagingDir | Out-Null
|
|
||||||
|
|
||||||
$binDir = Join-Path $stagingDir "bin"
|
|
||||||
|
|
||||||
# 8. Publish the project to staging/bin
|
|
||||||
|
|
||||||
Write-Log -Level "STEP" -Message "Publishing projects to bin folder..."
|
|
||||||
$publishSuccess = $true
|
|
||||||
$publishedProjects = @()
|
|
||||||
|
|
||||||
foreach ($projPath in $csprojPaths) {
|
|
||||||
$projName = [System.IO.Path]::GetFileNameWithoutExtension($projPath)
|
|
||||||
$projBinDir = Join-Path $binDir $projName
|
|
||||||
|
|
||||||
dotnet publish $projPath -c Release -o $projBinDir
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "dotnet publish failed for $projName."
|
|
||||||
$publishSuccess = $false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$exeBaseName = Resolve-ProjectExeName -projPath $projPath
|
|
||||||
$publishedProjects += [PSCustomObject]@{
|
|
||||||
ProjPath = $projPath
|
|
||||||
ProjName = $projName
|
|
||||||
BinDir = $projBinDir
|
|
||||||
ExeBaseName = $exeBaseName
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Log -Level "OK" -Message " Published $projName successfully to: $projBinDir"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $publishSuccess) {
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 12. Prepare release directory
|
|
||||||
if (!(Test-Path $releaseDir)) {
|
if (!(Test-Path $releaseDir)) {
|
||||||
New-Item -ItemType Directory -Path $releaseDir | Out-Null
|
New-Item -ItemType Directory -Path $releaseDir | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# 13. Create zip file
|
# 8. Pack NuGet package and resolve produced .nupkg/.snupkg files
|
||||||
$zipName = $zipNamePattern
|
|
||||||
$zipName = $zipName -replace '\{version\}', $version
|
|
||||||
$zipPath = Join-Path $releaseDir $zipName
|
|
||||||
|
|
||||||
if (Test-Path $zipPath) {
|
|
||||||
Remove-Item $zipPath -Force
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Log -Level "STEP" -Message "Creating archive $zipName..."
|
|
||||||
Compress-Archive -Path "$stagingDir\*" -DestinationPath $zipPath -Force
|
|
||||||
|
|
||||||
if (-not (Test-Path $zipPath)) {
|
|
||||||
Write-Error "Failed to create archive $zipPath"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Log -Level "OK" -Message " Archive created: $zipPath"
|
|
||||||
|
|
||||||
# 14. Pack NuGet package and resolve produced .nupkg file
|
|
||||||
$packageProjectPath = $csprojPaths[0]
|
$packageProjectPath = $csprojPaths[0]
|
||||||
Write-Log -Level "STEP" -Message "Packing NuGet package..."
|
Write-Log -Level "STEP" -Message "Packing NuGet package..."
|
||||||
dotnet pack $packageProjectPath -c Release -o $releaseDir --nologo
|
dotnet pack $packageProjectPath -c Release -o $releaseDir --nologo `
|
||||||
|
-p:IncludeSymbols=true `
|
||||||
|
-p:SymbolPackageFormat=snupkg
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Error "dotnet pack failed for $packageProjectPath."
|
Write-Error "dotnet pack failed for $packageProjectPath."
|
||||||
exit 1
|
exit 1
|
||||||
@ -573,7 +460,44 @@ if (-not $packageFile) {
|
|||||||
|
|
||||||
Write-Log -Level "OK" -Message " Package ready: $($packageFile.FullName)"
|
Write-Log -Level "OK" -Message " Package ready: $($packageFile.FullName)"
|
||||||
|
|
||||||
# 15. Extract release notes from CHANGELOG.md
|
# Find the symbols package if available
|
||||||
|
$symbolsPackageFile = Get-ChildItem -Path $releaseDir -Filter "*.snupkg" |
|
||||||
|
Where-Object { $_.Name -like "*$version*.snupkg" } |
|
||||||
|
Sort-Object LastWriteTime -Descending |
|
||||||
|
Select-Object -First 1
|
||||||
|
|
||||||
|
if ($symbolsPackageFile) {
|
||||||
|
Write-Log -Level "OK" -Message " Symbols package ready: $($symbolsPackageFile.FullName)"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log -Level "WARN" -Message " Symbols package (.snupkg) not found for version $version."
|
||||||
|
}
|
||||||
|
|
||||||
|
# 9. Create release archive with NuGet package artifacts
|
||||||
|
Write-Log -Level "STEP" -Message "Creating release archive..."
|
||||||
|
$resolvedZipNamePattern = if ([string]::IsNullOrWhiteSpace($zipNamePattern)) { "release-{version}.zip" } else { $zipNamePattern }
|
||||||
|
$zipFileName = $resolvedZipNamePattern -replace '\{version\}', $version
|
||||||
|
$zipPath = Join-Path $releaseDir $zipFileName
|
||||||
|
|
||||||
|
if (Test-Path $zipPath) {
|
||||||
|
Remove-Item $zipPath -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
$archiveArtifacts = @($packageFile.FullName)
|
||||||
|
if ($symbolsPackageFile) {
|
||||||
|
$archiveArtifacts += $symbolsPackageFile.FullName
|
||||||
|
}
|
||||||
|
|
||||||
|
Compress-Archive -Path $archiveArtifacts -DestinationPath $zipPath -CompressionLevel Optimal -Force
|
||||||
|
|
||||||
|
if (-not (Test-Path $zipPath)) {
|
||||||
|
Write-Error "Failed to create release archive at: $zipPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log -Level "OK" -Message " Release archive ready: $zipPath"
|
||||||
|
|
||||||
|
# 10. Extract release notes from CHANGELOG.md
|
||||||
Write-Log -Level "STEP" -Message "Extracting release notes..."
|
Write-Log -Level "STEP" -Message "Extracting release notes..."
|
||||||
$pattern = "(?ms)^##\s+v$([regex]::Escape($version))\b.*?(?=^##\s+v\d+\.\d+\.\d+|\Z)"
|
$pattern = "(?ms)^##\s+v$([regex]::Escape($version))\b.*?(?=^##\s+v\d+\.\d+\.\d+|\Z)"
|
||||||
$match = [regex]::Match($changelog, $pattern)
|
$match = [regex]::Match($changelog, $pattern)
|
||||||
@ -586,8 +510,21 @@ if (-not $match.Success) {
|
|||||||
$releaseNotes = $match.Value.Trim()
|
$releaseNotes = $match.Value.Trim()
|
||||||
Write-Log -Level "OK" -Message " Release notes extracted."
|
Write-Log -Level "OK" -Message " Release notes extracted."
|
||||||
|
|
||||||
# 16. Resolve repository info for GitHub release
|
# 11. Get repository info
|
||||||
$repo = Resolve-GitHubRepository -RepositorySetting $githubRepositorySetting
|
$remoteUrl = git config --get remote.origin.url
|
||||||
|
if ($LASTEXITCODE -ne 0 -or -not $remoteUrl) {
|
||||||
|
Write-Error "Could not determine git remote origin URL."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($remoteUrl -match "[:/](?<owner>[^/]+)/(?<repo>[^/.]+)(\.git)?$") {
|
||||||
|
$owner = $matches['owner']
|
||||||
|
$repoName = $matches['repo']
|
||||||
|
$repo = "$owner/$repoName"
|
||||||
|
} else {
|
||||||
|
Write-Error "Could not parse GitHub repo from remote URL: $remoteUrl"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
$releaseName = $releaseTitlePattern -replace '\{version\}', $version
|
$releaseName = $releaseTitlePattern -replace '\{version\}', $version
|
||||||
|
|
||||||
@ -596,7 +533,7 @@ Write-Log -Level "INFO" -Message " Repository: $repo"
|
|||||||
Write-Log -Level "INFO" -Message " Tag: $tag"
|
Write-Log -Level "INFO" -Message " Tag: $tag"
|
||||||
Write-Log -Level "INFO" -Message " Title: $releaseName"
|
Write-Log -Level "INFO" -Message " Title: $releaseName"
|
||||||
|
|
||||||
# 17. Check if tag is pushed to remote (skip on dev branch)
|
# 12. Check if tag is pushed to remote (skip on dev branch)
|
||||||
|
|
||||||
if (-not $isDevBranch) {
|
if (-not $isDevBranch) {
|
||||||
|
|
||||||
@ -618,6 +555,10 @@ if (-not $isDevBranch) {
|
|||||||
Write-Log -Level "STEP" -Message " Release branch ($releaseBranch) - will publish to GitHub."
|
Write-Log -Level "STEP" -Message " Release branch ($releaseBranch) - will publish to GitHub."
|
||||||
Assert-Command gh
|
Assert-Command gh
|
||||||
|
|
||||||
|
$ghApiAuthArgs = @(
|
||||||
|
"-H", "Authorization: token $githubToken"
|
||||||
|
)
|
||||||
|
|
||||||
# 6. Check GitHub authentication
|
# 6. Check GitHub authentication
|
||||||
|
|
||||||
Write-Log -Level "INFO" -Message "Checking GitHub authentication..."
|
Write-Log -Level "INFO" -Message "Checking GitHub authentication..."
|
||||||
@ -626,13 +567,8 @@ if (-not $isDevBranch) {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# gh release subcommands do not support custom auth headers.
|
$authArgs = @("api", "user") + $ghApiAuthArgs
|
||||||
# Scope GH_TOKEN to this block so all gh commands authenticate with the configured token.
|
$authTest = & gh @authArgs 2>$null
|
||||||
$previousGhToken = $env:GH_TOKEN
|
|
||||||
$env:GH_TOKEN = $githubToken
|
|
||||||
|
|
||||||
try {
|
|
||||||
$authTest = & gh api user 2>$null
|
|
||||||
|
|
||||||
if ($LASTEXITCODE -ne 0 -or -not $authTest) {
|
if ($LASTEXITCODE -ne 0 -or -not $authTest) {
|
||||||
Write-Error "GitHub CLI authentication failed. GitHub token may be invalid or missing repo scope."
|
Write-Error "GitHub CLI authentication failed. GitHub token may be invalid or missing repo scope."
|
||||||
@ -640,9 +576,15 @@ if (-not $isDevBranch) {
|
|||||||
}
|
}
|
||||||
Write-Log -Level "OK" -Message " GitHub CLI authenticated."
|
Write-Log -Level "OK" -Message " GitHub CLI authenticated."
|
||||||
|
|
||||||
# 18. Create or update GitHub release
|
# 13. Create or update GitHub release
|
||||||
Write-Log -Level "STEP" -Message "Creating GitHub release..."
|
Write-Log -Level "STEP" -Message "Creating GitHub release..."
|
||||||
|
|
||||||
|
# gh release subcommands do not support custom auth headers.
|
||||||
|
# Scope GH_TOKEN to this block so commands authenticate with the configured token.
|
||||||
|
$previousGhToken = $env:GH_TOKEN
|
||||||
|
$env:GH_TOKEN = $githubToken
|
||||||
|
|
||||||
|
try {
|
||||||
# Check if release already exists
|
# Check if release already exists
|
||||||
$releaseViewArgs = @(
|
$releaseViewArgs = @(
|
||||||
"release", "view", $tag,
|
"release", "view", $tag,
|
||||||
@ -665,8 +607,12 @@ if (-not $isDevBranch) {
|
|||||||
$notesFilePath = Join-Path $releaseDir "release-notes-temp.md"
|
$notesFilePath = Join-Path $releaseDir "release-notes-temp.md"
|
||||||
[System.IO.File]::WriteAllText($notesFilePath, $releaseNotes, [System.Text.UTF8Encoding]::new($false))
|
[System.IO.File]::WriteAllText($notesFilePath, $releaseNotes, [System.Text.UTF8Encoding]::new($false))
|
||||||
|
|
||||||
$createReleaseArgs = @(
|
$releaseAssets = @($packageFile.FullName)
|
||||||
"release", "create", $tag, $zipPath
|
if ($symbolsPackageFile) {
|
||||||
|
$releaseAssets += $symbolsPackageFile.FullName
|
||||||
|
}
|
||||||
|
|
||||||
|
$createReleaseArgs = @("release", "create", $tag) + $releaseAssets + @(
|
||||||
"--repo", $repo
|
"--repo", $repo
|
||||||
"--title", $releaseName
|
"--title", $releaseName
|
||||||
"--notes-file", $notesFilePath
|
"--notes-file", $notesFilePath
|
||||||
@ -726,11 +672,6 @@ else {
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Cleanup
|
#region Cleanup
|
||||||
if (Test-Path $stagingDir) {
|
|
||||||
Remove-Item $stagingDir -Recurse -Force
|
|
||||||
Write-Log -Level "INFO" -Message " Cleaned up staging directory."
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Test-Path $testResultsDir) {
|
if (Test-Path $testResultsDir) {
|
||||||
Remove-Item $testResultsDir -Recurse -Force
|
Remove-Item $testResultsDir -Recurse -Force
|
||||||
Write-Log -Level "INFO" -Message " Cleaned up test results directory."
|
Write-Log -Level "INFO" -Message " Cleaned up test results directory."
|
||||||
|
|||||||
@ -25,14 +25,13 @@
|
|||||||
"..\\..\\src\\MaksIT.Results\\MaksIT.Results.csproj"
|
"..\\..\\src\\MaksIT.Results\\MaksIT.Results.csproj"
|
||||||
],
|
],
|
||||||
"testResultsDir": "..\\..\\testResults",
|
"testResultsDir": "..\\..\\testResults",
|
||||||
"stagingDir": "..\\..\\staging",
|
|
||||||
"releaseDir": "..\\..\\release",
|
"releaseDir": "..\\..\\release",
|
||||||
"changelogPath": "..\\..\\CHANGELOG.md",
|
"changelogPath": "..\\..\\CHANGELOG.md",
|
||||||
"testProject": "..\\..\\src\\MaksIT.Results.Tests"
|
"testProject": "..\\..\\src\\MaksIT.Results.Tests"
|
||||||
},
|
},
|
||||||
|
|
||||||
"release": {
|
"release": {
|
||||||
"zipNamePattern": "maksit.results-{version}.zip",
|
"zipNamePattern": "maksit.dapr-{version}.zip",
|
||||||
"releaseTitlePattern": "Release {version}"
|
"releaseTitlePattern": "Release {version}"
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -54,7 +53,6 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"csprojPaths": "List of project files used for version discovery and publish output.",
|
"csprojPaths": "List of project files used for version discovery and publish output.",
|
||||||
"testResultsDir": "Directory where test artifacts are written.",
|
"testResultsDir": "Directory where test artifacts are written.",
|
||||||
"stagingDir": "Temporary staging directory before archive creation.",
|
|
||||||
"releaseDir": "Output directory for release archives and artifacts.",
|
"releaseDir": "Output directory for release archives and artifacts.",
|
||||||
"changelogPath": "Path to CHANGELOG.md used for version and release notes extraction.",
|
"changelogPath": "Path to CHANGELOG.md used for version and release notes extraction.",
|
||||||
"testProject": "Test project path used by TestRunner."
|
"testProject": "Test project path used by TestRunner."
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user