Functions/GenXdev.Coding/Publish-ReleaseNotesToManifest.ps1
|
<############################################################################## Part of PowerShell module : GenXdev.Coding Original cmdlet filename : Publish-ReleaseNotesToManifest.ps1 Original author : René Vaessen / GenXdev Version : 3.28.2026 ################################################################################ Copyright (c) 2026 René Vaessen / GenXdev Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ################################################################################> ############################################################################### <# .SYNOPSIS Publishes uncompleted release notes from README.md to the module manifest. .DESCRIPTION Reads the "## Release notes" section from a README.md file, collects all uncompleted release notes (lines starting with - ☐), writes them into the ReleaseNotes field of GenXdev.psd1, and marks them as completed (☐ → ☒) in the README. Both files are updated atomically in one operation. .PARAMETER UseHomeREADME Use README.md from PowerShell profile directory instead of current location. .PARAMETER UseOneDriveREADME Use README.md from OneDrive directory instead of current location. .PARAMETER ManifestPath Path to the .psd1 module manifest to update. If not specified, auto-detects the GenXdev.psd1 in the module directory. .EXAMPLE Publish-ReleaseNotesToManifest .EXAMPLE Publish-ReleaseNotesToManifest -ManifestPath ".\MyModule.psd1" #> function Publish-ReleaseNotesToManifest { [CmdletBinding(SupportsShouldProcess = $true)] [Alias('pubrelnotes')] param( ####################################################################### [Parameter( Mandatory = $true, HelpMessage = 'Path to the .psd1 module manifest' )] [string] $ManifestPath, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Use README in PowerShell profile directory' )] [switch] $UseHomeREADME, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Use README in OneDrive directory' )] [switch] $UseOneDriveREADME ) begin { Microsoft.PowerShell.Utility\Write-Verbose ` 'Starting Publish-ReleaseNotesToManifest' # Resolve README path (same logic as Add-LineToREADME begin block) if ($UseHomeREADME) { $readmePath = GenXdev\Expand-Path ` -FilePath "$([IO.Path]::GetDirectoryName($Profile))\README.md" } elseif ($UseOneDriveREADME) { $readmePath = GenXdev\Expand-Path ` -FilePath "$(GenXdev\Get-KnownFolderPath -KnownFolder 'OneDrive')\README.md" ` -CreateDirectory } else { # Walk up from current directory to find README $startDir = GenXdev\Expand-Path $PWD $readmePath = $null $currentDir = $startDir while ($true) { $candidate = "$currentDir\README.md" if ([System.IO.File]::Exists($candidate)) { $readmePath = $candidate break } $parent = GenXdev\Expand-Path "$currentDir\..\" if ($parent -eq $currentDir) { break } $currentDir = $parent } if (-not $readmePath) { $readmePath = GenXdev\Expand-Path ` -FilePath "$(GenXdev\Get-KnownFolderPath -KnownFolder 'OneDrive')\README.md" ` -CreateDirectory } } Microsoft.PowerShell.Utility\Write-Verbose ` "Using README at: ${readmePath}" if (![IO.File]::Exists($readmePath)) { throw "README file not found at: ${readmePath}" } if ([String]::IsNullOrWhiteSpace($ManifestPath)) { throw "Specify a valid ManifestPath -ManifestPath." } $ManifestPath = GenXdev\Expand-Path $ManifestPath; if (-not [IO.File]::Exists($ManifestPath)) { throw "Module manifest not found. Specify -ManifestPath." } Microsoft.PowerShell.Utility\Write-Verbose ` "Using manifest at: ${ManifestPath}" } process { # Read README content $readmeContent = [IO.File]::ReadAllText($readmePath) $lineEnding = if ($readmeContent.IndexOf("`r`n") -ge 0) { "`r`n" } ` else { "`n" } $section = '## Release notes' # Find the section $sectionStart = $readmeContent.IndexOf( $section, [System.StringComparison]::OrdinalIgnoreCase ) if ($sectionStart -lt 0) { Microsoft.PowerShell.Utility\Write-Warning ` "Section '${section}' not found in ${readmePath}" return } $sectionContentStart = $sectionStart + $section.Length + $lineEnding.Length $sectionLevel = ($section -replace '^([#]+).*$', '$1').Length $rest = $readmeContent.Substring($sectionContentStart) $nextHeading = [regex]::Match($rest, "(?m)^#{1,$sectionLevel}\s") $sectionEnd = if ($nextHeading.Success) { $sectionContentStart + $nextHeading.Index } else { $readmeContent.Length } $sectionContent = $readmeContent.Substring( $sectionContentStart, $sectionEnd - $sectionContentStart ) $sectionLines = $sectionContent -split $lineEnding # Collect uncompleted release notes (lines with ☐) $uncompletedLines = [System.Collections.Generic.List[string]]::new() $uncompletedRaw = [System.Collections.Generic.List[string]]::new() $lineIndices = [System.Collections.Generic.List[int]]::new() for ($i = 0; $i -lt $sectionLines.Count; $i++) { $ln = $sectionLines[$i] if ($ln -match '^- ☐ \[P') { # Strip prefix and date: "- ☐ [P{N}] yyyyMMdd --> " → raw text $rawText = $ln -replace '^- ☐ \[P-?\d+\] \d{8}\s*-->\s*', '' $null = $uncompletedLines.Add($ln) $null = $uncompletedRaw.Add($rawText) $null = $lineIndices.Add($i) } } if ($uncompletedRaw.Count -eq 0) { Microsoft.PowerShell.Utility\Write-Host ` 'No uncompleted release notes found.' return } # Build release notes text for the manifest $releaseNotesText = ($uncompletedRaw -join "`n") # Mark lines as done in README foreach ($idx in $lineIndices) { $sectionLines[$idx] = $sectionLines[$idx].Replace('☐', '☒') } $newSectionContent = $sectionLines -join $lineEnding $newReadmeContent = $readmeContent.Substring(0, $sectionContentStart) + $newSectionContent + $readmeContent.Substring($sectionEnd) # Write both files atomically if ($PSCmdlet.ShouldProcess( "README and ${ManifestPath}", 'Publish release notes to manifest' )) { # Save backup copies first $readmeBackup = $readmeContent $manifestBackup = [IO.File]::ReadAllText($ManifestPath) try { GenXdev\Write-TextFileAtomic ` -FilePath $readmePath ` -Data $newReadmeContent PowerShellGet\Update-ModuleManifest -Path $ManifestPath -ReleaseNotes $releaseNotesText -Confirm:$false Microsoft.PowerShell.Utility\Write-Host ` "Published $($uncompletedRaw.Count) release note(s) to ${ManifestPath}" } catch { # Rollback on failure Microsoft.PowerShell.Utility\Write-Warning ` "Write failed, rolling back: $_" try { GenXdev\Write-TextFileAtomic ` -FilePath $readmePath ` -Data $readmeBackup } catch { Microsoft.PowerShell.Utility\Write-Warning ` "Rollback also failed: $_" } throw } } } end { } } |