Skip to content

feat!: Migrate markdown and MAML help to Microsoft.PowerShell.PlatyPS 1.x - #173

Merged
tablackburn merged 3 commits into
mainfrom
feature/150-151-platyps-1x-markdown-maml
Aug 25, 2026
Merged

feat!: Migrate markdown and MAML help to Microsoft.PowerShell.PlatyPS 1.x#173
tablackburn merged 3 commits into
mainfrom
feature/150-151-platyps-1x-markdown-maml

Conversation

@tablackburn

Copy link
Copy Markdown
Contributor

Closes #150. Closes #151. Part of #105 (PlatyPS migration), Phase 2 of #120.

The first consumer-breaking change of the 1.0.0 cycle. Migration guide entries included.

Build-PSBuildMarkdown and Build-PSBuildMAMLHelp move to
Microsoft.PowerShell.PlatyPS 1.x together, as the route decision on #120 settled — 2b changes
the on-disk markdown schema that an unmigrated Build-PSBuildMAMLHelp would be handed, and the
two PlatyPS modules cannot be loaded into one psake session.

Baseline to regress against: #170, merged.

The 1.x API is not a rename

Three differences shaped the implementation, all found by running the module rather than reading
its docs (it ships no help content):

Both commands nest their output one level deeper. New-MarkdownCommandHelp and
Export-MamlCommandHelp write to <OutputFolder>/<ModuleName>/, and the MAML file name comes
from the document's front matter — including a casing change from -help.xml to -Help.xml.
Both functions now generate into a staging directory and move the results, so the published
layout is unchanged:

Path
Markdown <Docs.RootDir>/<locale>/<Command>.md
MAML <ModuleOutDir>/<locale>/<ModuleName>-help.xml

That is not a preference. tests/build.tests.ps1 already asserted
<out>/en-US/TestModule-help.xml, and consumers' committed docs trees, static-site
configuration, and .ExternalHelp directives all point at the old layout. On a case-sensitive
filesystem the casing change alone would silently break help lookup.

Neither command errors on an existing file. Without -Force they skip with a warning and
return nothing, so an Overwrite = $false build would quietly produce zero files and report
success. Existing documents are refreshed with Update-MarkdownCommandHelp — which preserves
hand-written prose where regenerating would destroy it — and only genuinely new documents are
moved in. -NoBackup keeps it from littering the docs tree with .md.bak files.

A module landing page poisons the MAML export. Import-MarkdownCommandHelp accepts one
without complaint and returns a bogus CommandHelp; exporting it throws and aborts the whole
batch, writing nothing. Landing pages are filtered out with Measure-PlatyPSMarkdown rather
than caught, since by then the batch is already lost.

PlatyPS is deliberately not a required module

This is the part I would most like reviewed.

Adding Microsoft.PowerShell.PlatyPS to RequiredModules failed 394 tests locally — the
suite dropped from 461 passing to 67. Removing that one line restored it to 458. RequiredModules
forces the module to load into every session that imports PowerShellBuild, including builds
that never generate documentation, and a session already holding platyPS 0.14.2 then cannot
load 1.x at all:

System.IO.FileLoadException: Could not load file or assembly 'YamlDotNet, Version=15.0.0.0,
Culture=neutral, PublicKeyToken=ec19458f3c15af5e'. Assembly with same name is already loaded

Every consumer part-way through this upgrade is in exactly that state. An optional dependency
also matches how the docs tasks have always behaved — they probe with Get-Module -ListAvailable
and skip with a warning — so a hard import-time requirement was arguably always the wrong shape
for an opt-in feature.

The cost is real and is in the migration guide: Install-Module PowerShellBuild no longer
installs PlatyPS.

Why Build-PSBuildUpdatableHelp is stubbed

Same root cause, and it surprised me. Merely naming New-ExternalHelpCab is enough — resolving
that command autoloads platyPS 0.14.2, which then locks 1.x out of the session:

=== does referencing New-ExternalHelpCab autoload old platyPS?
platyPS 0.14.2

So the "leave updatable help on the old module until #152" intermediate I proposed on #120 does
not actually work. The function now warns and returns, naming no PlatyPS command at all. It could
never succeed in 0.8.x either — see #169, three separate defects — so nothing regresses, and #152
implements it properly. Its parameters are kept so the public signature does not change twice.

