mirror of https://github.com/raandree/NTFSSecurity
Browse Source
Pushing a tag such as 5.0.0 or 5.0.0-rc1 on master now publishes the package that the build job built and tested to the PowerShell Gallery and creates the GitHub release with NTFSSecurity.zip. - New-ModulePackage.ps1 copies only the FileList files of the Release build, so no debug symbols, XML documentation, or copy of System.Management.Automation.dll ship. It builds the nupkg with Compress-PSResource and adds the command tags (PSIncludes_Cmdlet, PSCmdlet_*, PSCommand_*) that PSResourceGet leaves out and that the Gallery uses to list cmdlets and that Find-Command searches. Every CI run builds and uploads the packages. - Get-ReleaseInfo.ps1 returns the version and release notes: a dated CHANGELOG section for a release, the [Unreleased] section for a prerelease. - The release job checks that the tag matches the manifest version and points to a commit on master, reads the API key from the environment powershell-gallery, and skips steps already done, so a rerun is safe. - The manifest gets the prerelease label rc1 and a release notes link; the 5.0.0 changelog entries move back to [Unreleased] until the final release. - Tests/Release.Tests.ps1 covers both scripts and the packages; the changelog check moves there from Manifest.Tests.ps1. - Docs/Contributing/05-Releasing.md describes the one-time setup and the release steps; Docs/README.md explains -AllowPrerelease. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant <ai@example.com>pull/98/head
11 changed files with 776 additions and 26 deletions
@ -0,0 +1,119 @@ |
|||
<# |
|||
.SYNOPSIS |
|||
Returns the version of the module and its release notes from CHANGELOG.md. |
|||
|
|||
.DESCRIPTION |
|||
Reads ModuleVersion and the prerelease label (PrivateData.PSData.Prerelease) from the module manifest and returns |
|||
the version to release, such as 5.0.0 or 5.0.0-rc1, with its release notes: |
|||
|
|||
- A release without a prerelease label takes the notes of the section "## [<version>] - <yyyy-MM-dd>". The |
|||
section must exist and have a release date. |
|||
- A prerelease takes the notes of the section "## [Unreleased]", which must not be empty. CHANGELOG.md must not |
|||
have a section for the version yet; it gets one with the final release. |
|||
|
|||
The notes are the text below the heading up to the next section or up to the link definitions at the end of the |
|||
file, with line feeds as line breaks. |
|||
|
|||
.PARAMETER ManifestPath |
|||
Specifies the module manifest. |
|||
|
|||
.PARAMETER ChangelogPath |
|||
Specifies CHANGELOG.md. |
|||
|
|||
.EXAMPLE |
|||
.\.github\scripts\Get-ReleaseInfo.ps1 -ManifestPath .\NTFSSecurity\NTFSSecurity.psd1 -ChangelogPath .\CHANGELOG.md |
|||
|
|||
Returns the version, whether it is a prerelease, the release date, and the release notes. |
|||
#> |
|||
[CmdletBinding()] |
|||
[OutputType([pscustomobject])] |
|||
param ( |
|||
[Parameter(Mandatory)] |
|||
[ValidateScript({ Test-Path -LiteralPath $_ -PathType Leaf })] |
|||
[string] |
|||
$ManifestPath, |
|||
|
|||
[Parameter(Mandatory)] |
|||
[ValidateScript({ Test-Path -LiteralPath $_ -PathType Leaf })] |
|||
[string] |
|||
$ChangelogPath |
|||
) |
|||
|
|||
$ErrorActionPreference = 'Stop' |
|||
|
|||
function Get-ChangelogSection { |
|||
<# |
|||
Returns the date and the notes of the section with the given name, such as 5.0.0 or Unreleased, or nothing if |
|||
CHANGELOG.md has no such section. |
|||
#> |
|||
param ( |
|||
[Parameter(Mandatory)] |
|||
[string] |
|||
$Changelog, |
|||
|
|||
[Parameter(Mandatory)] |
|||
[string] |
|||
$Name |
|||
) |
|||
|
|||
$pattern = '(?ms)^## \[{0}\](?:[ \t]+-[ \t]+(?<Date>\S+))?[ \t]*$(?<Notes>.*?)(?=^## |^\[[^\]]+\]:[ \t]|\z)' -f |
|||
[regex]::Escape($Name) |
|||
$match = [regex]::Match($Changelog, $pattern) |
|||
if ($match.Success) { |
|||
[pscustomobject]@{ |
|||
Date = $match.Groups['Date'].Value |
|||
Notes = $match.Groups['Notes'].Value.Trim() |
|||
} |
|||
} |
|||
} |
|||
|
|||
$manifest = Import-PowerShellDataFile -LiteralPath $ManifestPath |
|||
$moduleVersion = "$($manifest.ModuleVersion)" |
|||
if ($moduleVersion -notmatch '^\d+\.\d+\.\d+$') { |
|||
throw "The module version '$moduleVersion' must have three parts, such as 5.0.0." |
|||
} |
|||
|
|||
$prerelease = "$($manifest.PrivateData.PSData.Prerelease)" |
|||
if ($prerelease -and $prerelease -notmatch '^[A-Za-z][0-9A-Za-z-]*$') { |
|||
throw ("The prerelease label '$prerelease' isn't valid in the PowerShell Gallery. Use letters, digits, and " + |
|||
'hyphens only, starting with a letter, such as rc1.') |
|||
} |
|||
|
|||
$changelog = (Get-Content -LiteralPath $ChangelogPath -Raw -Encoding UTF8) -replace '\r\n', "`n" |
|||
$versionSection = Get-ChangelogSection -Changelog $changelog -Name $moduleVersion |
|||
|
|||
if ($prerelease) { |
|||
if ($versionSection) { |
|||
throw ("CHANGELOG.md already has a section for $moduleVersion, so $moduleVersion is released. A prerelease " + |
|||
'needs a higher module version.') |
|||
} |
|||
|
|||
$section = Get-ChangelogSection -Changelog $changelog -Name 'Unreleased' |
|||
if (-not $section -or -not $section.Notes) { |
|||
throw "The [Unreleased] section of CHANGELOG.md is empty. It holds the notes of $moduleVersion-$prerelease." |
|||
} |
|||
$date = $null |
|||
} else { |
|||
if (-not $versionSection) { |
|||
throw ("CHANGELOG.md has no section for $moduleVersion. Rename the [Unreleased] section to " + |
|||
"[$moduleVersion] and add the release date.") |
|||
} |
|||
if ($versionSection.Date -notmatch '^\d{4}-\d{2}-\d{2}$') { |
|||
throw "The section of $moduleVersion in CHANGELOG.md has no release date in the format yyyy-MM-dd." |
|||
} |
|||
if (-not $versionSection.Notes) { |
|||
throw "The section of $moduleVersion in CHANGELOG.md is empty." |
|||
} |
|||
|
|||
$section = $versionSection |
|||
$date = [datetime]::ParseExact($versionSection.Date, 'yyyy-MM-dd', [cultureinfo]::InvariantCulture) |
|||
} |
|||
|
|||
[pscustomobject]@{ |
|||
Version = if ($prerelease) { "$moduleVersion-$prerelease" } else { $moduleVersion } |
|||
ModuleVersion = $moduleVersion |
|||
Prerelease = $prerelease |
|||
IsPrerelease = [bool] $prerelease |
|||
Date = $date |
|||
Notes = $section.Notes |
|||
} |
|||
@ -0,0 +1,148 @@ |
|||
<# |
|||
.SYNOPSIS |
|||
Builds the packages of a release from the build output of the module. |
|||
|
|||
.DESCRIPTION |
|||
Copies the files that the FileList of the module manifest names from BuildPath into the folder NTFSSecurity in |
|||
DestinationPath, so that the packages contain no debug symbols or other build output, and checks the copy with |
|||
Test-ModuleManifest. Then it creates the NuGet package for the PowerShell Gallery with Compress-PSResource, named |
|||
after the version and the prerelease label, and adds the command tags that the PowerShell Gallery uses to list the |
|||
cmdlets of the module. Last, it creates NTFSSecurity.zip, which contains the folder NTFSSecurity, for the GitHub |
|||
release. |
|||
|
|||
The script needs Compress-PSResource from Microsoft.PowerShell.PSResourceGet, which comes with PowerShell 7.4 and |
|||
later. |
|||
|
|||
.PARAMETER BuildPath |
|||
Specifies the build output folder, such as NTFSSecurity\bin\Release. |
|||
|
|||
.PARAMETER DestinationPath |
|||
Specifies the folder for the module folder and the packages. The script replaces the module folder and the |
|||
packages that an earlier run created there. |
|||
|
|||
.EXAMPLE |
|||
.\.github\scripts\New-ModulePackage.ps1 -BuildPath .\NTFSSecurity\bin\Release -DestinationPath .\out |
|||
|
|||
Creates the folder .\out\NTFSSecurity and the files .\out\NTFSSecurity.<version>.nupkg and .\out\NTFSSecurity.zip. |
|||
#> |
|||
[CmdletBinding()] |
|||
[OutputType([pscustomobject])] |
|||
param ( |
|||
[Parameter(Mandatory)] |
|||
[ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })] |
|||
[string] |
|||
$BuildPath, |
|||
|
|||
[Parameter(Mandatory)] |
|||
[ValidateNotNullOrEmpty()] |
|||
[string] |
|||
$DestinationPath |
|||
) |
|||
|
|||
$ErrorActionPreference = 'Stop' |
|||
$moduleName = 'NTFSSecurity' |
|||
|
|||
if (-not (Get-Command -Name Compress-PSResource -ErrorAction SilentlyContinue)) { |
|||
throw 'Compress-PSResource was not found. Run the script in PowerShell 7.4 or later, which includes PSResourceGet.' |
|||
} |
|||
|
|||
$buildRoot = (Resolve-Path -LiteralPath $BuildPath).ProviderPath |
|||
$manifestPath = Join-Path -Path $buildRoot -ChildPath "$moduleName.psd1" |
|||
if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) { |
|||
throw "The build output in '$buildRoot' has no module manifest $moduleName.psd1." |
|||
} |
|||
|
|||
$manifest = Import-PowerShellDataFile -LiteralPath $manifestPath |
|||
$version = "$($manifest.ModuleVersion)" |
|||
if ($manifest.PrivateData.PSData.Prerelease) { |
|||
$version = '{0}-{1}' -f $version, $manifest.PrivateData.PSData.Prerelease |
|||
} |
|||
|
|||
$missingFiles = @($manifest.FileList | Where-Object -FilterScript { |
|||
-not (Test-Path -LiteralPath (Join-Path -Path $buildRoot -ChildPath $_) -PathType Leaf) |
|||
}) |
|||
if ($missingFiles) { |
|||
throw "The build output in '$buildRoot' lacks these files of the FileList: $($missingFiles -join ', ')." |
|||
} |
|||
|
|||
$destinationRoot = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($DestinationPath) |
|||
$modulePath = Join-Path -Path $destinationRoot -ChildPath $moduleName |
|||
$packagePath = Join-Path -Path $destinationRoot -ChildPath "$moduleName.$version.nupkg" |
|||
$zipPath = Join-Path -Path $destinationRoot -ChildPath "$moduleName.zip" |
|||
foreach ($path in $modulePath, $packagePath, $zipPath) { |
|||
if (Test-Path -LiteralPath $path) { |
|||
Remove-Item -LiteralPath $path -Recurse -Force |
|||
} |
|||
} |
|||
New-Item -ItemType Directory -Path $modulePath -Force | Out-Null |
|||
|
|||
foreach ($file in $manifest.FileList) { |
|||
$target = Join-Path -Path $modulePath -ChildPath $file |
|||
$targetFolder = Split-Path -Path $target -Parent |
|||
if (-not (Test-Path -LiteralPath $targetFolder)) { |
|||
New-Item -ItemType Directory -Path $targetFolder | Out-Null |
|||
} |
|||
Copy-Item -LiteralPath (Join-Path -Path $buildRoot -ChildPath $file) -Destination $target |
|||
} |
|||
|
|||
$testParameters = @{ |
|||
Path = Join-Path -Path $modulePath -ChildPath "$moduleName.psd1" |
|||
ErrorAction = 'Stop' |
|||
WarningVariable = 'manifestWarnings' |
|||
WarningAction = 'SilentlyContinue' |
|||
} |
|||
$null = Test-ModuleManifest @testParameters |
|||
if ($manifestWarnings) { |
|||
throw "Test-ModuleManifest reported warnings for the module in '$modulePath': $($manifestWarnings -join ' ')" |
|||
} |
|||
|
|||
Compress-PSResource -Path $modulePath -DestinationPath $destinationRoot |
|||
if (-not (Test-Path -LiteralPath $packagePath -PathType Leaf)) { |
|||
throw "Compress-PSResource didn't create the package '$packagePath'." |
|||
} |
|||
|
|||
<# |
|||
PSResourceGet doesn't add the command tags that PowerShellGet 2 added when it published 4.2.6. The PowerShell |
|||
Gallery lists the cmdlets of a module from these tags, and Find-Command searches them, so add the missing ones to |
|||
the nuspec in the package. |
|||
#> |
|||
$commandTags = @('PSIncludes_Cmdlet') + @($manifest.CmdletsToExport | Sort-Object -Unique | |
|||
ForEach-Object -Process { "PSCmdlet_$_"; "PSCommand_$_" }) |
|||
$archive = [IO.Compression.ZipFile]::Open($packagePath, [IO.Compression.ZipArchiveMode]::Update) |
|||
try { |
|||
$stream = $archive.GetEntry("$moduleName.nuspec").Open() |
|||
try { |
|||
$nuspec = New-Object -TypeName 'System.Xml.XmlDocument' |
|||
$nuspec.Load($stream) |
|||
$tagsNode = $nuspec.SelectSingleNode("/*[local-name()='package']/*[local-name()='metadata']/*[local-name()='tags']") |
|||
if (-not $tagsNode) { |
|||
throw "The nuspec in the package '$packagePath' has no tags element." |
|||
} |
|||
$tags = @($tagsNode.InnerText -split '\s+' | Where-Object -FilterScript { $_ }) |
|||
$tagsNode.InnerText = ($tags + @($commandTags | Where-Object -FilterScript { $_ -notin $tags })) -join ' ' |
|||
|
|||
$stream.SetLength(0) |
|||
$settings = New-Object -TypeName 'System.Xml.XmlWriterSettings' |
|||
$settings.Encoding = New-Object -TypeName 'System.Text.UTF8Encoding' -ArgumentList $false |
|||
$settings.Indent = $true |
|||
$writer = [Xml.XmlWriter]::Create($stream, $settings) |
|||
try { |
|||
$nuspec.Save($writer) |
|||
} finally { |
|||
$writer.Dispose() |
|||
} |
|||
} finally { |
|||
$stream.Dispose() |
|||
} |
|||
} finally { |
|||
$archive.Dispose() |
|||
} |
|||
|
|||
[IO.Compression.ZipFile]::CreateFromDirectory($modulePath, $zipPath, [IO.Compression.CompressionLevel]::Optimal, $true) |
|||
|
|||
[pscustomobject]@{ |
|||
Version = $version |
|||
ModulePath = $modulePath |
|||
PackagePath = $packagePath |
|||
ZipPath = $zipPath |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
# Release a new version |
|||
|
|||
This page explains how a maintainer releases a new version of NTFSSecurity. |
|||
The CI workflow does the work: when you push a version tag, it builds and |
|||
tests the module, publishes the package to the PowerShell Gallery, and creates |
|||
the GitHub release with `NTFSSecurity.zip`. |
|||
|
|||
## Prepare the repository once |
|||
|
|||
1. In the PowerShell Gallery, [create an API key][api-key] for the account |
|||
that owns NTFSSecurity. Select the scope **Push new versions of existing |
|||
packages**, enter `NTFSSecurity` as the glob pattern, and choose an |
|||
expiration. |
|||
2. In the settings of the repository on GitHub, under **Environments**, |
|||
create the environment `powershell-gallery`. To approve every release |
|||
before it's published, add yourself as a required reviewer. Under |
|||
**Deployment branches and tags**, allow only tags such as |
|||
`[0-9]*.[0-9]*.[0-9]*`. |
|||
3. Add the API key as the secret `PSGALLERY_API_KEY` of the environment. |
|||
The command asks for the key: |
|||
|
|||
```powershell |
|||
gh secret set PSGALLERY_API_KEY --env powershell-gallery --repo raandree/NTFSSecurity |
|||
``` |
|||
|
|||
Renew the API key before it expires, and update the secret. |
|||
|
|||
## Versions |
|||
|
|||
- `ModuleVersion` in `NTFSSecurity\NTFSSecurity.psd1` and the |
|||
`AssemblyVersion` and `AssemblyFileVersion` of the `NTFSSecurity`, |
|||
`Security2`, and `PrivilegeControl` projects carry the same version. The |
|||
tests in `Tests\Manifest.Tests.ps1` check this. |
|||
- A prerelease, such as `5.0.0-rc1`, has its label in `Prerelease` under |
|||
`PrivateData.PSData` of the module manifest. Its release notes are the |
|||
`[Unreleased]` section of `CHANGELOG.md`. The PowerShell Gallery installs a |
|||
prerelease only when you add `-AllowPrerelease`. |
|||
- A release, such as `5.0.0`, has no `Prerelease` value. Its release notes |
|||
are the section `## [5.0.0] - <date>` of `CHANGELOG.md`. |
|||
|
|||
The tests in `Tests\Release.Tests.ps1` check that `CHANGELOG.md` has the |
|||
release notes for the version of the module manifest. |
|||
|
|||
## Publish a prerelease |
|||
|
|||
1. Set the version and the `Prerelease` label, such as `rc1`, and make sure |
|||
that the `[Unreleased]` section of `CHANGELOG.md` describes the changes. |
|||
Merge the change into `master`. |
|||
2. Tag the commit on `master` with the version and push the tag: |
|||
|
|||
```powershell |
|||
git switch master |
|||
git pull |
|||
git tag 5.0.0-rc1 |
|||
git push origin 5.0.0-rc1 |
|||
``` |
|||
|
|||
3. Watch the CI run of the tag. The **Release** job checks that the tag |
|||
matches the version in the module manifest and points to a commit on |
|||
`master`. Then it publishes the package and creates a GitHub prerelease. |
|||
If the environment requires a reviewer, approve the deployment. |
|||
4. Install the prerelease in a test environment and test it: |
|||
|
|||
```powershell |
|||
Install-Module -Name NTFSSecurity -AllowPrerelease -Scope CurrentUser |
|||
``` |
|||
|
|||
For another prerelease, increase the label, such as `rc2`. The PowerShell |
|||
Gallery compares labels as text, so `rc10` sorts before `rc2`. |
|||
|
|||
## Publish a release |
|||
|
|||
1. Remove the `Prerelease` value from the module manifest. |
|||
2. In `CHANGELOG.md`, rename `## [Unreleased]` to the version with the |
|||
release date, such as `## [5.0.0] - 2026-10-31`, add an empty |
|||
`## [Unreleased]` section above it, and update the links at the end of the |
|||
file: |
|||
|
|||
```markdown |
|||
[Unreleased]: https://github.com/raandree/NTFSSecurity/compare/5.0.0...HEAD |
|||
[5.0.0]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...5.0.0 |
|||
``` |
|||
|
|||
3. Merge the change into `master`. Then tag the commit with the version, such |
|||
as `5.0.0`, and push the tag, as for a prerelease. The **Release** job |
|||
warns if the date in `CHANGELOG.md` isn't the day of the release. |
|||
|
|||
## If a release fails |
|||
|
|||
The **Release** job skips what's already done: a version that the PowerShell |
|||
Gallery already has, and a GitHub release that already exists. If the |
|||
failure doesn't need a change in the repository, fix the cause and rerun the |
|||
failed job. |
|||
|
|||
If the fix needs a change in the repository and the PowerShell Gallery |
|||
doesn't have the version yet, delete the tag, merge the fix, and tag the new |
|||
commit: |
|||
|
|||
```powershell |
|||
git push origin --delete 5.0.0-rc1 |
|||
git tag --delete 5.0.0-rc1 |
|||
``` |
|||
|
|||
The PowerShell Gallery never accepts the same version twice. To replace a |
|||
published version, publish the next one, such as `5.0.0-rc2`, and |
|||
[unlist][unlist] the faulty version. |
|||
|
|||
<!-- External URLs --> |
|||
[api-key]: https://learn.microsoft.com/powershell/gallery/how-to/managing-profile/creating-apikeys |
|||
[unlist]: https://learn.microsoft.com/powershell/gallery/how-to/publishing-packages/unlisting-packages |
|||
@ -0,0 +1,255 @@ |
|||
<# |
|||
Tests the release scripts in .github\scripts: Get-ReleaseInfo.ps1, which reads the version of the module and its |
|||
release notes, and New-ModulePackage.ps1, which builds the packages from the module built in |
|||
NTFSSecurity\bin\Release. |
|||
#> |
|||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute( |
|||
'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.' |
|||
)] |
|||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute( |
|||
'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'The test helpers write only to TestDrive.' |
|||
)] |
|||
param () |
|||
|
|||
BeforeDiscovery { |
|||
# Compress-PSResource is part of Microsoft.PowerShell.PSResourceGet, which comes with PowerShell 7.4 and later. |
|||
$canPackage = [bool](Get-Command -Name Compress-PSResource -ErrorAction SilentlyContinue) |
|||
} |
|||
|
|||
Describe 'Get-ReleaseInfo.ps1' { |
|||
BeforeAll { |
|||
$scriptPath = Join-Path -Path $PSScriptRoot -ChildPath '..\.github\scripts\Get-ReleaseInfo.ps1' |
|||
$manifestPath = Join-Path -Path $TestDrive -ChildPath 'Module.psd1' |
|||
|
|||
function Set-TestManifest { |
|||
param ( |
|||
[Parameter(Mandatory)] |
|||
[string] |
|||
$Version, |
|||
|
|||
[Parameter()] |
|||
[string] |
|||
$Prerelease |
|||
) |
|||
|
|||
$psData = if ($Prerelease) { "Prerelease = '$Prerelease'" } else { '' } |
|||
Set-Content -LiteralPath $manifestPath -Value "@{ ModuleVersion = '$Version'; PrivateData = @{ PSData = @{ $psData } } }" |
|||
} |
|||
|
|||
$changelogPath = Join-Path -Path $TestDrive -ChildPath 'CHANGELOG.md' |
|||
Set-Content -LiteralPath $changelogPath -Value @' |
|||
# Changelog |
|||
|
|||
## [Unreleased] |
|||
|
|||
### Fixed |
|||
|
|||
- Fix a thing |
|||
|
|||
## [2.0.0] - 2026-01-02 |
|||
|
|||
### Changed |
|||
|
|||
- Change a thing |
|||
|
|||
## [1.0.0] |
|||
|
|||
### Added |
|||
|
|||
- Add a thing |
|||
|
|||
[Unreleased]: https://example.com/compare/2.0.0...HEAD |
|||
[2.0.0]: https://example.com/compare/1.0.0...2.0.0 |
|||
'@ |
|||
|
|||
$releasedChangelogPath = Join-Path -Path $TestDrive -ChildPath 'CHANGELOG-released.md' |
|||
Set-Content -LiteralPath $releasedChangelogPath -Value @' |
|||
# Changelog |
|||
|
|||
## [Unreleased] |
|||
|
|||
## [2.0.0] - 2026-01-02 |
|||
|
|||
### Changed |
|||
|
|||
- Change a thing |
|||
|
|||
[Unreleased]: https://example.com/compare/2.0.0...HEAD |
|||
[2.0.0]: https://example.com/compare/1.0.0...2.0.0 |
|||
'@ |
|||
} |
|||
|
|||
Context 'When the module manifest has no prerelease label' { |
|||
It 'Should return the version, the release date, and the notes of its section' { |
|||
Set-TestManifest -Version '2.0.0' |
|||
|
|||
$release = & $scriptPath -ManifestPath $manifestPath -ChangelogPath $changelogPath |
|||
|
|||
$release.Version | Should -BeExactly '2.0.0' |
|||
$release.IsPrerelease | Should -BeFalse |
|||
$release.Date | Should -Be ([datetime] '2026-01-02') |
|||
$release.Notes | Should -BeExactly "### Changed`n`n- Change a thing" |
|||
} |
|||
|
|||
It 'Should not include the link definitions after the last section in the notes' { |
|||
Set-TestManifest -Version '2.0.0' |
|||
|
|||
$release = & $scriptPath -ManifestPath $manifestPath -ChangelogPath $releasedChangelogPath |
|||
|
|||
$release.Notes | Should -BeExactly "### Changed`n`n- Change a thing" |
|||
} |
|||
|
|||
It 'Should fail if the section of the version has no release date' { |
|||
Set-TestManifest -Version '1.0.0' |
|||
|
|||
{ & $scriptPath -ManifestPath $manifestPath -ChangelogPath $changelogPath } | Should -Throw '*1.0.0*date*' |
|||
} |
|||
|
|||
It 'Should fail if CHANGELOG.md has no section for the version' { |
|||
Set-TestManifest -Version '3.0.0' |
|||
|
|||
{ & $scriptPath -ManifestPath $manifestPath -ChangelogPath $changelogPath } | Should -Throw '*3.0.0*' |
|||
} |
|||
} |
|||
|
|||
Context 'When the module manifest has a prerelease label' { |
|||
It 'Should return the prerelease version and the notes of the Unreleased section' { |
|||
Set-TestManifest -Version '3.0.0' -Prerelease 'rc1' |
|||
|
|||
$release = & $scriptPath -ManifestPath $manifestPath -ChangelogPath $changelogPath |
|||
|
|||
$release.Version | Should -BeExactly '3.0.0-rc1' |
|||
$release.IsPrerelease | Should -BeTrue |
|||
$release.Date | Should -BeNullOrEmpty |
|||
$release.Notes | Should -BeExactly "### Fixed`n`n- Fix a thing" |
|||
} |
|||
|
|||
It 'Should fail if the Unreleased section is empty' { |
|||
Set-TestManifest -Version '3.0.0' -Prerelease 'rc1' |
|||
|
|||
{ & $scriptPath -ManifestPath $manifestPath -ChangelogPath $releasedChangelogPath } | |
|||
Should -Throw '*Unreleased*' |
|||
} |
|||
|
|||
It 'Should fail if CHANGELOG.md already has a section for the version' { |
|||
Set-TestManifest -Version '2.0.0' -Prerelease 'rc1' |
|||
|
|||
{ & $scriptPath -ManifestPath $manifestPath -ChangelogPath $changelogPath } | Should -Throw '*2.0.0*' |
|||
} |
|||
|
|||
It 'Should fail if the label is not valid in the PowerShell Gallery' { |
|||
Set-TestManifest -Version '3.0.0' -Prerelease 'rc.1' |
|||
|
|||
{ & $scriptPath -ManifestPath $manifestPath -ChangelogPath $changelogPath } | Should -Throw '*rc.1*' |
|||
} |
|||
} |
|||
|
|||
Context 'When it reads the module manifest and CHANGELOG.md of the repository' { |
|||
It 'Should return release notes for the version of the module manifest' { |
|||
$repositoryPath = Join-Path -Path $PSScriptRoot -ChildPath '..' |
|||
$sourceManifestPath = Join-Path -Path $repositoryPath -ChildPath 'NTFSSecurity\NTFSSecurity.psd1' |
|||
$manifest = Import-PowerShellDataFile -LiteralPath $sourceManifestPath |
|||
|
|||
$release = & $scriptPath -ManifestPath $sourceManifestPath -ChangelogPath (Join-Path -Path $repositoryPath -ChildPath 'CHANGELOG.md') |
|||
|
|||
$release.Version | Should -BeLike "$($manifest.ModuleVersion)*" |
|||
$release.Notes | Should -Not -BeNullOrEmpty |
|||
$release.Notes | Should -Not -Match '(?m)^\[[^\]]+\]: ' |
|||
} |
|||
} |
|||
} |
|||
|
|||
Describe 'New-ModulePackage.ps1' -Skip:(-not $canPackage) { |
|||
BeforeAll { |
|||
$scriptPath = Join-Path -Path $PSScriptRoot -ChildPath '..\.github\scripts\New-ModulePackage.ps1' |
|||
$buildPath = Join-Path -Path $PSScriptRoot -ChildPath '..\NTFSSecurity\bin\Release' |
|||
$manifest = Import-PowerShellDataFile -LiteralPath (Join-Path -Path $buildPath -ChildPath 'NTFSSecurity.psd1') |
|||
$version = $manifest.ModuleVersion |
|||
if ($manifest.PrivateData.PSData.Prerelease) { |
|||
$version = '{0}-{1}' -f $version, $manifest.PrivateData.PSData.Prerelease |
|||
} |
|||
$expectedFiles = @($manifest.FileList | ForEach-Object -Process { $_ -replace '\\', '/' } | Sort-Object) |
|||
|
|||
function Get-ZipEntryName { |
|||
param ( |
|||
[Parameter(Mandatory)] |
|||
[string] |
|||
$Path |
|||
) |
|||
|
|||
$zip = [IO.Compression.ZipFile]::OpenRead($Path) |
|||
try { |
|||
$zip.Entries | Where-Object -Property Name | ForEach-Object -Process { $_.FullName } |
|||
} finally { |
|||
$zip.Dispose() |
|||
} |
|||
} |
|||
|
|||
$package = & $scriptPath -BuildPath $buildPath -DestinationPath (Join-Path -Path $TestDrive -ChildPath 'out') |
|||
} |
|||
|
|||
It 'Should copy exactly the files of the FileList into the module folder' { |
|||
$files = Get-ChildItem -LiteralPath $package.ModulePath -Recurse -File | |
|||
ForEach-Object -Process { $_.FullName.Substring($package.ModulePath.Length + 1) -replace '\\', '/' } |
|||
|
|||
($files | Sort-Object) -join ', ' | Should -BeExactly ($expectedFiles -join ', ') |
|||
} |
|||
|
|||
It 'Should name the NuGet package after the version, including the prerelease label' { |
|||
Split-Path -Path $package.PackagePath -Leaf | Should -BeExactly "NTFSSecurity.$version.nupkg" |
|||
$package.Version | Should -BeExactly $version |
|||
} |
|||
|
|||
It 'Should put exactly the module files into the NuGet package' { |
|||
$entries = Get-ZipEntryName -Path $package.PackagePath | |
|||
Where-Object -FilterScript { $_ -notmatch '^(_rels/|package/|\[Content_Types\]\.xml$|NTFSSecurity\.nuspec$)' } |
|||
|
|||
($entries | Sort-Object) -join ', ' | Should -BeExactly ($expectedFiles -join ', ') |
|||
} |
|||
|
|||
It 'Should set the version and the link to the release notes in the NuGet package' { |
|||
$zip = [IO.Compression.ZipFile]::OpenRead($package.PackagePath) |
|||
try { |
|||
$reader = New-Object -TypeName 'System.IO.StreamReader' -ArgumentList $zip.GetEntry('NTFSSecurity.nuspec').Open() |
|||
$nuspec = [xml] $reader.ReadToEnd() |
|||
$reader.Dispose() |
|||
} finally { |
|||
$zip.Dispose() |
|||
} |
|||
|
|||
$nuspec.package.metadata.version | Should -BeExactly $version |
|||
$nuspec.package.metadata.releaseNotes | Should -Match 'https://github\.com/raandree/NTFSSecurity/blob/master/CHANGELOG\.md' |
|||
} |
|||
|
|||
It 'Should tag the NuGet package with its cmdlets, which the PowerShell Gallery lists and Find-Command searches' { |
|||
$zip = [IO.Compression.ZipFile]::OpenRead($package.PackagePath) |
|||
try { |
|||
$reader = New-Object -TypeName 'System.IO.StreamReader' -ArgumentList $zip.GetEntry('NTFSSecurity.nuspec').Open() |
|||
$tags = ([xml] $reader.ReadToEnd()).package.metadata.tags -split '\s+' |
|||
$reader.Dispose() |
|||
} finally { |
|||
$zip.Dispose() |
|||
} |
|||
$expectedTags = @('PSModule', 'PSIncludes_Cmdlet') + $manifest.PrivateData.PSData.Tags + |
|||
@($manifest.CmdletsToExport | ForEach-Object -Process { "PSCmdlet_$_"; "PSCommand_$_" }) |
|||
|
|||
$expectedTags | Where-Object -FilterScript { $_ -notin $tags } | Should -BeNullOrEmpty |
|||
$tags | Group-Object | Where-Object -Property Count -GT -Value 1 | ForEach-Object -Process { $_.Name } | |
|||
Should -BeNullOrEmpty |
|||
} |
|||
|
|||
It 'Should put the module folder into NTFSSecurity.zip' { |
|||
$entries = Get-ZipEntryName -Path $package.ZipPath | Sort-Object |
|||
|
|||
$entries -join ', ' | Should -BeExactly (($expectedFiles | ForEach-Object -Process { "NTFSSecurity/$_" }) -join ', ') |
|||
} |
|||
|
|||
It 'Should fail if a file of the FileList is missing from the build output' { |
|||
$incompletePath = Join-Path -Path $TestDrive -ChildPath 'incomplete' |
|||
Copy-Item -LiteralPath $buildPath -Destination $incompletePath -Recurse |
|||
Remove-Item -LiteralPath (Join-Path -Path $incompletePath -ChildPath 'en-US\NTFSSecurity.dll-Help.xml') |
|||
|
|||
{ & $scriptPath -BuildPath $incompletePath -DestinationPath (Join-Path -Path $TestDrive -ChildPath 'out-incomplete') } | |
|||
Should -Throw '*NTFSSecurity.dll-Help.xml*' |
|||
} |
|||
} |
|||
Loading…
Reference in new issue