diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 71fc1d7..3df83f7 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -60,37 +60,56 @@ jobs: } } } + EventJson: '{"pull_request":{"head":{"ref":"feat/test-patch"},"labels":[{"name":"patch"}]}}' - name: Verify - Patch bump shell: pwsh env: + GH_TOKEN: ${{ github.token }} RESOLVE_CREATE_RELEASE: ${{ steps.resolve.outputs.CreateRelease }} RESOLVE_VERSION: ${{ steps.resolve.outputs.Version }} RESOLVE_RELEASE_TYPE: ${{ steps.resolve.outputs.ReleaseType }} RESOLVE_FULL_VERSION: ${{ steps.resolve.outputs.FullVersion }} run: | - $createRelease = $env:RESOLVE_CREATE_RELEASE - $version = $env:RESOLVE_VERSION - $releaseType = $env:RESOLVE_RELEASE_TYPE - $fullVersion = $env:RESOLVE_FULL_VERSION - - if ($createRelease -ne 'true') { - Write-Error "Expected CreateRelease='true', got '$createRelease'" - exit 1 + $PSStyle.OutputRendering = 'Ansi' + + $releases = (gh release list --json 'tagName,isLatest') | ConvertFrom-Json + $latestTag = ($releases | Where-Object { $_.isLatest }).tagName + if ([string]::IsNullOrEmpty($latestTag)) { $parts = @(0, 0, 0) } + else { $parts = ($latestTag -replace '^v', '').Split('.') | ForEach-Object { [int]$_ } } + $expectedVersion = "$($parts[0]).$($parts[1]).$($parts[2] + 1)" + $expectedFullVersion = "v$expectedVersion" + + Write-Host '--- Input ---' + Write-Host "EventJson: {`"pull_request`":{`"head`":{`"ref`":`"feat/test-patch`"},`"labels`":[{`"name`":`"patch`"}]}}" + Write-Host "Settings: ReleaseType=Release, AutoPatching=false, PatchLabels=patch" + Write-Host '' + + @( + [PSCustomObject]@{ Output = 'CreateRelease'; Expected = 'true'; Actual = $env:RESOLVE_CREATE_RELEASE; Passed = $env:RESOLVE_CREATE_RELEASE -eq 'true' } + [PSCustomObject]@{ Output = 'ReleaseType'; Expected = 'Release'; Actual = $env:RESOLVE_RELEASE_TYPE; Passed = $env:RESOLVE_RELEASE_TYPE -eq 'Release' } + [PSCustomObject]@{ Output = 'Version'; Expected = $expectedVersion; Actual = $env:RESOLVE_VERSION; Passed = $env:RESOLVE_VERSION -eq $expectedVersion } + [PSCustomObject]@{ Output = 'FullVersion'; Expected = $expectedFullVersion; Actual = $env:RESOLVE_FULL_VERSION; Passed = $env:RESOLVE_FULL_VERSION -eq $expectedFullVersion } + ) | Format-Table -AutoSize | Out-String -Width 200 + + $failed = $false + if ($env:RESOLVE_CREATE_RELEASE -ne 'true') { + Write-Error "CreateRelease: expected 'true', got '$env:RESOLVE_CREATE_RELEASE'" + $failed = $true } - if ([string]::IsNullOrEmpty($version)) { - Write-Error 'Expected a non-empty Version' - exit 1 + if ($env:RESOLVE_VERSION -ne $expectedVersion) { + Write-Error "Version: expected '$expectedVersion', got '$env:RESOLVE_VERSION'" + $failed = $true } - if ($releaseType -ne 'Release') { - Write-Error "Expected ReleaseType='Release', got '$releaseType'" - exit 1 + if ($env:RESOLVE_RELEASE_TYPE -ne 'Release') { + Write-Error "ReleaseType: expected 'Release', got '$env:RESOLVE_RELEASE_TYPE'" + $failed = $true } - - Write-Host "Version: $version" - Write-Host "FullVersion: $fullVersion" - Write-Host "ReleaseType: $releaseType" - Write-Host "CreateRelease: $createRelease" + if ($env:RESOLVE_FULL_VERSION -ne $expectedFullVersion) { + Write-Error "FullVersion: expected '$expectedFullVersion', got '$env:RESOLVE_FULL_VERSION'" + $failed = $true + } + if ($failed) { exit 1 } ActionTest-MinorBump: name: Action-Test - [Minor bump] @@ -135,37 +154,56 @@ jobs: } } } + EventJson: '{"pull_request":{"head":{"ref":"feat/test-minor"},"labels":[{"name":"minor"}]}}' - name: Verify - Minor bump shell: pwsh env: + GH_TOKEN: ${{ github.token }} RESOLVE_CREATE_RELEASE: ${{ steps.resolve.outputs.CreateRelease }} RESOLVE_VERSION: ${{ steps.resolve.outputs.Version }} RESOLVE_RELEASE_TYPE: ${{ steps.resolve.outputs.ReleaseType }} RESOLVE_FULL_VERSION: ${{ steps.resolve.outputs.FullVersion }} run: | - $createRelease = $env:RESOLVE_CREATE_RELEASE - $version = $env:RESOLVE_VERSION - $releaseType = $env:RESOLVE_RELEASE_TYPE - $fullVersion = $env:RESOLVE_FULL_VERSION - - if ($createRelease -ne 'true') { - Write-Error "Expected CreateRelease='true', got '$createRelease'" - exit 1 + $PSStyle.OutputRendering = 'Ansi' + + $releases = (gh release list --json 'tagName,isLatest') | ConvertFrom-Json + $latestTag = ($releases | Where-Object { $_.isLatest }).tagName + if ([string]::IsNullOrEmpty($latestTag)) { $parts = @(0, 0, 0) } + else { $parts = ($latestTag -replace '^v', '').Split('.') | ForEach-Object { [int]$_ } } + $expectedVersion = "$($parts[0]).$($parts[1] + 1).0" + $expectedFullVersion = "v$expectedVersion" + + Write-Host '--- Input ---' + Write-Host "EventJson: {`"pull_request`":{`"head`":{`"ref`":`"feat/test-minor`"},`"labels`":[{`"name`":`"minor`"}]}}" + Write-Host "Settings: ReleaseType=Release, AutoPatching=false, MinorLabels=minor" + Write-Host '' + + @( + [PSCustomObject]@{ Output = 'CreateRelease'; Expected = 'true'; Actual = $env:RESOLVE_CREATE_RELEASE; Passed = $env:RESOLVE_CREATE_RELEASE -eq 'true' } + [PSCustomObject]@{ Output = 'ReleaseType'; Expected = 'Release'; Actual = $env:RESOLVE_RELEASE_TYPE; Passed = $env:RESOLVE_RELEASE_TYPE -eq 'Release' } + [PSCustomObject]@{ Output = 'Version'; Expected = $expectedVersion; Actual = $env:RESOLVE_VERSION; Passed = $env:RESOLVE_VERSION -eq $expectedVersion } + [PSCustomObject]@{ Output = 'FullVersion'; Expected = $expectedFullVersion; Actual = $env:RESOLVE_FULL_VERSION; Passed = $env:RESOLVE_FULL_VERSION -eq $expectedFullVersion } + ) | Format-Table -AutoSize | Out-String -Width 200 + + $failed = $false + if ($env:RESOLVE_CREATE_RELEASE -ne 'true') { + Write-Error "CreateRelease: expected 'true', got '$env:RESOLVE_CREATE_RELEASE'" + $failed = $true } - if ([string]::IsNullOrEmpty($version)) { - Write-Error 'Expected a non-empty Version' - exit 1 + if ($env:RESOLVE_VERSION -ne $expectedVersion) { + Write-Error "Version: expected '$expectedVersion', got '$env:RESOLVE_VERSION'" + $failed = $true } - if ($releaseType -ne 'Release') { - Write-Error "Expected ReleaseType='Release', got '$releaseType'" - exit 1 + if ($env:RESOLVE_RELEASE_TYPE -ne 'Release') { + Write-Error "ReleaseType: expected 'Release', got '$env:RESOLVE_RELEASE_TYPE'" + $failed = $true } - - Write-Host "Version: $version" - Write-Host "FullVersion: $fullVersion" - Write-Host "ReleaseType: $releaseType" - Write-Host "CreateRelease: $createRelease" + if ($env:RESOLVE_FULL_VERSION -ne $expectedFullVersion) { + Write-Error "FullVersion: expected '$expectedFullVersion', got '$env:RESOLVE_FULL_VERSION'" + $failed = $true + } + if ($failed) { exit 1 } ActionTest-MajorBump: name: Action-Test - [Major bump] @@ -210,37 +248,56 @@ jobs: } } } + EventJson: '{"pull_request":{"head":{"ref":"feat/test-major"},"labels":[{"name":"major"}]}}' - name: Verify - Major bump shell: pwsh env: + GH_TOKEN: ${{ github.token }} RESOLVE_CREATE_RELEASE: ${{ steps.resolve.outputs.CreateRelease }} RESOLVE_VERSION: ${{ steps.resolve.outputs.Version }} RESOLVE_RELEASE_TYPE: ${{ steps.resolve.outputs.ReleaseType }} RESOLVE_FULL_VERSION: ${{ steps.resolve.outputs.FullVersion }} run: | - $createRelease = $env:RESOLVE_CREATE_RELEASE - $version = $env:RESOLVE_VERSION - $releaseType = $env:RESOLVE_RELEASE_TYPE - $fullVersion = $env:RESOLVE_FULL_VERSION - - if ($createRelease -ne 'true') { - Write-Error "Expected CreateRelease='true', got '$createRelease'" - exit 1 + $PSStyle.OutputRendering = 'Ansi' + + $releases = (gh release list --json 'tagName,isLatest') | ConvertFrom-Json + $latestTag = ($releases | Where-Object { $_.isLatest }).tagName + if ([string]::IsNullOrEmpty($latestTag)) { $parts = @(0, 0, 0) } + else { $parts = ($latestTag -replace '^v', '').Split('.') | ForEach-Object { [int]$_ } } + $expectedVersion = "$($parts[0] + 1).0.0" + $expectedFullVersion = "v$expectedVersion" + + Write-Host '--- Input ---' + Write-Host "EventJson: {`"pull_request`":{`"head`":{`"ref`":`"feat/test-major`"},`"labels`":[{`"name`":`"major`"}]}}" + Write-Host "Settings: ReleaseType=Release, AutoPatching=false, MajorLabels=major" + Write-Host '' + + @( + [PSCustomObject]@{ Output = 'CreateRelease'; Expected = 'true'; Actual = $env:RESOLVE_CREATE_RELEASE; Passed = $env:RESOLVE_CREATE_RELEASE -eq 'true' } + [PSCustomObject]@{ Output = 'ReleaseType'; Expected = 'Release'; Actual = $env:RESOLVE_RELEASE_TYPE; Passed = $env:RESOLVE_RELEASE_TYPE -eq 'Release' } + [PSCustomObject]@{ Output = 'Version'; Expected = $expectedVersion; Actual = $env:RESOLVE_VERSION; Passed = $env:RESOLVE_VERSION -eq $expectedVersion } + [PSCustomObject]@{ Output = 'FullVersion'; Expected = $expectedFullVersion; Actual = $env:RESOLVE_FULL_VERSION; Passed = $env:RESOLVE_FULL_VERSION -eq $expectedFullVersion } + ) | Format-Table -AutoSize | Out-String -Width 200 + + $failed = $false + if ($env:RESOLVE_CREATE_RELEASE -ne 'true') { + Write-Error "CreateRelease: expected 'true', got '$env:RESOLVE_CREATE_RELEASE'" + $failed = $true } - if ([string]::IsNullOrEmpty($version)) { - Write-Error 'Expected a non-empty Version' - exit 1 + if ($env:RESOLVE_VERSION -ne $expectedVersion) { + Write-Error "Version: expected '$expectedVersion', got '$env:RESOLVE_VERSION'" + $failed = $true } - if ($releaseType -ne 'Release') { - Write-Error "Expected ReleaseType='Release', got '$releaseType'" - exit 1 + if ($env:RESOLVE_RELEASE_TYPE -ne 'Release') { + Write-Error "ReleaseType: expected 'Release', got '$env:RESOLVE_RELEASE_TYPE'" + $failed = $true } - - Write-Host "Version: $version" - Write-Host "FullVersion: $fullVersion" - Write-Host "ReleaseType: $releaseType" - Write-Host "CreateRelease: $createRelease" + if ($env:RESOLVE_FULL_VERSION -ne $expectedFullVersion) { + Write-Error "FullVersion: expected '$expectedFullVersion', got '$env:RESOLVE_FULL_VERSION'" + $failed = $true + } + if ($failed) { exit 1 } ActionTest-AutoPatch: name: Action-Test - [Auto-patch] @@ -283,37 +340,56 @@ jobs: } } } + EventJson: '{"pull_request":{"head":{"ref":"feat/test-autopatch"},"labels":[]}}' - name: Verify - Auto-patch shell: pwsh env: + GH_TOKEN: ${{ github.token }} RESOLVE_CREATE_RELEASE: ${{ steps.resolve.outputs.CreateRelease }} RESOLVE_VERSION: ${{ steps.resolve.outputs.Version }} RESOLVE_RELEASE_TYPE: ${{ steps.resolve.outputs.ReleaseType }} RESOLVE_FULL_VERSION: ${{ steps.resolve.outputs.FullVersion }} run: | - $createRelease = $env:RESOLVE_CREATE_RELEASE - $version = $env:RESOLVE_VERSION - $releaseType = $env:RESOLVE_RELEASE_TYPE - $fullVersion = $env:RESOLVE_FULL_VERSION - - if ($createRelease -ne 'true') { - Write-Error "Expected CreateRelease='true', got '$createRelease'" - exit 1 + $PSStyle.OutputRendering = 'Ansi' + + $releases = (gh release list --json 'tagName,isLatest') | ConvertFrom-Json + $latestTag = ($releases | Where-Object { $_.isLatest }).tagName + if ([string]::IsNullOrEmpty($latestTag)) { $parts = @(0, 0, 0) } + else { $parts = ($latestTag -replace '^v', '').Split('.') | ForEach-Object { [int]$_ } } + $expectedVersion = "$($parts[0]).$($parts[1]).$($parts[2] + 1)" + $expectedFullVersion = "v$expectedVersion" + + Write-Host '--- Input ---' + Write-Host "EventJson: {`"pull_request`":{`"head`":{`"ref`":`"feat/test-autopatch`"},`"labels`":[]}}" + Write-Host "Settings: ReleaseType=Release, AutoPatching=true, PatchLabels=patch" + Write-Host '' + + @( + [PSCustomObject]@{ Output = 'CreateRelease'; Expected = 'true'; Actual = $env:RESOLVE_CREATE_RELEASE; Passed = $env:RESOLVE_CREATE_RELEASE -eq 'true' } + [PSCustomObject]@{ Output = 'ReleaseType'; Expected = 'Release'; Actual = $env:RESOLVE_RELEASE_TYPE; Passed = $env:RESOLVE_RELEASE_TYPE -eq 'Release' } + [PSCustomObject]@{ Output = 'Version'; Expected = $expectedVersion; Actual = $env:RESOLVE_VERSION; Passed = $env:RESOLVE_VERSION -eq $expectedVersion } + [PSCustomObject]@{ Output = 'FullVersion'; Expected = $expectedFullVersion; Actual = $env:RESOLVE_FULL_VERSION; Passed = $env:RESOLVE_FULL_VERSION -eq $expectedFullVersion } + ) | Format-Table -AutoSize | Out-String -Width 200 + + $failed = $false + if ($env:RESOLVE_CREATE_RELEASE -ne 'true') { + Write-Error "CreateRelease: expected 'true', got '$env:RESOLVE_CREATE_RELEASE'" + $failed = $true } - if ([string]::IsNullOrEmpty($version)) { - Write-Error 'Expected a non-empty Version' - exit 1 + if ($env:RESOLVE_VERSION -ne $expectedVersion) { + Write-Error "Version: expected '$expectedVersion', got '$env:RESOLVE_VERSION'" + $failed = $true } - if ($releaseType -ne 'Release') { - Write-Error "Expected ReleaseType='Release', got '$releaseType'" - exit 1 + if ($env:RESOLVE_RELEASE_TYPE -ne 'Release') { + Write-Error "ReleaseType: expected 'Release', got '$env:RESOLVE_RELEASE_TYPE'" + $failed = $true } - - Write-Host "Version: $version" - Write-Host "FullVersion: $fullVersion" - Write-Host "ReleaseType: $releaseType" - Write-Host "CreateRelease: $createRelease" + if ($env:RESOLVE_FULL_VERSION -ne $expectedFullVersion) { + Write-Error "FullVersion: expected '$expectedFullVersion', got '$env:RESOLVE_FULL_VERSION'" + $failed = $true + } + if ($failed) { exit 1 } ActionTest-IgnoreLabel: name: Action-Test - [Ignore label] @@ -368,20 +444,28 @@ jobs: RESOLVE_CREATE_RELEASE: ${{ steps.resolve.outputs.CreateRelease }} RESOLVE_RELEASE_TYPE: ${{ steps.resolve.outputs.ReleaseType }} run: | - $createRelease = $env:RESOLVE_CREATE_RELEASE - $releaseType = $env:RESOLVE_RELEASE_TYPE - - if ($createRelease -ne 'false') { - Write-Error "Expected CreateRelease='false', got '$createRelease'" - exit 1 + $PSStyle.OutputRendering = 'Ansi' + + Write-Host '--- Input ---' + Write-Host "EventJson: {`"pull_request`":{`"head`":{`"ref`":`"feat/test-ignore`"},`"labels`":[{`"name`":`"patch`"},{`"name`":`"skip-release`"}]}}" + Write-Host "Settings: ReleaseType=Release, AutoPatching=false, IgnoreLabels=skip-release" + Write-Host '' + + @( + [PSCustomObject]@{ Output = 'CreateRelease'; Expected = 'false'; Actual = $env:RESOLVE_CREATE_RELEASE; Passed = $env:RESOLVE_CREATE_RELEASE -eq 'false' } + [PSCustomObject]@{ Output = 'ReleaseType'; Expected = 'None'; Actual = $env:RESOLVE_RELEASE_TYPE; Passed = $env:RESOLVE_RELEASE_TYPE -eq 'None' } + ) | Format-Table -AutoSize | Out-String -Width 200 + + $failed = $false + if ($env:RESOLVE_CREATE_RELEASE -ne 'false') { + Write-Error "CreateRelease: expected 'false', got '$env:RESOLVE_CREATE_RELEASE'" + $failed = $true } - if ($releaseType -ne 'None') { - Write-Error "Expected ReleaseType='None', got '$releaseType'" - exit 1 + if ($env:RESOLVE_RELEASE_TYPE -ne 'None') { + Write-Error "ReleaseType: expected 'None', got '$env:RESOLVE_RELEASE_TYPE'" + $failed = $true } - - Write-Host "ReleaseType: $releaseType" - Write-Host "CreateRelease: $createRelease" + if ($failed) { exit 1 } ActionTest-None: name: Action-Test - [None release type] @@ -424,6 +508,7 @@ jobs: } } } + EventJson: '{"pull_request":{"head":{"ref":"feat/test-none"},"labels":[{"name":"patch"}]}}' - name: Verify - None release type shell: pwsh @@ -432,23 +517,133 @@ jobs: RESOLVE_VERSION: ${{ steps.resolve.outputs.Version }} RESOLVE_RELEASE_TYPE: ${{ steps.resolve.outputs.ReleaseType }} run: | - $createRelease = $env:RESOLVE_CREATE_RELEASE - $version = $env:RESOLVE_VERSION - $releaseType = $env:RESOLVE_RELEASE_TYPE - - if ($createRelease -ne 'false') { - Write-Error "Expected CreateRelease='false', got '$createRelease'" - exit 1 + $PSStyle.OutputRendering = 'Ansi' + + Write-Host '--- Input ---' + Write-Host "EventJson: {`"pull_request`":{`"head`":{`"ref`":`"feat/test-none`"},`"labels`":[{`"name`":`"patch`"}]}}" + Write-Host "Settings: ReleaseType=None, AutoPatching=false" + Write-Host '' + + @( + [PSCustomObject]@{ Output = 'CreateRelease'; Expected = 'false'; Actual = $env:RESOLVE_CREATE_RELEASE; Passed = $env:RESOLVE_CREATE_RELEASE -eq 'false' } + [PSCustomObject]@{ Output = 'ReleaseType'; Expected = 'None'; Actual = $env:RESOLVE_RELEASE_TYPE; Passed = $env:RESOLVE_RELEASE_TYPE -eq 'None' } + [PSCustomObject]@{ Output = 'Version'; Expected = '(empty)'; Actual = $env:RESOLVE_VERSION; Passed = [string]::IsNullOrEmpty($env:RESOLVE_VERSION) } + ) | Format-Table -AutoSize | Out-String -Width 200 + + $failed = $false + if ($env:RESOLVE_CREATE_RELEASE -ne 'false') { + Write-Error "CreateRelease: expected 'false', got '$env:RESOLVE_CREATE_RELEASE'" + $failed = $true } - if (-not [string]::IsNullOrEmpty($version)) { - Write-Error "Expected empty Version, got '$version'" - exit 1 + if (-not [string]::IsNullOrEmpty($env:RESOLVE_VERSION)) { + Write-Error "Version: expected empty, got '$env:RESOLVE_VERSION'" + $failed = $true } - if ($releaseType -ne 'None') { - Write-Error "Expected ReleaseType='None', got '$releaseType'" - exit 1 + if ($env:RESOLVE_RELEASE_TYPE -ne 'None') { + Write-Error "ReleaseType: expected 'None', got '$env:RESOLVE_RELEASE_TYPE'" + $failed = $true } + if ($failed) { exit 1 } + + ActionTest-Prerelease: + name: Action-Test - [Prerelease] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Create fake PR event + shell: pwsh + run: | + $event = @{ + pull_request = @{ + head = @{ ref = 'feat/add-prerelease-support' } + labels = @( + @{ name = 'minor' } + ) + } + } | ConvertTo-Json -Depth 5 + $event | Set-Content -Path "$env:RUNNER_TEMP/event.json" + "GITHUB_EVENT_PATH=$env:RUNNER_TEMP/event.json" | Out-File -FilePath $env:GITHUB_ENV -Append + + - name: Resolve-PSModuleVersion + id: resolve + uses: ./ + env: + GH_TOKEN: ${{ github.token }} + with: + Settings: | + { + "Publish": { + "Module": { + "ReleaseType": "Prerelease", + "AutoPatching": false, + "IncrementalPrerelease": true, + "DatePrereleaseFormat": "", + "VersionPrefix": "v", + "MajorLabels": "major", + "MinorLabels": "minor", + "PatchLabels": "patch", + "IgnoreLabels": "" + } + } + } + EventJson: '{"pull_request":{"head":{"ref":"feat/add-prerelease-support"},"labels":[{"name":"minor"}]}}' - Write-Host "Version: $version" - Write-Host "ReleaseType: $releaseType" - Write-Host "CreateRelease: $createRelease" + - name: Verify - Prerelease + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + RESOLVE_CREATE_RELEASE: ${{ steps.resolve.outputs.CreateRelease }} + RESOLVE_VERSION: ${{ steps.resolve.outputs.Version }} + RESOLVE_PRERELEASE: ${{ steps.resolve.outputs.Prerelease }} + RESOLVE_RELEASE_TYPE: ${{ steps.resolve.outputs.ReleaseType }} + RESOLVE_FULL_VERSION: ${{ steps.resolve.outputs.FullVersion }} + run: | + $PSStyle.OutputRendering = 'Ansi' + + $releases = (gh release list --json 'tagName,isLatest') | ConvertFrom-Json + $latestTag = ($releases | Where-Object { $_.isLatest }).tagName + if ([string]::IsNullOrEmpty($latestTag)) { $parts = @(0, 0, 0) } + else { $parts = ($latestTag -replace '^v', '').Split('.') | ForEach-Object { [int]$_ } } + $expectedVersion = "$($parts[0]).$($parts[1] + 1).0" + $expectedPrerelease = 'feataddprereleasesupport001' + $expectedFullVersion = "v$expectedVersion-$expectedPrerelease" + + Write-Host '--- Input ---' + Write-Host "EventJson: {`"pull_request`":{`"head`":{`"ref`":`"feat/add-prerelease-support`"},`"labels`":[{`"name`":`"minor`"}]}}" + Write-Host "Settings: ReleaseType=Prerelease, AutoPatching=false, IncrementalPrerelease=true, MinorLabels=minor" + Write-Host '' + + @( + [PSCustomObject]@{ Output = 'CreateRelease'; Expected = 'true'; Actual = $env:RESOLVE_CREATE_RELEASE; Passed = $env:RESOLVE_CREATE_RELEASE -eq 'true' } + [PSCustomObject]@{ Output = 'ReleaseType'; Expected = 'Prerelease'; Actual = $env:RESOLVE_RELEASE_TYPE; Passed = $env:RESOLVE_RELEASE_TYPE -eq 'Prerelease' } + [PSCustomObject]@{ Output = 'Version'; Expected = $expectedVersion; Actual = $env:RESOLVE_VERSION; Passed = $env:RESOLVE_VERSION -eq $expectedVersion } + [PSCustomObject]@{ Output = 'Prerelease'; Expected = $expectedPrerelease; Actual = $env:RESOLVE_PRERELEASE; Passed = $env:RESOLVE_PRERELEASE -eq $expectedPrerelease } + [PSCustomObject]@{ Output = 'FullVersion'; Expected = $expectedFullVersion; Actual = $env:RESOLVE_FULL_VERSION; Passed = $env:RESOLVE_FULL_VERSION -eq $expectedFullVersion } + ) | Format-Table -AutoSize | Out-String -Width 200 + + $failed = $false + if ($env:RESOLVE_CREATE_RELEASE -ne 'true') { + Write-Error "CreateRelease: expected 'true', got '$env:RESOLVE_CREATE_RELEASE'" + $failed = $true + } + if ($env:RESOLVE_RELEASE_TYPE -ne 'Prerelease') { + Write-Error "ReleaseType: expected 'Prerelease', got '$env:RESOLVE_RELEASE_TYPE'" + $failed = $true + } + if ($env:RESOLVE_VERSION -ne $expectedVersion) { + Write-Error "Version: expected '$expectedVersion', got '$env:RESOLVE_VERSION'" + $failed = $true + } + if ($env:RESOLVE_PRERELEASE -ne $expectedPrerelease) { + Write-Error "Prerelease: expected '$expectedPrerelease', got '$env:RESOLVE_PRERELEASE'" + $failed = $true + } + if ($env:RESOLVE_FULL_VERSION -ne $expectedFullVersion) { + Write-Error "FullVersion: expected '$expectedFullVersion', got '$env:RESOLVE_FULL_VERSION'" + $failed = $true + } + if ($failed) { exit 1 } diff --git a/scripts/Helpers.psm1 b/scripts/Helpers.psm1 new file mode 100644 index 0000000..cb3eb8a --- /dev/null +++ b/scripts/Helpers.psm1 @@ -0,0 +1,643 @@ +function Split-CommaSeparatedList { + <# + .SYNOPSIS + Splits a comma-separated string into a trimmed, non-empty array. + + .EXAMPLE + Split-CommaSeparatedList -Value 'Major, Minor, Patch' + + Returns @('Major', 'Minor', 'Patch'). +#> + [CmdletBinding()] + [OutputType([string[]])] + param( + # The comma-separated string to split. + [Parameter()] + [string] $Value + ) + + ($Value -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ } +} + +function Read-ActionInput { + <# + .SYNOPSIS + Reads and validates action inputs from environment variables. + + .DESCRIPTION + Reads the module name and settings JSON from GitHub Actions environment variables. + Falls back to the repository name when the module name input is not provided. + + .OUTPUTS + PSCustomObject with Name and SettingsJson properties. + + .EXAMPLE + $actionInput = Read-ActionInput +#> + [CmdletBinding()] + [OutputType([PSCustomObject])] + param() + + LogGroup 'Load inputs' { + $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' + + $name = if ([string]::IsNullOrEmpty($env:PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_Name)) { + $env:GITHUB_REPOSITORY_NAME + } else { + $env:PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_Name + } + Write-Host "Module name: [$name]" + + $settingsJson = $env:PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_Settings + if ([string]::IsNullOrWhiteSpace($settingsJson)) { + throw 'Settings input is required.' + } + + [PSCustomObject]@{ + Name = $name + SettingsJson = $settingsJson + } + } +} + +function Get-PublishConfiguration { + <# + .SYNOPSIS + Parses the settings JSON into a publish configuration object. + + .DESCRIPTION + Extracts publish module settings including auto-patching flags, version prefix, + release type, and label classification arrays. + + .OUTPUTS + PSCustomObject with publish configuration properties. + + .EXAMPLE + $config = Get-PublishConfiguration -SettingsJson $actionInput.SettingsJson +#> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = 'Parameter is used inside LogGroup script block.')] + [CmdletBinding()] + [OutputType([PSCustomObject])] + param( + # The JSON string containing the module settings. + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $SettingsJson + ) + + LogGroup 'Resolve configuration' { + $settings = $SettingsJson | ConvertFrom-Json + $publishModule = $settings.Publish.Module + + $config = [PSCustomObject]@{ + AutoPatching = [bool]$publishModule.AutoPatching + IncrementalPrerelease = [bool]$publishModule.IncrementalPrerelease + DatePrereleaseFormat = [string]$publishModule.DatePrereleaseFormat + VersionPrefix = [string]$publishModule.VersionPrefix + ReleaseType = [string]$publishModule.ReleaseType + IgnoreLabels = Split-CommaSeparatedList ([string]$publishModule.IgnoreLabels) + MajorLabels = Split-CommaSeparatedList ([string]$publishModule.MajorLabels) + MinorLabels = Split-CommaSeparatedList ([string]$publishModule.MinorLabels) + PatchLabels = Split-CommaSeparatedList ([string]$publishModule.PatchLabels) + } + + Write-Host '-------------------------------------------------' + Write-Host ([PSCustomObject]@{ + AutoPatching = $config.AutoPatching + IncrementalPrerelease = $config.IncrementalPrerelease + DatePrereleaseFormat = $config.DatePrereleaseFormat + VersionPrefix = $config.VersionPrefix + ReleaseType = $config.ReleaseType + IgnoreLabels = $config.IgnoreLabels -join ', ' + MajorLabels = $config.MajorLabels -join ', ' + MinorLabels = $config.MinorLabels -join ', ' + PatchLabels = $config.PatchLabels -join ', ' + } | Format-List | Out-String) + Write-Host '-------------------------------------------------' + + $config + } +} + +function Get-GitHubPullRequest { + <# + .SYNOPSIS + Reads and validates the GitHub pull request from the event payload. + + .DESCRIPTION + Loads the GitHub event from the input override or from the event path file, + then validates and extracts pull request data. + + .OUTPUTS + PSCustomObject with HeadRef and Labels properties. + + .EXAMPLE + $pullRequest = Get-GitHubPullRequest +#> + [CmdletBinding()] + [OutputType([PSCustomObject])] + param() + + LogGroup 'Event information' { + $eventJsonInput = $env:PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_EventJson + $githubEvent = if (-not [string]::IsNullOrWhiteSpace($eventJsonInput)) { + $eventJsonInput | ConvertFrom-Json + } else { + Get-Content $env:GITHUB_EVENT_PATH | ConvertFrom-Json + } + + $pr = $githubEvent.pull_request + if (-not $pr) { + throw 'GitHub event does not contain pull_request data. This action must be run from a pull_request event.' + } + + $labels = @() + $labels += $pr.labels.name + + Write-Host '-------------------------------------------------' + Write-Host ([PSCustomObject]@{ + PRHeadRef = $pr.head.ref + Labels = $labels -join ', ' + } | Format-List | Out-String) + Write-Host '-------------------------------------------------' + + [PSCustomObject]@{ + HeadRef = $pr.head.ref + Labels = $labels + } + } +} + +function Resolve-ReleaseDecision { + <# + .SYNOPSIS + Determines whether to publish a release and what kind of version bump to apply. + + .DESCRIPTION + Evaluates the PR labels against the configured label categories and release type + to produce a complete release decision. + + .OUTPUTS + PSCustomObject with ShouldPublish, CreateRelease, CreatePrerelease, MajorRelease, + MinorRelease, PatchRelease, HasVersionBump, and PrereleaseName properties. + + .EXAMPLE + $decision = Resolve-ReleaseDecision -Configuration $config -PullRequest $pullRequest +#> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = 'Parameter is used inside LogGroup script block.')] + [CmdletBinding()] + [OutputType([PSCustomObject])] + param( + # The publish configuration object. + [Parameter(Mandatory)] + [PSCustomObject] $Configuration, + + # The pull request data object. + [Parameter(Mandatory)] + [PSCustomObject] $PullRequest + ) + + LogGroup 'Determine release configuration' { + $prereleaseName = $PullRequest.HeadRef -replace '[^a-zA-Z0-9]' + $labels = $PullRequest.Labels + $releaseType = $Configuration.ReleaseType + + $validReleaseTypes = @('Release', 'Prerelease', 'None') + if ([string]::IsNullOrWhiteSpace($releaseType)) { + throw "Settings.Publish.Module.ReleaseType is required. Valid values are: $($validReleaseTypes -join ', ')" + } + if ($releaseType -notin $validReleaseTypes) { + throw "Invalid ReleaseType: [$releaseType]. Valid values are: $($validReleaseTypes -join ', ')" + } + + $createRelease = $releaseType -eq 'Release' + $createPrerelease = $releaseType -eq 'Prerelease' + $shouldPublish = $createRelease -or $createPrerelease + + $ignoreRelease = ($labels | Where-Object { $Configuration.IgnoreLabels -contains $_ }).Count -gt 0 + if ($ignoreRelease -and $shouldPublish) { + Write-Host 'Ignoring release creation due to ignore label.' + $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 + } + } + + Write-Host '-------------------------------------------------' + Write-Host ([PSCustomObject]@{ + ReleaseType = $releaseType + ShouldPublish = $shouldPublish + CreateRelease = $createRelease + CreatePrerelease = $createPrerelease + Major = $majorRelease + Minor = $minorRelease + Patch = $patchRelease + } | Format-List | Out-String) + Write-Host '-------------------------------------------------' + + [PSCustomObject]@{ + ShouldPublish = $shouldPublish + CreateRelease = $createRelease + CreatePrerelease = $createPrerelease + MajorRelease = $majorRelease + MinorRelease = $minorRelease + PatchRelease = $patchRelease + HasVersionBump = $hasVersionBump + PrereleaseName = $prereleaseName + } + } +} + +function Get-GitHubRelease { + <# + .SYNOPSIS + Retrieves all releases from the current GitHub repository. + + .OUTPUTS + Array of release objects. + + .EXAMPLE + $releases = Get-GitHubReleases +#> + [CmdletBinding()] + [OutputType([array])] + param() + + LogGroup 'Get releases - GitHub' { + $releasesJson = gh release list --json 'createdAt,isDraft,isLatest,isPrerelease,name,publishedAt,tagName' + if ($LASTEXITCODE -ne 0) { + Write-Error 'Failed to list releases for the repo.' + exit $LASTEXITCODE + } + $releases = $releasesJson | ConvertFrom-Json + + Write-Host '-------------------------------------------------' + Write-Host ($releases | Select-Object -Property name, isPrerelease, isLatest, publishedAt | + Format-Table | Out-String) + Write-Host '-------------------------------------------------' + + $releases + } +} + +function Get-LatestGitHubVersion { + <# + .SYNOPSIS + Extracts the latest stable version from a GitHub releases list. + + .OUTPUTS + PSSemVer representing the latest GitHub release version. + + .EXAMPLE + $ghVersion = Get-LatestGitHubVersion -Releases $releases +#> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = 'Parameter is used inside LogGroup script block.')] + [CmdletBinding()] + [OutputType([object])] + param( + # The GitHub releases array to search. + [Parameter(Mandatory)] + [array] $Releases + ) + + LogGroup 'Get latest version - GitHub' { + $latestRelease = $Releases | Where-Object { $_.isLatest -eq $true } + $tagName = [string]$latestRelease.tagName + + $version = if (-not [string]::IsNullOrEmpty($tagName)) { + New-PSSemVer -Version $tagName + } else { + Write-Warning "Could not find the latest GitHub release. Using '0.0.0'." + New-PSSemVer -Version '0.0.0' + } + + Write-Host "GitHub version: [$($version.ToString())]" + $version + } +} + +function Get-LatestPSGalleryVersion { + <# + .SYNOPSIS + Finds the latest stable version of a module in the PowerShell Gallery. + + .DESCRIPTION + Queries the PowerShell Gallery for the latest published version of the module. + Retries up to five times with a ten-second delay between attempts. + + .OUTPUTS + PSSemVer representing the latest PSGallery version. + + .EXAMPLE + $psGalleryVersion = Get-LatestPSGalleryVersion -ModuleName 'MyModule' +#> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = 'Parameter is used inside LogGroup script block.')] + [CmdletBinding()] + [OutputType([object])] + param( + # The name of the module to find in the PowerShell Gallery. + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $ModuleName + ) + + LogGroup 'Get latest version - PSGallery' { + $retryCount = 5 + $retryDelaySeconds = 10 + $latest = $null + + for ($i = 1; $i -le $retryCount; $i++) { + try { + Write-Host "Finding module [$ModuleName] in the PowerShell Gallery." + $latest = Find-PSResource -Name $ModuleName -Repository PSGallery -Verbose:$false + Write-Host ($latest | Format-Table | Out-String) + break + } catch { + if ($i -eq $retryCount) { + Write-Warning "Failed to find the module [$ModuleName] in the PowerShell Gallery." + Write-Warning $_.Exception.Message + } + Start-Sleep -Seconds $retryDelaySeconds + } + } + + $version = if ($latest.Version) { + New-PSSemVer -Version ($latest.Version).ToString() + } else { + Write-Warning "Could not find module online. Using '0.0.0'." + New-PSSemVer -Version '0.0.0' + } + + Write-Host "PSGallery version: [$($version.ToString())]" + $version + } +} + +function Get-LatestPublishedVersion { + <# + .SYNOPSIS + Returns the highest version between GitHub and the PowerShell Gallery. + + .OUTPUTS + PSSemVer representing the highest known published version. + + .EXAMPLE + $latestVersion = Get-LatestPublishedVersion -GitHubVersion $ghVersion -PSGalleryVersion $psGalleryVersion +#> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = 'Parameter is used inside LogGroup script block.')] + [CmdletBinding()] + [OutputType([object])] + param( + # The latest version found in GitHub releases. + [Parameter(Mandatory)] + [object] $GitHubVersion, + + # The latest version found in the PowerShell Gallery. + [Parameter(Mandatory)] + [object] $PSGalleryVersion + ) + + LogGroup 'Latest version' { + $latestVersion = New-PSSemVer -Version ( + $PSGalleryVersion, $GitHubVersion | Sort-Object -Descending | Select-Object -First 1 + ) + Write-Host "Latest version: [$($latestVersion.ToString())]" + $latestVersion + } +} + +function Get-NextPrereleaseNumber { + <# + .SYNOPSIS + Calculates the next incremental prerelease number across GitHub and PSGallery. + + .DESCRIPTION + Queries both GitHub releases and the PowerShell Gallery for existing prereleases + matching the base version and prerelease name, then returns the next number + zero-padded to three digits. + + .OUTPUTS + String. A zero-padded three-digit number (e.g. '001'). + + .EXAMPLE + $number = Get-NextPrereleaseNumber -ModuleName 'MyModule' -BaseVersion '1.2.3' -PrereleaseName 'mybranch' -Releases $releases +#> + [CmdletBinding()] + [OutputType([string])] + param( + # The module name to query in the PowerShell Gallery. + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $ModuleName, + + # The base version string without prerelease (e.g. '1.2.3'). + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $BaseVersion, + + # The sanitized prerelease name derived from the branch name. + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $PrereleaseName, + + # The GitHub releases list. + [Parameter(Mandatory)] + [array] $Releases + ) + + $params = @{ + Name = $ModuleName + Version = '*' + Prerelease = $true + Repository = 'PSGallery' + Verbose = $false + ErrorAction = 'SilentlyContinue' + } + $matchingPSGalleryPrereleases = Find-PSResource @params | + Where-Object { "$($_.Version.Major).$($_.Version.Minor).$($_.Version.Build)" -eq $BaseVersion } | + Where-Object { $_.Prerelease -like "$PrereleaseName*" } + + $latestPSGalleryNumber = $matchingPSGalleryPrereleases.Prerelease | ForEach-Object { + [long]($_ -replace $PrereleaseName) + } | Sort-Object | Select-Object -Last 1 + Write-Host "PSGallery prerelease: [$latestPSGalleryNumber]" + + $matchingGHPrereleases = $Releases | + Where-Object { $_.tagName -like "*$BaseVersion*" } | + Where-Object { $_.tagName -like "*$PrereleaseName*" } + + $latestGHNumber = $matchingGHPrereleases.tagName | ForEach-Object { + $tagWithoutDots = $_ -replace '\.' + [long](($tagWithoutDots -split $PrereleaseName, 2)[-1]) + } | Sort-Object | Select-Object -Last 1 + Write-Host "GitHub prerelease: [$latestGHNumber]" + + if ($null -eq $latestPSGalleryNumber) { $latestPSGalleryNumber = 0 } + if ($null -eq $latestGHNumber) { $latestGHNumber = 0 } + + $nextNumber = [Math]::Max($latestPSGalleryNumber, $latestGHNumber) + 1 + ([string]$nextNumber).PadLeft(3, '0') +} + +function Get-NextModuleVersion { + <# + .SYNOPSIS + Calculates the next module version based on the release decision. + + .DESCRIPTION + Increments the current version according to the version bump type (major, minor, or patch), + then optionally appends a prerelease suffix with support for date-based and incremental numbering. + + .OUTPUTS + PSSemVer representing the resolved next version. + + .EXAMPLE + $newVersion = Get-NextModuleVersion -LatestVersion $latestVersion -Decision $decision ` + -Configuration $config -ModuleName 'MyModule' -Releases $releases +#> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = 'Parameter is used inside LogGroup script block.')] + [CmdletBinding()] + [OutputType([object])] + param( + # The current latest published version. + [Parameter(Mandatory)] + [object] $LatestVersion, + + # The release decision object. + [Parameter(Mandatory)] + [PSCustomObject] $Decision, + + # The publish configuration object. + [Parameter(Mandatory)] + [PSCustomObject] $Configuration, + + # The name of the module. + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $ModuleName, + + # The GitHub releases list, used for incremental prerelease calculation. + [Parameter(Mandatory)] + [array] $Releases + ) + + LogGroup 'Calculate new version' { + $newVersion = New-PSSemVer -Version $LatestVersion + $newVersion.Prefix = $Configuration.VersionPrefix + + if ($Decision.MajorRelease) { + Write-Host 'Incrementing major version.' + $newVersion.BumpMajor() + } elseif ($Decision.MinorRelease) { + Write-Host 'Incrementing minor version.' + $newVersion.BumpMinor() + } elseif ($Decision.PatchRelease) { + Write-Host 'Incrementing patch version.' + $newVersion.BumpPatch() + } + Write-Host "Partial new version: [$newVersion]" + + if ($Decision.CreatePrerelease -and $Decision.HasVersionBump) { + $prereleaseName = $Decision.PrereleaseName + Write-Host "Adding a prerelease tag using the branch name [$prereleaseName]." + $newVersion.Prerelease = $prereleaseName + + if (-not [string]::IsNullOrEmpty($Configuration.DatePrereleaseFormat)) { + Write-Host "Using date-based prerelease format: [$($Configuration.DatePrereleaseFormat)]." + $newVersion.Prerelease += "$(Get-Date -Format $Configuration.DatePrereleaseFormat)" + } + + if ($Configuration.IncrementalPrerelease) { + $baseVersionString = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" + $params = @{ + ModuleName = $ModuleName + BaseVersion = $baseVersionString + PrereleaseName = $prereleaseName + Releases = $Releases + } + $newVersion.Prerelease += Get-NextPrereleaseNumber @params + } + } + + Write-Host "New version: [$($newVersion.ToString())]" + $newVersion + } +} + +function Write-ActionOutput { + <# + .SYNOPSIS + Emits the resolved version and release type as GitHub Actions step outputs. + + .EXAMPLE + Write-ActionOutput -Decision $decision -NewVersion $newVersion +#> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = 'Parameter is used inside LogGroup script block.')] + [CmdletBinding()] + param( + # The release decision object. + [Parameter(Mandatory)] + [PSCustomObject] $Decision, + + # The resolved next version, or $null when no release will be created. + [Parameter()] + [object] $NewVersion + ) + + LogGroup 'Emit outputs' { + $versionString = '' + $prereleaseString = '' + $fullVersionString = '' + + if ($NewVersion) { + $versionString = "$($NewVersion.Major).$($NewVersion.Minor).$($NewVersion.Patch)" + $prereleaseString = [string]$NewVersion.Prerelease + $fullVersionString = $NewVersion.ToString() + } + + $resolvedReleaseType = if ($Decision.ShouldPublish) { + if ($Decision.CreateRelease) { 'Release' } else { 'Prerelease' } + } else { + 'None' + } + + Add-Content -Path $env:GITHUB_OUTPUT -Value "Version=$versionString" + Add-Content -Path $env:GITHUB_OUTPUT -Value "Prerelease=$prereleaseString" + Add-Content -Path $env:GITHUB_OUTPUT -Value "FullVersion=$fullVersionString" + Add-Content -Path $env:GITHUB_OUTPUT -Value "ReleaseType=$resolvedReleaseType" + Add-Content -Path $env:GITHUB_OUTPUT -Value "CreateRelease=$($Decision.ShouldPublish.ToString().ToLower())" + + Write-Host '-------------------------------------------------' + Write-Host ([PSCustomObject]@{ + Version = $versionString + Prerelease = $prereleaseString + FullVersion = $fullVersionString + ReleaseType = $resolvedReleaseType + CreateRelease = $Decision.ShouldPublish + } | Format-List | Out-String) + Write-Host '-------------------------------------------------' + } +} diff --git a/scripts/main.ps1 b/scripts/main.ps1 index 60f0e18..73f6d36 100644 --- a/scripts/main.ps1 +++ b/scripts/main.ps1 @@ -1,293 +1,32 @@ -[Diagnostics.CodeAnalysis.SuppressMessageAttribute( - 'PSUseDeclaredVarsMoreThanAssignments', 'prereleaseName', - Justification = 'Variable is used in script blocks.' -)] -[Diagnostics.CodeAnalysis.SuppressMessageAttribute( - 'PSUseDeclaredVarsMoreThanAssignments', 'publishModule', - Justification = 'Variable is used in script blocks.' -)] -[CmdletBinding()] +[CmdletBinding()] param() $PSStyle.OutputRendering = 'Ansi' Import-Module -Name 'Helpers' -Force +Import-Module -Name "$PSScriptRoot/Helpers.psm1" -Force -LogGroup 'Load inputs' { - $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' - - $name = if ([string]::IsNullOrEmpty($env:PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_Name)) { - $env:GITHUB_REPOSITORY_NAME - } else { - $env:PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_Name - } - Write-Host "Module name: [$name]" - - $settingsJson = $env:PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_Settings - if ([string]::IsNullOrWhiteSpace($settingsJson)) { - Write-Error 'Settings input is required.' - exit 1 - } - $settings = $settingsJson | ConvertFrom-Json - $publishModule = $settings.Publish.Module -} - -LogGroup 'Resolve configuration' { - $autoPatching = [bool]$publishModule.AutoPatching - $incrementalPrerelease = [bool]$publishModule.IncrementalPrerelease - $datePrereleaseFormat = [string]$publishModule.DatePrereleaseFormat - $versionPrefix = [string]$publishModule.VersionPrefix - $releaseType = [string]$publishModule.ReleaseType - $ignoreLabels = (([string]$publishModule.IgnoreLabels) -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ } - $majorLabels = (([string]$publishModule.MajorLabels) -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ } - $minorLabels = (([string]$publishModule.MinorLabels) -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ } - $patchLabels = (([string]$publishModule.PatchLabels) -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ } - - Write-Host '-------------------------------------------------' - [pscustomobject]@{ - AutoPatching = $autoPatching - IncrementalPrerelease = $incrementalPrerelease - DatePrereleaseFormat = $datePrereleaseFormat - VersionPrefix = $versionPrefix - ReleaseType = $releaseType - IgnoreLabels = $ignoreLabels -join ', ' - MajorLabels = $majorLabels -join ', ' - MinorLabels = $minorLabels -join ', ' - PatchLabels = $patchLabels -join ', ' - } | Format-List | Out-String - Write-Host '-------------------------------------------------' -} - -LogGroup 'Event information' { - $eventJsonInput = $env:PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_EventJson - if (-not [string]::IsNullOrWhiteSpace($eventJsonInput)) { - $githubEvent = $eventJsonInput | ConvertFrom-Json - } else { - $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 action must be run from a pull_request event.' - } - $prHeadRef = $pull_request.head.ref - $labels = @() - $labels += $pull_request.labels.name - - Write-Host '-------------------------------------------------' - [PSCustomObject]@{ - PRHeadRef = $prHeadRef - Labels = $labels -join ', ' - } | Format-List | Out-String - Write-Host '-------------------------------------------------' -} - -LogGroup 'Determine release configuration' { - $prereleaseName = $prHeadRef -replace '[^a-zA-Z0-9]' - - $validReleaseTypes = @('Release', 'Prerelease', 'None') - if ([string]::IsNullOrWhiteSpace($releaseType)) { - Write-Error "Settings.Publish.Module.ReleaseType 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 -and $shouldPublish) { - Write-Host 'Ignoring release creation due to ignore label.' - $shouldPublish = $false - } - - $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 - - $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 - } - } - - Write-Host '-------------------------------------------------' - [PSCustomObject]@{ - ReleaseType = $releaseType - ShouldPublish = $shouldPublish - CreateRelease = $createRelease - CreatePrerelease = $createPrerelease - Major = $majorRelease - Minor = $minorRelease - Patch = $patchRelease - } | Format-List | Out-String - Write-Host '-------------------------------------------------' -} +$actionInput = Read-ActionInput +$config = Get-PublishConfiguration -SettingsJson $actionInput.SettingsJson +$pullRequest = Get-GitHubPullRequest +$decision = Resolve-ReleaseDecision -Configuration $config -PullRequest $pullRequest $newVersion = $null -$releases = @() - -if ($shouldPublish) { - LogGroup 'Get 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 releases for the repo.' - exit $LASTEXITCODE - } - Write-Host '-------------------------------------------------' - $releases | Select-Object -Property name, isPrerelease, isLatest, publishedAt | Format-Table | Out-String - Write-Host '-------------------------------------------------' - } - - LogGroup 'Get latest version - GitHub' { - $latestRelease = $releases | Where-Object { $_.isLatest -eq $true } - $ghReleaseVersionString = $latestRelease.tagName - if (-not [string]::IsNullOrEmpty($ghReleaseVersionString)) { - $ghReleaseVersion = New-PSSemVer -Version $ghReleaseVersionString - } else { - Write-Warning "Could not find the latest GitHub release. Using '0.0.0'." - $ghReleaseVersion = New-PSSemVer -Version '0.0.0' - } - Write-Host "GitHub version: [$($ghReleaseVersion.ToString())]" - } - - LogGroup 'Get latest version - PSGallery' { - $count = 5 - $delay = 10 - $latest = $null - for ($i = 1; $i -le $count; $i++) { - try { - Write-Host "Finding module [$name] in the PowerShell Gallery." - $latest = Find-PSResource -Name $name -Repository PSGallery -Verbose:$false - Write-Host "$($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'." - $psGalleryVersion = New-PSSemVer -Version '0.0.0' - } - Write-Host "PSGallery version: [$($psGalleryVersion.ToString())]" - } - - LogGroup 'Latest version' { - $latestVersion = New-PSSemVer -Version ($psGalleryVersion, $ghReleaseVersion | Sort-Object -Descending | Select-Object -First 1) - Write-Host "Latest version: [$($latestVersion.ToString())]" - } - LogGroup 'Calculate new version' { - $newVersion = New-PSSemVer -Version $latestVersion - $newVersion.Prefix = $versionPrefix - if ($majorRelease) { - Write-Host 'Incrementing major version.' - $newVersion.BumpMajor() - } elseif ($minorRelease) { - Write-Host 'Incrementing minor version.' - $newVersion.BumpMinor() - } elseif ($patchRelease) { - Write-Host 'Incrementing patch version.' - $newVersion.BumpPatch() - } - Write-Host "Partial new version: [$newVersion]" +if ($decision.ShouldPublish) { + $releases = Get-GitHubRelease + $ghVersion = Get-LatestGitHubVersion -Releases $releases + $psGalleryVersion = Get-LatestPSGalleryVersion -ModuleName $actionInput.Name + $latestVersion = Get-LatestPublishedVersion -GitHubVersion $ghVersion -PSGalleryVersion $psGalleryVersion - if ($createPrerelease -and $hasVersionBump) { - Write-Host "Adding a prerelease tag using the branch name [$prereleaseName]." - $newVersion.Prerelease = $prereleaseName - - if (-not [string]::IsNullOrEmpty($datePrereleaseFormat)) { - Write-Host "Using date-based prerelease format: [$datePrereleaseFormat]." - $newVersion.Prerelease += "$(Get-Date -Format $datePrereleaseFormat)" - } - - if ($incrementalPrerelease) { - $newVersionString = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" - $params = @{ - Name = $name - Version = '*' - Prerelease = $true - Repository = 'PSGallery' - Verbose = $false - ErrorAction = 'SilentlyContinue' - } - $psGalleryPrereleases = Find-PSResource @params | - Where-Object { "$($_.Version.Major).$($_.Version.Minor).$($_.Version.Build)" -eq $newVersionString } | - Where-Object { $_.Prerelease -like "$prereleaseName*" } - $latestPSGalleryPrerelease = $psGalleryPrereleases.Prerelease | ForEach-Object { - [long]($_ -replace $prereleaseName) - } | Sort-Object | Select-Object -Last 1 - Write-Host "PSGallery prerelease: [$latestPSGalleryPrerelease]" - - $ghPrereleases = $releases | - Where-Object { $_.tagName -like "*$newVersionString*" } | - Where-Object { $_.tagName -like "*$prereleaseName*" } - $latestGHPrereleases = $ghPrereleases.tagName | ForEach-Object { - $number = $_ -replace '\.' - $number = ($number -split $prereleaseName, 2)[-1] - [long]$number - } | Sort-Object | Select-Object -Last 1 - Write-Host "GitHub prerelease: [$latestGHPrereleases]" - - if ($null -eq $latestPSGalleryPrerelease) { $latestPSGalleryPrerelease = 0 } - if ($null -eq $latestGHPrereleases) { $latestGHPrereleases = 0 } - - $latestPrereleaseNumber = [Math]::Max($latestPSGalleryPrerelease, $latestGHPrereleases) + 1 - $latestPrereleaseNumber = ([string]$latestPrereleaseNumber).PadLeft(3, '0') - $newVersion.Prerelease += $latestPrereleaseNumber - } - } - - Write-Host "New version: [$($newVersion.ToString())]" + $params = @{ + LatestVersion = $latestVersion + Decision = $decision + Configuration = $config + ModuleName = $actionInput.Name + Releases = $releases } + $newVersion = Get-NextModuleVersion @params } -LogGroup 'Emit outputs' { - $versionString = '' - $prereleaseString = '' - $fullVersionString = '' - $resolvedReleaseType = $releaseType - if ($newVersion) { - $versionString = "$($newVersion.Major).$($newVersion.Minor).$($newVersion.Patch)" - $prereleaseString = [string]$newVersion.Prerelease - $fullVersionString = $newVersion.ToString() - } - if (-not $shouldPublish) { - $resolvedReleaseType = 'None' - } - - Add-Content -Path $env:GITHUB_OUTPUT -Value "Version=$versionString" - Add-Content -Path $env:GITHUB_OUTPUT -Value "Prerelease=$prereleaseString" - Add-Content -Path $env:GITHUB_OUTPUT -Value "FullVersion=$fullVersionString" - Add-Content -Path $env:GITHUB_OUTPUT -Value "ReleaseType=$resolvedReleaseType" - Add-Content -Path $env:GITHUB_OUTPUT -Value "CreateRelease=$($shouldPublish.ToString().ToLower())" - - Write-Host '-------------------------------------------------' - [PSCustomObject]@{ - Version = $versionString - Prerelease = $prereleaseString - FullVersion = $fullVersionString - ReleaseType = $resolvedReleaseType - CreateRelease = $shouldPublish - } | Format-List | Out-String - Write-Host '-------------------------------------------------' -} +Write-ActionOutput -Decision $decision -NewVersion $newVersion