Settings

  • $PSBPreference.Docs.AlphabeticParamsOrder is removed. PlatyPS 1.x always sorts
    alphabetically and offers no way back, so the setting could only have lied.
  • UseFullTypeName survives unchanged. 1.x inverted the sense of the option — full names are
    now the default — so $false maps to -AbbreviateParameterTypeName and behavior is preserved.
  • ExcludeDontShow maps directly.

A correction to something I put on #120

I claimed repeatedly that nothing observes these functions, and that the repository does not run
its own docs tasks. The second half is true — the root psakeFile.ps1 never invokes them — but
the conclusion was wrong. tests/build.tests.ps1 builds tests/TestModule through
-FromModule PowerShellBuild, whose Build task depends on BuildHelp, so GenerateMarkdown
and GenerateMAML are exercised end to end, and Has MAML help XML has been asserting the
output layout in two contexts all along. Only GenerateUpdatableHelp was genuinely unobserved.
That existing coverage is what pinned the layout contract this PR preserves. I will correct the
Docs task coverage row on #120.

Verification

  • Full suite: 460 passed, 0 failed
  • tests/build.tests.ps1 exercises the real pipeline end to end and still passes in both the
    compile and dot-source contexts
  • New assertions pin the 1.x schema markers, the absence of schema: 2.0.0, and that no
    <ModuleName> directory appears under the locale folder
  • Windows PowerShell 5.1 verified locally in addition to CI, after last time
  • Microsoft.PowerShell.PlatyPS 1.0.3 confirmed to import on Windows PowerShell 5.1, re-checking
    the support-floor decision rather than trusting it

Not included

-WithModulePage. 0.14.x never generated a module landing page, and generating one now would add
a file to every consumer's docs tree. It belongs with #152, which needs it for the cabinet step.

tablackburn and others added 2 commits August 24, 2026 16:41
… 1.x

Closes #150. Closes #151. Part of #105, Phase 2 of #120.

BREAKING CHANGE: help generation uses Microsoft.PowerShell.PlatyPS 1.x,
PlatyPS is no longer a required module, and
$PSBPreference.Docs.AlphabeticParamsOrder is removed.

The two functions are migrated together because they cannot be split: 2b
changes the on-disk markdown schema that an unmigrated
Build-PSBuildMAMLHelp would be handed, and the two PlatyPS modules cannot
be loaded into one psake session.

The 1.x API is not a rename. Three of its differences shape this change:

- New-MarkdownCommandHelp and Export-MamlCommandHelp both write to
  <OutputFolder>/<ModuleName>/, a level deeper than 0.14.x, and the MAML
  file name comes from the document's front matter with different casing.
  Both functions generate into a staging directory and move the results,
  so the published layout stays <Docs.RootDir>/<locale>/<Command>.md and
  <ModuleOutDir>/<locale>/<ModuleName>-help.xml. Consumers' committed
  docs trees, site configuration, and .ExternalHelp directives keep
  working, and tests/build.tests.ps1 already pinned that contract.
- Without -Force, both skip existing files with a warning rather than an
  error, so an Overwrite = $false build would silently produce nothing.
  Existing documents are refreshed with Update-MarkdownCommandHelp, which
  preserves hand-written prose, and only new documents are moved in.
- A module landing page imports as a CommandHelp but fails on export, and
  that failure aborts the batch and writes nothing. Landing pages are
  filtered out with Measure-PlatyPSMarkdown rather than caught.

PlatyPS is deliberately NOT in RequiredModules. Adding it there failed
394 tests locally: RequiredModules forces the load into every session
that imports PowerShellBuild, and a session already holding platyPS
0.14.2 cannot then load 1.x -- both ship their own YamlDotNet with
different assembly identities. Every consumer mid-upgrade is in exactly
that state. An optional dependency also matches how the docs tasks have
always behaved, probing with Get-Module -ListAvailable and skipping.

For the same reason Build-PSBuildUpdatableHelp no longer names
New-ExternalHelpCab. Merely resolving that command autoloads platyPS
0.14.2 and poisons the session against 1.x. It warns and returns until
#152 migrates the cabinet pipeline; it could never succeed before either,
see #169.

