diff --git a/.github/instructions/platyps-help.instructions.md b/.github/instructions/platyps-help.instructions.md index dcb320c..b2714da 100644 --- a/.github/instructions/platyps-help.instructions.md +++ b/.github/instructions/platyps-help.instructions.md @@ -66,6 +66,9 @@ Import-MarkdownCommandHelp -Path ./docs/NovaModuleTools/en-US/.md | - The `external help file` field must always use the module name, not the command name: `NovaModuleTools-Help.xml`. The `Module Name` field must match the project name. When both fields use the module name, Nova build produces a single `-Help.xml` under `dist//en-US/`. If either field contains a command name instead, the build produces per-command XML files and the module manifest cannot find its help. - Keep the H1 title equal to the exact command name. - Preserve the standard PlatyPS section order with uppercase H2 headers: `SYNOPSIS`, `SYNTAX`, optional `ALIASES`, `DESCRIPTION`, `EXAMPLES`, `PARAMETERS`, `INPUTS`, `OUTPUTS`, `NOTES`, and `RELATED LINKS`. +- In `## RELATED LINKS`, use only a bulleted list of Markdown links. Do not use bare URLs or backticked command names as list items. +- Prefer relative links to sibling command-help files when the related topic has a matching file in `docs/NovaModuleTools/en-US/`, for example `[Invoke-NovaBuild](./Invoke-NovaBuild.md)`. +- Do not use GitHub blob URLs in shipped command help. If a relative command-help link is not suitable after PlatyPS validation, use the related topic's `novamoduletools.com` `HelpUri` instead. - Keep at least one example under `## EXAMPLES`. - Keep parameter sections as `### -ParameterName` blocks with the PlatyPS-generated YAML metadata code block. - Only hand-edit parameter metadata when PlatyPS cannot infer it correctly, especially `DefaultValue` and `SupportsWildcards`. @@ -82,6 +85,7 @@ Import-MarkdownCommandHelp -Path ./docs/NovaModuleTools/en-US/.md | - Use `Update-MarkdownCommandHelp` after command or parameter changes so syntax, aliases, and parameter metadata stay synchronized with the implementation. - Use `Test-MarkdownCommandHelp` as the quick structural gate before handoff; use `Import-MarkdownCommandHelp` diagnostics when you need more detail. - Use existing valid files under `docs/NovaModuleTools/en-US/` as the structural template before inventing a new layout. +- Validate any new `RELATED LINKS` target style through the real PlatyPS import/export path before applying it repo-wide. - Keep command help separate from contributor docs and project/site docs. ## Review expectations @@ -90,4 +94,5 @@ Import-MarkdownCommandHelp -Path ./docs/NovaModuleTools/en-US/.md | - Reviewers should flag help files where `external help file` contains a command name (e.g., `Get-Something-Help.xml`) instead of the module name (`NovaModuleTools-Help.xml`). This produces per-command XML files that the module manifest cannot locate at runtime. - Reviewers should flag help files that would fail `Test-MarkdownCommandHelp` or produce diagnostics/errors when imported with `Import-MarkdownCommandHelp`. - Reviewers should flag any new public entry point that does not add its matching command-help file in the same change. +- Reviewers should flag `RELATED LINKS` entries that use GitHub blob URLs, plain URLs, or bare/backticked command names instead of Markdown links. - Treat build errors from `Import-MarkdownCommandHelp` as a sign that the file is not valid PlatyPS help yet. diff --git a/CHANGELOG.md b/CHANGELOG.md index 7955eb6..1bb9037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Changed +- `Deploy-NovaPackage` now shows a concise resolved-upload summary before execution, reports progress while multiple artifacts are uploading, and prints a short completion summary with a suggested verification step after successful raw uploads. + ### Deprecated ### Removed @@ -24,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Fixed - `Test-NovaBuild` now expands configured Pester coverage globs into concrete source files before invoking Pester, so nested helpers remain measurable in `artifacts/coverage.xml` while repository and scaffolded `project.json` defaults can stay on the simpler `src/private/**/*.ps1` entry. +- PowerShell command help `RELATED LINKS` now use valid PlatyPS Markdown links and point to shipped help topics instead of GitHub blob pages. ### Security diff --git a/RELEASE_NOTE.md b/RELEASE_NOTE.md index d318a84..811fba6 100644 --- a/RELEASE_NOTE.md +++ b/RELEASE_NOTE.md @@ -13,12 +13,16 @@ This file summarizes the release notes for NovaModuleTools. **UNRELEASED** chang ### Changed +- `Deploy-NovaPackage` now shows clearer terminal feedback during raw package uploads, including a concise pre-flight summary, progress across multiple artifacts, and a short verification hint after success. + ### Deprecated ### Removed ### Fixed +- PowerShell command help `RELATED LINKS` now use valid help-topic links instead of GitHub blob pages. + ### Security ## [3.0.1] - 2026-05-18 diff --git a/docs/NovaModuleTools/en-US/Deploy-NovaPackage.md b/docs/NovaModuleTools/en-US/Deploy-NovaPackage.md index 4a6fc55..7494f2a 100644 --- a/docs/NovaModuleTools/en-US/Deploy-NovaPackage.md +++ b/docs/NovaModuleTools/en-US/Deploy-NovaPackage.md @@ -1,10 +1,10 @@ ---- +--- document type: cmdlet external help file: NovaModuleTools-Help.xml -HelpUri: 'https://www.novamoduletools.com/packaging-and-delivery.html#upload' +HelpUri: https://www.novamoduletools.com/packaging-and-delivery.html#upload Locale: en-US Module Name: NovaModuleTools -ms.date: 04/25/2026 +ms.date: 05.24.2026 PlatyPS schema version: 2024-05-01 title: Deploy-NovaPackage --- @@ -19,10 +19,15 @@ Uploads one or more generated package artifacts to a raw HTTP endpoint. ### __AllParameterSets -```powershell -PS> Deploy-NovaPackage [[-PackagePath] ] [[-PackageType] ] [[-Url] ] [[-Repository] ] [[-UploadPath] ] [[-Headers] ] [[-Token] ] [[-TokenEnvironmentVariable] ] [[-AuthenticationScheme] ] [-WhatIf] [-Confirm] [] +``` +Deploy-NovaPackage [[-PackagePath] ] [[-PackageType] ] [[-Url] ] + [[-Repository] ] [-WhatIf] [-Confirm] [-UploadPath ] [-Headers ] + [-Token ] [-TokenEnvironmentVariable ] [-AuthenticationScheme ] + [] ``` +## ALIASES + ## DESCRIPTION `Deploy-NovaPackage` uploads existing package artifacts that were generated by `New-NovaModulePackage`. @@ -42,6 +47,8 @@ This command is intentionally separate from `Publish-NovaModule`. `Deploy-NovaPa When you run `Deploy-NovaPackage -Confirm`, PowerShell uses its native confirmation prompt against the full resolved upload set instead of prompting once per artifact. +Before uploads start, `Deploy-NovaPackage` prints a concise summary of the resolved upload set. During multi-artifact uploads it shows progress, and after success it prints a short completion summary plus a verification hint while still returning result objects for automation. + ## EXAMPLES ### EXAMPLE 1 @@ -87,10 +94,10 @@ Uploads the explicitly selected package files instead of discovering them from t ### EXAMPLE 6 ```powershell -PS> Deploy-NovaPackage -Repository LocalNexus -WhatIf +PS> Deploy-NovaPackage -PackageType @('NuGet', 'Zip') -Repository LocalNexus ``` -Previews which package artifacts would be uploaded and which destination URLs would be used. +Uploads both matching `.nupkg` and `.zip` artifacts, shows progress as each artifact is sent, and prints a short completion summary with a suggested verification step. ### EXAMPLE 7 @@ -102,130 +109,138 @@ Uses PowerShell's native confirmation prompt for the full resolved upload set be ## PARAMETERS -### -PackagePath +### -AuthenticationScheme -Optional explicit package file path list. When omitted, the command resolves matching artifacts from the configured package output directory. +Optional authentication scheme prefix used when formatting the resolved authentication header value. ```yaml -Type: System.String[] +Type: System.String DefaultValue: '' SupportsWildcards: false -Aliases: [ ] +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: Named + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` -### -PackageType +### -Confirm -Optional package type filter. Supported values follow the same normalization as `Package.Types`, including `NuGet`, -`Zip`, `.nupkg`, and `.zip`. +Prompts for confirmation before uploading package artifacts. ```yaml -Type: System.String[] -DefaultValue: '' +Type: System.Management.Automation.SwitchParameter +DefaultValue: False SupportsWildcards: false -Aliases: [ ] +Aliases: +- cf ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: Named + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` -### -Url +### -Headers -Explicit raw upload base URL. This takes precedence over repository or package configuration. +Optional additional HTTP headers. These are merged with `Package.Headers` and repository-specific headers. +Optional additional HTTP headers. +These are merged with `Package.Headers` and repository-specific headers. ```yaml -Type: System.String +Type: System.Collections.Hashtable DefaultValue: '' SupportsWildcards: false -Aliases: [ ] +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: Named + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` -### -Repository +### -PackagePath -Repository name to resolve from `Package.Repositories`. +Optional explicit package file path list. When omitted, the command resolves matching artifacts from the configured package output directory. +Optional explicit package file path list. +When omitted, the command resolves matching artifacts from the configured package output directory. ```yaml -Type: System.String +Type: System.String[] DefaultValue: '' SupportsWildcards: false -Aliases: [ ] +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: 0 + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` -### -UploadPath +### -PackageType -Optional extra path segment appended between the resolved base URL and the uploaded package file name. +Optional package type filter. Supported values follow the same normalization as `Package.Types`, including `NuGet`, +`Zip`, `.nupkg`, and `.zip`. +Optional package type filter. +Supported values follow the same normalization as `Package.Types`, including `NuGet`, +`Zip`, `.nupkg`, and `.zip`. ```yaml -Type: System.String +Type: System.String[] DefaultValue: '' SupportsWildcards: false -Aliases: [ ] +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: 1 + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` -### -Headers +### -Repository -Optional additional HTTP headers. These are merged with `Package.Headers` and repository-specific headers. +Repository name to resolve from `Package.Repositories`. ```yaml -Type: System.Collections.Hashtable +Type: System.String DefaultValue: '' SupportsWildcards: false -Aliases: [ ] +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: 3 + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` @@ -237,16 +252,16 @@ Optional explicit token value used to populate the resolved authentication heade Type: System.String DefaultValue: '' SupportsWildcards: false -Aliases: [ ] +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: Named + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` @@ -258,89 +273,93 @@ Optional environment variable name that holds the upload token. Type: System.String DefaultValue: '' SupportsWildcards: false -Aliases: [ ] +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: Named + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` -### -AuthenticationScheme +### -UploadPath -Optional authentication scheme prefix used when formatting the resolved authentication header value. +Optional extra path segment appended between the resolved base URL and the uploaded package file name. ```yaml Type: System.String DefaultValue: '' SupportsWildcards: false -Aliases: [ ] +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: Named + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` -### -WhatIf +### -Url -Shows what would happen if the cmdlet runs. The cmdlet is not run. +Explicit raw upload base URL. This takes precedence over repository or package configuration. +Explicit raw upload base URL. +This takes precedence over repository or package configuration. ```yaml -Type: System.Management.Automation.SwitchParameter -DefaultValue: False +Type: System.String +DefaultValue: '' SupportsWildcards: false -Aliases: - - wi +Aliases: [] ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: 2 + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` -### -Confirm +### -WhatIf -Prompts for confirmation before uploading package artifacts. +Shows what would happen if the cmdlet runs. The cmdlet is not run. +Shows what would happen if the cmdlet runs. +The cmdlet is not run. ```yaml Type: System.Management.Automation.SwitchParameter DefaultValue: False SupportsWildcards: false Aliases: - - cf +- wi ParameterSets: - - Name: (All) - Position: Named - IsRequired: false - ValueFromPipeline: false - ValueFromPipelineByPropertyName: false - ValueFromRemainingArguments: false +- Name: (All) + Position: Named + IsRequired: false + ValueFromPipeline: false + ValueFromPipelineByPropertyName: false + ValueFromRemainingArguments: false DontShow: false -AcceptedValues: [ ] +AcceptedValues: [] HelpMessage: '' ``` ### CommonParameters -This cmdlet supports the common parameters: `-Debug`, `-ErrorAction`, `-ErrorVariable`, `-InformationAction`, -`-InformationVariable`, `-OutBuffer`, `-OutVariable`, `-PipelineVariable`, `-ProgressAction`, `-Verbose`, -`-WarningAction`, `-WarningVariable`, `-WhatIf`, and `-Confirm`. +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, +-InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, +-ProgressAction, -Verbose, -WarningAction, and -WarningVariable. For more information, see +[about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS @@ -363,5 +382,5 @@ If no upload target can be resolved, `Deploy-NovaPackage` fails fast with a clea ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/New-NovaModulePackage.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Publish-NovaModule.md +- [New-NovaModulePackage](./New-NovaModulePackage.md) +- [Publish-NovaModule](./Publish-NovaModule.md) diff --git a/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md b/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md index 67168fc..e32f931 100644 --- a/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md +++ b/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md @@ -189,6 +189,6 @@ This command throws a clear error when `project.json` is missing or empty. ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Test-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Update-NovaModuleVersion.md +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) +- [Test-NovaBuild](./Test-NovaBuild.md) +- [Update-NovaModuleVersion](./Update-NovaModuleVersion.md) diff --git a/docs/NovaModuleTools/en-US/Get-NovaUpdateNotificationPreference.md b/docs/NovaModuleTools/en-US/Get-NovaUpdateNotificationPreference.md index 047c8da..f1c5941 100644 --- a/docs/NovaModuleTools/en-US/Get-NovaUpdateNotificationPreference.md +++ b/docs/NovaModuleTools/en-US/Get-NovaUpdateNotificationPreference.md @@ -81,6 +81,6 @@ When prerelease notifications are enabled again, `Update-NovaModuleTool` / `Upda ## RELATED LINKS -- `Invoke-NovaBuild` -- `Set-NovaUpdateNotificationPreference` -- `Update-NovaModuleTool` +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) +- [Set-NovaUpdateNotificationPreference](./Set-NovaUpdateNotificationPreference.md) +- [Update-NovaModuleTool](./Update-NovaModuleTools.md) diff --git a/docs/NovaModuleTools/en-US/Initialize-NovaModule.md b/docs/NovaModuleTools/en-US/Initialize-NovaModule.md index 88532b2..2036cd4 100644 --- a/docs/NovaModuleTools/en-US/Initialize-NovaModule.md +++ b/docs/NovaModuleTools/en-US/Initialize-NovaModule.md @@ -152,5 +152,5 @@ Generated projects start with NovaModuleTools defaults for recursive discovery, ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) +- [Get-NovaProjectInfo](./Get-NovaProjectInfo.md) diff --git a/docs/NovaModuleTools/en-US/Install-NovaCli.md b/docs/NovaModuleTools/en-US/Install-NovaCli.md index 897354e..03dc02d 100644 --- a/docs/NovaModuleTools/en-US/Install-NovaCli.md +++ b/docs/NovaModuleTools/en-US/Install-NovaCli.md @@ -172,4 +172,4 @@ After running `Install-NovaCli`, add the destination directory to your shell `PA ## RELATED LINKS -- `Publish-NovaModule` +- [Publish-NovaModule](./Publish-NovaModule.md) diff --git a/docs/NovaModuleTools/en-US/Invoke-NovaAgenticCopilotScaffold.md b/docs/NovaModuleTools/en-US/Invoke-NovaAgenticCopilotScaffold.md index 649c369..3d2bbc9 100644 --- a/docs/NovaModuleTools/en-US/Invoke-NovaAgenticCopilotScaffold.md +++ b/docs/NovaModuleTools/en-US/Invoke-NovaAgenticCopilotScaffold.md @@ -203,5 +203,5 @@ This workflow does not persist `ShortName` to `project.json` and does not infer ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Initialize-NovaModule.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md +- [Initialize-NovaModule](./Initialize-NovaModule.md) +- [Get-NovaProjectInfo](./Get-NovaProjectInfo.md) diff --git a/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md b/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md index f6c8f37..073ad7a 100644 --- a/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md +++ b/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md @@ -174,7 +174,7 @@ Files under `src/public` are expected to contain exactly one top-level function ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Test-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Publish-NovaModule.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Update-NovaModuleTools.md +- [Get-NovaProjectInfo](./Get-NovaProjectInfo.md) +- [Test-NovaBuild](./Test-NovaBuild.md) +- [Publish-NovaModule](./Publish-NovaModule.md) +- [Update-NovaModuleTool](./Update-NovaModuleTools.md) diff --git a/docs/NovaModuleTools/en-US/Invoke-NovaRelease.md b/docs/NovaModuleTools/en-US/Invoke-NovaRelease.md index 9b0683f..c8e3efe 100644 --- a/docs/NovaModuleTools/en-US/Invoke-NovaRelease.md +++ b/docs/NovaModuleTools/en-US/Invoke-NovaRelease.md @@ -325,7 +325,7 @@ and `-Confirm` support. ## RELATED LINKS -- `Invoke-NovaBuild` -- `Test-NovaBuild` -- `Update-NovaModuleVersion` -- `Publish-NovaModule` +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) +- [Test-NovaBuild](./Test-NovaBuild.md) +- [Update-NovaModuleVersion](./Update-NovaModuleVersion.md) +- [Publish-NovaModule](./Publish-NovaModule.md) diff --git a/docs/NovaModuleTools/en-US/New-NovaModulePackage.md b/docs/NovaModuleTools/en-US/New-NovaModulePackage.md index 751e72e..ae12493 100644 --- a/docs/NovaModuleTools/en-US/New-NovaModulePackage.md +++ b/docs/NovaModuleTools/en-US/New-NovaModulePackage.md @@ -277,5 +277,5 @@ Files under `src/public` are expected to contain exactly one top-level function ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Test-NovaBuild.md +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) +- [Test-NovaBuild](./Test-NovaBuild.md) diff --git a/docs/NovaModuleTools/en-US/Publish-NovaModule.md b/docs/NovaModuleTools/en-US/Publish-NovaModule.md index 886cca7..a1ae783 100644 --- a/docs/NovaModuleTools/en-US/Publish-NovaModule.md +++ b/docs/NovaModuleTools/en-US/Publish-NovaModule.md @@ -302,6 +302,6 @@ When `-ContinuousIntegration` is used, Nova restores the built `dist/` module af ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Test-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Invoke-NovaRelease.md +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) +- [Test-NovaBuild](./Test-NovaBuild.md) +- [Invoke-NovaRelease](./Invoke-NovaRelease.md) diff --git a/docs/NovaModuleTools/en-US/Set-NovaUpdateNotificationPreference.md b/docs/NovaModuleTools/en-US/Set-NovaUpdateNotificationPreference.md index 07a1d0f..6c09762 100644 --- a/docs/NovaModuleTools/en-US/Set-NovaUpdateNotificationPreference.md +++ b/docs/NovaModuleTools/en-US/Set-NovaUpdateNotificationPreference.md @@ -197,5 +197,5 @@ Use this command together with `Get-NovaUpdateNotificationPreference` when you w ## RELATED LINKS -- `Get-NovaUpdateNotificationPreference` -- `Update-NovaModuleTool` +- [Get-NovaUpdateNotificationPreference](./Get-NovaUpdateNotificationPreference.md) +- [Update-NovaModuleTool](./Update-NovaModuleTools.md) diff --git a/docs/NovaModuleTools/en-US/Test-NovaBuild.md b/docs/NovaModuleTools/en-US/Test-NovaBuild.md index 0c3ddd2..26a5b9d 100644 --- a/docs/NovaModuleTools/en-US/Test-NovaBuild.md +++ b/docs/NovaModuleTools/en-US/Test-NovaBuild.md @@ -260,6 +260,6 @@ If `project.json` configures `Pester.CodeCoverage.CoveragePercentTarget`, `Test- ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Publish-NovaModule.md +- [Get-NovaProjectInfo](./Get-NovaProjectInfo.md) +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) +- [Publish-NovaModule](./Publish-NovaModule.md) diff --git a/docs/NovaModuleTools/en-US/Update-NovaModuleTools.md b/docs/NovaModuleTools/en-US/Update-NovaModuleTools.md index de3b66d..2bc054a 100644 --- a/docs/NovaModuleTools/en-US/Update-NovaModuleTools.md +++ b/docs/NovaModuleTools/en-US/Update-NovaModuleTools.md @@ -149,6 +149,6 @@ Use `Get-NovaUpdateNotificationPreference` and `Set-NovaUpdateNotificationPrefer ## RELATED LINKS -- `Get-NovaUpdateNotificationPreference` -- `Set-NovaUpdateNotificationPreference` -- `Invoke-NovaBuild` +- [Get-NovaUpdateNotificationPreference](./Get-NovaUpdateNotificationPreference.md) +- [Set-NovaUpdateNotificationPreference](./Set-NovaUpdateNotificationPreference.md) +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) diff --git a/docs/NovaModuleTools/en-US/Update-NovaModuleVersion.md b/docs/NovaModuleTools/en-US/Update-NovaModuleVersion.md index 7c87e57..37ad859 100644 --- a/docs/NovaModuleTools/en-US/Update-NovaModuleVersion.md +++ b/docs/NovaModuleTools/en-US/Update-NovaModuleVersion.md @@ -303,6 +303,6 @@ When Git-based inference is unavailable, `Update-NovaModuleVersion` now requires ## RELATED LINKS -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Get-NovaProjectInfo.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Invoke-NovaBuild.md -- https://github.com/stiwicourage/NovaModuleTools/blob/main/docs/NovaModuleTools/en-US/Invoke-NovaRelease.md +- [Get-NovaProjectInfo](./Get-NovaProjectInfo.md) +- [Invoke-NovaBuild](./Invoke-NovaBuild.md) +- [Invoke-NovaRelease](./Invoke-NovaRelease.md) diff --git a/src/private/package/InvokeNovaPackageArtifactUpload.ps1 b/src/private/package/InvokeNovaPackageArtifactUpload.ps1 index 09a0f20..c11a3c1 100644 --- a/src/private/package/InvokeNovaPackageArtifactUpload.ps1 +++ b/src/private/package/InvokeNovaPackageArtifactUpload.ps1 @@ -6,13 +6,13 @@ function Invoke-NovaPackageArtifactUpload { ) if (-not (Test-Path -LiteralPath $UploadArtifact.PackagePath -PathType Leaf)) { - Stop-NovaOperation -Message "Package file not found: $( $UploadArtifact.PackagePath )" -ErrorId 'Nova.Environment.PackageUploadFileNotFound' -Category ObjectNotFound -TargetObject $UploadArtifact.PackagePath + Stop-NovaOperation -Message "Package file not found: $( $UploadArtifact.PackagePath ). Run New-NovaModulePackage first or provide a valid -PackagePath." -ErrorId 'Nova.Environment.PackageUploadFileNotFound' -Category ObjectNotFound -TargetObject $UploadArtifact.PackagePath } try { $response = Invoke-NovaPackageUploadRequest -UploadArtifact $UploadArtifact } catch { - Stop-NovaOperation -Message "Package upload failed for $( $UploadArtifact.PackagePath ) -> $( $UploadArtifact.UploadUrl ). $( $_.Exception.Message )" -ErrorId 'Nova.Dependency.PackageUploadRequestFailed' -Category ConnectionError -TargetObject $UploadArtifact.UploadUrl + Stop-NovaOperation -Message "Package upload failed for $( $UploadArtifact.PackagePath ) -> $( $UploadArtifact.UploadUrl ). Check the upload URL, authentication token, and network access, then try again. Details: $( $_.Exception.Message )" -ErrorId 'Nova.Dependency.PackageUploadRequestFailed' -Category ConnectionError -TargetObject $UploadArtifact.UploadUrl } return [pscustomobject]@{ diff --git a/src/private/package/InvokeNovaPackageUploadWorkflow.ps1 b/src/private/package/InvokeNovaPackageUploadWorkflow.ps1 index 079d8a4..4e3ef26 100644 --- a/src/private/package/InvokeNovaPackageUploadWorkflow.ps1 +++ b/src/private/package/InvokeNovaPackageUploadWorkflow.ps1 @@ -5,14 +5,60 @@ function Invoke-NovaPackageUploadWorkflow { [object[]]$UploadArtifactList = @() ) - $resolvedUploadArtifactList = @($UploadArtifactList) - if ($resolvedUploadArtifactList.Count -eq 0 -and @($WorkflowContext.UploadArtifactList).Count -eq 0) { + $resolvedUploadArtifactList = @(Get-NovaResolvedPackageUploadArtifactList -WorkflowContext $WorkflowContext -UploadArtifactList $UploadArtifactList) + if ($resolvedUploadArtifactList.Count -eq 0) { return @() } - return @( - $resolvedUploadArtifactList | ForEach-Object { - Invoke-NovaPackageArtifactUpload -UploadArtifact $_ + $uploadResult = @() + $artifactCount = $resolvedUploadArtifactList.Count + try { + for ($index = 0; $index -lt $artifactCount; $index++) { + $uploadArtifact = $resolvedUploadArtifactList[$index] + Write-NovaPackageUploadProgress -UploadArtifact $uploadArtifact -CurrentIndex ($index + 1) -TotalCount $artifactCount + $uploadResult += Invoke-NovaPackageArtifactUpload -UploadArtifact $uploadArtifact + } + } finally { + Complete-NovaPackageUploadProgress } + + return $uploadResult +} + +function Get-NovaResolvedPackageUploadArtifactList { + [CmdletBinding()] + param( + [Parameter(Mandatory)][pscustomobject]$WorkflowContext, + [object[]]$UploadArtifactList = @() + ) + + if (@($UploadArtifactList).Count -gt 0) { + return @($UploadArtifactList) + } + + return @($WorkflowContext.UploadArtifactList) +} + +function Write-NovaPackageUploadProgress { + [CmdletBinding()] + param( + [Parameter(Mandatory)][object]$UploadArtifact, + [Parameter(Mandatory)][int]$CurrentIndex, + [Parameter(Mandatory)][int]$TotalCount ) + + $percentComplete = [int](($CurrentIndex - 1) * 100 / $TotalCount) + $status = "Uploading $( $UploadArtifact.PackageFileName ) ($CurrentIndex of $TotalCount)" + if ([string]::IsNullOrWhiteSpace([string]$UploadArtifact.PackageFileName)) { + $status = "Uploading artifact $CurrentIndex of $TotalCount" + } + + Write-Progress -Activity 'Uploading package artifacts' -Status $status -PercentComplete $percentComplete +} + +function Complete-NovaPackageUploadProgress { + [CmdletBinding()] + param() + + Write-Progress -Activity 'Uploading package artifacts' -Completed } diff --git a/src/private/package/WriteNovaPackageUploadResultOutput.ps1 b/src/private/package/WriteNovaPackageUploadResultOutput.ps1 new file mode 100644 index 0000000..1a422b9 --- /dev/null +++ b/src/private/package/WriteNovaPackageUploadResultOutput.ps1 @@ -0,0 +1,50 @@ +function Write-NovaPackageUploadResultOutput { + [CmdletBinding()] + param( + [Parameter(Mandatory)][object[]]$Result + ) + + $resolvedResult = @($Result) + if ($resolvedResult.Count -eq 0) { + return + } + + Write-Host (Get-NovaPackageUploadResultSummaryMessage -Result $resolvedResult) + + $nextStepMessage = Get-NovaPackageUploadResultNextStepMessage -Result $resolvedResult + if ($null -ne $nextStepMessage) { + Write-Host $nextStepMessage + } +} + +function Get-NovaPackageUploadResultSummaryMessage { + [CmdletBinding()] + param( + [Parameter(Mandatory)][object[]]$Result + ) + + $artifactCount = @($Result).Count + if ($artifactCount -eq 1) { + return 'Uploaded 1 package artifact.' + } + + return "Uploaded $artifactCount package artifacts." +} + +function Get-NovaPackageUploadResultNextStepMessage { + [CmdletBinding()] + param( + [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Result + ) + + $resolvedResult = @($Result) + if ($resolvedResult.Count -eq 0) { + return $null + } + + if ($resolvedResult.Count -eq 1 -and -not [string]::IsNullOrWhiteSpace($resolvedResult[0].UploadUrl)) { + return "Next step: verify the uploaded artifact at $( $resolvedResult[0].UploadUrl )." + } + + return 'Next step: verify the uploaded artifacts at the target repository.' +} diff --git a/src/private/package/WriteNovaPackageUploadWorkflowContext.ps1 b/src/private/package/WriteNovaPackageUploadWorkflowContext.ps1 new file mode 100644 index 0000000..e441f8b --- /dev/null +++ b/src/private/package/WriteNovaPackageUploadWorkflowContext.ps1 @@ -0,0 +1,33 @@ +function Write-NovaPackageUploadWorkflowContext { + [CmdletBinding()] + param( + [Parameter(Mandatory)][pscustomobject]$WorkflowContext + ) + + $message = Get-NovaPackageUploadWorkflowContextMessage -WorkflowContext $WorkflowContext + if ($null -ne $message) { + Write-Host $message + } + + if (-not [string]::IsNullOrWhiteSpace($WorkflowContext.Target)) { + Write-Verbose "Target: $( $WorkflowContext.Target )" + } +} + +function Get-NovaPackageUploadWorkflowContextMessage { + [CmdletBinding()] + param( + [Parameter(Mandatory)][pscustomobject]$WorkflowContext + ) + + $artifactCount = @($WorkflowContext.UploadArtifactList).Count + if ($artifactCount -eq 0) { + return $null + } + + if ($artifactCount -eq 1) { + return "$( $WorkflowContext.Operation )." + } + + return "Ready to upload $artifactCount package artifacts." +} diff --git a/src/public/DeployNovaPackage.ps1 b/src/public/DeployNovaPackage.ps1 index f81e677..10af1ec 100644 --- a/src/public/DeployNovaPackage.ps1 +++ b/src/public/DeployNovaPackage.ps1 @@ -16,12 +16,17 @@ function Deploy-NovaPackage { $uploadOption = New-NovaPackageUploadOption -BoundParameters $PSBoundParameters $workflowContext = Get-NovaPackageUploadWorkflowContext -BoundParameters $PSBoundParameters -ProjectInfo $projectInfo -UploadOption $uploadOption + Write-NovaPackageUploadWorkflowContext -WorkflowContext $workflowContext + $shouldRun = $PSCmdlet.ShouldProcess($workflowContext.Target, $workflowContext.Operation) if (-not $shouldRun) { return @() } - return @(Invoke-NovaPackageUploadWorkflow -WorkflowContext $workflowContext -UploadArtifactList $workflowContext.UploadArtifactList) + $result = @(Invoke-NovaPackageUploadWorkflow -WorkflowContext $workflowContext -UploadArtifactList $workflowContext.UploadArtifactList) + Write-NovaPackageUploadResultOutput -Result $result + + return $result } } diff --git a/src/resources/agentic-copilot/.github/instructions/platyps-help.instructions.md b/src/resources/agentic-copilot/.github/instructions/platyps-help.instructions.md index 1b97779..15fab57 100644 --- a/src/resources/agentic-copilot/.github/instructions/platyps-help.instructions.md +++ b/src/resources/agentic-copilot/.github/instructions/platyps-help.instructions.md @@ -66,6 +66,9 @@ Import-MarkdownCommandHelp -Path ./docs/{{ProjectName}}/en-US/.md | - The `external help file` field must always use the module name, not the command name: `{{ProjectName}}-Help.xml`. The `Module Name` field must match the project name. When both fields use the module name, Nova build produces a single `-Help.xml` under `dist//en-US/`. If either field contains a command name instead, the build produces per-command XML files and the module manifest cannot find its help. - Keep the H1 title equal to the exact command name. - Preserve the standard PlatyPS section order with uppercase H2 headers: `SYNOPSIS`, `SYNTAX`, optional `ALIASES`, `DESCRIPTION`, `EXAMPLES`, `PARAMETERS`, `INPUTS`, `OUTPUTS`, `NOTES`, and `RELATED LINKS`. +- In `## RELATED LINKS`, use only a bulleted list of Markdown links. Do not use bare URLs or backticked command names as list items. +- Prefer relative links to sibling command-help files when the related topic has a matching file in `docs/{{ProjectName}}/en-US/`, for example `[Invoke-NovaBuild](./Invoke-NovaBuild.md)`. +- Do not use GitHub blob URLs in shipped command help. If a relative command-help link is not suitable after PlatyPS validation, use the related topic's `novamoduletools.com` `HelpUri` instead. - Keep at least one example under `## EXAMPLES`. - Keep parameter sections as `### -ParameterName` blocks with the PlatyPS-generated YAML metadata code block. - Only hand-edit parameter metadata when PlatyPS cannot infer it correctly, especially `DefaultValue` and `SupportsWildcards`. @@ -82,6 +85,7 @@ Import-MarkdownCommandHelp -Path ./docs/{{ProjectName}}/en-US/.md | - Use `Update-MarkdownCommandHelp` after command or parameter changes so syntax, aliases, and parameter metadata stay synchronized with the implementation. - Use `Test-MarkdownCommandHelp` as the quick structural gate before handoff; use `Import-MarkdownCommandHelp` diagnostics when you need more detail. - Use existing valid files under `docs/{{ProjectName}}/en-US/` as the structural template before inventing a new layout. +- Validate any new `RELATED LINKS` target style through the real PlatyPS import/export path before applying it repo-wide. - Keep command help separate from contributor docs and project/site docs. ## Review expectations @@ -90,4 +94,5 @@ Import-MarkdownCommandHelp -Path ./docs/{{ProjectName}}/en-US/.md | - Reviewers should flag help files where `external help file` contains a command name (e.g., `Get-Something-Help.xml`) instead of the module name (`{{ProjectName}}-Help.xml`). This produces per-command XML files that the module manifest cannot locate at runtime. - Reviewers should flag help files that would fail `Test-MarkdownCommandHelp` or produce diagnostics/errors when imported with `Import-MarkdownCommandHelp`. - Reviewers should flag any new public entry point that does not add its matching command-help file in the same change. +- Reviewers should flag `RELATED LINKS` entries that use GitHub blob URLs, plain URLs, or bare/backticked command names instead of Markdown links. - Treat build errors from `Import-MarkdownCommandHelp` as a sign that the file is not valid PlatyPS help yet. diff --git a/tests/ArchitectureGuardrails.Tests.ps1 b/tests/ArchitectureGuardrails.Tests.ps1 index a8b3eec..dc07e3d 100644 --- a/tests/ArchitectureGuardrails.Tests.ps1 +++ b/tests/ArchitectureGuardrails.Tests.ps1 @@ -82,7 +82,7 @@ Describe 'Architecture guardrails' { It 'public command files use only their approved Nova helper surface' { $testCases = @( - [pscustomobject]@{Path = 'src/public/DeployNovaPackage.ps1'; ExpectedHelpers = @('Get-NovaPackageUploadWorkflowContext', 'Get-NovaProjectInfo', 'Invoke-NovaPackageUploadWorkflow', 'New-NovaPackageUploadDynamicParameterDictionary', 'New-NovaPackageUploadOption')} + [pscustomobject]@{Path = 'src/public/DeployNovaPackage.ps1'; ExpectedHelpers = @('Get-NovaPackageUploadWorkflowContext', 'Get-NovaProjectInfo', 'Invoke-NovaPackageUploadWorkflow', 'New-NovaPackageUploadDynamicParameterDictionary', 'New-NovaPackageUploadOption', 'Write-NovaPackageUploadResultOutput', 'Write-NovaPackageUploadWorkflowContext')} [pscustomobject]@{Path = 'src/public/GetNovaProjectInfo.ps1'; ExpectedHelpers = @('Format-NovaCliVersionString', 'Get-NovaCliInstalledVersion', 'Get-NovaProjectInfoContext', 'Get-NovaProjectInfoResult')} [pscustomobject]@{Path = 'src/public/GetNovaUpdateNotificationPreference.ps1'; ExpectedHelpers = @('Get-NovaUpdateNotificationPreferenceStatus')} [pscustomobject]@{Path = 'src/public/InitializeNovaModule.ps1'; ExpectedHelpers = @('Get-NovaModuleInitializationWorkflowContext', 'Invoke-NovaModuleInitializationWorkflow')} diff --git a/tests/CommandHelpRelatedLinks.Tests.ps1 b/tests/CommandHelpRelatedLinks.Tests.ps1 new file mode 100644 index 0000000..405a8da --- /dev/null +++ b/tests/CommandHelpRelatedLinks.Tests.ps1 @@ -0,0 +1,58 @@ +BeforeAll { + $projectRoot = Split-Path -Parent $PSScriptRoot + $script:docsDirectory = Join-Path $projectRoot 'docs/NovaModuleTools/en-US' + $script:helpFiles = Get-ChildItem -LiteralPath $script:docsDirectory -Filter '*.md' -File | + Where-Object Name -ne 'NovaModuleTools.md' + + $script:relatedLinks = foreach ($file in $script:helpFiles) { + $content = Get-Content -LiteralPath $file.FullName -Raw + $sectionMatch = [regex]::Match($content, '(?ms)^## RELATED LINKS\r?\n\r?\n(?.*)$') + if (-not $sectionMatch.Success) { + continue + } + + $linkLineList = @( + $sectionMatch.Groups['body'].Value -split '\r?\n' | + Where-Object { $_ -match '^- ' } + ) + + foreach ($line in $linkLineList) { + $lineMatch = [regex]::Match($line, '^- \[(?[^\]]+)\]\((?[^)]+)\)$') + [pscustomobject]@{ + FileName = $file.Name + Line = $line + Text = $lineMatch.Groups['text'].Value + Uri = $lineMatch.Groups['uri'].Value + IsMarkdownLink = $lineMatch.Success + } + } + } +} + +Describe 'command help related links' { + It 'uses Markdown link syntax for every related-links entry' { + foreach ($entry in $script:relatedLinks) { + $entry.Line | Should -Match '^- \[[^\]]+\]\([^)]+\)$' -Because $entry.FileName + } + } + + It 'does not use GitHub blob links in related-links entries' { + foreach ($entry in $script:relatedLinks) { + $entry.Line | Should -Not -Match 'github\.com/.*/blob/' -Because $entry.FileName + } + } + + It 'uses resolvable internal help links or Nova website links in related-links entries' { + foreach ($entry in $script:relatedLinks) { + $entry.IsMarkdownLink | Should -BeTrue -Because $entry.FileName + + if ($entry.Uri -match '^\./(?[^/]+\.md)$') { + $targetPath = Join-Path $script:docsDirectory $Matches['target'] + (Test-Path -LiteralPath $targetPath -PathType Leaf) | Should -BeTrue -Because "$($entry.FileName): $($entry.Uri)" + continue + } + + $entry.Uri | Should -Match '^https://www\.novamoduletools\.com/' -Because "$($entry.FileName): $($entry.Uri)" + } + } +} diff --git a/tests/private/package/InvokeNovaPackageArtifactUpload.Tests.ps1 b/tests/private/package/InvokeNovaPackageArtifactUpload.Tests.ps1 index 9a72b05..7f4974c 100644 --- a/tests/private/package/InvokeNovaPackageArtifactUpload.Tests.ps1 +++ b/tests/private/package/InvokeNovaPackageArtifactUpload.Tests.ps1 @@ -17,7 +17,7 @@ Describe 'Invoke-NovaPackageArtifactUpload' { It 'throws when the package file does not exist' { Remove-Item -LiteralPath $script:file -Force - {Invoke-NovaPackageArtifactUpload -UploadArtifact $script:artifact} | Should -Throw '*Package file not found*' + {Invoke-NovaPackageArtifactUpload -UploadArtifact $script:artifact} | Should -Throw '*Run New-NovaModulePackage first or provide a valid -PackagePath*' } It 'returns a result object including the status code on success' { @@ -28,6 +28,6 @@ Describe 'Invoke-NovaPackageArtifactUpload' { It 'wraps upload errors into Stop-NovaOperation' { Mock Invoke-NovaPackageUploadRequest {throw 'boom'} - {Invoke-NovaPackageArtifactUpload -UploadArtifact $script:artifact} | Should -Throw '*Package upload failed*' + {Invoke-NovaPackageArtifactUpload -UploadArtifact $script:artifact} | Should -Throw '*Check the upload URL, authentication token, and network access, then try again*' } } diff --git a/tests/private/package/InvokeNovaPackageUploadWorkflow.Tests.ps1 b/tests/private/package/InvokeNovaPackageUploadWorkflow.Tests.ps1 index 916efa5..e15e66a 100644 --- a/tests/private/package/InvokeNovaPackageUploadWorkflow.Tests.ps1 +++ b/tests/private/package/InvokeNovaPackageUploadWorkflow.Tests.ps1 @@ -5,17 +5,45 @@ BeforeAll { } Describe 'Invoke-NovaPackageUploadWorkflow' { + BeforeEach { + Mock Write-Progress {} + } + It 'returns empty when neither supplied list nor context list has artifacts' { $ctx = [pscustomobject]@{UploadArtifactList=@()} $r = Invoke-NovaPackageUploadWorkflow -WorkflowContext $ctx @($r).Count | Should -Be 0 + Should -Invoke Write-Progress -Times 0 } - It 'uploads each supplied artifact and returns the responses' { + It 'uploads each supplied artifact, reports progress, and returns the responses' { Mock Invoke-NovaPackageArtifactUpload {[pscustomobject]@{StatusCode=200; Artifact=$UploadArtifact}} - $ctx = [pscustomobject]@{UploadArtifactList=@([pscustomobject]@{Name='a'})} - $r = @(Invoke-NovaPackageUploadWorkflow -WorkflowContext $ctx -UploadArtifactList @([pscustomobject]@{Name='a'},[pscustomobject]@{Name='b'})) + $ctx = [pscustomobject]@{UploadArtifactList=@([pscustomobject]@{PackageFileName='a.nupkg'})} + $r = @(Invoke-NovaPackageUploadWorkflow -WorkflowContext $ctx -UploadArtifactList @([pscustomobject]@{PackageFileName='a.nupkg'},[pscustomobject]@{PackageFileName='b.nupkg'})) $r.Count | Should -Be 2 Should -Invoke Invoke-NovaPackageArtifactUpload -Times 2 + Should -Invoke Write-Progress -Times 2 -ParameterFilter {-not $Completed} + Should -Invoke Write-Progress -Times 1 -ParameterFilter {$Completed} + } + + It 'uses a generic progress status when the artifact file name is blank' { + Mock Invoke-NovaPackageArtifactUpload {[pscustomobject]@{StatusCode=200; Artifact=$UploadArtifact}} + $ctx = [pscustomobject]@{UploadArtifactList=@()} + + $null = Invoke-NovaPackageUploadWorkflow -WorkflowContext $ctx -UploadArtifactList @([pscustomobject]@{PackageFileName=''}) + + Should -Invoke Write-Progress -Times 1 -ParameterFilter { + (-not $Completed) -and + $Status -eq 'Uploading artifact 1 of 1' + } + Should -Invoke Write-Progress -Times 1 -ParameterFilter {$Completed} + } + + It 'falls back to the workflow context artifact list when no explicit upload list is supplied' { + Mock Invoke-NovaPackageArtifactUpload {[pscustomobject]@{StatusCode=200; Artifact=$UploadArtifact}} + $ctx = [pscustomobject]@{UploadArtifactList=@([pscustomobject]@{PackageFileName='a.nupkg'})} + $r = @(Invoke-NovaPackageUploadWorkflow -WorkflowContext $ctx) + $r.Count | Should -Be 1 + Should -Invoke Invoke-NovaPackageArtifactUpload -Times 1 } } diff --git a/tests/private/package/WriteNovaPackageUploadResultOutput.Tests.ps1 b/tests/private/package/WriteNovaPackageUploadResultOutput.Tests.ps1 new file mode 100644 index 0000000..671831c --- /dev/null +++ b/tests/private/package/WriteNovaPackageUploadResultOutput.Tests.ps1 @@ -0,0 +1,37 @@ +BeforeAll { + $projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) + . (Join-Path $projectRoot 'src/private/package/WriteNovaPackageUploadResultOutput.ps1') +} + +Describe 'Write-NovaPackageUploadResultOutput' { + BeforeEach { + Mock Write-Host {} + } + + It 'returns no repository next-step message for an empty result list' { + $message = Get-NovaPackageUploadResultNextStepMessage -Result @() + + $message | Should -BeNullOrEmpty + } + + It 'writes a single-artifact summary and URL-specific next step' { + $result = @([pscustomobject]@{UploadUrl = 'https://packages.example/raw/NovaModuleTools.1.0.0.nupkg'}) + + Write-NovaPackageUploadResultOutput -Result $result + + Should -Invoke Write-Host -Times 1 -ParameterFilter {$Object -eq 'Uploaded 1 package artifact.'} + Should -Invoke Write-Host -Times 1 -ParameterFilter {$Object -eq 'Next step: verify the uploaded artifact at https://packages.example/raw/NovaModuleTools.1.0.0.nupkg.'} + } + + It 'writes a multi-artifact summary and repository-level next step' { + $result = @( + [pscustomobject]@{UploadUrl = 'https://packages.example/raw/a.nupkg'} + [pscustomobject]@{UploadUrl = 'https://packages.example/raw/b.nupkg'} + ) + + Write-NovaPackageUploadResultOutput -Result $result + + Should -Invoke Write-Host -Times 1 -ParameterFilter {$Object -eq 'Uploaded 2 package artifacts.'} + Should -Invoke Write-Host -Times 1 -ParameterFilter {$Object -eq 'Next step: verify the uploaded artifacts at the target repository.'} + } +} diff --git a/tests/private/package/WriteNovaPackageUploadWorkflowContext.Tests.ps1 b/tests/private/package/WriteNovaPackageUploadWorkflowContext.Tests.ps1 new file mode 100644 index 0000000..9e3f51f --- /dev/null +++ b/tests/private/package/WriteNovaPackageUploadWorkflowContext.Tests.ps1 @@ -0,0 +1,50 @@ +BeforeAll { + $projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) + . (Join-Path $projectRoot 'src/private/package/WriteNovaPackageUploadWorkflowContext.ps1') +} + +Describe 'Write-NovaPackageUploadWorkflowContext' { + BeforeEach { + Mock Write-Host {} + Mock Write-Verbose {} + } + + It 'writes nothing when no artifacts are resolved' { + $workflowContext = [pscustomobject]@{ + UploadArtifactList = @() + Target = '' + Operation = 'Upload package artifacts' + } + + Write-NovaPackageUploadWorkflowContext -WorkflowContext $workflowContext + + Should -Invoke Write-Host -Times 0 + Should -Invoke Write-Verbose -Times 0 + } + + It 'writes a ready message and verbose target for multiple artifacts' { + $workflowContext = [pscustomobject]@{ + UploadArtifactList = @([pscustomobject]@{}, [pscustomobject]@{}) + Target = 'https://packages.example/raw/' + Operation = 'Upload 2 package artifacts' + } + + Write-NovaPackageUploadWorkflowContext -WorkflowContext $workflowContext + + Should -Invoke Write-Host -Times 1 -ParameterFilter {$Object -eq 'Ready to upload 2 package artifacts.'} + Should -Invoke Write-Verbose -Times 1 -ParameterFilter {$Message -eq 'Target: https://packages.example/raw/'} + } + + It 'writes the operation message for a single artifact' { + $workflowContext = [pscustomobject]@{ + UploadArtifactList = @([pscustomobject]@{}) + Target = '' + Operation = 'Upload NuGet package artifact NovaModuleTools.1.0.0.nupkg' + } + + Write-NovaPackageUploadWorkflowContext -WorkflowContext $workflowContext + + Should -Invoke Write-Host -Times 1 -ParameterFilter {$Object -eq 'Upload NuGet package artifact NovaModuleTools.1.0.0.nupkg.'} + Should -Invoke Write-Verbose -Times 0 + } +} diff --git a/tests/public/DeployNovaPackage.TestSupport.ps1 b/tests/public/DeployNovaPackage.TestSupport.ps1 index 66a2370..b9da4bd 100644 --- a/tests/public/DeployNovaPackage.TestSupport.ps1 +++ b/tests/public/DeployNovaPackage.TestSupport.ps1 @@ -4,7 +4,9 @@ function New-NovaPackageUploadOption {param($BoundParameters) return [pscustomob function Get-NovaPackageUploadWorkflowContext {param($BoundParameters, $ProjectInfo, $UploadOption) return [pscustomobject]@{Target='https://x/repo'; Operation='Upload'; UploadArtifactList=@([pscustomobject]@{Name='a.nupkg'})} } +function Write-NovaPackageUploadWorkflowContext {param($WorkflowContext) $script:wroteContext = $true} function Invoke-NovaPackageUploadWorkflow {param($WorkflowContext, $UploadArtifactList) $script:invoked = $true return @([pscustomobject]@{StatusCode=201}) } +function Write-NovaPackageUploadResultOutput {param($Result) $script:wroteResult = $true} diff --git a/tests/public/DeployNovaPackage.Tests.ps1 b/tests/public/DeployNovaPackage.Tests.ps1 index 3b03e43..9ab9bcc 100644 --- a/tests/public/DeployNovaPackage.Tests.ps1 +++ b/tests/public/DeployNovaPackage.Tests.ps1 @@ -6,18 +6,26 @@ BeforeAll { } Describe 'Deploy-NovaPackage' { - BeforeEach {$script:invoked = $false} + BeforeEach { + $script:invoked = $false + $script:wroteContext = $false + $script:wroteResult = $false + } - It 'invokes the upload workflow and returns its results' { + It 'writes context, invokes the upload workflow, and returns its results' { $result = Deploy-NovaPackage -PackagePath '/o/a.nupkg' -Url 'https://x' -Repository 'Nexus' + $script:wroteContext | Should -BeTrue $script:invoked | Should -BeTrue + $script:wroteResult | Should -BeTrue @($result).Count | Should -Be 1 $result[0].StatusCode | Should -Be 201 } - It 'returns an empty array and does not invoke the workflow when -WhatIf is set' { + It 'writes context and returns an empty array without invoking the workflow when -WhatIf is set' { $result = Deploy-NovaPackage -PackagePath '/o/a.nupkg' -WhatIf + $script:wroteContext | Should -BeTrue $script:invoked | Should -BeFalse + $script:wroteResult | Should -BeFalse @($result).Count | Should -Be 0 } }