Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions Public/Invoke-DotNetReleaseBuild.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
function Invoke-DotNetReleaseBuild {
<#
.SYNOPSIS
Builds a .NET project in Release configuration and prepares release artefacts.

.DESCRIPTION
Wrapper around the build, pack and signing process typically used for publishing
.NET projects. The function cleans the Release directory, builds the project,
signs DLLs and NuGet packages when a certificate is provided, compresses the
build output and returns details about the generated files.

.PARAMETER ProjectPath
Path to the folder containing the project (*.csproj) file.

.PARAMETER CertificateThumbprint
Optional certificate thumbprint used to sign the built assemblies and NuGet
packages. When omitted no signing is performed.

.PARAMETER LocalStore
Certificate store used when searching for the signing certificate. Defaults
to 'CurrentUser'.

.PARAMETER TimeStampServer
Timestamp server URL used while signing.

.OUTPUTS
PSCustomObject with properties Version, ReleasePath and ZipPath.

.EXAMPLE
Invoke-DotNetReleaseBuild -ProjectPath 'C:\Git\MyProject' -CertificateThumbprint '483292C9E317AA13B07BB7A96AE9D1A5ED9E7703'
Builds and signs the project located in C:\Git\MyProject and returns paths to
the release output.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$ProjectPath,
[Parameter()]
[string]$CertificateThumbprint,
[string]$LocalStore = 'CurrentUser',
[string]$TimeStampServer = 'http://timestamp.digicert.com'
)
$result = [ordered]@{
Success = $false
Version = $null
ReleasePath = $null
ZipPath = $null
Packages = @()
ErrorMessage = $null
}

if (-not (Get-Command dotnet -ErrorAction SilentlyContinue)) {
$result.ErrorMessage = 'dotnet CLI is not available.'
return [PSCustomObject]$result
}
if (-not (Test-Path -LiteralPath $ProjectPath)) {
$result.ErrorMessage = "Project path '$ProjectPath' not found."
return [PSCustomObject]$result
}
$csproj = Get-ChildItem -Path $ProjectPath -Filter '*.csproj' -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $csproj) {
$result.ErrorMessage = "No csproj found in $ProjectPath"
return [PSCustomObject]$result
}
try {
[xml]$xml = Get-Content -LiteralPath $csproj.FullName -Raw -ErrorAction Stop
} catch {
$result.ErrorMessage = "Failed to read '$($csproj.FullName)' as XML: $_"
return [PSCustomObject]$result
}
$version = $xml.Project.PropertyGroup.VersionPrefix
if (-not $version) {
$result.ErrorMessage = "VersionPrefix not found in '$($csproj.FullName)'"
return [PSCustomObject]$result
}
$releasePath = Join-Path -Path $csproj.Directory.FullName -ChildPath 'bin/Release'
if (Test-Path -LiteralPath $releasePath) {
try {
Get-ChildItem -Path $releasePath -Recurse -File | Remove-Item -Force
Get-ChildItem -Path $releasePath -Recurse -Filter '*.nupkg' | Remove-Item -Force
Get-ChildItem -Path $releasePath -Directory | Remove-Item -Force -Recurse
} catch {
$result.ErrorMessage = "Failed to clean $releasePath: $_"
return [PSCustomObject]$result
}
} else {
$null = New-Item -ItemType Directory -Path $releasePath -Force
}

dotnet build $csproj.FullName --configuration Release
if ($LASTEXITCODE -ne 0) {
$result.ErrorMessage = 'dotnet build failed.'
return [PSCustomObject]$result
}
if ($CertificateThumbprint) {
Register-Certificate -Path $releasePath -LocalStore $LocalStore -Include @('*.dll') -TimeStampServer $TimeStampServer -Thumbprint $CertificateThumbprint
}
$zipPath = Join-Path -Path $releasePath -ChildPath ("{0}.{1}.zip" -f $csproj.BaseName, $version)
Compress-Archive -Path (Join-Path $releasePath '*') -DestinationPath $zipPath -Force

