From dce5e933a6a051c903f99d1ed694528541c078bc Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Wed, 27 May 2026 18:31:43 +0200 Subject: [PATCH 1/5] feat: Add Pester tests for label parsing, bump selection, and prerelease sequencing Cover Resolve-ReleaseDecision (label parsing and bump selection), Get-NextModuleVersion (version increments and prerelease suffix), and Get-NextPrereleaseNumber (incremental sequencing and zero-padding). --- tests/Helpers.Tests.ps1 | 579 ++++++++++++++++++++++++++++++++++++++++ tests/README.md | 3 - 2 files changed, 579 insertions(+), 3 deletions(-) create mode 100644 tests/Helpers.Tests.ps1 delete mode 100644 tests/README.md diff --git a/tests/Helpers.Tests.ps1 b/tests/Helpers.Tests.ps1 new file mode 100644 index 0000000..979c5d9 --- /dev/null +++ b/tests/Helpers.Tests.ps1 @@ -0,0 +1,579 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', + Justification = 'Pester variables are used across It blocks via BeforeAll/BeforeEach.')] +param() + +BeforeAll { + # Remove any pre-loaded Helpers module (from Install-PSModuleHelpers) to avoid name collisions. + Get-Module -Name Helpers -All | Remove-Module -Force -ErrorAction SilentlyContinue + + # Provide a minimal LogGroup implementation so Helpers.psm1 can be imported without the GitHub module. + function global:LogGroup { + param( + [Parameter(Mandatory)] + [string] $Name, + [Parameter(Mandatory)] + [scriptblock] $ScriptBlock + ) + . $ScriptBlock + } + + Import-Module -Name PSSemVer -Force + Import-Module "$PSScriptRoot/../scripts/Helpers.psm1" -Force +} + +AfterAll { + Remove-Item -Path Function:\LogGroup -ErrorAction SilentlyContinue +} + +Describe 'Split-CommaSeparatedList' { + It 'Splits comma-separated values and trims whitespace' { + $result = Split-CommaSeparatedList -Value 'Major, Minor , Patch' + $result | Should -Be @('Major', 'Minor', 'Patch') + } + + It 'Returns empty array for empty string' { + $result = Split-CommaSeparatedList -Value '' + $result | Should -BeNullOrEmpty + } + + It 'Handles single value without comma' { + $result = Split-CommaSeparatedList -Value 'OnlyOne' + $result | Should -Be @('OnlyOne') + } + + It 'Filters out empty segments from trailing comma' { + $result = Split-CommaSeparatedList -Value 'A, B,' + $result | Should -Be @('A', 'B') + } +} + +Describe 'Resolve-ReleaseDecision' { + BeforeAll { + $defaultConfig = [PSCustomObject]@{ + AutoPatching = $false + IncrementalPrerelease = $false + DatePrereleaseFormat = '' + VersionPrefix = 'v' + ReleaseType = 'Release' + IgnoreLabels = @('skip-release') + MajorLabels = @('Major') + MinorLabels = @('Minor') + PatchLabels = @('Patch') + } + } + + Context 'Label parsing - bump selection' { + It 'Recognizes a Major label and sets MajorRelease' { + $pr = [PSCustomObject]@{ HeadRef = 'feat/big-change'; Labels = @('Major') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.MajorRelease | Should -BeTrue + $decision.MinorRelease | Should -BeFalse + $decision.PatchRelease | Should -BeFalse + $decision.HasVersionBump | Should -BeTrue + $decision.ShouldPublish | Should -BeTrue + } + + It 'Recognizes a Minor label and sets MinorRelease' { + $pr = [PSCustomObject]@{ HeadRef = 'feat/add-feature'; Labels = @('Minor') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.MajorRelease | Should -BeFalse + $decision.MinorRelease | Should -BeTrue + $decision.PatchRelease | Should -BeFalse + $decision.HasVersionBump | Should -BeTrue + $decision.ShouldPublish | Should -BeTrue + } + + It 'Recognizes a Patch label and sets PatchRelease' { + $pr = [PSCustomObject]@{ HeadRef = 'fix/typo'; Labels = @('Patch') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.MajorRelease | Should -BeFalse + $decision.MinorRelease | Should -BeFalse + $decision.PatchRelease | Should -BeTrue + $decision.HasVersionBump | Should -BeTrue + $decision.ShouldPublish | Should -BeTrue + } + + It 'Major wins when both Major and Minor labels are present' { + $pr = [PSCustomObject]@{ HeadRef = 'feat/breaking'; Labels = @('Major', 'Minor') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.MajorRelease | Should -BeTrue + $decision.MinorRelease | Should -BeFalse + $decision.PatchRelease | Should -BeFalse + } + + It 'Minor wins when both Minor and Patch labels are present' { + $pr = [PSCustomObject]@{ HeadRef = 'feat/mixed'; Labels = @('Minor', 'Patch') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.MajorRelease | Should -BeFalse + $decision.MinorRelease | Should -BeTrue + $decision.PatchRelease | Should -BeFalse + } + + It 'No bump labels and AutoPatching disabled means no release' { + $pr = [PSCustomObject]@{ HeadRef = 'chore/cleanup'; Labels = @('documentation') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.HasVersionBump | Should -BeFalse + $decision.ShouldPublish | Should -BeFalse + } + + It 'AutoPatching triggers a patch bump when no bump labels are present' { + $config = [PSCustomObject]@{ + AutoPatching = $true + IncrementalPrerelease = $false + DatePrereleaseFormat = '' + VersionPrefix = 'v' + ReleaseType = 'Release' + IgnoreLabels = @() + MajorLabels = @('Major') + MinorLabels = @('Minor') + PatchLabels = @('Patch') + } + $pr = [PSCustomObject]@{ HeadRef = 'chore/deps'; Labels = @() } + $decision = Resolve-ReleaseDecision -Configuration $config -PullRequest $pr + + $decision.PatchRelease | Should -BeTrue + $decision.HasVersionBump | Should -BeTrue + $decision.ShouldPublish | Should -BeTrue + } + } + + Context 'Ignore labels' { + It 'Suppresses release when an ignore label is present' { + $pr = [PSCustomObject]@{ HeadRef = 'feat/skipped'; Labels = @('Patch', 'skip-release') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.ShouldPublish | Should -BeFalse + } + + It 'Ignore label overrides even a Major bump label' { + $pr = [PSCustomObject]@{ HeadRef = 'feat/ignored'; Labels = @('Major', 'skip-release') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.ShouldPublish | Should -BeFalse + } + } + + Context 'ReleaseType behavior' { + It 'ReleaseType None means ShouldPublish is false regardless of labels' { + $config = [PSCustomObject]@{ + AutoPatching = $false + IncrementalPrerelease = $false + DatePrereleaseFormat = '' + VersionPrefix = 'v' + ReleaseType = 'None' + IgnoreLabels = @() + MajorLabels = @('Major') + MinorLabels = @('Minor') + PatchLabels = @('Patch') + } + $pr = [PSCustomObject]@{ HeadRef = 'feat/none'; Labels = @('Major') } + $decision = Resolve-ReleaseDecision -Configuration $config -PullRequest $pr + + $decision.ShouldPublish | Should -BeFalse + $decision.CreateRelease | Should -BeFalse + $decision.CreatePrerelease | Should -BeFalse + } + + It 'ReleaseType Prerelease sets CreatePrerelease and not CreateRelease' { + $config = [PSCustomObject]@{ + AutoPatching = $false + IncrementalPrerelease = $false + DatePrereleaseFormat = '' + VersionPrefix = 'v' + ReleaseType = 'Prerelease' + IgnoreLabels = @() + MajorLabels = @('Major') + MinorLabels = @('Minor') + PatchLabels = @('Patch') + } + $pr = [PSCustomObject]@{ HeadRef = 'feat/pre'; Labels = @('Minor') } + $decision = Resolve-ReleaseDecision -Configuration $config -PullRequest $pr + + $decision.CreatePrerelease | Should -BeTrue + $decision.CreateRelease | Should -BeFalse + $decision.ShouldPublish | Should -BeTrue + } + + It 'ReleaseType Release sets CreateRelease and not CreatePrerelease' { + $pr = [PSCustomObject]@{ HeadRef = 'feat/release'; Labels = @('Minor') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.CreateRelease | Should -BeTrue + $decision.CreatePrerelease | Should -BeFalse + $decision.ShouldPublish | Should -BeTrue + } + } + + Context 'PrereleaseName derivation' { + It 'Strips non-alphanumeric characters from branch name' { + $pr = [PSCustomObject]@{ HeadRef = 'feat/add-prerelease-support'; Labels = @('Minor') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.PrereleaseName | Should -Be 'feataddprereleasesupport' + } + + It 'Handles branch names with dots and underscores' { + $pr = [PSCustomObject]@{ HeadRef = 'release/v2.0_rc1'; Labels = @('Major') } + $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr + + $decision.PrereleaseName | Should -Be 'releasev20rc1' + } + } +} + +Describe 'Get-NextModuleVersion' { + BeforeAll { + $baseConfig = [PSCustomObject]@{ + AutoPatching = $false + IncrementalPrerelease = $false + DatePrereleaseFormat = '' + VersionPrefix = 'v' + ReleaseType = 'Release' + IgnoreLabels = @() + MajorLabels = @('Major') + MinorLabels = @('Minor') + PatchLabels = @('Patch') + } + } + + Context 'Bump selection' { + BeforeAll { + # Dummy releases array to satisfy the mandatory [array] parameter. + $script:dummyReleases = @([PSCustomObject]@{ tagName = 'v0.0.1'; isLatest = $true; isPrerelease = $false }) + } + + It 'Major bump increments major and resets minor and patch' { + $latestVersion = New-PSSemVer -Version '1.2.3' + $decision = [PSCustomObject]@{ + ShouldPublish = $true + CreateRelease = $true + CreatePrerelease = $false + MajorRelease = $true + MinorRelease = $false + PatchRelease = $false + HasVersionBump = $true + PrereleaseName = 'featbigchange' + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $baseConfig -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.Major | Should -Be 2 + $result.Minor | Should -Be 0 + $result.Patch | Should -Be 0 + $result.Prefix | Should -Be 'v' + } + + It 'Minor bump increments minor and resets patch' { + $latestVersion = New-PSSemVer -Version '1.2.3' + $decision = [PSCustomObject]@{ + ShouldPublish = $true + CreateRelease = $true + CreatePrerelease = $false + MajorRelease = $false + MinorRelease = $true + PatchRelease = $false + HasVersionBump = $true + PrereleaseName = 'featnewapi' + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $baseConfig -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.Major | Should -Be 1 + $result.Minor | Should -Be 3 + $result.Patch | Should -Be 0 + } + + It 'Patch bump increments patch only' { + $latestVersion = New-PSSemVer -Version '1.2.3' + $decision = [PSCustomObject]@{ + ShouldPublish = $true + CreateRelease = $true + CreatePrerelease = $false + MajorRelease = $false + MinorRelease = $false + PatchRelease = $true + HasVersionBump = $true + PrereleaseName = 'fixtypo' + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $baseConfig -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.Major | Should -Be 1 + $result.Minor | Should -Be 2 + $result.Patch | Should -Be 4 + } + + It 'Applies version prefix from configuration' { + $latestVersion = New-PSSemVer -Version '0.0.0' + $decision = [PSCustomObject]@{ + ShouldPublish = $true + CreateRelease = $true + CreatePrerelease = $false + MajorRelease = $false + MinorRelease = $true + PatchRelease = $false + HasVersionBump = $true + PrereleaseName = 'feat' + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $baseConfig -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.ToString() | Should -BeLike 'v*' + } + + It 'Starts from 0.0.0 correctly for first release' { + $latestVersion = New-PSSemVer -Version '0.0.0' + $decision = [PSCustomObject]@{ + ShouldPublish = $true + CreateRelease = $true + CreatePrerelease = $false + MajorRelease = $false + MinorRelease = $false + PatchRelease = $true + HasVersionBump = $true + PrereleaseName = 'init' + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $baseConfig -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.Major | Should -Be 0 + $result.Minor | Should -Be 0 + $result.Patch | Should -Be 1 + } + } + + Context 'Prerelease suffix' { + BeforeAll { + $script:dummyReleases = @([PSCustomObject]@{ tagName = 'v0.0.1'; isLatest = $true; isPrerelease = $false }) + } + + It 'Does not add prerelease tag for a stable release' { + $latestVersion = New-PSSemVer -Version '1.0.0' + $decision = [PSCustomObject]@{ + ShouldPublish = $true + CreateRelease = $true + CreatePrerelease = $false + MajorRelease = $false + MinorRelease = $true + PatchRelease = $false + HasVersionBump = $true + PrereleaseName = 'featstable' + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $baseConfig -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.Prerelease | Should -BeNullOrEmpty + } + + It 'Adds branch-derived prerelease tag for a prerelease' { + $latestVersion = New-PSSemVer -Version '1.0.0' + $decision = [PSCustomObject]@{ + ShouldPublish = $true + CreateRelease = $false + CreatePrerelease = $true + MajorRelease = $false + MinorRelease = $true + PatchRelease = $false + HasVersionBump = $true + PrereleaseName = 'featnewfeature' + } + $config = [PSCustomObject]@{ + AutoPatching = $false + IncrementalPrerelease = $false + DatePrereleaseFormat = '' + VersionPrefix = 'v' + ReleaseType = 'Prerelease' + IgnoreLabels = @() + MajorLabels = @('Major') + MinorLabels = @('Minor') + PatchLabels = @('Patch') + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $config -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.Prerelease | Should -Be 'featnewfeature' + } + + It 'Appends incremental number when IncrementalPrerelease is enabled' { + Mock Find-PSResource { return @() } -ModuleName Helpers + + $latestVersion = New-PSSemVer -Version '2.0.0' + $decision = [PSCustomObject]@{ + ShouldPublish = $true + CreateRelease = $false + CreatePrerelease = $true + MajorRelease = $false + MinorRelease = $false + PatchRelease = $true + HasVersionBump = $true + PrereleaseName = 'featincr' + } + $config = [PSCustomObject]@{ + AutoPatching = $false + IncrementalPrerelease = $true + DatePrereleaseFormat = '' + VersionPrefix = 'v' + ReleaseType = 'Prerelease' + IgnoreLabels = @() + MajorLabels = @('Major') + MinorLabels = @('Minor') + PatchLabels = @('Patch') + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $config -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.Prerelease | Should -Be 'featincr001' + } + } +} + +Describe 'Get-NextPrereleaseNumber' { + BeforeAll { + # A non-matching release entry so the mandatory [array] parameter is satisfied. + $script:noMatchRelease = @([PSCustomObject]@{ tagName = 'v99.99.99'; isPrerelease = $false; isLatest = $false }) + } + + Context 'Sequencing from empty state' { + It 'Returns 001 when no existing prereleases exist' { + Mock Find-PSResource { return @() } -ModuleName Helpers + + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '1.1.0' ` + -PrereleaseName 'featbranch' -Releases $script:noMatchRelease + + $result | Should -Be '001' + } + } + + Context 'Sequencing from existing PSGallery prereleases' { + It 'Increments past the highest PSGallery prerelease number' { + $mockPSGalleryResults = @( + [PSCustomObject]@{ Version = [version]'1.1.0'; Prerelease = 'featbranch002' } + [PSCustomObject]@{ Version = [version]'1.1.0'; Prerelease = 'featbranch001' } + ) + Mock Find-PSResource { return $mockPSGalleryResults } -ModuleName Helpers + + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '1.1.0' ` + -PrereleaseName 'featbranch' -Releases $script:noMatchRelease + + $result | Should -Be '003' + } + } + + Context 'Sequencing from existing GitHub releases' { + It 'Increments past the highest GitHub release prerelease number' { + Mock Find-PSResource { return @() } -ModuleName Helpers + + $releases = @( + [PSCustomObject]@{ tagName = 'v1.1.0-featbranch001'; isPrerelease = $true; isLatest = $false } + [PSCustomObject]@{ tagName = 'v1.1.0-featbranch003'; isPrerelease = $true; isLatest = $false } + ) + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '1.1.0' ` + -PrereleaseName 'featbranch' -Releases $releases + + $result | Should -Be '004' + } + } + + Context 'Uses maximum across both sources' { + It 'Takes the higher number between PSGallery and GitHub' { + $mockPSGalleryResults = @( + [PSCustomObject]@{ Version = [version]'2.0.0'; Prerelease = 'mybranch005' } + ) + Mock Find-PSResource { return $mockPSGalleryResults } -ModuleName Helpers + + $releases = @( + [PSCustomObject]@{ tagName = 'v2.0.0-mybranch003'; isPrerelease = $true; isLatest = $false } + ) + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '2.0.0' ` + -PrereleaseName 'mybranch' -Releases $releases + + $result | Should -Be '006' + } + + It 'Takes the higher number when GitHub has more' { + $mockPSGalleryResults = @( + [PSCustomObject]@{ Version = [version]'2.0.0'; Prerelease = 'mybranch002' } + ) + Mock Find-PSResource { return $mockPSGalleryResults } -ModuleName Helpers + + $releases = @( + [PSCustomObject]@{ tagName = 'v2.0.0-mybranch007'; isPrerelease = $true; isLatest = $false } + ) + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '2.0.0' ` + -PrereleaseName 'mybranch' -Releases $releases + + $result | Should -Be '008' + } + } + + Context 'Zero-padding' { + It 'Pads single-digit numbers to three digits' { + Mock Find-PSResource { return @() } -ModuleName Helpers + + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '1.0.0' ` + -PrereleaseName 'feat' -Releases $script:noMatchRelease + + $result | Should -Be '001' + $result.Length | Should -Be 3 + } + + It 'Pads double-digit numbers to three digits' { + Mock Find-PSResource { return @() } -ModuleName Helpers + + $releases = @( + [PSCustomObject]@{ tagName = 'v1.0.0-feat098'; isPrerelease = $true; isLatest = $false } + ) + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '1.0.0' ` + -PrereleaseName 'feat' -Releases $releases + + $result | Should -Be '099' + $result.Length | Should -Be 3 + } + } + + Context 'Filtering' { + It 'Ignores prereleases for different base versions' { + $mockPSGalleryResults = @( + [PSCustomObject]@{ Version = [version]'2.0.0'; Prerelease = 'feat010' } + ) + Mock Find-PSResource { return $mockPSGalleryResults } -ModuleName Helpers + + $releases = @( + [PSCustomObject]@{ tagName = 'v2.0.0-feat005'; isPrerelease = $true; isLatest = $false } + ) + # Query for a DIFFERENT base version + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '3.0.0' ` + -PrereleaseName 'feat' -Releases $releases + + $result | Should -Be '001' + } + + It 'Ignores prereleases for different branch names' { + Mock Find-PSResource { return @() } -ModuleName Helpers + + $releases = @( + [PSCustomObject]@{ tagName = 'v1.0.0-otherbranch010'; isPrerelease = $true; isLatest = $false } + ) + $result = Get-NextPrereleaseNumber -ModuleName 'TestModule' -BaseVersion '1.0.0' ` + -PrereleaseName 'mybranch' -Releases $releases + + $result | Should -Be '001' + } + } +} diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 43816d3..0000000 --- a/tests/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Placeholder for tests - -Location for tests of the action. From 60a77dac8f634e3e5dd8a0313b88444c5013230e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Wed, 27 May 2026 18:34:05 +0200 Subject: [PATCH 2/5] ci: Add Pester-Tests workflow to run unit tests on PRs --- .github/workflows/Pester-Tests.yml | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/Pester-Tests.yml diff --git a/.github/workflows/Pester-Tests.yml b/.github/workflows/Pester-Tests.yml new file mode 100644 index 0000000..addcaaf --- /dev/null +++ b/.github/workflows/Pester-Tests.yml @@ -0,0 +1,41 @@ +name: Pester-Tests + +run-name: "Pester-Tests - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" + +on: + workflow_dispatch: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + Pester: + name: Pester Tests + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install dependencies + shell: pwsh + run: | + Install-PSResource -Name PSSemVer -Repository PSGallery -TrustRepository + Install-PSResource -Name Pester -Repository PSGallery -TrustRepository + + - name: Run Pester tests + shell: pwsh + run: | + $config = New-PesterConfiguration + $config.Run.Path = './tests' + $config.Output.Verbosity = 'Detailed' + $config.TestResult.Enabled = $true + $config.TestResult.OutputPath = './tests/testResults.xml' + $config.TestResult.OutputFormat = 'NUnitXml' + Invoke-Pester -Configuration $config From 67dddd8e006aeb10b7c517dc087e66044ac454a6 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Wed, 27 May 2026 18:35:23 +0200 Subject: [PATCH 3/5] ci: Move Pester tests into Action-Test workflow --- .github/workflows/Action-Test.yml | 26 +++++++++++++++++++ .github/workflows/Pester-Tests.yml | 41 ------------------------------ 2 files changed, 26 insertions(+), 41 deletions(-) delete mode 100644 .github/workflows/Pester-Tests.yml diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 3df83f7..9722419 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -647,3 +647,29 @@ jobs: $failed = $true } if ($failed) { exit 1 } + + Pester-Unit-Tests: + name: Pester Unit Tests + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install dependencies + shell: pwsh + run: | + Install-PSResource -Name PSSemVer -Repository PSGallery -TrustRepository + Install-PSResource -Name Pester -Repository PSGallery -TrustRepository + + - name: Run Pester tests + shell: pwsh + run: | + $config = New-PesterConfiguration + $config.Run.Path = './tests' + $config.Output.Verbosity = 'Detailed' + $config.TestResult.Enabled = $true + $config.TestResult.OutputPath = './tests/testResults.xml' + $config.TestResult.OutputFormat = 'NUnitXml' + Invoke-Pester -Configuration $config diff --git a/.github/workflows/Pester-Tests.yml b/.github/workflows/Pester-Tests.yml deleted file mode 100644 index addcaaf..0000000 --- a/.github/workflows/Pester-Tests.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Pester-Tests - -run-name: "Pester-Tests - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" - -on: - workflow_dispatch: - pull_request: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - Pester: - name: Pester Tests - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Install dependencies - shell: pwsh - run: | - Install-PSResource -Name PSSemVer -Repository PSGallery -TrustRepository - Install-PSResource -Name Pester -Repository PSGallery -TrustRepository - - - name: Run Pester tests - shell: pwsh - run: | - $config = New-PesterConfiguration - $config.Run.Path = './tests' - $config.Output.Verbosity = 'Detailed' - $config.TestResult.Enabled = $true - $config.TestResult.OutputPath = './tests/testResults.xml' - $config.TestResult.OutputFormat = 'NUnitXml' - Invoke-Pester -Configuration $config From 2cbadc659a096253a4e2bacc4ae716374ee29afc Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Wed, 27 May 2026 18:36:24 +0200 Subject: [PATCH 4/5] ci: Use PSModule/Invoke-Pester action for unit tests --- .github/workflows/Action-Test.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 9722419..e54f437 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -657,19 +657,13 @@ jobs: with: persist-credentials: false - - name: Install dependencies + - name: Install PSSemVer shell: pwsh run: | Install-PSResource -Name PSSemVer -Repository PSGallery -TrustRepository - Install-PSResource -Name Pester -Repository PSGallery -TrustRepository - name: Run Pester tests - shell: pwsh - run: | - $config = New-PesterConfiguration - $config.Run.Path = './tests' - $config.Output.Verbosity = 'Detailed' - $config.TestResult.Enabled = $true - $config.TestResult.OutputPath = './tests/testResults.xml' - $config.TestResult.OutputFormat = 'NUnitXml' - Invoke-Pester -Configuration $config + uses: PSModule/Invoke-Pester@266d1cf2532f572470b7c4463fa1072f2bfe4455 # v4.2.5 + with: + Run_Path: ./tests + Output_Verbosity: Detailed From 3160bdcfb43d55d3c9523bfd6d56d6b41b0af54b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Wed, 27 May 2026 18:41:03 +0200 Subject: [PATCH 5/5] fix: Always compute version regardless of release type or publish decision --- scripts/Helpers.psm1 | 28 +++++++++-------------- scripts/main.ps1 | 26 +++++++++------------- tests/Helpers.Tests.ps1 | 49 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/scripts/Helpers.psm1 b/scripts/Helpers.psm1 index cb3eb8a..96695cf 100644 --- a/scripts/Helpers.psm1 +++ b/scripts/Helpers.psm1 @@ -222,23 +222,17 @@ function Resolve-ReleaseDecision { $shouldPublish = $false } - $majorRelease = $false - $minorRelease = $false - $patchRelease = $false - $hasVersionBump = $false - - if ($shouldPublish) { - $majorRelease = ($labels | Where-Object { $Configuration.MajorLabels -contains $_ }).Count -gt 0 - $minorRelease = ($labels | Where-Object { $Configuration.MinorLabels -contains $_ }).Count -gt 0 -and -not $majorRelease - $patchRelease = ( - (($labels | Where-Object { $Configuration.PatchLabels -contains $_ }).Count -gt 0) -or $Configuration.AutoPatching - ) -and -not $majorRelease -and -not $minorRelease - - $hasVersionBump = $majorRelease -or $minorRelease -or $patchRelease - if (-not $hasVersionBump) { - Write-Host 'No version bump label found and AutoPatching is disabled. No release will be created.' - $shouldPublish = $false - } + $majorRelease = ($labels | Where-Object { $Configuration.MajorLabels -contains $_ }).Count -gt 0 + $minorRelease = ($labels | Where-Object { $Configuration.MinorLabels -contains $_ }).Count -gt 0 -and -not $majorRelease + $patchRelease = ( + (($labels | Where-Object { $Configuration.PatchLabels -contains $_ }).Count -gt 0) -or $Configuration.AutoPatching + ) -and -not $majorRelease -and -not $minorRelease + + $hasVersionBump = $majorRelease -or $minorRelease -or $patchRelease + + if ($shouldPublish -and -not $hasVersionBump) { + Write-Host 'No version bump label found and AutoPatching is disabled. No release will be created.' + $shouldPublish = $false } Write-Host '-------------------------------------------------' diff --git a/scripts/main.ps1 b/scripts/main.ps1 index 73f6d36..b22dd6a 100644 --- a/scripts/main.ps1 +++ b/scripts/main.ps1 @@ -11,22 +11,18 @@ $config = Get-PublishConfiguration -SettingsJson $actionInput.SettingsJson $pullRequest = Get-GitHubPullRequest $decision = Resolve-ReleaseDecision -Configuration $config -PullRequest $pullRequest -$newVersion = $null +$releases = Get-GitHubRelease +$ghVersion = Get-LatestGitHubVersion -Releases $releases +$psGalleryVersion = Get-LatestPSGalleryVersion -ModuleName $actionInput.Name +$latestVersion = Get-LatestPublishedVersion -GitHubVersion $ghVersion -PSGalleryVersion $psGalleryVersion -if ($decision.ShouldPublish) { - $releases = Get-GitHubRelease - $ghVersion = Get-LatestGitHubVersion -Releases $releases - $psGalleryVersion = Get-LatestPSGalleryVersion -ModuleName $actionInput.Name - $latestVersion = Get-LatestPublishedVersion -GitHubVersion $ghVersion -PSGalleryVersion $psGalleryVersion - - $params = @{ - LatestVersion = $latestVersion - Decision = $decision - Configuration = $config - ModuleName = $actionInput.Name - Releases = $releases - } - $newVersion = Get-NextModuleVersion @params +$params = @{ + LatestVersion = $latestVersion + Decision = $decision + Configuration = $config + ModuleName = $actionInput.Name + Releases = $releases } +$newVersion = Get-NextModuleVersion @params Write-ActionOutput -Decision $decision -NewVersion $newVersion diff --git a/tests/Helpers.Tests.ps1 b/tests/Helpers.Tests.ps1 index 979c5d9..3ba6879 100644 --- a/tests/Helpers.Tests.ps1 +++ b/tests/Helpers.Tests.ps1 @@ -153,16 +153,18 @@ Describe 'Resolve-ReleaseDecision' { $decision.ShouldPublish | Should -BeFalse } - It 'Ignore label overrides even a Major bump label' { + It 'Ignore label overrides even a Major bump label but still evaluates version bump' { $pr = [PSCustomObject]@{ HeadRef = 'feat/ignored'; Labels = @('Major', 'skip-release') } $decision = Resolve-ReleaseDecision -Configuration $defaultConfig -PullRequest $pr $decision.ShouldPublish | Should -BeFalse + $decision.MajorRelease | Should -BeTrue + $decision.HasVersionBump | Should -BeTrue } } Context 'ReleaseType behavior' { - It 'ReleaseType None means ShouldPublish is false regardless of labels' { + It 'ReleaseType None means ShouldPublish is false but still evaluates version bump labels' { $config = [PSCustomObject]@{ AutoPatching = $false IncrementalPrerelease = $false @@ -180,6 +182,27 @@ Describe 'Resolve-ReleaseDecision' { $decision.ShouldPublish | Should -BeFalse $decision.CreateRelease | Should -BeFalse $decision.CreatePrerelease | Should -BeFalse + $decision.MajorRelease | Should -BeTrue + $decision.HasVersionBump | Should -BeTrue + } + + It 'ReleaseType None with no bump labels has HasVersionBump false' { + $config = [PSCustomObject]@{ + AutoPatching = $false + IncrementalPrerelease = $false + DatePrereleaseFormat = '' + VersionPrefix = 'v' + ReleaseType = 'None' + IgnoreLabels = @() + MajorLabels = @('Major') + MinorLabels = @('Minor') + PatchLabels = @('Patch') + } + $pr = [PSCustomObject]@{ HeadRef = 'feat/none'; Labels = @('documentation') } + $decision = Resolve-ReleaseDecision -Configuration $config -PullRequest $pr + + $decision.ShouldPublish | Should -BeFalse + $decision.HasVersionBump | Should -BeFalse } It 'ReleaseType Prerelease sets CreatePrerelease and not CreateRelease' { @@ -353,6 +376,28 @@ Describe 'Get-NextModuleVersion' { $result.Minor | Should -Be 0 $result.Patch | Should -Be 1 } + + It 'Returns latest version unchanged when HasVersionBump is false (e.g. ReleaseType None)' { + $latestVersion = New-PSSemVer -Version '1.2.3' + $decision = [PSCustomObject]@{ + ShouldPublish = $false + CreateRelease = $false + CreatePrerelease = $false + MajorRelease = $false + MinorRelease = $false + PatchRelease = $false + HasVersionBump = $false + PrereleaseName = 'featnone' + } + + $result = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $baseConfig -ModuleName 'TestModule' -Releases $script:dummyReleases + + $result.Major | Should -Be 1 + $result.Minor | Should -Be 2 + $result.Patch | Should -Be 3 + $result.Prerelease | Should -BeNullOrEmpty + } } Context 'Prerelease suffix' {