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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,47 @@ function Resolve-WorkflowEventRouting {
}
}

function Resolve-PSModulePublishSetting {
<#
.SYNOPSIS
Resolves module publication settings and their defaults.

.DESCRIPTION
Applies Process-PSModule defaults while preserving every consumer-provided
publication and release-label mapping.

.OUTPUTS
System.Management.Automation.PSCustomObject

.EXAMPLE
Resolve-PSModulePublishSetting -PublishModule $settings.Publish.Module
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param(
[Parameter()]
[AllowNull()]
[object] $PublishModule
)

[pscustomobject]@{
Skip = $PublishModule.Skip ?? $false
AutoCleanup = $PublishModule.AutoCleanup ?? $true
AutoPatching = $PublishModule.AutoPatching ?? $true
IncrementalPrerelease = $PublishModule.IncrementalPrerelease ?? $true
DatePrereleaseFormat = $PublishModule.DatePrereleaseFormat ?? ''
VersionPrefix = $PublishModule.VersionPrefix ?? 'v'
MajorLabels = $PublishModule.MajorLabels ?? 'release:major'
MinorLabels = $PublishModule.MinorLabels ?? 'release:minor'
PatchLabels = $PublishModule.PatchLabels ?? 'release:patch'
IgnoreLabels = $PublishModule.IgnoreLabels ?? 'release:skip'
PrereleaseLabels = $PublishModule.PrereleaseLabels ?? 'release:pre-release'
UsePRTitleAsReleaseName = $PublishModule.UsePRTitleAsReleaseName ?? $false
UsePRBodyAsReleaseNotes = $PublishModule.UsePRBodyAsReleaseNotes ?? $true
UsePRTitleAsNotesHeading = $PublishModule.UsePRTitleAsNotesHeading ?? $true
}
}