AlphabeticParamsOrder has no 1.x equivalent -- ordering is unconditional
now -- so keeping the setting would have been a lie. UseFullTypeName
survives unchanged: 1.x inverted the sense of the option, so $false maps
to -AbbreviateParameterTypeName.

Suite: 460 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012AKaM9i6NyMFDcJNeC34h5
The entry was inserted by a script that joined lines with CRLF and then
converted newlines again, turning every added line ending into \r\r\n.
That rewrote the whole file as far as git was concerned, burying a
twenty-line addition in a 691-line diff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012AKaM9i6NyMFDcJNeC34h5
Copilot AI lite review requested due to automatic review settings August 24, 2026 20:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This pull request migrates PowerShellBuild’s markdown and MAML help generation from platyPS 0.14.x to Microsoft.PowerShell.PlatyPS 1.x while preserving the existing on-disk output layout expected by downstream consumers, and makes the new PlatyPS dependency optional to avoid load conflicts during upgrades.

Changes:

  • Update Build-PSBuildMarkdown and Build-PSBuildMAMLHelp to use PlatyPS 1.x cmdlets, including staging+flattening to preserve existing docs/help output paths and file naming.
  • Remove the platyPS RequiredModules dependency (PlatyPS becomes optional) and update psake/InvokeBuild tasks and tests to probe for Microsoft.PowerShell.PlatyPS.
  • Stub Build-PSBuildUpdatableHelp to warn-and-return without referencing legacy PlatyPS cmdlets, pending the #152 migration.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
