(chore): migrate to .slnx and refine release scripts/docs

This commit is contained in:
Maksym Sadovnychyy 2026-02-27 18:51:03 +01:00
parent 4a4c0ea29e
commit 0b3c482960
7 changed files with 99 additions and 135 deletions

View File

@ -21,7 +21,7 @@ Thank you for your interest in contributing to MaksIT.Core! This document provid
```bash ```bash
cd src cd src
dotnet build MaksIT.Core.sln dotnet build MaksIT.Core.slnx
``` ```
### Running Tests ### Running Tests
@ -246,4 +246,4 @@ If the release partially failed (e.g., NuGet succeeded but GitHub failed):
## License ## License
By contributing, you agree that your contributions will be licensed under the MIT License. By contributing, you agree that your contributions are licensed under the terms in `LICENSE.md`.

View File

@ -1614,7 +1614,10 @@ string completedName = completed.GetDisplayName(); // "Completed"
## Contact ## Contact
For any inquiries or contributions, feel free to reach out: If you have any questions or need further assistance, feel free to reach out:
- **Email**: maksym.sadovnychyy@gmail.com - **Email**: [maksym.sadovnychyy@gmail.com](mailto:maksym.sadovnychyy@gmail.com)
- **Author**: Maksym Sadovnychyy (MAKS-IT)
## License
See `LICENSE.md`.

View File

@ -1,31 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MaksIT.Core", "MaksIT.Core\MaksIT.Core.csproj", "{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaksIT.Core.Tests", "MaksIT.Core.Tests\MaksIT.Core.Tests.csproj", "{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AE39520-D4F7-4C5F-ACE9-9E79AEAF3228}.Release|Any CPU.Build.0 = Release|Any CPU
{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B67A43DA-AFFC-4510-8D51-08F1FF84CC5B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9BCC72D1-8BE8-4924-AF73-C8E86E16EC59}
EndGlobalSection
EndGlobal

4
src/MaksIT.Core.slnx Normal file
View File

@ -0,0 +1,4 @@
<Solution>
<Project Path="MaksIT.Core.Tests/MaksIT.Core.Tests.csproj" />
<Project Path="MaksIT.Core/MaksIT.Core.csproj" />
</Solution>

View File

@ -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
@ -116,8 +139,11 @@ if ($tags.Count -eq 0) {
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

View File

@ -17,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
@ -213,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
@ -248,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(
@ -443,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
@ -536,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)
@ -549,7 +510,7 @@ 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. Get repository info # 11. Get repository info
$remoteUrl = git config --get remote.origin.url $remoteUrl = git config --get remote.origin.url
if ($LASTEXITCODE -ne 0 -or -not $remoteUrl) { if ($LASTEXITCODE -ne 0 -or -not $remoteUrl) {
Write-Error "Could not determine git remote origin URL." Write-Error "Could not determine git remote origin URL."
@ -572,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) {
@ -615,7 +576,7 @@ 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. # gh release subcommands do not support custom auth headers.
@ -646,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
@ -707,15 +672,14 @@ 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."
} }
Get-ChildItem -Path $releaseDir -File |
Where-Object { $_.Name -like "*$version*.nupkg" -or $_.Name -like "*$version*.snupkg" } |
Remove-Item -Force -ErrorAction SilentlyContinue
#endregion #endregion
#region Summary #region Summary

View File

@ -24,7 +24,6 @@
"..\\..\\src\\MaksIT.Core\\MaksIT.Core.csproj" "..\\..\\src\\MaksIT.Core\\MaksIT.Core.csproj"
], ],
"testResultsDir": "..\\..\\testResults", "testResultsDir": "..\\..\\testResults",
"stagingDir": "..\\..\\staging",
"releaseDir": "..\\..\\release", "releaseDir": "..\\..\\release",
"changelogPath": "..\\..\\CHANGELOG.md", "changelogPath": "..\\..\\CHANGELOG.md",
"testProject": "..\\..\\src\\MaksIT.Core.Tests" "testProject": "..\\..\\src\\MaksIT.Core.Tests"
@ -36,7 +35,7 @@
}, },
"release": { "release": {
"zipNamePattern": "maksit.core-{version}.zip", "zipNamePattern": "maksit.dapr-{version}.zip",
"releaseTitlePattern": "Release {version}" "releaseTitlePattern": "Release {version}"
}, },
@ -58,7 +57,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."