From 68919b74acc011b3ed050377b63dc7ab3f890bf0 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 01:40:15 +0100 Subject: [PATCH 01/24] Add ReleaseType input for explicit release control --- action.yml | 5 ++++ scripts/helpers/Publish-PSModule.ps1 | 34 +++++++++------------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/action.yml b/action.yml index 7d0c08c..39a679b 100644 --- a/action.yml +++ b/action.yml @@ -49,6 +49,10 @@ inputs: description: A comma separated list of labels that do not trigger a release. required: false default: NoRelease + ReleaseType: + description: The type of release to create. Values are 'Release' (stable), 'Prerelease', 'Cleanup' (delete old prereleases), or 'None'. When set, this overrides automatic detection. + required: false + default: '' WhatIf: description: If specified, the action will only log the changes it would make, but will not actually create or delete any releases or tags. required: false @@ -87,6 +91,7 @@ runs: PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching: ${{ inputs.AutoPatching }} PSMODULE_PUBLISH_PSMODULE_INPUT_DatePrereleaseFormat: ${{ inputs.DatePrereleaseFormat }} PSMODULE_PUBLISH_PSMODULE_INPUT_IgnoreLabels: ${{ inputs.IgnoreLabels }} + PSMODULE_PUBLISH_PSMODULE_INPUT_ReleaseType: ${{ inputs.ReleaseType }} PSMODULE_PUBLISH_PSMODULE_INPUT_IncrementalPrerelease: ${{ inputs.IncrementalPrerelease }} PSMODULE_PUBLISH_PSMODULE_INPUT_MajorLabels: ${{ inputs.MajorLabels }} PSMODULE_PUBLISH_PSMODULE_INPUT_MinorLabels: ${{ inputs.MinorLabels }} diff --git a/scripts/helpers/Publish-PSModule.ps1 b/scripts/helpers/Publish-PSModule.ps1 index addd0b8..b8f63ee 100644 --- a/scripts/helpers/Publish-PSModule.ps1 +++ b/scripts/helpers/Publish-PSModule.ps1 @@ -45,6 +45,7 @@ $versionPrefix = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_VersionPrefix $whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' $ignoreLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IgnoreLabels -split ',' | ForEach-Object { $_.Trim() } + $releaseType = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_ReleaseType # 'Release', 'Prerelease', 'Cleanup', or 'None' $majorLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_MajorLabels -split ',' | ForEach-Object { $_.Trim() } $minorLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_MinorLabels -split ',' | ForEach-Object { $_.Trim() } $patchLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_PatchLabels -split ',' | ForEach-Object { $_.Trim() } @@ -60,6 +61,7 @@ VersionPrefix = $versionPrefix WhatIf = $whatIf IgnoreLabels = $ignoreLabels + ReleaseType = $releaseType MajorLabels = $majorLabels MinorLabels = $minorLabels PatchLabels = $patchLabels @@ -81,28 +83,11 @@ } Set-GitHubLogGroup 'Event information - Details' { - $defaultBranchName = (gh repo view --json defaultBranchRef | ConvertFrom-Json | Select-Object -ExpandProperty defaultBranchRef).name - $isPullRequest = $githubEvent.PSObject.Properties.Name -contains 'pull_request' - if (-not ($isPullRequest -or $whatIf)) { - Write-Warning '⚠️ A release should not be created in this context. Exiting.' - exit - } - $actionType = $githubEvent.action - $isMerged = $pull_request.merged -eq 'True' - $prIsClosed = $pull_request.state -eq 'closed' - $prBaseRef = $pull_request.base.ref $prHeadRef = $pull_request.head.ref - $targetIsDefaultBranch = $pull_request.base.ref -eq $defaultBranchName Write-Output '-------------------------------------------------' - Write-Output "Default branch: [$defaultBranchName]" - Write-Output "Is a pull request event: [$isPullRequest]" - Write-Output "Action type: [$actionType]" - Write-Output "PR Merged: [$isMerged]" - Write-Output "PR Closed: [$prIsClosed]" - Write-Output "PR Base Ref: [$prBaseRef]" Write-Output "PR Head Ref: [$prHeadRef]" - Write-Output "Target is default branch: [$targetIsDefaultBranch]" + Write-Output "ReleaseType: [$releaseType]" Write-Output '-------------------------------------------------' } @@ -117,14 +102,16 @@ } Set-GitHubLogGroup 'Calculate release type' { - $createRelease = $isMerged -and $targetIsDefaultBranch - $closedPullRequest = $prIsClosed -and -not $isMerged - $createPrerelease = $labels -contains 'prerelease' -and -not $createRelease -and -not $closedPullRequest $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' + # ReleaseType is determined by Get-PSModuleSettings + $createRelease = $releaseType -eq 'Release' + $createPrerelease = $releaseType -eq 'Prerelease' + $closedPullRequest = $releaseType -eq 'Cleanup' + $ignoreRelease = ($labels | Where-Object { $ignoreLabels -contains $_ }).Count -gt 0 if ($ignoreRelease) { - Write-Output 'Ignoring release creation.' + Write-Output 'Ignoring release creation due to ignore label.' return } @@ -135,12 +122,13 @@ ).Count -gt 0 -or $autoPatching) -and -not $majorRelease -and -not $minorRelease Write-Output '-------------------------------------------------' + Write-Output "ReleaseType: [$releaseType]" Write-Output "Create a release: [$createRelease]" Write-Output "Create a prerelease: [$createPrerelease]" Write-Output "Create a major release: [$majorRelease]" Write-Output "Create a minor release: [$minorRelease]" Write-Output "Create a patch release: [$patchRelease]" - Write-Output "Closed pull request: [$closedPullRequest]" + Write-Output "Cleanup prereleases: [$closedPullRequest]" Write-Output '-------------------------------------------------' } From 2f56a038cd172a3fabfce42a0cff8c012db639c1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 01:44:13 +0100 Subject: [PATCH 02/24] Make ReleaseType required and remove autodetection fallback --- action.yml | 5 ++--- scripts/helpers/Publish-PSModule.ps1 | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/action.yml b/action.yml index 39a679b..e5e73b3 100644 --- a/action.yml +++ b/action.yml @@ -50,9 +50,8 @@ inputs: required: false default: NoRelease ReleaseType: - description: The type of release to create. Values are 'Release' (stable), 'Prerelease', 'Cleanup' (delete old prereleases), or 'None'. When set, this overrides automatic detection. - required: false - default: '' + description: The type of release to create. Values are 'Release' (stable), 'Prerelease', 'Cleanup' (delete old prereleases), or 'None'. This input is required and must be provided by the caller. + required: true WhatIf: description: If specified, the action will only log the changes it would make, but will not actually create or delete any releases or tags. required: false diff --git a/scripts/helpers/Publish-PSModule.ps1 b/scripts/helpers/Publish-PSModule.ps1 index b8f63ee..a9d7a8d 100644 --- a/scripts/helpers/Publish-PSModule.ps1 +++ b/scripts/helpers/Publish-PSModule.ps1 @@ -104,11 +104,26 @@ Set-GitHubLogGroup 'Calculate release type' { $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' - # ReleaseType is determined by Get-PSModuleSettings + # Validate ReleaseType - must be provided by Get-PSModuleSettings + $validReleaseTypes = @('Release', 'Prerelease', 'Cleanup', 'None') + if ([string]::IsNullOrWhiteSpace($releaseType)) { + Write-Error "ReleaseType input is required. Valid values are: $($validReleaseTypes -join ', ')" + exit 1 + } + if ($releaseType -notin $validReleaseTypes) { + Write-Error "Invalid ReleaseType: [$releaseType]. Valid values are: $($validReleaseTypes -join ', ')" + exit 1 + } + $createRelease = $releaseType -eq 'Release' $createPrerelease = $releaseType -eq 'Prerelease' $closedPullRequest = $releaseType -eq 'Cleanup' + if ($releaseType -eq 'None') { + Write-Output 'ReleaseType is None. Skipping release creation.' + return + } + $ignoreRelease = ($labels | Where-Object { $ignoreLabels -contains $_ }).Count -gt 0 if ($ignoreRelease) { Write-Output 'Ignoring release creation due to ignore label.' From 9d1cc767d3abcc9ea2aea6b3b5288b917ecb0c72 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 01:55:36 +0100 Subject: [PATCH 03/24] Update for PR --- action.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index e5e73b3..b45b18d 100644 --- a/action.yml +++ b/action.yml @@ -50,8 +50,9 @@ inputs: required: false default: NoRelease ReleaseType: - description: The type of release to create. Values are 'Release' (stable), 'Prerelease', 'Cleanup' (delete old prereleases), or 'None'. This input is required and must be provided by the caller. - required: true + description: The type of release to create. Values are 'Release' (stable), 'Prerelease', 'Cleanup' (delete old prereleases), or 'None'. + required: false + default: Release WhatIf: description: If specified, the action will only log the changes it would make, but will not actually create or delete any releases or tags. required: false From d405e6b530a2d486bbdeeea3c8cd0c2c5d69fc0d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 02:12:22 +0100 Subject: [PATCH 04/24] Add warning for WhatIf mode in Publish-PSModule function --- scripts/helpers/Publish-PSModule.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/helpers/Publish-PSModule.ps1 b/scripts/helpers/Publish-PSModule.ps1 index a9d7a8d..68c7f1e 100644 --- a/scripts/helpers/Publish-PSModule.ps1 +++ b/scripts/helpers/Publish-PSModule.ps1 @@ -53,6 +53,10 @@ $usePRTitleAsReleaseName = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsReleaseName -eq 'true' $usePRTitleAsNotesHeading = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsNotesHeading -eq 'true' + if ($whatIf) { + Write-Host '::warning::WhatIf mode is enabled. No actual releases will be created, no modules will be published, and no tags will be deleted.' + } + [pscustomobject]@{ AutoCleanup = $autoCleanup AutoPatching = $autoPatching From 8f403240058399d0ac1dd80d114aeb488c4e19e7 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 02:22:52 +0100 Subject: [PATCH 05/24] Update scripts/helpers/Publish-PSModule.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/helpers/Publish-PSModule.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/helpers/Publish-PSModule.ps1 b/scripts/helpers/Publish-PSModule.ps1 index 68c7f1e..f02e732 100644 --- a/scripts/helpers/Publish-PSModule.ps1 +++ b/scripts/helpers/Publish-PSModule.ps1 @@ -87,6 +87,9 @@ } Set-GitHubLogGroup 'Event information - Details' { + if (-not $pull_request) { + throw 'GitHub event does not contain pull_request data. This script must be run from a pull_request event.' + } $prHeadRef = $pull_request.head.ref Write-Output '-------------------------------------------------' From 958ddabdf206e7dadd44a5acefa1deb988fc9d8a Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 02:28:00 +0100 Subject: [PATCH 06/24] Validate ReleaseType input to ensure it is provided and valid, updating related variables for clarity --- scripts/helpers/Publish-PSModule.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/helpers/Publish-PSModule.ps1 b/scripts/helpers/Publish-PSModule.ps1 index 68c7f1e..02ec4f2 100644 --- a/scripts/helpers/Publish-PSModule.ps1 +++ b/scripts/helpers/Publish-PSModule.ps1 @@ -108,7 +108,7 @@ Set-GitHubLogGroup 'Calculate release type' { $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' - # Validate ReleaseType - must be provided by Get-PSModuleSettings + # Validate ReleaseType - fail if not provided or invalid to catch configuration errors $validReleaseTypes = @('Release', 'Prerelease', 'Cleanup', 'None') if ([string]::IsNullOrWhiteSpace($releaseType)) { Write-Error "ReleaseType input is required. Valid values are: $($validReleaseTypes -join ', ')" @@ -121,7 +121,7 @@ $createRelease = $releaseType -eq 'Release' $createPrerelease = $releaseType -eq 'Prerelease' - $closedPullRequest = $releaseType -eq 'Cleanup' + $isCleanupMode = $releaseType -eq 'Cleanup' if ($releaseType -eq 'None') { Write-Output 'ReleaseType is None. Skipping release creation.' @@ -147,7 +147,7 @@ Write-Output "Create a major release: [$majorRelease]" Write-Output "Create a minor release: [$minorRelease]" Write-Output "Create a patch release: [$patchRelease]" - Write-Output "Cleanup prereleases: [$closedPullRequest]" + Write-Output "ReleaseType is Cleanup: [$isCleanupMode]" Write-Output '-------------------------------------------------' } @@ -450,7 +450,7 @@ $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String } - if ((($closedPullRequest -or $createRelease) -and $autoCleanup) -or $whatIf) { + if ((($isCleanupMode -or $createRelease) -and $autoCleanup) -or $whatIf) { Set-GitHubLogGroup "Cleanup prereleases for [$prereleaseName]" { foreach ($rel in $prereleasesToCleanup) { $relTagName = $rel.tagName From 6dc9cd1590ff286f6d7e6ec1272865baff1f7222 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 04:03:32 +0100 Subject: [PATCH 07/24] Improve warning message for WhatIf mode in Publish-PSModule function for clarity --- scripts/helpers/Publish-PSModule.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/helpers/Publish-PSModule.ps1 b/scripts/helpers/Publish-PSModule.ps1 index cf5515f..fad5fb4 100644 --- a/scripts/helpers/Publish-PSModule.ps1 +++ b/scripts/helpers/Publish-PSModule.ps1 @@ -54,7 +54,9 @@ $usePRTitleAsNotesHeading = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsNotesHeading -eq 'true' if ($whatIf) { - Write-Host '::warning::WhatIf mode is enabled. No actual releases will be created, no modules will be published, and no tags will be deleted.' + $message = 'WhatIf mode is enabled. No actual releases will be created, ' + + 'no modules will be published, and no tags will be deleted.' + Write-Host "::warning::$message" } [pscustomobject]@{ From f5e1a40ec72ceb186fcb7b23aa7fa5d53354281b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 10:37:03 +0100 Subject: [PATCH 08/24] Rename AutoCleanup to CleanupPrereleases and remove Cleanup from ReleaseType enum --- action.yml | 10 +++++----- scripts/helpers/Publish-PSModule.ps1 | 21 ++++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/action.yml b/action.yml index b45b18d..5df8b08 100644 --- a/action.yml +++ b/action.yml @@ -13,10 +13,10 @@ inputs: APIKey: description: PowerShell Gallery API Key. required: true - AutoCleanup: - description: Control wether to automatically delete the prerelease tags after the stable release is created. + CleanupPrereleases: + description: When enabled, only performs cleanup of old prerelease tags without creating a new release or publishing to PSGallery. required: false - default: 'true' + default: 'false' AutoPatching: description: Control wether to automatically handle patches. If disabled, the action will only create a patch release if the pull request has a 'patch' label. required: false @@ -50,7 +50,7 @@ inputs: required: false default: NoRelease ReleaseType: - description: The type of release to create. Values are 'Release' (stable), 'Prerelease', 'Cleanup' (delete old prereleases), or 'None'. + description: The type of release to create. Values are 'Release' (stable), 'Prerelease', or 'None'. Cleanup is handled separately via CleanupPrereleases. required: false default: Release WhatIf: @@ -87,7 +87,7 @@ runs: PSMODULE_PUBLISH_PSMODULE_INPUT_Name: ${{ inputs.Name }} PSMODULE_PUBLISH_PSMODULE_INPUT_ModulePath: ${{ inputs.ModulePath }} PSMODULE_PUBLISH_PSMODULE_INPUT_APIKey: ${{ inputs.APIKey }} - PSMODULE_PUBLISH_PSMODULE_INPUT_AutoCleanup: ${{ inputs.AutoCleanup }} + PSMODULE_PUBLISH_PSMODULE_INPUT_CleanupPrereleases: ${{ inputs.CleanupPrereleases }} PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching: ${{ inputs.AutoPatching }} PSMODULE_PUBLISH_PSMODULE_INPUT_DatePrereleaseFormat: ${{ inputs.DatePrereleaseFormat }} PSMODULE_PUBLISH_PSMODULE_INPUT_IgnoreLabels: ${{ inputs.IgnoreLabels }} diff --git a/scripts/helpers/Publish-PSModule.ps1 b/scripts/helpers/Publish-PSModule.ps1 index fad5fb4..b9830c6 100644 --- a/scripts/helpers/Publish-PSModule.ps1 +++ b/scripts/helpers/Publish-PSModule.ps1 @@ -38,14 +38,14 @@ ) Set-GitHubLogGroup 'Set configuration' { - $autoCleanup = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_AutoCleanup -eq 'true' + $cleanupPrereleases = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_CleanupPrereleases -eq 'true' $autoPatching = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching -eq 'true' $incrementalPrerelease = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IncrementalPrerelease -eq 'true' $datePrereleaseFormat = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_DatePrereleaseFormat $versionPrefix = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_VersionPrefix $whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' $ignoreLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IgnoreLabels -split ',' | ForEach-Object { $_.Trim() } - $releaseType = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_ReleaseType # 'Release', 'Prerelease', 'Cleanup', or 'None' + $releaseType = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_ReleaseType # 'Release', 'Prerelease', or 'None' $majorLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_MajorLabels -split ',' | ForEach-Object { $_.Trim() } $minorLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_MinorLabels -split ',' | ForEach-Object { $_.Trim() } $patchLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_PatchLabels -split ',' | ForEach-Object { $_.Trim() } @@ -60,7 +60,7 @@ } [pscustomobject]@{ - AutoCleanup = $autoCleanup + CleanupPrereleases = $cleanupPrereleases AutoPatching = $autoPatching IncrementalPrerelease = $incrementalPrerelease DatePrereleaseFormat = $datePrereleaseFormat @@ -114,7 +114,7 @@ $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' # Validate ReleaseType - fail if not provided or invalid to catch configuration errors - $validReleaseTypes = @('Release', 'Prerelease', 'Cleanup', 'None') + $validReleaseTypes = @('Release', 'Prerelease', 'None') if ([string]::IsNullOrWhiteSpace($releaseType)) { Write-Error "ReleaseType input is required. Valid values are: $($validReleaseTypes -join ', ')" exit 1 @@ -126,10 +126,13 @@ $createRelease = $releaseType -eq 'Release' $createPrerelease = $releaseType -eq 'Prerelease' - $isCleanupMode = $releaseType -eq 'Cleanup' - if ($releaseType -eq 'None') { - Write-Output 'ReleaseType is None. Skipping release creation.' + # Note: cleanupPrereleases is pre-calculated by Get-PSModuleSettings based on: + # - The release situation (Release or Abandoned PR) + # - The user's CleanupPrereleases preference + + if ($releaseType -eq 'None' -and -not $cleanupPrereleases) { + Write-Output 'ReleaseType is None and no cleanup needed. Skipping.' return } @@ -147,12 +150,12 @@ Write-Output '-------------------------------------------------' Write-Output "ReleaseType: [$releaseType]" + Write-Output "CleanupPrereleases: [$cleanupPrereleases]" Write-Output "Create a release: [$createRelease]" Write-Output "Create a prerelease: [$createPrerelease]" Write-Output "Create a major release: [$majorRelease]" Write-Output "Create a minor release: [$minorRelease]" Write-Output "Create a patch release: [$patchRelease]" - Write-Output "ReleaseType is Cleanup: [$isCleanupMode]" Write-Output '-------------------------------------------------' } @@ -455,7 +458,7 @@ $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String } - if ((($isCleanupMode -or $createRelease) -and $autoCleanup) -or $whatIf) { + if ($cleanupPrereleases -or $whatIf) { Set-GitHubLogGroup "Cleanup prereleases for [$prereleaseName]" { foreach ($rel in $prereleasesToCleanup) { $relTagName = $rel.tagName From de0ed310a3e4eb718349e832b821695bde348a3c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 12:50:28 +0100 Subject: [PATCH 09/24] Refactor publishing workflow: - Remove main.ps1 and split functionality into init.ps1, publish.ps1, and cleanup.ps1 for better organization. - Implement init.ps1 to handle dependency installation, input loading, and configuration setup. - Create publish.ps1 to manage module publishing to PSGallery and GitHub releases, including versioning and release notes handling. - Add cleanup.ps1 to remove old prereleases based on specified tags. --- action.yml | 33 +- scripts/cleanup.ps1 | 49 +++ scripts/helpers/Publish-PSModule.ps1 | 479 --------------------------- scripts/init.ps1 | 347 +++++++++++++++++++ scripts/main.ps1 | 50 --- scripts/publish.ps1 | 234 +++++++++++++ 6 files changed, 659 insertions(+), 533 deletions(-) create mode 100644 scripts/cleanup.ps1 delete mode 100644 scripts/helpers/Publish-PSModule.ps1 create mode 100644 scripts/init.ps1 delete mode 100644 scripts/main.ps1 create mode 100644 scripts/publish.ps1 diff --git a/action.yml b/action.yml index 5df8b08..9ddd0d5 100644 --- a/action.yml +++ b/action.yml @@ -80,13 +80,12 @@ runs: - name: Install-PSModuleHelpers uses: PSModule/Install-PSModuleHelpers@d60d63e4be477d1ca0c67c6085101fb109bce8f1 # v1.0.6 - - name: Run Publish-PSModule + - name: Initialize Publish Context + id: init shell: pwsh working-directory: ${{ inputs.WorkingDirectory }} env: PSMODULE_PUBLISH_PSMODULE_INPUT_Name: ${{ inputs.Name }} - PSMODULE_PUBLISH_PSMODULE_INPUT_ModulePath: ${{ inputs.ModulePath }} - PSMODULE_PUBLISH_PSMODULE_INPUT_APIKey: ${{ inputs.APIKey }} PSMODULE_PUBLISH_PSMODULE_INPUT_CleanupPrereleases: ${{ inputs.CleanupPrereleases }} PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching: ${{ inputs.AutoPatching }} PSMODULE_PUBLISH_PSMODULE_INPUT_DatePrereleaseFormat: ${{ inputs.DatePrereleaseFormat }} @@ -98,7 +97,33 @@ runs: PSMODULE_PUBLISH_PSMODULE_INPUT_PatchLabels: ${{ inputs.PatchLabels }} PSMODULE_PUBLISH_PSMODULE_INPUT_VersionPrefix: ${{ inputs.VersionPrefix }} PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf: ${{ inputs.WhatIf }} + run: ${{ github.action_path }}/scripts/init.ps1 + + - name: Download module artifact + if: env.PUBLISH_CONTEXT_ShouldPublish == 'true' || inputs.WhatIf == 'true' + uses: actions/download-artifact@v4 + with: + name: module + path: ${{ inputs.ModulePath }} + + - name: Publish Module + if: env.PUBLISH_CONTEXT_ShouldPublish == 'true' || inputs.WhatIf == 'true' + shell: pwsh + working-directory: ${{ inputs.WorkingDirectory }} + env: + PSMODULE_PUBLISH_PSMODULE_INPUT_Name: ${{ inputs.Name }} + PSMODULE_PUBLISH_PSMODULE_INPUT_ModulePath: ${{ inputs.ModulePath }} + PSMODULE_PUBLISH_PSMODULE_INPUT_APIKey: ${{ inputs.APIKey }} + PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf: ${{ inputs.WhatIf }} PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRBodyAsReleaseNotes: ${{ inputs.UsePRBodyAsReleaseNotes }} PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsReleaseName: ${{ inputs.UsePRTitleAsReleaseName }} PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsNotesHeading: ${{ inputs.UsePRTitleAsNotesHeading }} - run: ${{ github.action_path }}/scripts/main.ps1 + run: ${{ github.action_path }}/scripts/publish.ps1 + + - name: Cleanup Prereleases + if: env.PUBLISH_CONTEXT_ShouldCleanup == 'true' || inputs.WhatIf == 'true' + shell: pwsh + working-directory: ${{ inputs.WorkingDirectory }} + env: + PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf: ${{ inputs.WhatIf }} + run: ${{ github.action_path }}/scripts/cleanup.ps1 diff --git a/scripts/cleanup.ps1 b/scripts/cleanup.ps1 new file mode 100644 index 0000000..7dbdc1a --- /dev/null +++ b/scripts/cleanup.ps1 @@ -0,0 +1,49 @@ +[CmdletBinding()] +param() + +#region Load context from environment +$prereleaseName = $env:PUBLISH_CONTEXT_PrereleaseName +$prereleaseTagsToCleanup = $env:PUBLISH_CONTEXT_PrereleaseTagsToCleanup +$whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' + +if ([string]::IsNullOrWhiteSpace($prereleaseName)) { + Write-Error 'PUBLISH_CONTEXT_PrereleaseName is not set. Run main.ps1 first.' + exit 1 +} +#endregion Load context from environment + +#region Cleanup prereleases +Set-GitHubLogGroup "Cleanup prereleases for [$prereleaseName]" { + if ([string]::IsNullOrWhiteSpace($prereleaseTagsToCleanup)) { + Write-Output "No prereleases found to cleanup for [$prereleaseName]." + return + } + + $tagsToDelete = $prereleaseTagsToCleanup -split ',' | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } + + if ($tagsToDelete.Count -eq 0) { + Write-Output "No prereleases found to cleanup for [$prereleaseName]." + return + } + + Write-Output "Found $($tagsToDelete.Count) prereleases to cleanup:" + $tagsToDelete | ForEach-Object { Write-Output " - $_" } + Write-Output '' + + foreach ($tag in $tagsToDelete) { + Write-Output "Deleting prerelease: [$tag]" + if ($whatIf) { + Write-Output "WhatIf: gh release delete $tag --cleanup-tag --yes" + } else { + gh release delete $tag --cleanup-tag --yes + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to delete release [$tag]." + exit $LASTEXITCODE + } + Write-Output "Successfully deleted release [$tag]." + } + } + + Write-Host "::notice::Cleaned up $($tagsToDelete.Count) prerelease(s) for [$prereleaseName]." +} +#endregion Cleanup prereleases diff --git a/scripts/helpers/Publish-PSModule.ps1 b/scripts/helpers/Publish-PSModule.ps1 deleted file mode 100644 index b9830c6..0000000 --- a/scripts/helpers/Publish-PSModule.ps1 +++ /dev/null @@ -1,479 +0,0 @@ -function Publish-PSModule { - <# - .SYNOPSIS - Publishes a module to the PowerShell Gallery and creates a GitHub Release. - - .DESCRIPTION - Publishes a module to the PowerShell Gallery and creates a GitHub Release. - - .EXAMPLE - Publish-PSModule -Name 'PSModule.FX' -APIKey $env:PSGALLERY_API_KEY - #> - [OutputType([void])] - [CmdletBinding()] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute( - 'PSReviewUnusedParameter', '', Scope = 'Function', - Justification = 'LogGroup - Scoping affects the variables line of sight.' - )] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute( - 'PSUseDeclaredVarsMoreThanAssignments', '', - Justification = 'LogGroup - Scoping affects the variables line of sight.' - )] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute( - 'PSAvoidUsingWriteHost', '', - Justification = 'Log outputs to GitHub Actions logs.' - )] - param( - # Name of the module to process. - [Parameter()] - [string] $Name, - - # The path to the module to process. - [Parameter(Mandatory)] - [string] $ModulePath, - - # The API key for the destination repository. - [Parameter(Mandatory)] - [string] $APIKey - ) - - Set-GitHubLogGroup 'Set configuration' { - $cleanupPrereleases = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_CleanupPrereleases -eq 'true' - $autoPatching = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching -eq 'true' - $incrementalPrerelease = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IncrementalPrerelease -eq 'true' - $datePrereleaseFormat = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_DatePrereleaseFormat - $versionPrefix = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_VersionPrefix - $whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' - $ignoreLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IgnoreLabels -split ',' | ForEach-Object { $_.Trim() } - $releaseType = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_ReleaseType # 'Release', 'Prerelease', or 'None' - $majorLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_MajorLabels -split ',' | ForEach-Object { $_.Trim() } - $minorLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_MinorLabels -split ',' | ForEach-Object { $_.Trim() } - $patchLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_PatchLabels -split ',' | ForEach-Object { $_.Trim() } - $usePRBodyAsReleaseNotes = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRBodyAsReleaseNotes -eq 'true' - $usePRTitleAsReleaseName = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsReleaseName -eq 'true' - $usePRTitleAsNotesHeading = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsNotesHeading -eq 'true' - - if ($whatIf) { - $message = 'WhatIf mode is enabled. No actual releases will be created, ' + - 'no modules will be published, and no tags will be deleted.' - Write-Host "::warning::$message" - } - - [pscustomobject]@{ - CleanupPrereleases = $cleanupPrereleases - AutoPatching = $autoPatching - IncrementalPrerelease = $incrementalPrerelease - DatePrereleaseFormat = $datePrereleaseFormat - VersionPrefix = $versionPrefix - WhatIf = $whatIf - IgnoreLabels = $ignoreLabels - ReleaseType = $releaseType - MajorLabels = $majorLabels - MinorLabels = $minorLabels - PatchLabels = $patchLabels - UsePRBodyAsReleaseNotes = $usePRBodyAsReleaseNotes - UsePRTitleAsReleaseName = $usePRTitleAsReleaseName - UsePRTitleAsNotesHeading = $usePRTitleAsNotesHeading - } | Format-List | Out-String - } - - Set-GitHubLogGroup 'Event information - JSON' { - $githubEventJson = Get-Content $env:GITHUB_EVENT_PATH - $githubEventJson | Format-List | Out-String - } - - Set-GitHubLogGroup 'Event information - Object' { - $githubEvent = $githubEventJson | ConvertFrom-Json - $pull_request = $githubEvent.pull_request - $githubEvent | Format-List | Out-String - } - - Set-GitHubLogGroup 'Event information - Details' { - if (-not $pull_request) { - throw 'GitHub event does not contain pull_request data. This script must be run from a pull_request event.' - } - $prHeadRef = $pull_request.head.ref - - Write-Output '-------------------------------------------------' - Write-Output "PR Head Ref: [$prHeadRef]" - Write-Output "ReleaseType: [$releaseType]" - Write-Output '-------------------------------------------------' - } - - Set-GitHubLogGroup 'Pull request - details' { - $pull_request | Format-List | Out-String - } - - Set-GitHubLogGroup 'Pull request - Labels' { - $labels = @() - $labels += $pull_request.labels.name - $labels | Format-List | Out-String - } - - Set-GitHubLogGroup 'Calculate release type' { - $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' - - # Validate ReleaseType - fail if not provided or invalid to catch configuration errors - $validReleaseTypes = @('Release', 'Prerelease', 'None') - if ([string]::IsNullOrWhiteSpace($releaseType)) { - Write-Error "ReleaseType input is required. Valid values are: $($validReleaseTypes -join ', ')" - exit 1 - } - if ($releaseType -notin $validReleaseTypes) { - Write-Error "Invalid ReleaseType: [$releaseType]. Valid values are: $($validReleaseTypes -join ', ')" - exit 1 - } - - $createRelease = $releaseType -eq 'Release' - $createPrerelease = $releaseType -eq 'Prerelease' - - # Note: cleanupPrereleases is pre-calculated by Get-PSModuleSettings based on: - # - The release situation (Release or Abandoned PR) - # - The user's CleanupPrereleases preference - - if ($releaseType -eq 'None' -and -not $cleanupPrereleases) { - Write-Output 'ReleaseType is None and no cleanup needed. Skipping.' - return - } - - $ignoreRelease = ($labels | Where-Object { $ignoreLabels -contains $_ }).Count -gt 0 - if ($ignoreRelease) { - Write-Output 'Ignoring release creation due to ignore label.' - return - } - - $majorRelease = ($labels | Where-Object { $majorLabels -contains $_ }).Count -gt 0 - $minorRelease = ($labels | Where-Object { $minorLabels -contains $_ }).Count -gt 0 -and -not $majorRelease - $patchRelease = ( - ($labels | Where-Object { $patchLabels -contains $_ } - ).Count -gt 0 -or $autoPatching) -and -not $majorRelease -and -not $minorRelease - - Write-Output '-------------------------------------------------' - Write-Output "ReleaseType: [$releaseType]" - Write-Output "CleanupPrereleases: [$cleanupPrereleases]" - Write-Output "Create a release: [$createRelease]" - Write-Output "Create a prerelease: [$createPrerelease]" - Write-Output "Create a major release: [$majorRelease]" - Write-Output "Create a minor release: [$minorRelease]" - Write-Output "Create a patch release: [$patchRelease]" - Write-Output '-------------------------------------------------' - } - - Set-GitHubLogGroup 'Get latest version - GitHub' { - $releases = gh release list --json 'createdAt,isDraft,isLatest,isPrerelease,name,publishedAt,tagName' | ConvertFrom-Json - if ($LASTEXITCODE -ne 0) { - Write-Error 'Failed to list all releases for the repo.' - exit $LASTEXITCODE - } - $releases | Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table | Out-String - - $latestRelease = $releases | Where-Object { $_.isLatest -eq $true } - $latestRelease | Format-List | Out-String - $ghReleaseVersionString = $latestRelease.tagName - if (-not [string]::IsNullOrEmpty($ghReleaseVersionString)) { - $ghReleaseVersion = New-PSSemVer -Version $ghReleaseVersionString - } else { - Write-Warning 'Could not find the latest release version. Using ''0.0.0'' as the version.' - $ghReleaseVersion = New-PSSemVer -Version '0.0.0' - } - Write-Output '-------------------------------------------------' - Write-Output 'GitHub version:' - Write-Output $ghReleaseVersion.ToString() - Write-Output '-------------------------------------------------' - } - - Set-GitHubLogGroup 'Get latest version - PSGallery' { - $count = 5 - $delay = 10 - for ($i = 1; $i -le $count; $i++) { - try { - Write-Output "Finding module [$Name] in the PowerShell Gallery." - $latest = Find-PSResource -Name $Name -Repository PSGallery -Verbose:$false - Write-Output "$($latest | Format-Table | Out-String)" - break - } catch { - if ($i -eq $count) { - Write-Warning "Failed to find the module [$Name] in the PowerShell Gallery." - Write-Warning $_.Exception.Message - } - Start-Sleep -Seconds $delay - } - } - if ($latest.Version) { - $psGalleryVersion = New-PSSemVer -Version ($latest.Version).ToString() - } else { - Write-Warning 'Could not find module online. Using ''0.0.0'' as the version.' - $psGalleryVersion = New-PSSemVer -Version '0.0.0' - } - Write-Output '-------------------------------------------------' - Write-Output 'PSGallery version:' - Write-Output $psGalleryVersion.ToString() - Write-Output '-------------------------------------------------' - } - - Set-GitHubLogGroup 'Get latest version - Manifest' { - Add-PSModulePath -Path (Split-Path -Path $ModulePath -Parent) - $manifestFilePath = Join-Path $ModulePath "$Name.psd1" - Write-Output "Module manifest file path: [$manifestFilePath]" - if (-not (Test-Path -Path $manifestFilePath)) { - Write-Error "Module manifest file not found at [$manifestFilePath]" - return - } - try { - $manifestVersion = New-PSSemVer -Version (Test-ModuleManifest $manifestFilePath -Verbose:$false).Version - } catch { - if ([string]::IsNullOrEmpty($manifestVersion)) { - Write-Warning 'Could not find the module version in the manifest. Using ''0.0.0'' as the version.' - $manifestVersion = New-PSSemVer -Version '0.0.0' - } - } - Write-Output '-------------------------------------------------' - Write-Output 'Manifest version:' - Write-Output $manifestVersion.ToString() - Write-Output '-------------------------------------------------' - } - - Set-GitHubLogGroup 'Get latest version' { - Write-Output "GitHub: [$($ghReleaseVersion.ToString())]" - Write-Output "PSGallery: [$($psGalleryVersion.ToString())]" - Write-Output "Manifest: [$($manifestVersion.ToString())] (ignored)" - $latestVersion = New-PSSemVer -Version ($psGalleryVersion, $ghReleaseVersion | Sort-Object -Descending | Select-Object -First 1) - Write-Output '-------------------------------------------------' - Write-Output 'Latest version:' - Write-Output ($latestVersion | Format-Table | Out-String) - Write-Output $latestVersion.ToString() - Write-Output '-------------------------------------------------' - } - - Set-GitHubLogGroup 'Calculate new version' { - # - Increment based on label on PR - $newVersion = New-PSSemVer -Version $latestVersion - $newVersion.Prefix = $versionPrefix - if ($majorRelease) { - Write-Output 'Incrementing major version.' - $newVersion.BumpMajor() - } elseif ($minorRelease) { - Write-Output 'Incrementing minor version.' - $newVersion.BumpMinor() - } elseif ($patchRelease) { - Write-Output 'Incrementing patch version.' - $newVersion.BumpPatch() - } else { - Write-Output 'Skipping release creation, exiting.' - return - } - - Write-Output "Partial new version: [$newVersion]" - - if ($createPrerelease) { - Write-Output "Adding a prerelease tag to the version using the branch name [$prereleaseName]." - Write-Output ($releases | Where-Object { $_.tagName -like "*$prereleaseName*" } | - Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table -AutoSize | Out-String) - - $newVersion.Prerelease = $prereleaseName - Write-Output "Partial new version: [$newVersion]" - - if (-not [string]::IsNullOrEmpty($datePrereleaseFormat)) { - Write-Output "Using date-based prerelease: [$datePrereleaseFormat]." - $newVersion.Prerelease += "$(Get-Date -Format $datePrereleaseFormat)" - Write-Output "Partial new version: [$newVersion]" - } - - if ($incrementalPrerelease) { - # Find the latest prerelease version - $newVersionString = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" - - # PowerShell Gallery - $params = @{ - Name = $Name - Version = '*' - Prerelease = $true - Repository = 'PSGallery' - Verbose = $false - ErrorAction = 'SilentlyContinue' - } - Write-Output 'Finding the latest prerelease version in the PowerShell Gallery.' - Write-Output ($params | Format-Table | Out-String) - $psGalleryPrereleases = Find-PSResource @params - $psGalleryPrereleases = $psGalleryPrereleases | Where-Object { $_.Version -like "$newVersionString" } - $psGalleryPrereleases = $psGalleryPrereleases | Where-Object { $_.Prerelease -like "$prereleaseName*" } - $latestPSGalleryPrerelease = $psGalleryPrereleases.Prerelease | ForEach-Object { - [int]($_ -replace $prereleaseName) - } | Sort-Object | Select-Object -Last 1 - Write-Output "PSGallery prerelease: [$latestPSGalleryPrerelease]" - - # GitHub - $ghPrereleases = $releases | Where-Object { $_.tagName -like "*$newVersionString*" } - $ghPrereleases = $ghPrereleases | Where-Object { $_.tagName -like "*$prereleaseName*" } - $latestGHPrereleases = $ghPrereleases.tagName | ForEach-Object { - $number = $_ - $number = $number -replace '\.' - $number = ($number -split $prereleaseName, 2)[-1] - [int]$number - } | Sort-Object | Select-Object -Last 1 - Write-Output "GitHub prerelease: [$latestGHPrereleases]" - - $latestPrereleaseNumber = [Math]::Max($latestPSGalleryPrerelease, $latestGHPrereleases) - $latestPrereleaseNumber++ - $latestPrereleaseNumber = ([string]$latestPrereleaseNumber).PadLeft(3, '0') - $newVersion.Prerelease += $latestPrereleaseNumber - } - } - Write-Output '-------------------------------------------------' - Write-Output 'New version:' - Write-Output ($newVersion | Format-Table | Out-String) - Write-Output $newVersion.ToString() - Write-Output '-------------------------------------------------' - } - Write-Output "New version is [$($newVersion.ToString())]" - - Set-GitHubLogGroup 'Update module manifest' { - Write-Output 'Bump module version -> module metadata: Update-ModuleMetadata' - $manifestNewVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" - Set-ModuleManifest -Path $manifestFilePath -ModuleVersion $manifestNewVersion -Verbose:$false - if ($createPrerelease) { - Write-Output "Prerelease is: [$($newVersion.Prerelease)]" - Set-ModuleManifest -Path $manifestFilePath -Prerelease $($newVersion.Prerelease) -Verbose:$false - } - - Show-FileContent -Path $manifestFilePath - } - - Set-GitHubLogGroup 'Install module dependencies' { - Resolve-PSModuleDependency -ManifestFilePath $manifestFilePath - } - - if ($createPrerelease -or $createRelease -or $whatIf) { - Set-GitHubLogGroup 'Publish-ToPSGallery' { - if ($createPrerelease) { - $publishPSVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)-$($newVersion.Prerelease)" - } else { - $publishPSVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" - } - $psGalleryReleaseLink = "https://www.powershellgallery.com/packages/$Name/$publishPSVersion" - Write-Output "Publish module to PowerShell Gallery using [$APIKey]" - if ($whatIf) { - Write-Output "Publish-PSResource -Path $ModulePath -Repository PSGallery -ApiKey $APIKey" - } else { - try { - Publish-PSResource -Path $ModulePath -Repository PSGallery -ApiKey $APIKey - } catch { - Write-Error $_.Exception.Message - exit $LASTEXITCODE - } - } - if ($whatIf) { - Write-Output ( - "gh pr comment $($pull_request.number) -b 'Published to the" + - " PowerShell Gallery [$publishPSVersion]($psGalleryReleaseLink) has been created.'" - ) - } else { - Write-Host "::notice::Module [$Name - $publishPSVersion] published to the PowerShell Gallery." - gh pr comment $pull_request.number -b "Module [$Name - $publishPSVersion]($psGalleryReleaseLink) published to the PowerShell Gallery." - if ($LASTEXITCODE -ne 0) { - Write-Error 'Failed to comment on the pull request.' - exit $LASTEXITCODE - } - } - } - - Set-GitHubLogGroup 'New-GitHubRelease' { - Write-Output 'Create new GitHub release' - $releaseCreateCommand = @('release', 'create', $newVersion.ToString()) - $notesFilePath = $null - - # Add title parameter - if ($usePRTitleAsReleaseName -and $pull_request.title) { - $prTitle = $pull_request.title - $releaseCreateCommand += @('--title', $prTitle) - Write-Output "Using PR title as release name: [$prTitle]" - } else { - $releaseCreateCommand += @('--title', $newVersion.ToString()) - } - - # Build release notes content. Uses temp file to avoid escaping issues with special characters. - # Precedence rules for the three UsePR* parameters: - # 1. UsePRTitleAsNotesHeading + UsePRBodyAsReleaseNotes: Creates "# Title (#PR)\n\nBody" format. - # Requires both parameters enabled AND both PR title and body to be present. - # 2. UsePRBodyAsReleaseNotes only: Uses PR body as-is for release notes. - # Takes effect when heading option is disabled/missing title, but body is available. - # 3. Fallback: Auto-generates notes via GitHub's --generate-notes when no PR content is used. - if ($usePRTitleAsNotesHeading -and $usePRBodyAsReleaseNotes -and $pull_request.title -and $pull_request.body) { - # Path 1: Full PR-based notes with title as H1 heading and PR number link - $prTitle = $pull_request.title - $prNumber = $pull_request.number - $prBody = $pull_request.body - $notes = "# $prTitle (#$prNumber)`n`n$prBody" - $notesFilePath = [System.IO.Path]::GetTempFileName() - Set-Content -Path $notesFilePath -Value $notes -Encoding utf8 - $releaseCreateCommand += @('--notes-file', $notesFilePath) - Write-Output 'Using PR title as H1 heading with link and body as release notes' - } elseif ($usePRBodyAsReleaseNotes -and $pull_request.body) { - # Path 2: PR body only - no heading, just the body content - $prBody = $pull_request.body - $notesFilePath = [System.IO.Path]::GetTempFileName() - Set-Content -Path $notesFilePath -Value $prBody -Encoding utf8 - $releaseCreateCommand += @('--notes-file', $notesFilePath) - Write-Output 'Using PR body as release notes' - } else { - # Path 3: Fallback to GitHub's auto-generated release notes - $releaseCreateCommand += @('--generate-notes') - } - - # Add remaining parameters - if ($createPrerelease) { - $releaseCreateCommand += @('--target', $prHeadRef, '--prerelease') - } - - if ($whatIf) { - Write-Output "WhatIf: gh $($releaseCreateCommand -join ' ')" - } else { - $releaseURL = gh @releaseCreateCommand - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to create the release [$newVersion]." - exit $LASTEXITCODE - } - } - - # Clean up temporary notes file if created - if ($notesFilePath -and (Test-Path -Path $notesFilePath)) { - Remove-Item -Path $notesFilePath -Force - } - - if ($whatIf) { - Write-Output 'WhatIf: gh pr comment $pull_request.number -b "The release [$newVersion] has been created."' - } else { - gh pr comment $pull_request.number -b "GitHub release for $Name [$newVersion]($releaseURL) has been created." - if ($LASTEXITCODE -ne 0) { - Write-Error 'Failed to comment on the pull request.' - exit $LASTEXITCODE - } - } - Write-Host "::notice::Release created: [$newVersion]" - } - } - - Set-GitHubLogGroup 'List prereleases using the same name' { - $prereleasesToCleanup = $releases | Where-Object { $_.tagName -like "*$prereleaseName*" } - $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String - } - - if ($cleanupPrereleases -or $whatIf) { - Set-GitHubLogGroup "Cleanup prereleases for [$prereleaseName]" { - foreach ($rel in $prereleasesToCleanup) { - $relTagName = $rel.tagName - Write-Output "Deleting prerelease: [$relTagName]." - if ($whatIf) { - Write-Output "WhatIf: gh release delete $($rel.tagName) --cleanup-tag --yes" - } else { - gh release delete $rel.tagName --cleanup-tag --yes - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to delete release [$relTagName]." - exit $LASTEXITCODE - } - } - } - } - } - -} diff --git a/scripts/init.ps1 b/scripts/init.ps1 new file mode 100644 index 0000000..8fddc29 --- /dev/null +++ b/scripts/init.ps1 @@ -0,0 +1,347 @@ +[CmdletBinding()] +param() + +#region Install dependencies +$retryCount = 5 +$retryDelay = 10 +for ($i = 0; $i -lt $retryCount; $i++) { + try { + Install-PSResource -Name 'PSSemVer' -TrustRepository -Repository PSGallery + break + } catch { + Write-Warning "Installation of PSSemVer failed with error: $_" + if ($i -eq $retryCount - 1) { + throw + } + Write-Warning "Retrying in $retryDelay seconds..." + Start-Sleep -Seconds $retryDelay + } +} +#endregion Install dependencies + +#region Load inputs +$env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' + +$name = if ([string]::IsNullOrEmpty($env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name)) { + $env:GITHUB_REPOSITORY_NAME +} else { + $env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name +} +Write-Output "Module name: [$name]" +#endregion Load inputs + +#region Set configuration +Set-GitHubLogGroup 'Set configuration' { + $cleanupPrereleases = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_CleanupPrereleases -eq 'true' + $autoPatching = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching -eq 'true' + $incrementalPrerelease = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IncrementalPrerelease -eq 'true' + $datePrereleaseFormat = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_DatePrereleaseFormat + $versionPrefix = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_VersionPrefix + $whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' + $ignoreLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IgnoreLabels -split ',' | ForEach-Object { $_.Trim() } + $releaseType = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_ReleaseType # 'Release', 'Prerelease', or 'None' + $majorLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_MajorLabels -split ',' | ForEach-Object { $_.Trim() } + $minorLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_MinorLabels -split ',' | ForEach-Object { $_.Trim() } + $patchLabels = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_PatchLabels -split ',' | ForEach-Object { $_.Trim() } + + if ($whatIf) { + $message = 'WhatIf mode is enabled. No actual releases will be created, ' + + 'no modules will be published, and no tags will be deleted.' + Write-Host "::warning::$message" + } + + [pscustomobject]@{ + CleanupPrereleases = $cleanupPrereleases + AutoPatching = $autoPatching + IncrementalPrerelease = $incrementalPrerelease + DatePrereleaseFormat = $datePrereleaseFormat + VersionPrefix = $versionPrefix + WhatIf = $whatIf + IgnoreLabels = $ignoreLabels + ReleaseType = $releaseType + MajorLabels = $majorLabels + MinorLabels = $minorLabels + PatchLabels = $patchLabels + } | Format-List | Out-String +} +#endregion Set configuration + +#region Event information +Set-GitHubLogGroup 'Event information - JSON' { + $githubEventJson = Get-Content $env:GITHUB_EVENT_PATH + $githubEventJson | Format-List | Out-String +} + +Set-GitHubLogGroup 'Event information - Object' { + $githubEvent = $githubEventJson | ConvertFrom-Json + $pull_request = $githubEvent.pull_request + $githubEvent | Format-List | Out-String +} + +Set-GitHubLogGroup 'Event information - Details' { + if (-not $pull_request) { + throw 'GitHub event does not contain pull_request data. This script must be run from a pull_request event.' + } + $prHeadRef = $pull_request.head.ref + + Write-Output '-------------------------------------------------' + Write-Output "PR Head Ref: [$prHeadRef]" + Write-Output "ReleaseType: [$releaseType]" + Write-Output '-------------------------------------------------' +} + +Set-GitHubLogGroup 'Pull request - details' { + $pull_request | Format-List | Out-String +} + +Set-GitHubLogGroup 'Pull request - Labels' { + $labels = @() + $labels += $pull_request.labels.name + $labels | Format-List | Out-String +} +#endregion Event information + +#region Calculate release type +Set-GitHubLogGroup 'Calculate release type' { + $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' + + # Validate ReleaseType - fail if not provided or invalid to catch configuration errors + $validReleaseTypes = @('Release', 'Prerelease', 'None') + if ([string]::IsNullOrWhiteSpace($releaseType)) { + Write-Error "ReleaseType input is required. Valid values are: $($validReleaseTypes -join ', ')" + exit 1 + } + if ($releaseType -notin $validReleaseTypes) { + Write-Error "Invalid ReleaseType: [$releaseType]. Valid values are: $($validReleaseTypes -join ', ')" + exit 1 + } + + $createRelease = $releaseType -eq 'Release' + $createPrerelease = $releaseType -eq 'Prerelease' + $shouldPublish = $createRelease -or $createPrerelease + + $ignoreRelease = ($labels | Where-Object { $ignoreLabels -contains $_ }).Count -gt 0 + if ($ignoreRelease) { + Write-Output 'Ignoring release creation due to ignore label.' + $shouldPublish = $false + } + + $majorRelease = ($labels | Where-Object { $majorLabels -contains $_ }).Count -gt 0 + $minorRelease = ($labels | Where-Object { $minorLabels -contains $_ }).Count -gt 0 -and -not $majorRelease + $patchRelease = ( + ($labels | Where-Object { $patchLabels -contains $_ } + ).Count -gt 0 -or $autoPatching) -and -not $majorRelease -and -not $minorRelease + + # Check if any version bump applies + $hasVersionBump = $majorRelease -or $minorRelease -or $patchRelease + if (-not $hasVersionBump) { + Write-Output 'No version bump label found and AutoPatching is disabled. Skipping publish.' + $shouldPublish = $false + } + + Write-Output '-------------------------------------------------' + Write-Output "ReleaseType: [$releaseType]" + Write-Output "CleanupPrereleases: [$cleanupPrereleases]" + Write-Output "Should publish: [$shouldPublish]" + Write-Output "Create a release: [$createRelease]" + Write-Output "Create a prerelease: [$createPrerelease]" + Write-Output "Create a major release: [$majorRelease]" + Write-Output "Create a minor release: [$minorRelease]" + Write-Output "Create a patch release: [$patchRelease]" + Write-Output '-------------------------------------------------' +} +#endregion Calculate release type + +#region Get releases and versions +Set-GitHubLogGroup 'Get all releases - GitHub' { + $releases = gh release list --json 'createdAt,isDraft,isLatest,isPrerelease,name,publishedAt,tagName' | ConvertFrom-Json + if ($LASTEXITCODE -ne 0) { + Write-Error 'Failed to list all releases for the repo.' + exit $LASTEXITCODE + } + $releases | Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table | Out-String +} + +Set-GitHubLogGroup 'Get latest version - GitHub' { + $latestRelease = $releases | Where-Object { $_.isLatest -eq $true } + $latestRelease | Format-List | Out-String + $ghReleaseVersionString = $latestRelease.tagName + if (-not [string]::IsNullOrEmpty($ghReleaseVersionString)) { + $ghReleaseVersion = New-PSSemVer -Version $ghReleaseVersionString + } else { + Write-Warning 'Could not find the latest release version. Using ''0.0.0'' as the version.' + $ghReleaseVersion = New-PSSemVer -Version '0.0.0' + } + Write-Output '-------------------------------------------------' + Write-Output 'GitHub version:' + Write-Output $ghReleaseVersion.ToString() + Write-Output '-------------------------------------------------' +} + +Set-GitHubLogGroup 'Get latest version - PSGallery' { + $count = 5 + $delay = 10 + for ($i = 1; $i -le $count; $i++) { + try { + Write-Output "Finding module [$name] in the PowerShell Gallery." + $latest = Find-PSResource -Name $name -Repository PSGallery -Verbose:$false + Write-Output "$($latest | Format-Table | Out-String)" + break + } catch { + if ($i -eq $count) { + Write-Warning "Failed to find the module [$name] in the PowerShell Gallery." + Write-Warning $_.Exception.Message + } + Start-Sleep -Seconds $delay + } + } + if ($latest.Version) { + $psGalleryVersion = New-PSSemVer -Version ($latest.Version).ToString() + } else { + Write-Warning 'Could not find module online. Using ''0.0.0'' as the version.' + $psGalleryVersion = New-PSSemVer -Version '0.0.0' + } + Write-Output '-------------------------------------------------' + Write-Output 'PSGallery version:' + Write-Output $psGalleryVersion.ToString() + Write-Output '-------------------------------------------------' +} + +Set-GitHubLogGroup 'Get latest version' { + Write-Output "GitHub: [$($ghReleaseVersion.ToString())]" + Write-Output "PSGallery: [$($psGalleryVersion.ToString())]" + $latestVersion = New-PSSemVer -Version ($psGalleryVersion, $ghReleaseVersion | Sort-Object -Descending | Select-Object -First 1) + Write-Output '-------------------------------------------------' + Write-Output 'Latest version:' + Write-Output ($latestVersion | Format-Table | Out-String) + Write-Output $latestVersion.ToString() + Write-Output '-------------------------------------------------' +} +#endregion Get releases and versions + +#region Calculate new version +Set-GitHubLogGroup 'Calculate new version' { + # - Increment based on label on PR + $newVersion = New-PSSemVer -Version $latestVersion + $newVersion.Prefix = $versionPrefix + if ($majorRelease) { + Write-Output 'Incrementing major version.' + $newVersion.BumpMajor() + } elseif ($minorRelease) { + Write-Output 'Incrementing minor version.' + $newVersion.BumpMinor() + } elseif ($patchRelease) { + Write-Output 'Incrementing patch version.' + $newVersion.BumpPatch() + } else { + Write-Output 'No version bump required.' + } + + Write-Output "Partial new version: [$newVersion]" + + if ($createPrerelease -and $hasVersionBump) { + Write-Output "Adding a prerelease tag to the version using the branch name [$prereleaseName]." + Write-Output ($releases | Where-Object { $_.tagName -like "*$prereleaseName*" } | + Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table -AutoSize | Out-String) + + $newVersion.Prerelease = $prereleaseName + Write-Output "Partial new version: [$newVersion]" + + if (-not [string]::IsNullOrEmpty($datePrereleaseFormat)) { + Write-Output "Using date-based prerelease: [$datePrereleaseFormat]." + $newVersion.Prerelease += "$(Get-Date -Format $datePrereleaseFormat)" + Write-Output "Partial new version: [$newVersion]" + } + + if ($incrementalPrerelease) { + # Find the latest prerelease version + $newVersionString = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" + + # PowerShell Gallery + $params = @{ + Name = $name + Version = '*' + Prerelease = $true + Repository = 'PSGallery' + Verbose = $false + ErrorAction = 'SilentlyContinue' + } + Write-Output 'Finding the latest prerelease version in the PowerShell Gallery.' + Write-Output ($params | Format-Table | Out-String) + $psGalleryPrereleases = Find-PSResource @params + $psGalleryPrereleases = $psGalleryPrereleases | Where-Object { $_.Version -like "$newVersionString" } + $psGalleryPrereleases = $psGalleryPrereleases | Where-Object { $_.Prerelease -like "$prereleaseName*" } + $latestPSGalleryPrerelease = $psGalleryPrereleases.Prerelease | ForEach-Object { + [int]($_ -replace $prereleaseName) + } | Sort-Object | Select-Object -Last 1 + Write-Output "PSGallery prerelease: [$latestPSGalleryPrerelease]" + + # GitHub + $ghPrereleases = $releases | Where-Object { $_.tagName -like "*$newVersionString*" } + $ghPrereleases = $ghPrereleases | Where-Object { $_.tagName -like "*$prereleaseName*" } + $latestGHPrereleases = $ghPrereleases.tagName | ForEach-Object { + $number = $_ + $number = $number -replace '\.' + $number = ($number -split $prereleaseName, 2)[-1] + [int]$number + } | Sort-Object | Select-Object -Last 1 + Write-Output "GitHub prerelease: [$latestGHPrereleases]" + + $latestPrereleaseNumber = [Math]::Max($latestPSGalleryPrerelease, $latestGHPrereleases) + $latestPrereleaseNumber++ + $latestPrereleaseNumber = ([string]$latestPrereleaseNumber).PadLeft(3, '0') + $newVersion.Prerelease += $latestPrereleaseNumber + } + } + Write-Output '-------------------------------------------------' + Write-Output 'New version:' + Write-Output ($newVersion | Format-Table | Out-String) + Write-Output $newVersion.ToString() + Write-Output '-------------------------------------------------' +} +#endregion Calculate new version + +#region Find prereleases to cleanup +Set-GitHubLogGroup 'Find prereleases to cleanup' { + $prereleasesToCleanup = $releases | Where-Object { $_.tagName -like "*$prereleaseName*" } + $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String + $prereleaseTagsToCleanup = ($prereleasesToCleanup | ForEach-Object { $_.tagName }) -join ',' + Write-Output "Prereleases to cleanup: [$prereleaseTagsToCleanup]" +} +#endregion Find prereleases to cleanup + +#region Store context in environment variables +Set-GitHubLogGroup 'Store context in environment variables' { + # Store values for subsequent steps + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_ShouldPublish' -Value $shouldPublish.ToString().ToLower() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_ShouldCleanup' -Value $cleanupPrereleases.ToString().ToLower() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_CreateRelease' -Value $createRelease.ToString().ToLower() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_CreatePrerelease' -Value $createPrerelease.ToString().ToLower() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_MajorRelease' -Value $majorRelease.ToString().ToLower() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_MinorRelease' -Value $minorRelease.ToString().ToLower() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PatchRelease' -Value $patchRelease.ToString().ToLower() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_NewVersion' -Value $newVersion.ToString() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PrereleaseName' -Value $prereleaseName + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PrereleaseTagsToCleanup' -Value $prereleaseTagsToCleanup + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PRNumber' -Value $pull_request.number.ToString() + Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PRHeadRef' -Value $prHeadRef + + Write-Output '-------------------------------------------------' + Write-Output 'Stored environment variables:' + Write-Output " PUBLISH_CONTEXT_ShouldPublish: [$shouldPublish]" + Write-Output " PUBLISH_CONTEXT_ShouldCleanup: [$cleanupPrereleases]" + Write-Output " PUBLISH_CONTEXT_CreateRelease: [$createRelease]" + Write-Output " PUBLISH_CONTEXT_CreatePrerelease: [$createPrerelease]" + Write-Output " PUBLISH_CONTEXT_MajorRelease: [$majorRelease]" + Write-Output " PUBLISH_CONTEXT_MinorRelease: [$minorRelease]" + Write-Output " PUBLISH_CONTEXT_PatchRelease: [$patchRelease]" + Write-Output " PUBLISH_CONTEXT_NewVersion: [$($newVersion.ToString())]" + Write-Output " PUBLISH_CONTEXT_PrereleaseName: [$prereleaseName]" + Write-Output " PUBLISH_CONTEXT_PrereleaseTagsToCleanup: [$prereleaseTagsToCleanup]" + Write-Output " PUBLISH_CONTEXT_PRNumber: [$($pull_request.number)]" + Write-Output " PUBLISH_CONTEXT_PRHeadRef: [$prHeadRef]" + Write-Output '-------------------------------------------------' +} +#endregion Store context in environment variables + +Write-Output "Context initialization complete. ShouldPublish=[$shouldPublish], ShouldCleanup=[$cleanupPrereleases]" diff --git a/scripts/main.ps1 b/scripts/main.ps1 deleted file mode 100644 index a1d014d..0000000 --- a/scripts/main.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -[CmdletBinding()] -param() - -$retryCount = 5 -$retryDelay = 10 -for ($i = 0; $i -lt $retryCount; $i++) { - try { - Install-PSResource -Name 'PSSemVer' -TrustRepository -Repository PSGallery - break - } catch { - Write-Warning "Installation of $($psResourceParams.Name) failed with error: $_" - if ($i -eq $retryCount - 1) { - throw - } - Write-Warning "Retrying in $retryDelay seconds..." - Start-Sleep -Seconds $retryDelay - } -} - -$path = (Join-Path -Path $PSScriptRoot -ChildPath 'helpers') -Set-GitHubLogGroup "Loading helper scripts from [$path]" { - Get-ChildItem -Path $path -Filter '*.ps1' -Recurse | ForEach-Object { - Write-Verbose "[$($_.FullName)]" - . $_.FullName - } -} - -$env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' - -Set-GitHubLogGroup 'Loading inputs' { - $name = if ([string]::IsNullOrEmpty($env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name)) { - $env:GITHUB_REPOSITORY_NAME - } else { - $env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name - } - $modulePath = Resolve-Path -Path "$env:PSMODULE_PUBLISH_PSMODULE_INPUT_ModulePath/$name" | Select-Object -ExpandProperty Path - [pscustomobject]@{ - Name = $name - ModulePath = $modulePath - APIKey = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_APIKey - } | Format-List | Out-String - -} - -$params = @{ - Name = $name - ModulePath = $modulePath - APIKey = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_APIKey -} -Publish-PSModule @params diff --git a/scripts/publish.ps1 b/scripts/publish.ps1 new file mode 100644 index 0000000..5ca2c03 --- /dev/null +++ b/scripts/publish.ps1 @@ -0,0 +1,234 @@ +[CmdletBinding()] +param() + +#region Install dependencies +$retryCount = 5 +$retryDelay = 10 +for ($i = 0; $i -lt $retryCount; $i++) { + try { + Install-PSResource -Name 'PSSemVer' -TrustRepository -Repository PSGallery + break + } catch { + Write-Warning "Installation of PSSemVer failed with error: $_" + if ($i -eq $retryCount - 1) { + throw + } + Write-Warning "Retrying in $retryDelay seconds..." + Start-Sleep -Seconds $retryDelay + } +} +#endregion Install dependencies + +#region Load inputs +$env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' + +$name = if ([string]::IsNullOrEmpty($env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name)) { + $env:GITHUB_REPOSITORY_NAME +} else { + $env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name +} +$modulePath = Resolve-Path -Path "$env:PSMODULE_PUBLISH_PSMODULE_INPUT_ModulePath/$name" | Select-Object -ExpandProperty Path +$apiKey = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_APIKey + +Write-Output "Module name: [$name]" +Write-Output "Module path: [$modulePath]" +#endregion Load inputs + +#region Load publish context from environment +Set-GitHubLogGroup 'Load publish context from environment' { + $createRelease = $env:PUBLISH_CONTEXT_CreateRelease -eq 'true' + $createPrerelease = $env:PUBLISH_CONTEXT_CreatePrerelease -eq 'true' + $newVersionString = $env:PUBLISH_CONTEXT_NewVersion + $prNumber = $env:PUBLISH_CONTEXT_PRNumber + $prHeadRef = $env:PUBLISH_CONTEXT_PRHeadRef + $whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' + $usePRBodyAsReleaseNotes = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRBodyAsReleaseNotes -eq 'true' + $usePRTitleAsReleaseName = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsReleaseName -eq 'true' + $usePRTitleAsNotesHeading = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsNotesHeading -eq 'true' + + if ([string]::IsNullOrWhiteSpace($newVersionString)) { + Write-Error 'PUBLISH_CONTEXT_NewVersion is not set. Run main.ps1 first.' + exit 1 + } + + $newVersion = New-PSSemVer -Version $newVersionString + + Write-Output '-------------------------------------------------' + Write-Output 'Publish context:' + Write-Output " CreateRelease: [$createRelease]" + Write-Output " CreatePrerelease: [$createPrerelease]" + Write-Output " NewVersion: [$($newVersion.ToString())]" + Write-Output " PRNumber: [$prNumber]" + Write-Output " PRHeadRef: [$prHeadRef]" + Write-Output " WhatIf: [$whatIf]" + Write-Output '-------------------------------------------------' +} +#endregion Load publish context from environment + +#region Load PR information +Set-GitHubLogGroup 'Load PR information' { + $githubEventJson = Get-Content $env:GITHUB_EVENT_PATH + $githubEvent = $githubEventJson | ConvertFrom-Json + $pull_request = $githubEvent.pull_request + if (-not $pull_request) { + throw 'GitHub event does not contain pull_request data. This script must be run from a pull_request event.' + } +} +#endregion Load PR information + +#region Get manifest version +Set-GitHubLogGroup 'Get latest version - Manifest' { + Add-PSModulePath -Path (Split-Path -Path $modulePath -Parent) + $manifestFilePath = Join-Path $modulePath "$name.psd1" + Write-Output "Module manifest file path: [$manifestFilePath]" + if (-not (Test-Path -Path $manifestFilePath)) { + Write-Error "Module manifest file not found at [$manifestFilePath]" + exit 1 + } + try { + $manifestVersion = New-PSSemVer -Version (Test-ModuleManifest $manifestFilePath -Verbose:$false).Version + } catch { + if ([string]::IsNullOrEmpty($manifestVersion)) { + Write-Warning 'Could not find the module version in the manifest. Using ''0.0.0'' as the version.' + $manifestVersion = New-PSSemVer -Version '0.0.0' + } + } + Write-Output '-------------------------------------------------' + Write-Output 'Manifest version:' + Write-Output $manifestVersion.ToString() + Write-Output '-------------------------------------------------' +} +#endregion Get manifest version + +#region Update module manifest +Set-GitHubLogGroup 'Update module manifest' { + Write-Output 'Bump module version -> module metadata: Update-ModuleMetadata' + $manifestNewVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" + Set-ModuleManifest -Path $manifestFilePath -ModuleVersion $manifestNewVersion -Verbose:$false + if ($createPrerelease) { + Write-Output "Prerelease is: [$($newVersion.Prerelease)]" + Set-ModuleManifest -Path $manifestFilePath -Prerelease $($newVersion.Prerelease) -Verbose:$false + } + + Show-FileContent -Path $manifestFilePath +} +#endregion Update module manifest + +#region Install module dependencies +Set-GitHubLogGroup 'Install module dependencies' { + Resolve-PSModuleDependency -ManifestFilePath $manifestFilePath +} +#endregion Install module dependencies + +#region Publish to PSGallery +Set-GitHubLogGroup 'Publish-ToPSGallery' { + if ($createPrerelease) { + $publishPSVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)-$($newVersion.Prerelease)" + } else { + $publishPSVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" + } + $psGalleryReleaseLink = "https://www.powershellgallery.com/packages/$name/$publishPSVersion" + Write-Output "Publish module to PowerShell Gallery using [$apiKey]" + if ($whatIf) { + Write-Output "Publish-PSResource -Path $modulePath -Repository PSGallery -ApiKey $apiKey" + } else { + try { + Publish-PSResource -Path $modulePath -Repository PSGallery -ApiKey $apiKey + } catch { + Write-Error $_.Exception.Message + exit $LASTEXITCODE + } + } + if ($whatIf) { + Write-Output ( + "gh pr comment $prNumber -b 'Published to the" + + " PowerShell Gallery [$publishPSVersion]($psGalleryReleaseLink) has been created.'" + ) + } else { + Write-Host "::notice::Module [$name - $publishPSVersion] published to the PowerShell Gallery." + gh pr comment $prNumber -b "Module [$name - $publishPSVersion]($psGalleryReleaseLink) published to the PowerShell Gallery." + if ($LASTEXITCODE -ne 0) { + Write-Error 'Failed to comment on the pull request.' + exit $LASTEXITCODE + } + } +} +#endregion Publish to PSGallery + +#region Create GitHub Release +Set-GitHubLogGroup 'New-GitHubRelease' { + Write-Output 'Create new GitHub release' + $releaseCreateCommand = @('release', 'create', $newVersion.ToString()) + $notesFilePath = $null + + # Add title parameter + if ($usePRTitleAsReleaseName -and $pull_request.title) { + $prTitle = $pull_request.title + $releaseCreateCommand += @('--title', $prTitle) + Write-Output "Using PR title as release name: [$prTitle]" + } else { + $releaseCreateCommand += @('--title', $newVersion.ToString()) + } + + # Build release notes content. Uses temp file to avoid escaping issues with special characters. + # Precedence rules for the three UsePR* parameters: + # 1. UsePRTitleAsNotesHeading + UsePRBodyAsReleaseNotes: Creates "# Title (#PR)\n\nBody" format. + # Requires both parameters enabled AND both PR title and body to be present. + # 2. UsePRBodyAsReleaseNotes only: Uses PR body as-is for release notes. + # Takes effect when heading option is disabled/missing title, but body is available. + # 3. Fallback: Auto-generates notes via GitHub's --generate-notes when no PR content is used. + if ($usePRTitleAsNotesHeading -and $usePRBodyAsReleaseNotes -and $pull_request.title -and $pull_request.body) { + # Path 1: Full PR-based notes with title as H1 heading and PR number link + $prTitle = $pull_request.title + $prBody = $pull_request.body + $notes = "# $prTitle (#$prNumber)`n`n$prBody" + $notesFilePath = [System.IO.Path]::GetTempFileName() + Set-Content -Path $notesFilePath -Value $notes -Encoding utf8 + $releaseCreateCommand += @('--notes-file', $notesFilePath) + Write-Output 'Using PR title as H1 heading with link and body as release notes' + } elseif ($usePRBodyAsReleaseNotes -and $pull_request.body) { + # Path 2: PR body only - no heading, just the body content + $prBody = $pull_request.body + $notesFilePath = [System.IO.Path]::GetTempFileName() + Set-Content -Path $notesFilePath -Value $prBody -Encoding utf8 + $releaseCreateCommand += @('--notes-file', $notesFilePath) + Write-Output 'Using PR body as release notes' + } else { + # Path 3: Fallback to GitHub's auto-generated release notes + $releaseCreateCommand += @('--generate-notes') + } + + # Add remaining parameters + if ($createPrerelease) { + $releaseCreateCommand += @('--target', $prHeadRef, '--prerelease') + } + + if ($whatIf) { + Write-Output "WhatIf: gh $($releaseCreateCommand -join ' ')" + } else { + $releaseURL = gh @releaseCreateCommand + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to create the release [$newVersion]." + exit $LASTEXITCODE + } + } + + # Clean up temporary notes file if created + if ($notesFilePath -and (Test-Path -Path $notesFilePath)) { + Remove-Item -Path $notesFilePath -Force + } + + if ($whatIf) { + Write-Output 'WhatIf: gh pr comment $prNumber -b "The release [$newVersion] has been created."' + } else { + gh pr comment $prNumber -b "GitHub release for $name [$newVersion]($releaseURL) has been created." + if ($LASTEXITCODE -ne 0) { + Write-Error 'Failed to comment on the pull request.' + exit $LASTEXITCODE + } + } + Write-Host "::notice::Release created: [$newVersion]" +} +#endregion Create GitHub Release + +Write-Output "Publishing complete. Version: [$($newVersion.ToString())]" From 19f2e0acc8df66925991d2c9dd60f1847b9337ed Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 13:00:57 +0100 Subject: [PATCH 10/24] Add step to update Microsoft.PowerShell.PSResourceGet and update download-artifact action version --- action.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 9ddd0d5..a5f5c2e 100644 --- a/action.yml +++ b/action.yml @@ -80,6 +80,11 @@ runs: - name: Install-PSModuleHelpers uses: PSModule/Install-PSModuleHelpers@d60d63e4be477d1ca0c67c6085101fb109bce8f1 # v1.0.6 + - name: Update Microsoft.PowerShell.PSResourceGet + shell: pwsh + run: | + Install-PSResource -Name Microsoft.PowerShell.PSResourceGet -Repository PSGallery -TrustRepository + - name: Initialize Publish Context id: init shell: pwsh @@ -101,7 +106,7 @@ runs: - name: Download module artifact if: env.PUBLISH_CONTEXT_ShouldPublish == 'true' || inputs.WhatIf == 'true' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: module path: ${{ inputs.ModulePath }} From ad87f5e2b7e64f41e524e7009a7b90959271d8fe Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 13:16:06 +0100 Subject: [PATCH 11/24] Refactor environment variable storage to use GITHUB_ENV and improve output formatting --- scripts/init.ps1 | 52 +++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/scripts/init.ps1 b/scripts/init.ps1 index 8fddc29..04da22e 100644 --- a/scripts/init.ps1 +++ b/scripts/init.ps1 @@ -312,34 +312,36 @@ Set-GitHubLogGroup 'Find prereleases to cleanup' { #region Store context in environment variables Set-GitHubLogGroup 'Store context in environment variables' { - # Store values for subsequent steps - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_ShouldPublish' -Value $shouldPublish.ToString().ToLower() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_ShouldCleanup' -Value $cleanupPrereleases.ToString().ToLower() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_CreateRelease' -Value $createRelease.ToString().ToLower() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_CreatePrerelease' -Value $createPrerelease.ToString().ToLower() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_MajorRelease' -Value $majorRelease.ToString().ToLower() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_MinorRelease' -Value $minorRelease.ToString().ToLower() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PatchRelease' -Value $patchRelease.ToString().ToLower() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_NewVersion' -Value $newVersion.ToString() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PrereleaseName' -Value $prereleaseName - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PrereleaseTagsToCleanup' -Value $prereleaseTagsToCleanup - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PRNumber' -Value $pull_request.number.ToString() - Set-GitHubEnvironmentVariable -Name 'PUBLISH_CONTEXT_PRHeadRef' -Value $prHeadRef + # Store values for subsequent steps by appending to GITHUB_ENV + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_ShouldPublish=$($shouldPublish.ToString().ToLower())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_ShouldCleanup=$($cleanupPrereleases.ToString().ToLower())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_CreateRelease=$($createRelease.ToString().ToLower())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_CreatePrerelease=$($createPrerelease.ToString().ToLower())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_MajorRelease=$($majorRelease.ToString().ToLower())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_MinorRelease=$($minorRelease.ToString().ToLower())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PatchRelease=$($patchRelease.ToString().ToLower())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_NewVersion=$($newVersion.ToString())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PrereleaseName=$prereleaseName" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PrereleaseTagsToCleanup=$prereleaseTagsToCleanup" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PRNumber=$($pull_request.number.ToString())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PRHeadRef=$prHeadRef" Write-Output '-------------------------------------------------' Write-Output 'Stored environment variables:' - Write-Output " PUBLISH_CONTEXT_ShouldPublish: [$shouldPublish]" - Write-Output " PUBLISH_CONTEXT_ShouldCleanup: [$cleanupPrereleases]" - Write-Output " PUBLISH_CONTEXT_CreateRelease: [$createRelease]" - Write-Output " PUBLISH_CONTEXT_CreatePrerelease: [$createPrerelease]" - Write-Output " PUBLISH_CONTEXT_MajorRelease: [$majorRelease]" - Write-Output " PUBLISH_CONTEXT_MinorRelease: [$minorRelease]" - Write-Output " PUBLISH_CONTEXT_PatchRelease: [$patchRelease]" - Write-Output " PUBLISH_CONTEXT_NewVersion: [$($newVersion.ToString())]" - Write-Output " PUBLISH_CONTEXT_PrereleaseName: [$prereleaseName]" - Write-Output " PUBLISH_CONTEXT_PrereleaseTagsToCleanup: [$prereleaseTagsToCleanup]" - Write-Output " PUBLISH_CONTEXT_PRNumber: [$($pull_request.number)]" - Write-Output " PUBLISH_CONTEXT_PRHeadRef: [$prHeadRef]" + [PSCustomObject]@{ + ShouldPublish = $shouldPublish + ShouldCleanup = $cleanupPrereleases + CreateRelease = $createRelease + CreatePrerelease = $createPrerelease + MajorRelease = $majorRelease + MinorRelease = $minorRelease + PatchRelease = $patchRelease + NewVersion = $newVersion.ToString() + PrereleaseName = $prereleaseName + PrereleaseTagsToCleanup = $prereleaseTagsToCleanup + PRNumber = $pull_request.number + PRHeadRef = $prHeadRef + } | Format-List | Out-String Write-Output '-------------------------------------------------' } #endregion Store context in environment variables From 36cbcebc258483f3a61a4ea3d3761de7895b809a Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 13:40:16 +0100 Subject: [PATCH 12/24] Refactor cleanup input handling: rename CleanupPrereleases to AutoCleanup and update related logic --- action.yml | 10 +++++----- scripts/init.ps1 | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/action.yml b/action.yml index a5f5c2e..7074a1b 100644 --- a/action.yml +++ b/action.yml @@ -13,10 +13,10 @@ inputs: APIKey: description: PowerShell Gallery API Key. required: true - CleanupPrereleases: - description: When enabled, only performs cleanup of old prerelease tags without creating a new release or publishing to PSGallery. + AutoCleanup: + description: Control wether to automatically delete the prerelease tags after the stable release is created. required: false - default: 'false' + default: 'true' AutoPatching: description: Control wether to automatically handle patches. If disabled, the action will only create a patch release if the pull request has a 'patch' label. required: false @@ -50,7 +50,7 @@ inputs: required: false default: NoRelease ReleaseType: - description: The type of release to create. Values are 'Release' (stable), 'Prerelease', or 'None'. Cleanup is handled separately via CleanupPrereleases. + description: The type of release to create. Values are 'Release' (stable), 'Prerelease', or 'None'. required: false default: Release WhatIf: @@ -91,7 +91,7 @@ runs: working-directory: ${{ inputs.WorkingDirectory }} env: PSMODULE_PUBLISH_PSMODULE_INPUT_Name: ${{ inputs.Name }} - PSMODULE_PUBLISH_PSMODULE_INPUT_CleanupPrereleases: ${{ inputs.CleanupPrereleases }} + PSMODULE_PUBLISH_PSMODULE_INPUT_AutoCleanup: ${{ inputs.AutoCleanup }} PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching: ${{ inputs.AutoPatching }} PSMODULE_PUBLISH_PSMODULE_INPUT_DatePrereleaseFormat: ${{ inputs.DatePrereleaseFormat }} PSMODULE_PUBLISH_PSMODULE_INPUT_IgnoreLabels: ${{ inputs.IgnoreLabels }} diff --git a/scripts/init.ps1 b/scripts/init.ps1 index 04da22e..adb9215 100644 --- a/scripts/init.ps1 +++ b/scripts/init.ps1 @@ -32,7 +32,7 @@ Write-Output "Module name: [$name]" #region Set configuration Set-GitHubLogGroup 'Set configuration' { - $cleanupPrereleases = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_CleanupPrereleases -eq 'true' + $autoCleanup = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_AutoCleanup -eq 'true' $autoPatching = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching -eq 'true' $incrementalPrerelease = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IncrementalPrerelease -eq 'true' $datePrereleaseFormat = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_DatePrereleaseFormat @@ -51,7 +51,7 @@ Set-GitHubLogGroup 'Set configuration' { } [pscustomobject]@{ - CleanupPrereleases = $cleanupPrereleases + AutoCleanup = $autoCleanup AutoPatching = $autoPatching IncrementalPrerelease = $incrementalPrerelease DatePrereleaseFormat = $datePrereleaseFormat @@ -141,7 +141,7 @@ Set-GitHubLogGroup 'Calculate release type' { Write-Output '-------------------------------------------------' Write-Output "ReleaseType: [$releaseType]" - Write-Output "CleanupPrereleases: [$cleanupPrereleases]" + Write-Output "AutoCleanup: [$autoCleanup]" Write-Output "Should publish: [$shouldPublish]" Write-Output "Create a release: [$createRelease]" Write-Output "Create a prerelease: [$createPrerelease]" @@ -314,7 +314,7 @@ Set-GitHubLogGroup 'Find prereleases to cleanup' { Set-GitHubLogGroup 'Store context in environment variables' { # Store values for subsequent steps by appending to GITHUB_ENV Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_ShouldPublish=$($shouldPublish.ToString().ToLower())" - Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_ShouldCleanup=$($cleanupPrereleases.ToString().ToLower())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_ShouldCleanup=$($autoCleanup.ToString().ToLower())" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_CreateRelease=$($createRelease.ToString().ToLower())" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_CreatePrerelease=$($createPrerelease.ToString().ToLower())" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_MajorRelease=$($majorRelease.ToString().ToLower())" @@ -330,7 +330,7 @@ Set-GitHubLogGroup 'Store context in environment variables' { Write-Output 'Stored environment variables:' [PSCustomObject]@{ ShouldPublish = $shouldPublish - ShouldCleanup = $cleanupPrereleases + ShouldCleanup = $autoCleanup CreateRelease = $createRelease CreatePrerelease = $createPrerelease MajorRelease = $majorRelease @@ -346,4 +346,4 @@ Set-GitHubLogGroup 'Store context in environment variables' { } #endregion Store context in environment variables -Write-Output "Context initialization complete. ShouldPublish=[$shouldPublish], ShouldCleanup=[$cleanupPrereleases]" +Write-Output "Context initialization complete. ShouldPublish=[$shouldPublish], ShouldCleanup=[$autoCleanup]" From 695752f6cd836f341835de0a99789abe7625be7a Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 15:36:42 +0100 Subject: [PATCH 13/24] Fix error message to reference init.ps1 instead of main.ps1 for missing PUBLISH_CONTEXT_PrereleaseName --- scripts/cleanup.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cleanup.ps1 b/scripts/cleanup.ps1 index 7dbdc1a..3c49714 100644 --- a/scripts/cleanup.ps1 +++ b/scripts/cleanup.ps1 @@ -7,7 +7,7 @@ $prereleaseTagsToCleanup = $env:PUBLISH_CONTEXT_PrereleaseTagsToCleanup $whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' if ([string]::IsNullOrWhiteSpace($prereleaseName)) { - Write-Error 'PUBLISH_CONTEXT_PrereleaseName is not set. Run main.ps1 first.' + Write-Error 'PUBLISH_CONTEXT_PrereleaseName is not set. Run init.ps1 first.' exit 1 } #endregion Load context from environment From 4c96fb4dbd23e441739d7005fc094a5870d5596c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 15:36:57 +0100 Subject: [PATCH 14/24] Remove dependency installation logic for PSSemVer from publish script as it is in init --- scripts/publish.ps1 | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/scripts/publish.ps1 b/scripts/publish.ps1 index 5ca2c03..05751d0 100644 --- a/scripts/publish.ps1 +++ b/scripts/publish.ps1 @@ -1,24 +1,6 @@ [CmdletBinding()] param() -#region Install dependencies -$retryCount = 5 -$retryDelay = 10 -for ($i = 0; $i -lt $retryCount; $i++) { - try { - Install-PSResource -Name 'PSSemVer' -TrustRepository -Repository PSGallery - break - } catch { - Write-Warning "Installation of PSSemVer failed with error: $_" - if ($i -eq $retryCount - 1) { - throw - } - Write-Warning "Retrying in $retryDelay seconds..." - Start-Sleep -Seconds $retryDelay - } -} -#endregion Install dependencies - #region Load inputs $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' From 600e0f3ebec27fc076c3eba70aaffcb4568bf4fa Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 15:46:19 +0100 Subject: [PATCH 15/24] Enhance ReleaseType validation and version bump logic for improved error handling and clarity --- scripts/init.ps1 | 338 ++++++++++++++++++++++++++--------------------- 1 file changed, 185 insertions(+), 153 deletions(-) diff --git a/scripts/init.ps1 b/scripts/init.ps1 index adb9215..822fbc7 100644 --- a/scripts/init.ps1 +++ b/scripts/init.ps1 @@ -105,7 +105,9 @@ Set-GitHubLogGroup 'Pull request - Labels' { Set-GitHubLogGroup 'Calculate release type' { $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' - # Validate ReleaseType - fail if not provided or invalid to catch configuration errors + # Validate ReleaseType - defensive check for invalid values. + # Note: The empty check below should never trigger since action.yml provides a default of 'Release', + # but is included for defensive programming in case the script is called directly or defaults change. $validReleaseTypes = @('Release', 'Prerelease', 'None') if ([string]::IsNullOrWhiteSpace($releaseType)) { Write-Error "ReleaseType input is required. Valid values are: $($validReleaseTypes -join ', ')" @@ -126,17 +128,31 @@ Set-GitHubLogGroup 'Calculate release type' { $shouldPublish = $false } - $majorRelease = ($labels | Where-Object { $majorLabels -contains $_ }).Count -gt 0 - $minorRelease = ($labels | Where-Object { $minorLabels -contains $_ }).Count -gt 0 -and -not $majorRelease - $patchRelease = ( - ($labels | Where-Object { $patchLabels -contains $_ } - ).Count -gt 0 -or $autoPatching) -and -not $majorRelease -and -not $minorRelease - - # Check if any version bump applies - $hasVersionBump = $majorRelease -or $minorRelease -or $patchRelease - if (-not $hasVersionBump) { - Write-Output 'No version bump label found and AutoPatching is disabled. Skipping publish.' - $shouldPublish = $false + # Only calculate version bumps if we intend to publish (ReleaseType is not 'None') + $majorRelease = $false + $minorRelease = $false + $patchRelease = $false + $hasVersionBump = $false + + if ($shouldPublish) { + $majorRelease = ($labels | Where-Object { $majorLabels -contains $_ }).Count -gt 0 + $minorRelease = ($labels | Where-Object { $minorLabels -contains $_ }).Count -gt 0 -and -not $majorRelease + $patchRelease = ( + ($labels | Where-Object { $patchLabels -contains $_ } + ).Count -gt 0 -or $autoPatching) -and -not $majorRelease -and -not $minorRelease + + # Check if any version bump applies + $hasVersionBump = $majorRelease -or $minorRelease -or $patchRelease + if (-not $hasVersionBump) { + Write-Output 'No version bump label found and AutoPatching is disabled. Skipping publish.' + $shouldPublish = $false + } + } else { + if ($ignoreRelease) { + Write-Output 'Skipping version bump calculation due to ignore label.' + } else { + Write-Output "ReleaseType is [$releaseType]. Skipping version bump calculation." + } } Write-Output '-------------------------------------------------' @@ -152,167 +168,183 @@ Set-GitHubLogGroup 'Calculate release type' { } #endregion Calculate release type -#region Get releases and versions -Set-GitHubLogGroup 'Get all releases - GitHub' { - $releases = gh release list --json 'createdAt,isDraft,isLatest,isPrerelease,name,publishedAt,tagName' | ConvertFrom-Json - if ($LASTEXITCODE -ne 0) { - Write-Error 'Failed to list all releases for the repo.' - exit $LASTEXITCODE +# Initialize version-related variables with defaults +$newVersion = $null +$releases = @() +$prereleaseTagsToCleanup = '' +$prereleaseName = '' + +if ($shouldPublish) { + #region Get releases and versions + Set-GitHubLogGroup 'Get all releases - GitHub' { + $releases = gh release list --json 'createdAt,isDraft,isLatest,isPrerelease,name,publishedAt,tagName' | ConvertFrom-Json + if ($LASTEXITCODE -ne 0) { + Write-Error 'Failed to list all releases for the repo.' + exit $LASTEXITCODE + } + $releases | Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table | Out-String } - $releases | Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table | Out-String -} -Set-GitHubLogGroup 'Get latest version - GitHub' { - $latestRelease = $releases | Where-Object { $_.isLatest -eq $true } - $latestRelease | Format-List | Out-String - $ghReleaseVersionString = $latestRelease.tagName - if (-not [string]::IsNullOrEmpty($ghReleaseVersionString)) { - $ghReleaseVersion = New-PSSemVer -Version $ghReleaseVersionString - } else { - Write-Warning 'Could not find the latest release version. Using ''0.0.0'' as the version.' - $ghReleaseVersion = New-PSSemVer -Version '0.0.0' + Set-GitHubLogGroup 'Get latest version - GitHub' { + $latestRelease = $releases | Where-Object { $_.isLatest -eq $true } + $latestRelease | Format-List | Out-String + $ghReleaseVersionString = $latestRelease.tagName + if (-not [string]::IsNullOrEmpty($ghReleaseVersionString)) { + $ghReleaseVersion = New-PSSemVer -Version $ghReleaseVersionString + } else { + Write-Warning 'Could not find the latest release version. Using ''0.0.0'' as the version.' + $ghReleaseVersion = New-PSSemVer -Version '0.0.0' + } + Write-Output '-------------------------------------------------' + Write-Output 'GitHub version:' + Write-Output $ghReleaseVersion.ToString() + Write-Output '-------------------------------------------------' } - Write-Output '-------------------------------------------------' - Write-Output 'GitHub version:' - Write-Output $ghReleaseVersion.ToString() - Write-Output '-------------------------------------------------' -} -Set-GitHubLogGroup 'Get latest version - PSGallery' { - $count = 5 - $delay = 10 - for ($i = 1; $i -le $count; $i++) { - try { - Write-Output "Finding module [$name] in the PowerShell Gallery." - $latest = Find-PSResource -Name $name -Repository PSGallery -Verbose:$false - Write-Output "$($latest | Format-Table | Out-String)" - break - } catch { - if ($i -eq $count) { - Write-Warning "Failed to find the module [$name] in the PowerShell Gallery." - Write-Warning $_.Exception.Message + Set-GitHubLogGroup 'Get latest version - PSGallery' { + $count = 5 + $delay = 10 + for ($i = 1; $i -le $count; $i++) { + try { + Write-Output "Finding module [$name] in the PowerShell Gallery." + $latest = Find-PSResource -Name $name -Repository PSGallery -Verbose:$false + Write-Output "$($latest | Format-Table | Out-String)" + break + } catch { + if ($i -eq $count) { + Write-Warning "Failed to find the module [$name] in the PowerShell Gallery." + Write-Warning $_.Exception.Message + } + Start-Sleep -Seconds $delay } - Start-Sleep -Seconds $delay } + if ($latest.Version) { + $psGalleryVersion = New-PSSemVer -Version ($latest.Version).ToString() + } else { + Write-Warning 'Could not find module online. Using ''0.0.0'' as the version.' + $psGalleryVersion = New-PSSemVer -Version '0.0.0' + } + Write-Output '-------------------------------------------------' + Write-Output 'PSGallery version:' + Write-Output $psGalleryVersion.ToString() + Write-Output '-------------------------------------------------' } - if ($latest.Version) { - $psGalleryVersion = New-PSSemVer -Version ($latest.Version).ToString() - } else { - Write-Warning 'Could not find module online. Using ''0.0.0'' as the version.' - $psGalleryVersion = New-PSSemVer -Version '0.0.0' - } - Write-Output '-------------------------------------------------' - Write-Output 'PSGallery version:' - Write-Output $psGalleryVersion.ToString() - Write-Output '-------------------------------------------------' -} -Set-GitHubLogGroup 'Get latest version' { - Write-Output "GitHub: [$($ghReleaseVersion.ToString())]" - Write-Output "PSGallery: [$($psGalleryVersion.ToString())]" - $latestVersion = New-PSSemVer -Version ($psGalleryVersion, $ghReleaseVersion | Sort-Object -Descending | Select-Object -First 1) - Write-Output '-------------------------------------------------' - Write-Output 'Latest version:' - Write-Output ($latestVersion | Format-Table | Out-String) - Write-Output $latestVersion.ToString() - Write-Output '-------------------------------------------------' -} -#endregion Get releases and versions - -#region Calculate new version -Set-GitHubLogGroup 'Calculate new version' { - # - Increment based on label on PR - $newVersion = New-PSSemVer -Version $latestVersion - $newVersion.Prefix = $versionPrefix - if ($majorRelease) { - Write-Output 'Incrementing major version.' - $newVersion.BumpMajor() - } elseif ($minorRelease) { - Write-Output 'Incrementing minor version.' - $newVersion.BumpMinor() - } elseif ($patchRelease) { - Write-Output 'Incrementing patch version.' - $newVersion.BumpPatch() - } else { - Write-Output 'No version bump required.' + Set-GitHubLogGroup 'Get latest version' { + Write-Output "GitHub: [$($ghReleaseVersion.ToString())]" + Write-Output "PSGallery: [$($psGalleryVersion.ToString())]" + $latestVersion = New-PSSemVer -Version ($psGalleryVersion, $ghReleaseVersion | Sort-Object -Descending | Select-Object -First 1) + Write-Output '-------------------------------------------------' + Write-Output 'Latest version:' + Write-Output ($latestVersion | Format-Table | Out-String) + Write-Output $latestVersion.ToString() + Write-Output '-------------------------------------------------' } + #endregion Get releases and versions + + #region Calculate new version + Set-GitHubLogGroup 'Calculate new version' { + # - Increment based on label on PR + $newVersion = New-PSSemVer -Version $latestVersion + $newVersion.Prefix = $versionPrefix + if ($majorRelease) { + Write-Output 'Incrementing major version.' + $newVersion.BumpMajor() + } elseif ($minorRelease) { + Write-Output 'Incrementing minor version.' + $newVersion.BumpMinor() + } elseif ($patchRelease) { + Write-Output 'Incrementing patch version.' + $newVersion.BumpPatch() + } else { + Write-Output 'No version bump required.' + } - Write-Output "Partial new version: [$newVersion]" - - if ($createPrerelease -and $hasVersionBump) { - Write-Output "Adding a prerelease tag to the version using the branch name [$prereleaseName]." - Write-Output ($releases | Where-Object { $_.tagName -like "*$prereleaseName*" } | - Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table -AutoSize | Out-String) - - $newVersion.Prerelease = $prereleaseName Write-Output "Partial new version: [$newVersion]" - if (-not [string]::IsNullOrEmpty($datePrereleaseFormat)) { - Write-Output "Using date-based prerelease: [$datePrereleaseFormat]." - $newVersion.Prerelease += "$(Get-Date -Format $datePrereleaseFormat)" + if ($createPrerelease -and $hasVersionBump) { + Write-Output "Adding a prerelease tag to the version using the branch name [$prereleaseName]." + Write-Output ($releases | Where-Object { $_.tagName -like "*$prereleaseName*" } | + Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table -AutoSize | Out-String) + + $newVersion.Prerelease = $prereleaseName Write-Output "Partial new version: [$newVersion]" - } - if ($incrementalPrerelease) { - # Find the latest prerelease version - $newVersionString = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" - - # PowerShell Gallery - $params = @{ - Name = $name - Version = '*' - Prerelease = $true - Repository = 'PSGallery' - Verbose = $false - ErrorAction = 'SilentlyContinue' + if (-not [string]::IsNullOrEmpty($datePrereleaseFormat)) { + Write-Output "Using date-based prerelease: [$datePrereleaseFormat]." + $newVersion.Prerelease += "$(Get-Date -Format $datePrereleaseFormat)" + Write-Output "Partial new version: [$newVersion]" + } + + if ($incrementalPrerelease) { + # Find the latest prerelease version + $newVersionString = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" + + # PowerShell Gallery + $params = @{ + Name = $name + Version = '*' + Prerelease = $true + Repository = 'PSGallery' + Verbose = $false + ErrorAction = 'SilentlyContinue' + } + Write-Output 'Finding the latest prerelease version in the PowerShell Gallery.' + Write-Output ($params | Format-Table | Out-String) + $psGalleryPrereleases = Find-PSResource @params + $psGalleryPrereleases = $psGalleryPrereleases | Where-Object { $_.Version -like "$newVersionString" } + $psGalleryPrereleases = $psGalleryPrereleases | Where-Object { $_.Prerelease -like "$prereleaseName*" } + $latestPSGalleryPrerelease = $psGalleryPrereleases.Prerelease | ForEach-Object { + [int]($_ -replace $prereleaseName) + } | Sort-Object | Select-Object -Last 1 + Write-Output "PSGallery prerelease: [$latestPSGalleryPrerelease]" + + # GitHub + $ghPrereleases = $releases | Where-Object { $_.tagName -like "*$newVersionString*" } + $ghPrereleases = $ghPrereleases | Where-Object { $_.tagName -like "*$prereleaseName*" } + $latestGHPrereleases = $ghPrereleases.tagName | ForEach-Object { + $number = $_ + $number = $number -replace '\.' + $number = ($number -split $prereleaseName, 2)[-1] + [int]$number + } | Sort-Object | Select-Object -Last 1 + Write-Output "GitHub prerelease: [$latestGHPrereleases]" + + # Handle null values explicitly to ensure Math.Max works correctly + if ($null -eq $latestPSGalleryPrerelease) { $latestPSGalleryPrerelease = 0 } + if ($null -eq $latestGHPrereleases) { $latestGHPrereleases = 0 } + + $latestPrereleaseNumber = [Math]::Max($latestPSGalleryPrerelease, $latestGHPrereleases) + $latestPrereleaseNumber++ + $latestPrereleaseNumber = ([string]$latestPrereleaseNumber).PadLeft(3, '0') + $newVersion.Prerelease += $latestPrereleaseNumber } - Write-Output 'Finding the latest prerelease version in the PowerShell Gallery.' - Write-Output ($params | Format-Table | Out-String) - $psGalleryPrereleases = Find-PSResource @params - $psGalleryPrereleases = $psGalleryPrereleases | Where-Object { $_.Version -like "$newVersionString" } - $psGalleryPrereleases = $psGalleryPrereleases | Where-Object { $_.Prerelease -like "$prereleaseName*" } - $latestPSGalleryPrerelease = $psGalleryPrereleases.Prerelease | ForEach-Object { - [int]($_ -replace $prereleaseName) - } | Sort-Object | Select-Object -Last 1 - Write-Output "PSGallery prerelease: [$latestPSGalleryPrerelease]" - - # GitHub - $ghPrereleases = $releases | Where-Object { $_.tagName -like "*$newVersionString*" } - $ghPrereleases = $ghPrereleases | Where-Object { $_.tagName -like "*$prereleaseName*" } - $latestGHPrereleases = $ghPrereleases.tagName | ForEach-Object { - $number = $_ - $number = $number -replace '\.' - $number = ($number -split $prereleaseName, 2)[-1] - [int]$number - } | Sort-Object | Select-Object -Last 1 - Write-Output "GitHub prerelease: [$latestGHPrereleases]" - - $latestPrereleaseNumber = [Math]::Max($latestPSGalleryPrerelease, $latestGHPrereleases) - $latestPrereleaseNumber++ - $latestPrereleaseNumber = ([string]$latestPrereleaseNumber).PadLeft(3, '0') - $newVersion.Prerelease += $latestPrereleaseNumber } + Write-Output '-------------------------------------------------' + Write-Output 'New version:' + Write-Output ($newVersion | Format-Table | Out-String) + Write-Output $newVersion.ToString() + Write-Output '-------------------------------------------------' } - Write-Output '-------------------------------------------------' - Write-Output 'New version:' - Write-Output ($newVersion | Format-Table | Out-String) - Write-Output $newVersion.ToString() - Write-Output '-------------------------------------------------' -} -#endregion Calculate new version - -#region Find prereleases to cleanup -Set-GitHubLogGroup 'Find prereleases to cleanup' { - $prereleasesToCleanup = $releases | Where-Object { $_.tagName -like "*$prereleaseName*" } - $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String - $prereleaseTagsToCleanup = ($prereleasesToCleanup | ForEach-Object { $_.tagName }) -join ',' - Write-Output "Prereleases to cleanup: [$prereleaseTagsToCleanup]" + #endregion Calculate new version + + #region Find prereleases to cleanup + Set-GitHubLogGroup 'Find prereleases to cleanup' { + $prereleasesToCleanup = $releases | Where-Object { $_.tagName -like "*$prereleaseName*" } + $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String + $prereleaseTagsToCleanup = ($prereleasesToCleanup | ForEach-Object { $_.tagName }) -join ',' + Write-Output "Prereleases to cleanup: [$prereleaseTagsToCleanup]" + } + #endregion Find prereleases to cleanup +} else { + Write-Output "Skipping version calculation and release lookup because ShouldPublish is [$shouldPublish]." } -#endregion Find prereleases to cleanup #region Store context in environment variables Set-GitHubLogGroup 'Store context in environment variables' { # Store values for subsequent steps by appending to GITHUB_ENV + $newVersionString = if ($newVersion) { $newVersion.ToString() } else { '' } + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_ShouldPublish=$($shouldPublish.ToString().ToLower())" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_ShouldCleanup=$($autoCleanup.ToString().ToLower())" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_CreateRelease=$($createRelease.ToString().ToLower())" @@ -320,7 +352,7 @@ Set-GitHubLogGroup 'Store context in environment variables' { Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_MajorRelease=$($majorRelease.ToString().ToLower())" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_MinorRelease=$($minorRelease.ToString().ToLower())" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PatchRelease=$($patchRelease.ToString().ToLower())" - Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_NewVersion=$($newVersion.ToString())" + Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_NewVersion=$newVersionString" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PrereleaseName=$prereleaseName" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PrereleaseTagsToCleanup=$prereleaseTagsToCleanup" Add-Content -Path $env:GITHUB_ENV -Value "PUBLISH_CONTEXT_PRNumber=$($pull_request.number.ToString())" @@ -336,7 +368,7 @@ Set-GitHubLogGroup 'Store context in environment variables' { MajorRelease = $majorRelease MinorRelease = $minorRelease PatchRelease = $patchRelease - NewVersion = $newVersion.ToString() + NewVersion = $newVersionString PrereleaseName = $prereleaseName PrereleaseTagsToCleanup = $prereleaseTagsToCleanup PRNumber = $pull_request.number From 11e4fe61c5caffaa819608ac06705a63b821c895 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 15:48:21 +0100 Subject: [PATCH 16/24] Fix typos in descriptions for AutoCleanup and AutoPatching inputs in action.yml --- action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index 7074a1b..9e70917 100644 --- a/action.yml +++ b/action.yml @@ -14,11 +14,11 @@ inputs: description: PowerShell Gallery API Key. required: true AutoCleanup: - description: Control wether to automatically delete the prerelease tags after the stable release is created. + description: Control whether to automatically delete the prerelease tags after the stable release is created. required: false default: 'true' AutoPatching: - description: Control wether to automatically handle patches. If disabled, the action will only create a patch release if the pull request has a 'patch' label. + description: Control whether to automatically handle patches. If disabled, the action will only create a patch release if the pull request has a 'patch' label. required: false default: 'true' IncrementalPrerelease: From c495e6a297fb727590b225aa0730b149fb3d528d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 16:04:41 +0100 Subject: [PATCH 17/24] Fix error message to reference init.ps1 for missing PUBLISH_CONTEXT_NewVersion and refactor manifest validation region --- scripts/publish.ps1 | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/scripts/publish.ps1 b/scripts/publish.ps1 index 05751d0..a2cc5fd 100644 --- a/scripts/publish.ps1 +++ b/scripts/publish.ps1 @@ -29,7 +29,7 @@ Set-GitHubLogGroup 'Load publish context from environment' { $usePRTitleAsNotesHeading = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsNotesHeading -eq 'true' if ([string]::IsNullOrWhiteSpace($newVersionString)) { - Write-Error 'PUBLISH_CONTEXT_NewVersion is not set. Run main.ps1 first.' + Write-Error 'PUBLISH_CONTEXT_NewVersion is not set. Run init.ps1 first.' exit 1 } @@ -58,8 +58,8 @@ Set-GitHubLogGroup 'Load PR information' { } #endregion Load PR information -#region Get manifest version -Set-GitHubLogGroup 'Get latest version - Manifest' { +#region Validate manifest and set module path +Set-GitHubLogGroup 'Validate manifest and set module path' { Add-PSModulePath -Path (Split-Path -Path $modulePath -Parent) $manifestFilePath = Join-Path $modulePath "$name.psd1" Write-Output "Module manifest file path: [$manifestFilePath]" @@ -67,20 +67,8 @@ Set-GitHubLogGroup 'Get latest version - Manifest' { Write-Error "Module manifest file not found at [$manifestFilePath]" exit 1 } - try { - $manifestVersion = New-PSSemVer -Version (Test-ModuleManifest $manifestFilePath -Verbose:$false).Version - } catch { - if ([string]::IsNullOrEmpty($manifestVersion)) { - Write-Warning 'Could not find the module version in the manifest. Using ''0.0.0'' as the version.' - $manifestVersion = New-PSSemVer -Version '0.0.0' - } - } - Write-Output '-------------------------------------------------' - Write-Output 'Manifest version:' - Write-Output $manifestVersion.ToString() - Write-Output '-------------------------------------------------' } -#endregion Get manifest version +#endregion Validate manifest and set module path #region Update module manifest Set-GitHubLogGroup 'Update module manifest' { From f4bd2883deb1b86cba6e4e5110d5ffd887ecf490 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 16:11:09 +0100 Subject: [PATCH 18/24] Add module artifact upload step to Action-Test workflow --- .github/workflows/Action-Test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 03da0a6..30f2ee2 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -26,6 +26,12 @@ jobs: with: persist-credentials: false + - name: Upload module artifact + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: module + path: tests/outputs/module + - name: Action-Test uses: ./ env: From d5fb3697fe7996a4e796e6a23a661d57e09a9630 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 16:21:54 +0100 Subject: [PATCH 19/24] Update PR comment messages for clarity and consistency in publish script --- scripts/publish.ps1 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/publish.ps1 b/scripts/publish.ps1 index a2cc5fd..75d97c8 100644 --- a/scripts/publish.ps1 +++ b/scripts/publish.ps1 @@ -111,8 +111,8 @@ Set-GitHubLogGroup 'Publish-ToPSGallery' { } if ($whatIf) { Write-Output ( - "gh pr comment $prNumber -b 'Published to the" + - " PowerShell Gallery [$publishPSVersion]($psGalleryReleaseLink) has been created.'" + "gh pr comment $prNumber -b " + + "'Module [$name - $publishPSVersion]($psGalleryReleaseLink) published to the PowerShell Gallery.'" ) } else { Write-Host "::notice::Module [$name - $publishPSVersion] published to the PowerShell Gallery." @@ -189,7 +189,10 @@ Set-GitHubLogGroup 'New-GitHubRelease' { } if ($whatIf) { - Write-Output 'WhatIf: gh pr comment $prNumber -b "The release [$newVersion] has been created."' + Write-Output ( + "gh pr comment $prNumber -b " + + "'GitHub release for $name $newVersion has been created.'" + ) } else { gh pr comment $prNumber -b "GitHub release for $name [$newVersion]($releaseURL) has been created." if ($LASTEXITCODE -ne 0) { From 651597c37e7b0b16933d94f622d2399853a464b7 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 16:29:17 +0100 Subject: [PATCH 20/24] Refactor logging regions in init.ps1 and cleanup.ps1 for improved readability and consistency --- scripts/cleanup.ps1 | 6 +-- scripts/init.ps1 | 101 +++++++++++++++++++++----------------------- 2 files changed, 50 insertions(+), 57 deletions(-) diff --git a/scripts/cleanup.ps1 b/scripts/cleanup.ps1 index 3c49714..c4b5731 100644 --- a/scripts/cleanup.ps1 +++ b/scripts/cleanup.ps1 @@ -1,7 +1,6 @@ [CmdletBinding()] param() -#region Load context from environment $prereleaseName = $env:PUBLISH_CONTEXT_PrereleaseName $prereleaseTagsToCleanup = $env:PUBLISH_CONTEXT_PrereleaseTagsToCleanup $whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' @@ -10,10 +9,8 @@ if ([string]::IsNullOrWhiteSpace($prereleaseName)) { Write-Error 'PUBLISH_CONTEXT_PrereleaseName is not set. Run init.ps1 first.' exit 1 } -#endregion Load context from environment -#region Cleanup prereleases -Set-GitHubLogGroup "Cleanup prereleases for [$prereleaseName]" { +LogGroup "Cleanup prereleases for [$prereleaseName]" { if ([string]::IsNullOrWhiteSpace($prereleaseTagsToCleanup)) { Write-Output "No prereleases found to cleanup for [$prereleaseName]." return @@ -46,4 +43,3 @@ Set-GitHubLogGroup "Cleanup prereleases for [$prereleaseName]" { Write-Host "::notice::Cleaned up $($tagsToDelete.Count) prerelease(s) for [$prereleaseName]." } -#endregion Cleanup prereleases diff --git a/scripts/init.ps1 b/scripts/init.ps1 index 822fbc7..f730686 100644 --- a/scripts/init.ps1 +++ b/scripts/init.ps1 @@ -1,37 +1,36 @@ [CmdletBinding()] param() -#region Install dependencies -$retryCount = 5 -$retryDelay = 10 -for ($i = 0; $i -lt $retryCount; $i++) { - try { - Install-PSResource -Name 'PSSemVer' -TrustRepository -Repository PSGallery - break - } catch { - Write-Warning "Installation of PSSemVer failed with error: $_" - if ($i -eq $retryCount - 1) { - throw +LogGroup 'Install dependencies' { + $retryCount = 5 + $retryDelay = 10 + for ($i = 0; $i -lt $retryCount; $i++) { + try { + Install-PSResource -Name 'PSSemVer' -TrustRepository -Repository PSGallery + break + } catch { + Write-Warning "Installation of PSSemVer failed with error: $_" + if ($i -eq $retryCount - 1) { + throw + } + Write-Warning "Retrying in $retryDelay seconds..." + Start-Sleep -Seconds $retryDelay } - Write-Warning "Retrying in $retryDelay seconds..." - Start-Sleep -Seconds $retryDelay } } -#endregion Install dependencies -#region Load inputs -$env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' +LogGroup 'Load inputs' { + $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' -$name = if ([string]::IsNullOrEmpty($env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name)) { - $env:GITHUB_REPOSITORY_NAME -} else { - $env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name + $name = if ([string]::IsNullOrEmpty($env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name)) { + $env:GITHUB_REPOSITORY_NAME + } else { + $env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name + } + Write-Output "Module name: [$name]" } -Write-Output "Module name: [$name]" -#endregion Load inputs -#region Set configuration -Set-GitHubLogGroup 'Set configuration' { +LogGroup 'Set configuration' { $autoCleanup = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_AutoCleanup -eq 'true' $autoPatching = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_AutoPatching -eq 'true' $incrementalPrerelease = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_IncrementalPrerelease -eq 'true' @@ -64,21 +63,19 @@ Set-GitHubLogGroup 'Set configuration' { PatchLabels = $patchLabels } | Format-List | Out-String } -#endregion Set configuration -#region Event information -Set-GitHubLogGroup 'Event information - JSON' { +LogGroup 'Event information - JSON' { $githubEventJson = Get-Content $env:GITHUB_EVENT_PATH $githubEventJson | Format-List | Out-String } -Set-GitHubLogGroup 'Event information - Object' { +LogGroup 'Event information - Object' { $githubEvent = $githubEventJson | ConvertFrom-Json $pull_request = $githubEvent.pull_request $githubEvent | Format-List | Out-String } -Set-GitHubLogGroup 'Event information - Details' { +LogGroup 'Event information - Details' { if (-not $pull_request) { throw 'GitHub event does not contain pull_request data. This script must be run from a pull_request event.' } @@ -90,19 +87,17 @@ Set-GitHubLogGroup 'Event information - Details' { Write-Output '-------------------------------------------------' } -Set-GitHubLogGroup 'Pull request - details' { +LogGroup 'Pull request - details' { $pull_request | Format-List | Out-String } -Set-GitHubLogGroup 'Pull request - Labels' { +LogGroup 'Pull request - Labels' { $labels = @() $labels += $pull_request.labels.name $labels | Format-List | Out-String } -#endregion Event information -#region Calculate release type -Set-GitHubLogGroup 'Calculate release type' { +LogGroup 'Calculate release type' { $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' # Validate ReleaseType - defensive check for invalid values. @@ -172,11 +167,11 @@ Set-GitHubLogGroup 'Calculate release type' { $newVersion = $null $releases = @() $prereleaseTagsToCleanup = '' -$prereleaseName = '' -if ($shouldPublish) { - #region Get releases and versions - Set-GitHubLogGroup 'Get all releases - GitHub' { +# Fetch releases if publishing OR if cleanup is enabled (cleanup can work independently) +if ($shouldPublish -or $autoCleanup) { + #region Get releases + LogGroup 'Get all releases - GitHub' { $releases = gh release list --json 'createdAt,isDraft,isLatest,isPrerelease,name,publishedAt,tagName' | ConvertFrom-Json if ($LASTEXITCODE -ne 0) { Write-Error 'Failed to list all releases for the repo.' @@ -184,8 +179,13 @@ if ($shouldPublish) { } $releases | Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table | Out-String } + #endregion Get releases +} - Set-GitHubLogGroup 'Get latest version - GitHub' { +# Version calculation is only needed when publishing +if ($shouldPublish) { + #region Get versions + LogGroup 'Get latest version - GitHub' { $latestRelease = $releases | Where-Object { $_.isLatest -eq $true } $latestRelease | Format-List | Out-String $ghReleaseVersionString = $latestRelease.tagName @@ -201,7 +201,7 @@ if ($shouldPublish) { Write-Output '-------------------------------------------------' } - Set-GitHubLogGroup 'Get latest version - PSGallery' { + LogGroup 'Get latest version - PSGallery' { $count = 5 $delay = 10 for ($i = 1; $i -le $count; $i++) { @@ -230,7 +230,7 @@ if ($shouldPublish) { Write-Output '-------------------------------------------------' } - Set-GitHubLogGroup 'Get latest version' { + LogGroup 'Get latest version' { Write-Output "GitHub: [$($ghReleaseVersion.ToString())]" Write-Output "PSGallery: [$($psGalleryVersion.ToString())]" $latestVersion = New-PSSemVer -Version ($psGalleryVersion, $ghReleaseVersion | Sort-Object -Descending | Select-Object -First 1) @@ -240,10 +240,8 @@ if ($shouldPublish) { Write-Output $latestVersion.ToString() Write-Output '-------------------------------------------------' } - #endregion Get releases and versions - #region Calculate new version - Set-GitHubLogGroup 'Calculate new version' { + LogGroup 'Calculate new version' { # - Increment based on label on PR $newVersion = New-PSSemVer -Version $latestVersion $newVersion.Prefix = $versionPrefix @@ -327,21 +325,21 @@ if ($shouldPublish) { Write-Output '-------------------------------------------------' } #endregion Calculate new version +} - #region Find prereleases to cleanup - Set-GitHubLogGroup 'Find prereleases to cleanup' { +#region Find prereleases to cleanup +# This runs independently when cleanup is enabled, even if not publishing +if ($autoCleanup) { + LogGroup 'Find prereleases to cleanup' { $prereleasesToCleanup = $releases | Where-Object { $_.tagName -like "*$prereleaseName*" } $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String $prereleaseTagsToCleanup = ($prereleasesToCleanup | ForEach-Object { $_.tagName }) -join ',' Write-Output "Prereleases to cleanup: [$prereleaseTagsToCleanup]" } - #endregion Find prereleases to cleanup -} else { - Write-Output "Skipping version calculation and release lookup because ShouldPublish is [$shouldPublish]." } +#endregion Find prereleases to cleanup -#region Store context in environment variables -Set-GitHubLogGroup 'Store context in environment variables' { +LogGroup 'Store context in environment variables' { # Store values for subsequent steps by appending to GITHUB_ENV $newVersionString = if ($newVersion) { $newVersion.ToString() } else { '' } @@ -376,6 +374,5 @@ Set-GitHubLogGroup 'Store context in environment variables' { } | Format-List | Out-String Write-Output '-------------------------------------------------' } -#endregion Store context in environment variables Write-Output "Context initialization complete. ShouldPublish=[$shouldPublish], ShouldCleanup=[$autoCleanup]" From 9a630d8151f5cf43b7d4fc5da2150702fab32dc1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 16:30:47 +0100 Subject: [PATCH 21/24] Fix exit code in publish script to ensure consistent error handling --- scripts/publish.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/publish.ps1 b/scripts/publish.ps1 index 75d97c8..dcdd6ca 100644 --- a/scripts/publish.ps1 +++ b/scripts/publish.ps1 @@ -106,7 +106,7 @@ Set-GitHubLogGroup 'Publish-ToPSGallery' { Publish-PSResource -Path $modulePath -Repository PSGallery -ApiKey $apiKey } catch { Write-Error $_.Exception.Message - exit $LASTEXITCODE + exit 1 } } if ($whatIf) { From c4adf9470668839f94db113b69ef48eaa26b41f9 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 16:41:29 +0100 Subject: [PATCH 22/24] Add Import-Module 'Helpers' to cleanup and init scripts for consistency --- scripts/cleanup.ps1 | 2 ++ scripts/init.ps1 | 2 ++ scripts/publish.ps1 | 54 ++++++++++++++++++--------------------------- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/scripts/cleanup.ps1 b/scripts/cleanup.ps1 index c4b5731..2363894 100644 --- a/scripts/cleanup.ps1 +++ b/scripts/cleanup.ps1 @@ -1,6 +1,8 @@ [CmdletBinding()] param() +Import-Module -Name 'Helpers' -Force + $prereleaseName = $env:PUBLISH_CONTEXT_PrereleaseName $prereleaseTagsToCleanup = $env:PUBLISH_CONTEXT_PrereleaseTagsToCleanup $whatIf = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_WhatIf -eq 'true' diff --git a/scripts/init.ps1 b/scripts/init.ps1 index f730686..5c47312 100644 --- a/scripts/init.ps1 +++ b/scripts/init.ps1 @@ -1,6 +1,8 @@ [CmdletBinding()] param() +Import-Module -Name 'Helpers' -Force + LogGroup 'Install dependencies' { $retryCount = 5 $retryDelay = 10 diff --git a/scripts/publish.ps1 b/scripts/publish.ps1 index dcdd6ca..a7eb2fd 100644 --- a/scripts/publish.ps1 +++ b/scripts/publish.ps1 @@ -1,23 +1,24 @@ [CmdletBinding()] param() -#region Load inputs -$env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' +Import-Module -Name 'Helpers' -Force -$name = if ([string]::IsNullOrEmpty($env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name)) { - $env:GITHUB_REPOSITORY_NAME -} else { - $env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name -} -$modulePath = Resolve-Path -Path "$env:PSMODULE_PUBLISH_PSMODULE_INPUT_ModulePath/$name" | Select-Object -ExpandProperty Path -$apiKey = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_APIKey +LogGroup 'Load inputs' { + $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' -Write-Output "Module name: [$name]" -Write-Output "Module path: [$modulePath]" -#endregion Load inputs + $name = if ([string]::IsNullOrEmpty($env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name)) { + $env:GITHUB_REPOSITORY_NAME + } else { + $env:PSMODULE_PUBLISH_PSMODULE_INPUT_Name + } + $modulePath = Resolve-Path -Path "$env:PSMODULE_PUBLISH_PSMODULE_INPUT_ModulePath/$name" | Select-Object -ExpandProperty Path + $apiKey = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_APIKey + + Write-Output "Module name: [$name]" + Write-Output "Module path: [$modulePath]" +} -#region Load publish context from environment -Set-GitHubLogGroup 'Load publish context from environment' { +LogGroup 'Load publish context from environment' { $createRelease = $env:PUBLISH_CONTEXT_CreateRelease -eq 'true' $createPrerelease = $env:PUBLISH_CONTEXT_CreatePrerelease -eq 'true' $newVersionString = $env:PUBLISH_CONTEXT_NewVersion @@ -45,10 +46,8 @@ Set-GitHubLogGroup 'Load publish context from environment' { Write-Output " WhatIf: [$whatIf]" Write-Output '-------------------------------------------------' } -#endregion Load publish context from environment -#region Load PR information -Set-GitHubLogGroup 'Load PR information' { +LogGroup 'Load PR information' { $githubEventJson = Get-Content $env:GITHUB_EVENT_PATH $githubEvent = $githubEventJson | ConvertFrom-Json $pull_request = $githubEvent.pull_request @@ -56,10 +55,8 @@ Set-GitHubLogGroup 'Load PR information' { throw 'GitHub event does not contain pull_request data. This script must be run from a pull_request event.' } } -#endregion Load PR information -#region Validate manifest and set module path -Set-GitHubLogGroup 'Validate manifest and set module path' { +LogGroup 'Validate manifest and set module path' { Add-PSModulePath -Path (Split-Path -Path $modulePath -Parent) $manifestFilePath = Join-Path $modulePath "$name.psd1" Write-Output "Module manifest file path: [$manifestFilePath]" @@ -68,10 +65,8 @@ Set-GitHubLogGroup 'Validate manifest and set module path' { exit 1 } } -#endregion Validate manifest and set module path -#region Update module manifest -Set-GitHubLogGroup 'Update module manifest' { +LogGroup 'Update module manifest' { Write-Output 'Bump module version -> module metadata: Update-ModuleMetadata' $manifestNewVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" Set-ModuleManifest -Path $manifestFilePath -ModuleVersion $manifestNewVersion -Verbose:$false @@ -82,16 +77,12 @@ Set-GitHubLogGroup 'Update module manifest' { Show-FileContent -Path $manifestFilePath } -#endregion Update module manifest -#region Install module dependencies -Set-GitHubLogGroup 'Install module dependencies' { +LogGroup 'Install module dependencies' { Resolve-PSModuleDependency -ManifestFilePath $manifestFilePath } -#endregion Install module dependencies -#region Publish to PSGallery -Set-GitHubLogGroup 'Publish-ToPSGallery' { +LogGroup 'Publish-ToPSGallery' { if ($createPrerelease) { $publishPSVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)-$($newVersion.Prerelease)" } else { @@ -123,10 +114,8 @@ Set-GitHubLogGroup 'Publish-ToPSGallery' { } } } -#endregion Publish to PSGallery -#region Create GitHub Release -Set-GitHubLogGroup 'New-GitHubRelease' { +LogGroup 'New-GitHubRelease' { Write-Output 'Create new GitHub release' $releaseCreateCommand = @('release', 'create', $newVersion.ToString()) $notesFilePath = $null @@ -202,6 +191,5 @@ Set-GitHubLogGroup 'New-GitHubRelease' { } Write-Host "::notice::Release created: [$newVersion]" } -#endregion Create GitHub Release Write-Output "Publishing complete. Version: [$($newVersion.ToString())]" From dfe5b087584286830cdd08c3ecab1dbea3a8047d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 16:50:11 +0100 Subject: [PATCH 23/24] Update publish script to mask API key in output for security --- scripts/publish.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/publish.ps1 b/scripts/publish.ps1 index a7eb2fd..6f074ce 100644 --- a/scripts/publish.ps1 +++ b/scripts/publish.ps1 @@ -89,9 +89,9 @@ LogGroup 'Publish-ToPSGallery' { $publishPSVersion = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" } $psGalleryReleaseLink = "https://www.powershellgallery.com/packages/$name/$publishPSVersion" - Write-Output "Publish module to PowerShell Gallery using [$apiKey]" + Write-Output "Publish module to PowerShell Gallery using API key from environment." if ($whatIf) { - Write-Output "Publish-PSResource -Path $modulePath -Repository PSGallery -ApiKey $apiKey" + Write-Output "Publish-PSResource -Path $modulePath -Repository PSGallery -ApiKey ***" } else { try { Publish-PSResource -Path $modulePath -Repository PSGallery -ApiKey $apiKey From d5eff732cfd16256b06f0dace7309a7378c52785 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 18 Jan 2026 21:13:10 +0100 Subject: [PATCH 24/24] Refactor release type calculation for clarity and accuracy in init.ps1 --- scripts/init.ps1 | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/scripts/init.ps1 b/scripts/init.ps1 index 5c47312..d562d48 100644 --- a/scripts/init.ps1 +++ b/scripts/init.ps1 @@ -99,12 +99,12 @@ LogGroup 'Pull request - Labels' { $labels | Format-List | Out-String } -LogGroup 'Calculate release type' { +LogGroup 'Determine release configuration' { $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' - # Validate ReleaseType - defensive check for invalid values. - # Note: The empty check below should never trigger since action.yml provides a default of 'Release', - # but is included for defensive programming in case the script is called directly or defaults change. + # Validate ReleaseType input from Get-PSModuleSettings. + # The ReleaseType is pre-calculated based on PR state and labels by the settings action, + # so we trust it here rather than recalculating from labels. $validReleaseTypes = @('Release', 'Prerelease', 'None') if ([string]::IsNullOrWhiteSpace($releaseType)) { Write-Error "ReleaseType input is required. Valid values are: $($validReleaseTypes -join ', ')" @@ -119,13 +119,14 @@ LogGroup 'Calculate release type' { $createPrerelease = $releaseType -eq 'Prerelease' $shouldPublish = $createRelease -or $createPrerelease + # Check for ignore labels that override the release type $ignoreRelease = ($labels | Where-Object { $ignoreLabels -contains $_ }).Count -gt 0 - if ($ignoreRelease) { + if ($ignoreRelease -and $shouldPublish) { Write-Output 'Ignoring release creation due to ignore label.' $shouldPublish = $false } - # Only calculate version bumps if we intend to publish (ReleaseType is not 'None') + # Determine version bump type from labels (only when publishing) $majorRelease = $false $minorRelease = $false $patchRelease = $false @@ -138,18 +139,13 @@ LogGroup 'Calculate release type' { ($labels | Where-Object { $patchLabels -contains $_ } ).Count -gt 0 -or $autoPatching) -and -not $majorRelease -and -not $minorRelease - # Check if any version bump applies $hasVersionBump = $majorRelease -or $minorRelease -or $patchRelease if (-not $hasVersionBump) { Write-Output 'No version bump label found and AutoPatching is disabled. Skipping publish.' $shouldPublish = $false } - } else { - if ($ignoreRelease) { - Write-Output 'Skipping version bump calculation due to ignore label.' - } else { - Write-Output "ReleaseType is [$releaseType]. Skipping version bump calculation." - } + } elseif (-not $ignoreRelease) { + Write-Output "ReleaseType is [$releaseType]. No publishing required." } Write-Output '-------------------------------------------------'