PowerShellBuild/Public/Build-PSBuildMarkdown.ps1 Migrates markdown generation to PlatyPS 1.x, refreshes existing docs, and stages output to preserve the <Docs>/<locale> layout.
PowerShellBuild/Public/Build-PSBuildMAMLHelp.ps1 Migrates MAML export to PlatyPS 1.x via import/export pipeline and stages output to preserve the <Out>/<locale>/<Module>-help.xml layout.
PowerShellBuild/Public/Build-PSBuildUpdatableHelp.ps1 Temporarily stubs updatable help generation to avoid autoloading legacy platyPS during partial upgrades.
PowerShellBuild/psakeFile.ps1 Updates docs task preconditions and parameters to use the new PlatyPS module name and removes the removed docs preference.
PowerShellBuild/IB.tasks.ps1 Mirrors the psake task updates for InvokeBuild consumers.
PowerShellBuild/PowerShellBuild.psd1 Removes platyPS from RequiredModules to keep PlatyPS optional.
requirements.psd1 Replaces the platyPS dependency with Microsoft.PowerShell.PlatyPS 1.0.3 for bootstrap installs.
PowerShellBuild/build.properties.ps1 Removes AlphabeticParamsOrder and updates docs preference comments for PlatyPS 1.x behavior.
PowerShellBuild/en-US/Messages.psd1 Adds a localized warning message for the updatable-help stub behavior.
tests/Build-PSBuildHelp.tests.ps1 Updates tests to detect PlatyPS 1.x schema markers and validates flattening behavior; updates updatable-help expectations for the stubbed function.
tests/fixtures/FixtureHelpers.psm1 Updates helper parameter scaffolding for the changed Build-PSBuildMarkdown signature.
docs/migration-v0.8-to-v1.0.md Adds migration guide entries describing the PlatyPS 1.x move, optional dependency, schema change, and temporary updatable-help unavailability.
CHANGELOG.md Records the breaking help-generation migration and associated behavioral changes.
instructions/repository-specific.instructions.md Updates dependency documentation to reference Microsoft.PowerShell.PlatyPS as optional.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +94 to +99
$newMarkdownParams = @{
ModuleInfo = $moduleInfo
OutputFolder = $stagingPath
Locale = $Locale
Verbose = $VerbosePreference
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch on the line, though the mechanism is not quite that — and chasing it down turned up a real defect, so this is fixed in 2ec2ea1.

$VerbosePreference is an ActionPreference enum, not a string, and binding it to a cmdlet's common -Verbose parameter converts by the underlying number rather than by truthiness. Measured on PowerShell 7.6.5 against a cmdlet that actually emits verbose output:

SilentlyContinue   -> OK, verbose records emitted: 0
Continue           -> OK, verbose records emitted: 1

So the default case behaves correctly, and the two most common values work. It is not "any non-empty string coerces to $true".

The real defect is narrower and still worth fixing: because the conversion is numeric, Stop (1) and Inquire (3) also bind as $true, so setting either turns verbose output on despite neither asking for it. Build-PSBuildMAMLHelp already used the explicit comparison in this same pull request, so the two functions disagreed with each other. Both now use:

Verbose = ($VerbosePreference -eq 'Continue')

One boundary worth recording for anyone who finds this thread later: the same splat against a custom function's own [switch]$Verbose parameter does not silently coerce at all, it throws — Invalid cast from 'System.Management.Automation.ActionPreference' to 'System.Management.Automation.SwitchParameter'. Cmdlet common parameters and script-function switch parameters take different binding paths here.

Suite after the change: 460 passed, 0 failed.

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

Test Results

    4 files  ± 0    724 suites   - 8   4m 30s ⏱️ -44s
  462 tests  -  6    459 ✅  -  4   3 💤  -  2  0 ❌ ±0 
1 852 runs   - 24  1 801 ✅  - 10  51 💤  - 14  0 ❌ ±0 

Results for commit 2ec2ea1. ± Comparison against base commit 51468d0.

This pull request removes 10 and adds 4 tests. Note that renamed tests count towards both.
Help building functions.Build-PSBuildMarkdown.produces markdown carrying the 0.14.x schema marker
Help building functions.Build-PSBuildUpdatableHelp.creates the output directory
Help building functions.Build-PSBuildUpdatableHelp.declines to run on platforms without makecab
Help building functions.Build-PSBuildUpdatableHelp.fails parameter binding on the cab step
Help building functions.Build-PSBuildUpdatableHelp.produces a cabinet file
Help building functions.Build-PSBuildUpdatableHelp.produces the help info manifest
Test help for Build-PSBuildMarkdown.Parameter AlphabeticParamsOrder.Has correct [mandatory] value
Test help for Build-PSBuildMarkdown.Parameter AlphabeticParamsOrder.Has correct parameter type
Test help for Build-PSBuildMarkdown.Parameter AlphabeticParamsOrder.Has description
Test help for Build-PSBuildMarkdown.Test AlphabeticParamsOrder help parameter help for Build-PSBuildMarkdown.finds help parameter in code: AlphabeticParamsOrder
Help building functions.Build-PSBuildMarkdown.keeps the markdown directly under the locale directory
Help building functions.Build-PSBuildMarkdown.produces markdown carrying the 1.x schema marker
Help building functions.Build-PSBuildUpdatableHelp.returns without throwing
Help building functions.Build-PSBuildUpdatableHelp.writes nothing

♻️ This comment has been updated with latest results.

…ename

PlatyPS 1.x names the MAML file after the document's "external help file"
front matter key, which it defaults to "<Module>-Help.xml" where 0.14.x
wrote "<Module>-help.xml". The previous commit corrected the casing by
renaming the file after export, which fixed the name and left the markdown
still claiming the other one.

That disagreement is the exact condition upstream punishes: PowerShell/platyPS#797
was closed as expected behavior with "You need to be consistent with the
name you use in .EXTERNALHELPFILE", because inconsistent names across a
batch cause files to be silently skipped or truncated.

Build-PSBuildMarkdown now sets the key explicitly through -Metadata, so the
markdown, the exported MAML file name, and any existing .ExternalHelp
directive all agree, and the export step no longer renames anything.

Verified: generated front matter reads "external help file:
TestModule-help.xml" and the exported file matches.

Suite: 460 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012AKaM9i6NyMFDcJNeC34h5
@tablackburn
tablackburn merged commit 48366eb into main Aug 25, 2026
9 checks passed
@tablackburn
tablackburn deleted the feature/150-151-platyps-1x-markdown-maml branch August 25, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PlatyPS 2c: migrate Build-PSBuildMAMLHelp to Export-MamlCommandHelp PlatyPS 2b: migrate Build-PSBuildMarkdown to New-MarkdownCommandHelp

2 participants