Skip to content

🚀 [Feature]: Module manifests now stamped with the resolved version at build time#136

Merged
Marius Storhaug (MariusStorhaug) merged 9 commits into
mainfrom
feat/326-stamp-version
May 24, 2026
Merged

🚀 [Feature]: Module manifests now stamped with the resolved version at build time#136
Marius Storhaug (MariusStorhaug) merged 9 commits into
mainfrom
feat/326-stamp-version

Conversation

@MariusStorhaug
Copy link
Copy Markdown
Member

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

Module manifests are now stamped with the resolved version and prerelease tag at build time. The resulting artifact contains its final ModuleVersion (and PrivateData.PSData.Prerelease) before tests run, so the bytes that are tested are the bytes that ship.

Inputs on Build-PSModule

Build-PSModule now exposes new module-centric inputs:

Input Required Description
Name No Name of the module to build. Defaults to the repository name.
Version Yes Module version (Major.Minor.Patch) to stamp into the manifest. Build fails with a clear error when omitted or malformed.
Prerelease No Prerelease tag (for example mybranch001) to stamp into PrivateData.PSData.Prerelease. When empty, no prerelease tag is written.
OutputFolder No Path (relative to WorkingDirectory) where the built module is placed. Defaults to outputs/module.

Typical usage downstream of PSModule/Resolve-PSModuleVersion:

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

Breaking changes

  • Version is now required. Callers that previously omitted it (relying on the 999.0.0 placeholder) must now pass an explicit version in Major.Minor.Patch format. Builds fail immediately with a clear error when Version is missing or malformed.

Technical details

  • action.yml: adds OutputFolder (default outputs/module), Version (required: true), and Prerelease inputs; Name remains optional and still defaults to the repository name.
  • src/main.ps1: reads OutputFolder, Version, and Prerelease from env; throws immediately when Version is missing or not in Major.Minor.Patch format.
  • src/helpers/Build-PSModule.ps1: ModuleVersion parameter is now [Parameter(Mandatory)].
  • src/helpers/Build/Build-PSModuleManifest.ps1: ModuleVersion is [Parameter(Mandatory)]; the 999.0.0 fallback is removed — the version is assigned directly.

Related PRs:

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.
- Remove Name input; module name is always inferred from GITHUB_REPOSITORY_NAME
- Add OutputFolder input (default: outputs/module) to configure the build output path
- Make Version required in action.yml, mandatory in Build-PSModule and Build-PSModuleManifest helpers
- Remove 999.0.0 version fallback; build now fails explicitly when Version is not provided
- Update Action-Test.yml: remove Name input, add Version: 1.0.0 to all test jobs
@MariusStorhaug Marius Storhaug (MariusStorhaug) marked this pull request as ready for review May 24, 2026 22:39
@MariusStorhaug Marius Storhaug (MariusStorhaug) requested a review from a team as a code owner May 24, 2026 22:39
Copilot AI review requested due to automatic review settings May 24, 2026 22:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Build-PSModule GitHub Action so module manifests are stamped at build time with the resolved ModuleVersion and optional prerelease tag, ensuring the tested artifact matches what will be published.

Changes:

  • Add/propagate action inputs for Version, Prerelease, and OutputFolder, and require Version.
  • Stamp ModuleVersion (and prerelease via manifest generation) during Build-PSModuleManifest.
  • Extend the Action-Test workflow to verify the generated manifest contains the expected version/prerelease values.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/main.ps1 Reads Version/Prerelease/OutputFolder inputs, enforces Version is provided, and passes values into Build-PSModule.
src/helpers/Build/Build-PSModuleManifest.ps1 Accepts ModuleVersion/ModulePrerelease parameters and uses them when generating the output manifest.
src/helpers/Build-PSModule.ps1 Makes ModuleVersion mandatory and forwards version/prerelease to manifest generation.
action.yml Defines new inputs/environment wiring and marks Version as required.
.github/workflows/Action-Test.yml Adds manifest verification steps (including prerelease scenario) and supplies required Version input in tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread action.yml
Comment thread src/main.ps1
@MariusStorhaug Marius Storhaug (MariusStorhaug) merged commit 672aaa7 into main May 24, 2026
21 checks passed
@MariusStorhaug Marius Storhaug (MariusStorhaug) deleted the feat/326-stamp-version branch May 24, 2026 22:54
@github-project-automation github-project-automation Bot moved this from 🆕 New to ✅ Done in PSModule Framework May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: ✅ Done

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