function Select-PullRequestForPush {
<#
.SYNOPSIS
Expand Down
18 changes: 12 additions & 6 deletions .github/actions/Get-PSModuleSettings/src/Settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@
},
"AutoPatching": {
"type": "boolean",
"description": "Automatically apply patches"
"description": "Automatically apply patches",
"default": true
},
"IncrementalPrerelease": {
"type": "boolean",
Expand All @@ -156,23 +157,28 @@
},
"MajorLabels": {
"type": "string",
"description": "Comma-separated labels that trigger major version bump"
"description": "Comma-separated labels that trigger major version bump",
"default": "release:major"
},
"MinorLabels": {
"type": "string",
"description": "Comma-separated labels that trigger minor version bump"
"description": "Comma-separated labels that trigger minor version bump",
"default": "release:minor"
},
"PatchLabels": {
"type": "string",
"description": "Comma-separated labels that trigger patch version bump"
"description": "Comma-separated labels that trigger patch version bump",
"default": "release:patch"
},
"IgnoreLabels": {
"type": "string",
"description": "Comma-separated labels that prevent release"
"description": "Comma-separated labels that prevent release",
"default": "release:skip"
},
"PrereleaseLabels": {
"type": "string",
"description": "Comma-separated labels that trigger a prerelease"
"description": "Comma-separated labels that trigger a prerelease",
"default": "release:pre-release"
},
"UsePRTitleAsReleaseName": {
"type": "boolean",
Expand Down
17 changes: 1 addition & 16 deletions .github/actions/Get-PSModuleSettings/src/main.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -187,22 +187,7 @@ $settings = [pscustomobject]@{
}
}
Publish = [pscustomobject]@{
Module = [pscustomobject]@{
Skip = $settings.Publish.Module.Skip ?? $false
AutoCleanup = $settings.Publish.Module.AutoCleanup ?? $true
AutoPatching = $settings.Publish.Module.AutoPatching ?? $true
IncrementalPrerelease = $settings.Publish.Module.IncrementalPrerelease ?? $true
DatePrereleaseFormat = $settings.Publish.Module.DatePrereleaseFormat ?? ''
VersionPrefix = $settings.Publish.Module.VersionPrefix ?? 'v'
MajorLabels = $settings.Publish.Module.MajorLabels ?? 'major, breaking'
MinorLabels = $settings.Publish.Module.MinorLabels ?? 'minor, feature'
PatchLabels = $settings.Publish.Module.PatchLabels ?? 'patch, fix'
IgnoreLabels = $settings.Publish.Module.IgnoreLabels ?? 'NoRelease'
PrereleaseLabels = $settings.Publish.Module.PrereleaseLabels ?? 'prerelease'
UsePRTitleAsReleaseName = $settings.Publish.Module.UsePRTitleAsReleaseName ?? $false
UsePRBodyAsReleaseNotes = $settings.Publish.Module.UsePRBodyAsReleaseNotes ?? $true
UsePRTitleAsNotesHeading = $settings.Publish.Module.UsePRTitleAsNotesHeading ?? $true
}
Module = Resolve-PSModulePublishSetting -PublishModule $settings.Publish.Module
Site = [pscustomobject]@{
Skip = $settings.Publish.Site.Skip ?? $false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,52 @@ Describe 'Resolve-WorkflowEventRouting' {
}
}

Describe 'Resolve-PSModulePublishSetting' {
It 'uses the canonical release-label defaults' {
$result = Resolve-PSModulePublishSetting -PublishModule $null

$result.AutoPatching | Should -BeTrue
$result.MajorLabels | Should -BeExactly 'release:major'
$result.MinorLabels | Should -BeExactly 'release:minor'
$result.PatchLabels | Should -BeExactly 'release:patch'
$result.PrereleaseLabels | Should -BeExactly 'release:pre-release'
$result.IgnoreLabels | Should -BeExactly 'release:skip'
}

It 'preserves consumer-controlled release settings' {
$publishModule = [pscustomobject]@{
AutoPatching = $false
MajorLabels = 'custom:major'
MinorLabels = 'custom:minor'
PatchLabels = 'custom:patch'
PrereleaseLabels = 'custom:pre-release'
IgnoreLabels = 'custom:skip'
}

$result = Resolve-PSModulePublishSetting -PublishModule $publishModule

$result.AutoPatching | Should -BeFalse
$result.MajorLabels | Should -BeExactly 'custom:major'
$result.MinorLabels | Should -BeExactly 'custom:minor'
$result.PatchLabels | Should -BeExactly 'custom:patch'
$result.PrereleaseLabels | Should -BeExactly 'custom:pre-release'
$result.IgnoreLabels | Should -BeExactly 'custom:skip'
}

It 'declares the same canonical defaults in the settings schema' {
$schemaPath = Join-Path -Path $PSScriptRoot -ChildPath '../src/Settings.schema.json'
$schema = Get-Content -Path $schemaPath -Raw | ConvertFrom-Json
$moduleProperties = $schema.properties.Publish.properties.Module.properties

$moduleProperties.AutoPatching.default | Should -BeTrue
$moduleProperties.MajorLabels.default | Should -BeExactly 'release:major'
$moduleProperties.MinorLabels.default | Should -BeExactly 'release:minor'
$moduleProperties.PatchLabels.default | Should -BeExactly 'release:patch'
$moduleProperties.PrereleaseLabels.default | Should -BeExactly 'release:pre-release'
$moduleProperties.IgnoreLabels.default | Should -BeExactly 'release:skip'
}
}

Describe 'Select-PullRequestForPush' {
It 'selects the merged PR whose merge commit matches the pushed commit' {
$pullRequests = @(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,23 @@ BeforeAll {

# The release type resolved from the pull request labels.
[Parameter()]
[string] $ReleaseType = 'Release'
[string] $ReleaseType = 'Release',

# Labels indicating no release.
[Parameter()]
[string[]] $IgnoreLabels = @('release:skip'),

# Labels indicating a major release.
[Parameter()]
[string[]] $MajorLabels = @('release:major'),

# Labels indicating a minor release.
[Parameter()]
[string[]] $MinorLabels = @('release:minor'),

# Labels indicating a patch release.
[Parameter()]
[string[]] $PatchLabels = @('release:patch')
)

[PSCustomObject]@{
Expand All @@ -44,10 +60,10 @@ BeforeAll {
DatePrereleaseFormat = $DatePrereleaseFormat
VersionPrefix = $VersionPrefix
ReleaseType = $ReleaseType
IgnoreLabels = @('NoRelease')
MajorLabels = @('major')
MinorLabels = @('minor')
PatchLabels = @('patch')
IgnoreLabels = $IgnoreLabels
MajorLabels = $MajorLabels
MinorLabels = $MinorLabels
PatchLabels = $PatchLabels
}
}

Expand Down Expand Up @@ -495,7 +511,7 @@ Describe 'Resolve-PSModuleVersion' {
PullRequest = @{
Number = 390
HeadRef = 'feature/push-release'
Labels = @('minor')
Labels = @('release:minor')
}
}
} | ConvertTo-Json -Depth 5
Expand All @@ -504,7 +520,7 @@ Describe 'Resolve-PSModuleVersion' {

$result.Number | Should -Be 390
$result.HeadRef | Should -Be 'feature/push-release'
$result.Labels | Should -Be @('minor')
$result.Labels | Should -Be @('release:minor')
}

It 'creates default patch context for a direct default-branch push' {
Expand Down Expand Up @@ -544,7 +560,7 @@ Describe 'Resolve-PSModuleVersion' {

It 'does not validate cleanup-only pull request labels' {
$result = Resolve-ReleaseDecision -Configuration (Get-TestConfiguration -ReleaseType None) `
-PullRequest ([pscustomobject]@{ HeadRef = 'feature'; Labels = @('NoRelease', 'patch') })
-PullRequest ([pscustomobject]@{ HeadRef = 'feature'; Labels = @('release:skip', 'release:patch') })

$result.ShouldPublish | Should -BeFalse
$result.HasVersionBump | Should -BeFalse
Expand All @@ -553,15 +569,61 @@ Describe 'Resolve-PSModuleVersion' {
It 'rejects multiple version labels' {
{
Resolve-ReleaseDecision -Configuration (Get-TestConfiguration) `
-PullRequest ([pscustomobject]@{ HeadRef = 'main'; Labels = @('major', 'patch') })
-PullRequest ([pscustomobject]@{ HeadRef = 'main'; Labels = @('release:major', 'release:patch') })
} | Should -Throw '*Conflicting version labels*'
}

It 'rejects a NoRelease label combined with a version label' {
It 'rejects release:skip combined with a version label' {
{
Resolve-ReleaseDecision -Configuration (Get-TestConfiguration) `
-PullRequest ([pscustomobject]@{ HeadRef = 'main'; Labels = @('NoRelease', 'patch') })
-PullRequest ([pscustomobject]@{ HeadRef = 'main'; Labels = @('release:skip', 'release:patch') })
} | Should -Throw '*ignore label cannot be combined*'
}

It 'honors the canonical <Bump> default' -ForEach @(
@{ Bump = 'Major'; Label = 'release:major'; Flag = 'MajorRelease' }
@{ Bump = 'Minor'; Label = 'release:minor'; Flag = 'MinorRelease' }
@{ Bump = 'Patch'; Label = 'release:patch'; Flag = 'PatchRelease' }
) {
$pullRequest = [pscustomobject]@{
HeadRef = 'main'
Labels = @($Label)
}

$result = Resolve-ReleaseDecision -Configuration (Get-TestConfiguration) -PullRequest $pullRequest

$result.$Flag | Should -BeTrue
$result.ShouldPublish | Should -BeTrue
}

It 'honors the configured <Bump> label mapping' -ForEach @(
@{ Bump = 'Major'; Setting = 'MajorLabels'; Label = 'custom:major'; Flag = 'MajorRelease' }
@{ Bump = 'Minor'; Setting = 'MinorLabels'; Label = 'custom:minor'; Flag = 'MinorRelease' }
@{ Bump = 'Patch'; Setting = 'PatchLabels'; Label = 'custom:patch'; Flag = 'PatchRelease' }
) {
$configuration = Get-TestConfiguration
$configuration.$Setting = @($Label)
$pullRequest = [pscustomobject]@{
HeadRef = 'main'
Labels = @($Label)
}

$result = Resolve-ReleaseDecision -Configuration $configuration -PullRequest $pullRequest

$result.$Flag | Should -BeTrue
$result.ShouldPublish | Should -BeTrue
}

It 'honors a configured ignore label' {
$configuration = Get-TestConfiguration -IgnoreLabels @('custom:skip')
$pullRequest = [pscustomobject]@{
HeadRef = 'main'
Labels = @('custom:skip')
}

$result = Resolve-ReleaseDecision -Configuration $configuration -PullRequest $pullRequest

$result.ShouldPublish | Should -BeFalse
}
}
}
2 changes: 1 addition & 1 deletion .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ jobs:
Settings: ${{ needs.Plan.outputs.Settings }}

# Runs on:
# - ✅ Open/Updated PR - Only with prerelease label: publishes prerelease version
# - ✅ Open/Updated PR - With prerelease intent and a resolved bump: publishes a prerelease
# - ✅ Default push - Publishes a stable release when all tests/coverage/build succeed
# - ✅ Closed PR - Cleans up prereleases for the closed branch (no version published)
# - ✅ Manual run - Publishes a stable default-branch release
Expand Down
24 changes: 13 additions & 11 deletions docs/content/get-started/your-first-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,26 @@ release with commit-based notes. It has no pull-request labels or body to use as

| Label | Effect |
| --- | --- |
| `major` / `breaking` | Bump `MAJOR`. |
| `minor` / `feature` | Bump `MINOR`. |
| `patch` / `fix` | Bump `PATCH`. This is the default for an unlabeled PR when `AutoPatching` is enabled. |
| `Prerelease` | Publish a prerelease version from the pull request, before it is merged. |
| `NoRelease` | Run the pipeline but skip publication. |
| `release:major` | Bump `MAJOR`. |
| `release:minor` | Bump `MINOR`. |
| `release:patch` | Bump `PATCH`. This is the default for an unlabeled PR when `AutoPatching` is enabled. |
| `release:pre-release` | Publish a prerelease version from the pull request, before it is merged. |
| `release:skip` | Run the pipeline but skip publication. |

Conflicting labels (for example `major` together with `NoRelease`) are rejected and block the merge. The label names are
configurable — see [Settings](../reference/settings.md).
Conflicting labels (for example `release:major` together with `release:skip`) are rejected whenever that run resolves a
release. A prerelease conflict fails the pull-request check; a stable conflict fails the resulting release run. These
are the defaults; every label mapping remains configurable — see [Settings](../reference/settings.md).

For the full model, including prerelease promotion and what a release produces, see
[Versioning and releases](../guides/versioning-and-releases.md).

## Testing before you merge

Add the `Prerelease` label to publish a prerelease version from the open pull request. The prerelease is installable
from the PowerShell Gallery but is not promoted as the latest stable version, so it can be validated before the pull
request is merged. When the pull request is closed without merging, the prerelease versions and tags created for it are
cleaned up automatically.
With the default `AutoPatching: true`, add `release:pre-release` to publish a patch prerelease from the open pull
request. When AutoPatching is disabled, also apply one configured bump label. The prerelease is installable from the
PowerShell Gallery but is not promoted as the latest stable version, so it can be validated before the pull request is
merged. When the pull request is closed without merging, the prerelease versions and tags created for it are cleaned up
automatically.

## When nothing is released

Expand Down
Loading
Loading