dotnet pack $csproj.FullName --configuration Release --no-restore --no-build
if ($LASTEXITCODE -ne 0) {
$result.ErrorMessage = 'dotnet pack failed.'
return [PSCustomObject]$result
}
$nupkgs = Get-ChildItem -Path $releasePath -Recurse -Filter '*.nupkg' -ErrorAction SilentlyContinue
if ($CertificateThumbprint -and $nupkgs) {
foreach ($pkg in $nupkgs) {
dotnet nuget sign $pkg.FullName --certificate-fingerprint $CertificateThumbprint --timestamper $TimeStampServer --overwrite
if ($LASTEXITCODE -ne 0) {
Write-Warning "Invoke-DotNetReleaseBuild - Failed to sign $($pkg.FullName)"
}
}
}
$result.Success = $true
$result.Version = $version
$result.ReleasePath = $releasePath
$result.ZipPath = $zipPath
$result.Packages = $nupkgs.FullName
return [PSCustomObject]$result
}
92 changes: 92 additions & 0 deletions Public/Publish-GitHubReleaseAsset.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
function Publish-GitHubReleaseAsset {
<#
.SYNOPSIS
Publishes a release asset to GitHub.

.DESCRIPTION
Uses `Send-GitHubRelease` to create or update a GitHub release based on the
project version and upload the generated zip archive.

.PARAMETER ProjectPath
Path to the project folder containing the *.csproj file.

.PARAMETER GitHubUsername
GitHub account name owning the repository.

.PARAMETER GitHubRepositoryName
Name of the GitHub repository.

.PARAMETER GitHubAccessToken
Personal access token used for authentication.

.PARAMETER IsPreRelease
Publish the release as a pre-release.

.EXAMPLE
Publish-GitHubReleaseAsset -ProjectPath 'C:\Git\MyProject' -GitHubUsername 'EvotecIT' -GitHubRepositoryName 'MyRepo' -GitHubAccessToken $Token
Uploads the current project zip to the specified GitHub repository.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$ProjectPath,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$GitHubUsername,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$GitHubRepositoryName,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$GitHubAccessToken,
[switch]$IsPreRelease
)
$result = [ordered]@{
Success = $false
TagName = $null
ZipPath = $null
ReleaseUrl = $null
ErrorMessage = $null
}

if (-not (Test-Path -LiteralPath $ProjectPath)) {
$result.ErrorMessage = "Project path '$ProjectPath' not found."
return [PSCustomObject]$result
}
$csproj = Get-ChildItem -Path $ProjectPath -Filter '*.csproj' -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $csproj) {
$result.ErrorMessage = "No csproj found in $ProjectPath"
return [PSCustomObject]$result
}
try {
[xml]$xml = Get-Content -LiteralPath $csproj.FullName -Raw -ErrorAction Stop
} catch {
$result.ErrorMessage = "Failed to read '$($csproj.FullName)' as XML: $_"
return [PSCustomObject]$result
}
$version = $xml.Project.PropertyGroup.VersionPrefix
if (-not $version) {
$result.ErrorMessage = "VersionPrefix not found in '$($csproj.FullName)'"
return [PSCustomObject]$result
}
$zipPath = Join-Path -Path $csproj.Directory.FullName -ChildPath ("bin/Release/{0}.{1}.zip" -f $csproj.BaseName, $version)
if (-not (Test-Path -LiteralPath $zipPath)) {
$result.ErrorMessage = "Zip file '$zipPath' not found."
return [PSCustomObject]$result
}
$tagName = "v$version"
$result.TagName = $tagName
$result.ZipPath = $zipPath
try {
$statusGithub = Send-GitHubRelease -GitHubUsername $GitHubUsername -GitHubRepositoryName $GitHubRepositoryName -GitHubAccessToken $GitHubAccessToken -TagName $tagName -AssetFilePaths $zipPath -IsPreRelease:$IsPreRelease.IsPresent
$result.Success = $statusGithub.Succeeded
$result.ReleaseUrl = $statusGithub.ReleaseUrl
if (-not $statusGithub.Succeeded) {
$result.ErrorMessage = $statusGithub.ErrorMessage
}
} catch {
$result.ErrorMessage = $_.Exception.Message
}
return [PSCustomObject]$result
}
61 changes: 61 additions & 0 deletions Public/Publish-NugetPackage.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
function Publish-NugetPackage {
<#
.SYNOPSIS
Pushes NuGet packages to a feed.

.DESCRIPTION
Finds all *.nupkg files in the specified path and uploads them using
`dotnet nuget push` with the provided API key and feed URL.

.PARAMETER Path
Directory to search for NuGet packages.

.PARAMETER ApiKey
API key used to authenticate against the NuGet feed.

.PARAMETER Source
NuGet feed URL. Defaults to https://api.nuget.org/v3/index.json.

.EXAMPLE
Publish-NugetPackage -Path 'C:\Git\Project\bin\Release' -ApiKey $MyKey
Uploads all packages in the Release folder to NuGet.org.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$Path,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$ApiKey,
[string]$Source = 'https://api.nuget.org/v3/index.json'
)
$result = [ordered]@{
Success = $true
Pushed = @()
Failed = @()
ErrorMessage = $null
}

if (-not (Test-Path -LiteralPath $Path)) {
$result.Success = $false
$result.ErrorMessage = "Path '$Path' not found."
return [PSCustomObject]$result
}
$packages = Get-ChildItem -Path $Path -Recurse -Filter '*.nupkg' -ErrorAction SilentlyContinue
if (-not $packages) {
$result.Success = $false
$result.ErrorMessage = "No packages found in $Path"
return [PSCustomObject]$result
}
foreach ($pkg in $packages) {
dotnet nuget push $pkg.FullName --api-key $ApiKey --source $Source
if ($LASTEXITCODE -eq 0) {
$result.Pushed += $pkg.FullName
} else {
$result.Failed += $pkg.FullName
$result.Success = $false
}
}
return [PSCustomObject]$result
}
Loading