Skip to content

🚀 [Minor]: Plan job decides version before build so tested artifact equals published artifact (#326)#342

Draft
Marius Storhaug (MariusStorhaug) wants to merge 6 commits into
mainfrom
feat/326-resolve-psmodule-version
Draft

🚀 [Minor]: Plan job decides version before build so tested artifact equals published artifact (#326)#342
Marius Storhaug (MariusStorhaug) wants to merge 6 commits into
mainfrom
feat/326-resolve-psmodule-version

Conversation

@MariusStorhaug
Copy link
Copy Markdown
Member

@MariusStorhaug Marius Storhaug (MariusStorhaug) commented May 22, 2026

Closes #326.

The version a module ships with is now decided in a new Plan job that runs before the module is built. The same artifact then flows through tests and publish without being mutated, so the artifact you tested is the artifact that lands in the PowerShell Gallery and on the GitHub Release.

New: Plan job replaces Get-Settings

Get-Settings.yml is renamed to Plan.yml. The Plan job runs two steps in sequence and exposes everything downstream jobs need:

  1. Get-PSModuleSettings — loads .github/PSModule.yml and emits the resolved Settings JSON.
  2. Resolve-PSModuleVersion — reads settings + PR labels, queries existing releases and the PowerShell Gallery, and emits the next version.

Plan job outputs: Settings, ModuleVersion, ModulePrerelease, ModuleFullVersion, ReleaseType, CreateRelease.

flowchart LR
  A[Plan<br/>Get-PSModuleSettings + Resolve-PSModuleVersion] --> B[Build-Module<br/>stamps version into manifest]
  B --> C[Test-* / Coverage]
  C --> D[Publish-Module<br/>publishes artifact unchanged]
Loading

Changed: Build-Module now stamps the real version

Build-Module.yml accepts new ModuleVersion and ModulePrerelease inputs and forwards them to Build-PSModule. The built manifest contains the version the module will ship with before any test runs. The 999.0.0 placeholder only appears when no version is provided (for example, direct callers that opt out of the Plan job).

Changed: Publish-Module no longer calculates or mutates versions

Publish-Module.yml drops every input that used to drive version calculation:

  • AutoPatching
  • DatePrereleaseFormat
  • IgnoreLabels
  • IncrementalPrerelease
  • MajorLabels / MinorLabels / PatchLabels
  • ReleaseType
  • VersionPrefix

Publish-Module now reads the version straight from the manifest that arrived from Build-Module and pushes it to the PowerShell Gallery as-is. The PR-title / PR-body / heading inputs are unchanged. The job also gains permission to upload release assets so the zipped module can be attached to the GitHub Release.

Fixed: Tested artifact equals published artifact

The placeholder-then-rewrite flow is gone. The bytes tested in Test-Module / Test-ModuleLocal are the same bytes published to the PowerShell Gallery and attached to the GitHub Release.

Technical Details

  • Plan.yml (renamed from Get-Settings.yml): adds the Resolve-Version step guarded by Publish.Module.ReleaseType != 'None'. New job outputs are wired through outputs: on both the job and workflow_call.
  • workflow.yml: the Get-Settings job is renamed to Plan and every downstream needs: / with: reference is repointed. Build-Module now receives ModuleVersion and ModulePrerelease from needs.Plan.outputs.
  • Build-Module.yml: adds ModuleVersion / ModulePrerelease inputs (defaulting to empty for backward compatibility with direct callers) and passes them as Version / Prerelease to Build-PSModule.
  • Publish-Module.yml: removes the seven version-calculation inputs and grants contents: write permission for release-asset uploads.
  • README.md: replaces the Get-Settings section with a Plan section that documents both steps and the artifact-integrity guarantee, and notes the new release-asset upload.
  • PSModule/Build-PSModule and PSModule/Publish-PSModule references are temporarily pinned to @main until the companion releases ship. They will be repinned to released SHAs before this PR leaves draft.

Companion repos

Repo Change Link
PSModule/Resolve-PSModuleVersion New action — implementation in progress https://github.com/PSModule/Resolve-PSModuleVersion
PSModule/Build-PSModule Minor — adds Version / Prerelease inputs (PR #135 was closed; needs to be redone) https://github.com/PSModule/Build-PSModule
PSModule/Publish-PSModule Major / BREAKING — drops version calculation, publishes pre-stamped artifact, uploads module zip to release PSModule/Publish-PSModule#71

Implementation plan progress

Notes

  • This PR stays draft until the companion changes in Resolve-PSModuleVersion, Build-PSModule, and Publish-PSModule are released and the @main references are repinned.
  • The public input/output contract of workflow.yml is unchanged for external consumers — this is internal wiring.
  • PR ✨ [Major]: Move version calculation to Plan job and ship the built artifact (#326) #339 covered the same scope and was closed; this PR restarts the work cleanly off the current main.

@MariusStorhaug Marius Storhaug (MariusStorhaug) changed the title WIP: Plan job (#326) 🚀 [Minor]: Plan job decides version before build so tested artifact equals published artifact (#326) May 22, 2026

- name: Build module
uses: PSModule/Build-PSModule@345728124d201f371a8b0f1aacb98f89000a06dc # v4.0.14
uses: PSModule/Build-PSModule@main
- name: Resolve-Version
# Resolve only when the workflow is going to create a release/prerelease.
if: fromJson(steps.Get-Settings.outputs.Settings).Publish.Module.ReleaseType != 'None'
uses: PSModule/Resolve-PSModuleVersion@main

- name: Publish module
uses: PSModule/Publish-PSModule@8917aed588dae1bd1aa2873b1caec1c50c20d255 # v2.2.4
uses: PSModule/Publish-PSModule@main
@github-actions
Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
POWERSHELL Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@github-actions
Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
POWERSHELL Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Marius Storhaug (MariusStorhaug) added a commit to PSModule/Resolve-PSModuleVersion that referenced this pull request May 22, 2026
…tion (#1)

Module version resolution is now available as a standalone action.
Workflows can call it before building so the resolved version is stamped
into the artifact at build time, making the bytes that are tested the
bytes that ship.

- Resolves PSModule/Process-PSModule#326

## New: Standalone `Resolve-PSModuleVersion` action

The action consumes the JSON `Settings` output from
[`PSModule/Get-PSModuleSettings`](https://github.com/PSModule/Get-PSModuleSettings)
and emits:

| Output | Description |
| --- | --- |
| `Version` | `Major.Minor.Patch` portion of the resolved version. |
| `Prerelease` | Prerelease tag, empty when not a prerelease. |
| `FullVersion` | Full version string including `VersionPrefix` and
prerelease tag. |
| `ReleaseType` | `Release`, `Prerelease`, or `None` when no version
bump label is present. |
| `CreateRelease` | `true` when a release or prerelease should be
created. |

Typical usage in the Plan job:

```yaml
- name: Resolve module version
  id: resolve
  uses: PSModule/Resolve-PSModuleVersion@v1
  env:
    GH_TOKEN: ${{ github.token }}
  with:
    Settings: ${{ steps.settings.outputs.Settings }}

- name: Build module
  uses: PSModule/Build-PSModule@v5
  with:
    Version: ${{ steps.resolve.outputs.Version }}
    Prerelease: ${{ steps.resolve.outputs.Prerelease }}
```

The action validates `Settings.Publish.Module.ReleaseType`, applies
`IgnoreLabels` overrides, picks the bump type from PR labels
(`MajorLabels` > `MinorLabels` > `PatchLabels` / `AutoPatching`), then
computes the next version from the higher of the latest GitHub Release
and the latest PowerShell Gallery version. For prereleases it appends
the sanitized branch name, optional `DatePrereleaseFormat` timestamp,
and an incremental counter calculated from existing prereleases on the
same baseline + branch.

## Technical Details

- `action.yml`: composite action with inputs `Settings` (required JSON),
`Name`, `WorkingDirectory`, `Debug`, `Verbose`, `Version`, `Prerelease`,
plus `EventPath` and `EventJson` (both optional, for test overrides —
`EventJson` takes precedence over reading the file at `EventPath`). All
`${{ }}` template expressions are isolated in `env:` sections per zizmor
template-injection requirements. Installs
`PSModule/Install-PSModuleHelpers` and `PSSemVer` before running the
script.
- `scripts/main.ps1`: ports the version-resolution logic that previously
lived in `Publish-PSModule/src/init.ps1`. Reads configuration from
`PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_Settings` JSON instead of
separate env vars. Reads the PR event from
`PSMODULE_RESOLVE_PSMODULEVERSION_INPUT_EventJson` when set, falling
back to the file at `GITHUB_EVENT_PATH`. Emits outputs via
`$env:GITHUB_OUTPUT`. Cleanup-tag discovery stays in
`Publish-PSModule/cleanup.ps1` and is intentionally out of scope here.
- `.github/workflows/Action-Test.yml`: 6 test jobs covering patch,
minor, major, auto-patch, ignore-label, and None scenarios. The
ignore-label job passes the fake PR event as a JSON string via
`EventJson` to bypass the runner's real event file, which cannot be
reliably overridden at the file-system level.
- `README.md`: replaces the template scaffold with the action's contract
and usage examples.

**Implementation plan progress** (PSModule/Process-PSModule#326):
- ✅ Create `Resolve-PSModuleVersion` (LICENSE, README, `action.yml`,
`scripts/main.ps1`, Action-Test workflow)
- ✅ Inputs: `Settings`, `Name`, `WorkingDirectory` (plus
`EventPath`/`EventJson` for test overrides)
- ✅ Outputs: `Version`, `Prerelease`, `FullVersion`, `ReleaseType`,
`CreateRelease`
- ✅ Port version-resolution logic from `Publish-PSModule/src/init.ps1`
(PSSemVer install, GitHub Releases query, PSGallery query, PR-label
parsing, bump selection, prerelease sequencing, `DatePrereleaseFormat`,
`VersionPrefix`)
- ⬜ Dedicated Pester unit tests for label parsing, bump selection, and
prerelease sequencing — covered by the six integration test jobs; a
focused unit-test suite remains open

Related PRs:
- PSModule/Process-PSModule#342 — rewires the workflow's Plan → Build →
Test → Publish chain to consume the resolved version.
- PSModule/Build-PSModule#136 — accepts `Version` / `Prerelease` inputs
and stamps them into the manifest at build time.
- PSModule/Publish-PSModule#71 — removes the version-calculation logic
that moved here.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move version calculation to a Plan job (Resolve-PSModuleVersion) so Build and Publish never calculate or mutate versions

2 participants