Skip to content
Open
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
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,44 @@

### Fixed

- [**#210**](https://github.com/psake/PowerShellBuild/issues/210)
Compiling a module no longer drops its culture directory. `-Compile` staged the
manifest, the root module and `$PSBPreference.Build.CopyDirectories`, and nothing
else — so a hand-written `en-US/about_<Module>.help.txt` was left in the source
tree and the built module shipped without its about topic, while the build reported
success. `CopyDirectories` naming the culture directory was the only way to ship one,
and that setting reads as an escape hatch for extra content rather than as the
mechanism help travels by. Compile mode now stages a source culture directory on its
own, so `Get-Help about_<Module>` works in a compiled build. Staging is decided by
content, not by name alone: a directory is staged only when it holds an
`about_*.help.txt`, a `*-help.xml` or a `*.psd1`, because `bin` is a real culture name
(Bini) and `ps` is Pashto and neither should be copied into a built module.

- [**#211**](https://github.com/psake/PowerShellBuild/issues/211)
The built module no longer carries a stray copy of a culture directory's `.psd1` at its
root. The staging glob used `-Depth 1`, which recursed one level and matched
`en-US/Messages.psd1`, and the copy wrote it flat into the output root where nothing
reads it. On Windows PowerShell 5.1 it was worse: `-Depth` combined with `-Include`
degrades to a full `-Recurse` there, so files at any depth were flattened into the root
and same-named files at different depths could collide, which made the contents of a
published package depend on which host built it. The glob now matches the module root
only, in both modes. The same pattern in the readme discovery in `psakeFile.ps1` and
`IB.tasks.ps1` is fixed with it, where on 5.1 it walked the whole project root and
`Select-Object -First 1` then took an arbitrary readme.

- [**#212**](https://github.com/psake/PowerShellBuild/issues/212)
Compile and non-compile mode now agree on what wins when a module has both a readme and
a hand-written about topic. The two used to disagree by accident of statement ordering —
the non-compile bulk copy runs after the readme block and overwrote the readme-derived
file, while in compile mode the readme landed last and replaced whatever
`CopyDirectories` had staged — so the winner depended on
`$PSBPreference.Build.CompileModule`, a setting with nothing to do with help. A source
about topic now wins in both modes, and a warning reports that the readme was not used.
Source wins because nothing is converted here: `ConvertReadMeToAboutHelp` copies the
Markdown as-is, and Markdown satisfies none of the structure `Get-Help` documents for an
about topic, so letting the readme win would replace conformant help with content
`Get-Help` cannot present.

- [**#206**](https://github.com/psake/PowerShellBuild/issues/206)
`Build-PSBuildModule -Compile` no longer compiles your working directory when no
compile directories are given. `CompileDirectories` defaulted to `@()`, and
Expand Down Expand Up @@ -300,7 +338,7 @@
that passed before may now correctly fail.
- [**#96**](https://github.com/psake/PowerShellBuild/issues/96)
`Test-PSBuildScriptAnalysis` no longer fails with a path-resolution error
when `SettingsPath` is not supplied. An unsupplied path was forwarded to

Check warning on line 341 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (unsupplied) Suggestions: (unapplied, unsullied, unspoiled, unstapled, unsupported)
PSScriptAnalyzer as `-Settings ''`, which resolved against the current
directory and threw before any analysis ran, so the function's own
documented example could not run as written.
Expand Down
13 changes: 11 additions & 2 deletions PowerShellBuild/IB.tasks.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,17 @@
}

if ($PSBPreference.Help.ConvertReadMeToAboutHelp) {
$readMePath = Get-ChildItem -Path $PSBPreference.General.ProjectRoot -Include 'readme.md', 'readme.markdown', 'readme.txt' -Depth 1 |
Select-Object -First 1
# The project root only, and only the trailing wildcard reaches it. -Depth 1 combined
# with -Include degrades to a full -Recurse on Windows PowerShell 5.1, so this used to
# walk the entire project root -- including the build output -- and Select-Object then
# took whichever readme the enumeration happened to reach first. Removing -Depth without
# adding the wildcard matches nothing, because without recursion -Include filters against
# the leaf of -Path. See psake/PowerShellBuild#211.
$getReadMeSplat = @{
Path = [IO.Path]::Combine($PSBPreference.General.ProjectRoot, '*')
Include = 'readme.md', 'readme.markdown', 'readme.txt'
}
$readMePath = Get-ChildItem @getReadMeSplat | Select-Object -First 1
if ($readMePath) {
$buildParams.ReadMePath = $readMePath
}
Expand All @@ -45,7 +54,7 @@



$analyzePreReqs = {

Check warning on line 57 in PowerShellBuild/IB.tasks.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (Reqs) Suggestions: (re's, Re's, rebs, recs, reds)
$result = $true
if (-not $PSBPreference.Test.ScriptAnalysis.Enabled) {
Write-Warning 'Script analysis is not enabled.'
Expand All @@ -59,7 +68,7 @@
}

# Synopsis: Execute PSScriptAnalyzer tests
Task Analyze -If (. $analyzePreReqs) Build, {

Check warning on line 71 in PowerShellBuild/IB.tasks.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (Reqs) Suggestions: (re's, Re's, rebs, recs, reds)
$analyzeParams = @{
Path = $PSBPreference.Build.ModuleOutDir
SeverityThreshold = $PSBPreference.Test.ScriptAnalysis.FailBuildOnSeverityLevel
Expand All @@ -68,7 +77,7 @@
Test-PSBuildScriptAnalysis @analyzeParams
}

$pesterPreReqs = {

Check warning on line 80 in PowerShellBuild/IB.tasks.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (Reqs) Suggestions: (re's, Re's, rebs, recs, reds)
$result = $true
if (-not $PSBPreference.Test.Enabled) {
Write-Warning 'Pester testing is not enabled.'
Expand All @@ -86,7 +95,7 @@
}

# Synopsis: Execute Pester tests
Task Pester -If (. $pesterPreReqs) Build, {

Check warning on line 98 in PowerShellBuild/IB.tasks.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (Reqs) Suggestions: (re's, Re's, rebs, recs, reds)
$pesterParams = @{
Path = $PSBPreference.Test.RootDir
ModuleName = $PSBPreference.General.ModuleName
Expand All @@ -107,7 +116,7 @@



$genMarkdownPreReqs = {

Check warning on line 119 in PowerShellBuild/IB.tasks.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (Reqs) Suggestions: (re's, Re's, rebs, recs, reds)
$result = $true
if (-not (Get-Module Microsoft.PowerShell.PlatyPS -ListAvailable)) {
Write-Warning "Microsoft.PowerShell.PlatyPS module is not installed. Skipping [$($task.name)] task."
Expand Down
9 changes: 8 additions & 1 deletion PowerShellBuild/Private/Get-PSBuildHelpLocale.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# spell-checker:ignore Bini Pashto
function Get-PSBuildHelpLocale {
<#
.SYNOPSIS
Expand All @@ -23,8 +24,14 @@ function Get-PSBuildHelpLocale {
codes is reported as a locale even if it holds no help at all. That is the safe way to
be wrong. Callers already handle a locale that turns out to have nothing to build, and
over-reporting costs a warning where under-reporting silently drops a consumer's help.

A caller that acts on the result by copying rather than by reporting has to narrow it
further, because there the same over-report is not safe: 'bin' is a real culture name
(Bini) and 'ps' is Pashto, so staging by name alone would copy a binary directory into a
built module. Build-PSBuildModule adds a content test for that reason.
.PARAMETER Path
Path to the docs tree whose subdirectories are being classified.
Path to the tree whose subdirectories are being classified. A docs tree in the help
tasks, and a module source directory when staging a build.
.PARAMETER ModulePath
Path to the built module. Its subdirectories are the locales the MAML step has
already written help for. Optional; without it only the culture name test applies.
Expand Down
117 changes: 99 additions & 18 deletions PowerShellBuild/Public/Build-PSBuildModule.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# spell-checker:ignore modulename
# spell-checker:ignore modulename Bini Pashto
function Build-PSBuildModule {
<#
.SYNOPSIS
Expand Down Expand Up @@ -26,7 +26,10 @@ function Build-PSBuildModule {
String that will be added to your PSM1 file after each script file.
.PARAMETER ReadMePath
Path to project README. If present, this will become the
"about_<ModuleName>.help.txt" file in the build module.
"about_<ModuleName>.help.txt" file in the build module. A hand-written
about topic in the source tree's culture directory takes precedence over
it, in both compile and non-compile mode, and a warning reports that the
readme was not used.
.PARAMETER CompileDirectories
List of directories containing .ps1 files that will also be compiled
into the PSM1.
Expand Down Expand Up @@ -98,11 +101,21 @@ function Build-PSBuildModule {
New-Item @newItemSplat > $null
}

# Copy "non-processed files"
# Copy "non-processed files". This stages the module's loose root files -- the manifest, the
# root module, and any format or type data -- and nothing else. Anything below the root is
# CopyDirectories' job, or the culture staging below.
#
# Both halves of this splat matter, and either one on its own is wrong. -Depth 1 implied
# recursion one level down, so a localized en-US/Messages.psd1 matched and Copy-Item wrote it
# flat into the output root, where nothing reads it (psake/PowerShellBuild#211). Windows
# PowerShell 5.1 makes that worse: -Depth combined with -Include degrades to a full -Recurse
# there, so files at any depth were flattened into the root and same-named files at different
# depths collided. Removing -Depth alone matches nothing at all, because without recursion
# -Include filters against the leaf of -Path rather than against that directory's children --
# so the trailing wildcard has to be added at the same time.
$getChildItemSplat = @{
Path = $Path
Path = [IO.Path]::Combine($Path, '*')
Include = '*.psm1', '*.psd1', '*.ps1xml'
Depth = 1
}
Get-ChildItem @getChildItemSplat |
Copy-Item -Destination $DestinationPath -Force
Expand All @@ -111,28 +124,96 @@ function Build-PSBuildModule {
Copy-Item -Path $copyPath -Destination $DestinationPath -Recurse -Force
}

# A module's culture directory carries its localized data and its about topics. Compile mode
# stages the loose root files and CopyDirectories and nothing else, so a hand-written
# en-US/about_<Module>.help.txt was left behind and the built module shipped without its about
# topic while the build reported success. Naming the culture directory in CopyDirectories was
# the only way to ship one, and that setting reads as an escape hatch for extra content rather
# than as the mechanism help travels by. See psake/PowerShellBuild#210.
#
# Non-compile mode needs none of this: the bulk copy below already brings the whole source
# tree, culture directories included.
if ($Compile.IsPresent) {
foreach ($localeName in (Get-PSBuildHelpLocale -Path $Path)) {
# Already staged verbatim by the loop above.
if ($localeName -in $CopyDirectories) {
continue
}

# Get-PSBuildHelpLocale deliberately over-reports: a directory counts as a locale when
# its name is a culture the runtime knows, whether or not it holds any help. That is
# the safe way to be wrong where the cost is a warning, and the unsafe way here --
# 'bin' is a real culture name (Bini) and 'ps' is Pashto, so staging on the name alone
# would copy a binary directory into the shipped module. Content is what decides: an
# about topic, MAML help, or localized data is what makes a directory a culture
# directory rather than a directory that happens to share a name with one.
$localePath = [IO.Path]::Combine($Path, $localeName)
$getChildItemSplat = @{
Path = [IO.Path]::Combine($localePath, '*')
Include = 'about_*.help.txt', '*-help.xml', '*.psd1'
File = $true
ErrorAction = 'SilentlyContinue'
}
$localeContent = Get-ChildItem @getChildItemSplat | Select-Object -First 1
if (-not $localeContent) {
continue
}

Copy-Item -Path $localePath -Destination $DestinationPath -Recurse -Force
}
}

# Copy README as about_<modulename>.help.txt
if (-not [string]::IsNullOrEmpty($ReadMePath)) {
$culturePath = [IO.Path]::Combine($DestinationPath, $Culture)
$aboutModulePath = [IO.Path]::Combine(
$culturePath,
"about_$($ModuleName).help.txt"
)
# The guard belongs to New-Item alone. With the copy inside it, an existing
# culture directory meant no about help file was written at all -- and
# CopyDirectories runs above, so naming the culture directory there was enough
# to suppress it silently. That is psake/PowerShellBuild#207. The Force below was
# already here and unreachable; this restores the overwrite it was written for.
if (-not (Test-Path -LiteralPath $culturePath -PathType Container)) {
New-Item -Path $culturePath -ItemType Directory -Force > $null
}

$copyItemSplat = @{
LiteralPath = $ReadMePath
Destination = $aboutModulePath
Force = $true
# A hand-written about topic in the source tree wins over the readme, in both modes.
# The two modes used to disagree by accident of statement ordering: non-compile mode's
# bulk copy runs after this block and overwrote the readme-derived file, while in compile
# mode the readme landed last and replaced whatever CopyDirectories had staged. Same two
# inputs, opposite results, decided by a setting that has nothing to do with help. See
# psake/PowerShellBuild#212.
#
# Source wins because no conversion happens here: this is a plain copy of the Markdown
# readme, which satisfies none of the TOPIC and four-space-indent structure Get-Help
# documents for an about topic. Letting the readme win would replace a conformant help
# file with raw Markdown. Warned about rather than done silently, because
# ConvertReadMeToAboutHelp is an explicit instruction that is not being carried out.
#
# Tested against the source tree, not the output: non-compile mode has not copied the
# source about topic yet at this point, so the output cannot answer the question in
# either mode. This is a narrower guard than the one psake/PowerShellBuild#207 removed --
# that one skipped the copy whenever the culture *directory* existed, whatever was in it.
$sourceAboutModulePath = [IO.Path]::Combine(
$Path,
$Culture,
"about_$($ModuleName).help.txt"
)
if (Test-Path -LiteralPath $sourceAboutModulePath -PathType Leaf) {
Write-Warning (
$LocalizedData.SourceAboutTopicOverridesReadMe -f $sourceAboutModulePath, $ReadMePath
)
} else {
# The guard belongs to New-Item alone. With the copy inside it, an existing
# culture directory meant no about help file was written at all -- and
# CopyDirectories runs above, so naming the culture directory there was enough
# to suppress it silently. That is psake/PowerShellBuild#207. The Force below was
# already here and unreachable; this restores the overwrite it was written for.
if (-not (Test-Path -LiteralPath $culturePath -PathType Container)) {
New-Item -Path $culturePath -ItemType Directory -Force > $null
}

$copyItemSplat = @{
LiteralPath = $ReadMePath
Destination = $aboutModulePath
Force = $true
}
Copy-Item @copyItemSplat
}
Copy-Item @copyItemSplat
}

# Copy source files to destination and optionally combine *.ps1 files
Expand Down
1 change: 1 addition & 0 deletions PowerShellBuild/en-US/Messages.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ CertificateExpired=The resolved certificate has expired (NotAfter: {0}). Code si
CertificateMissingCodeSigningEku=The resolved certificate does not have the Code Signing Enhanced Key Usage (EKU: 1.3.6.1.5.5.7.3.3). Subject=[{0}]
CertificateSourceStoreNotSupported=CertificateSource 'Store' is only supported on Windows platforms.
CertificateValidationRelaxed=No unexpired code signing certificate was found, and validation was skipped, so an expired certificate was selected (NotAfter: {0}). Subject=[{1}]
SourceAboutTopicOverridesReadMe=The source tree already provides an about help topic at [{0}], so the readme at [{1}] was not converted into about help. The readme is copied as-is rather than converted, and Markdown is not a conformant about topic, so using it would replace hand-written help with content Get-Help cannot present. Remove the source about topic to have the readme used instead, or stop setting ConvertReadMeToAboutHelp.
ExportModuleMemberInSourceRootModule=The source root module [{0}] calls Export-ModuleMember. Compiling appends that call after the concatenated function files, where it runs against an output directory that no longer has the function directories to discover, and the effective export set is the intersection of that call and FunctionsToExport in the manifest. A loader that finds nothing therefore leaves the built module exporting nothing. Guard the call so it does nothing when the function directories are absent, or remove it and let the manifest govern the export set.
'@
13 changes: 11 additions & 2 deletions PowerShellBuild/psakeFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,17 @@ Task StageFiles -Depends $PSBStageFilesDependency {
}

if ($PSBPreference.Help.ConvertReadMeToAboutHelp) {
$readMePath = Get-ChildItem -Path $PSBPreference.General.ProjectRoot -Include 'readme.md', 'readme.markdown', 'readme.txt' -Depth 1 |
Select-Object -First 1
# The project root only, and only the trailing wildcard reaches it. -Depth 1 combined
# with -Include degrades to a full -Recurse on Windows PowerShell 5.1, so this used to
# walk the entire project root -- including the build output -- and Select-Object then
# took whichever readme the enumeration happened to reach first. Removing -Depth without
# adding the wildcard matches nothing, because without recursion -Include filters against
# the leaf of -Path. See psake/PowerShellBuild#211.
$getReadMeSplat = @{
Path = [IO.Path]::Combine($PSBPreference.General.ProjectRoot, '*')
Include = 'readme.md', 'readme.markdown', 'readme.txt'
}
$readMePath = Get-ChildItem @getReadMeSplat | Select-Object -First 1
if ($readMePath) {
$buildParams.ReadMePath = $readMePath
}
Expand Down
Loading
Loading