diff --git a/help/Update-ModuleManifest.md b/help/Update-ModuleManifest.md new file mode 100644 index 000000000..100e7550f --- /dev/null +++ b/help/Update-ModuleManifest.md @@ -0,0 +1,698 @@ +--- +external help file: PowerShellGet.dll-Help.xml +Module Name: PowerShellGet +online version: +schema: 2.0.0 +--- + +# Update-ModuleManifest + +## SYNOPSIS +Updates a module manifest file. + +## SYNTAX + +### NameParameterSet (Default) +``` +Update-ModuleManifest [-Path] [-NestedModules ] [-Guid ] [-Author ] + [-CompanyName ] [-Copyright ] [-RootModule ] [-ModuleVersion ] + [-Description ] [-ProcessorArchitecture ] [-CompatiblePSEditions ] + [-PowerShellVersion ] [-ClrVersion ] [-DotNetFrameworkVersion ] + [-PowerShellHostName ] [-PowerShellHostVersion ] [-RequiredModules ] + [-TypesToProcess ] [-FormatsToProcess ] [-ScriptsToProcess ] + [-RequiredAssemblies ] [-FileList ] [-ModuleList ] [-FunctionsToExport ] + [-AliasesToExport ] [-VariablesToExport ] [-CmdletsToExport ] + [-DscResourcesToExport ] [-PrivateData ] [-Tags ] [-ProjectUri ] + [-LicenseUri ] [-IconUri ] [-ReleaseNotes ] [-Prerelease ] [-HelpInfoUri ] + [-DefaultCommandPrefix ] [-ExternalModuleDependencies ] [-RequireLicenseAcceptance] + [] +``` + +## DESCRIPTION +The Update-ModuleManifest cmdlet replaces the Update-ModuleManifest cmdlet from V2. +It updates a module manifest based on the `-Path` parameter argument. +It does not return an object. Other parameters allow specific properties of the manifest to be updated. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Update-ModuleManifest -Path "C:\MyModules\TestModule" -Author "New Author" +``` +In this example the author property in the module manifest will be updated to "New Author". + +```powershell +PS C:\> Update-ModuleManifest -Path "C:\MyModules\TestModule" -Prerelease "beta2" +``` +In this example the prerelease property will be updated to "beta2" + +```powershell +PS C:\> Update-ModuleManifest -Path "C:\MyModules\TestModule" -Tags "Windows", "Linux" -Description "A module for managing packages." +``` +In this example the tags and description will be updated to the passed in values. + +## PARAMETERS + +### -Path +Specifies the path and file name of the module manifest. Enter a path and file name with a .psd1 file name extension, such as `"$PSHOME\Modules\MyModule\MyModule.psd1`. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True +Accept wildcard characters: False +``` + +### -NestedModules +Specifies script modules (.psm1) and binary modules (.dll) that are imported into the module's session state. The files in the NestedModules key run in the order in which they're listed in the value. + +Enter each module name as a string or as a hash table with ModuleName and ModuleVersion keys. The hash table can also have an optional GUID key. You can combine strings and hash tables in the parameter value. + +```yaml +Type: Object[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Guid +Specifies a unique identifier for the module. The GUID can be used to distinguish among modules with the same name. + +```yaml +Type: System.Guid +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Author +Specifies the module author. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CompanyName +Specifies the company or vendor who created the module. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Copyright +Specifies a copyright statement for the module. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RootModule +Specifies the primary or root file of the module. Enter the file name of a script (.ps1), a script module (.psm1), a module manifest (.psd1), an assembly (.dll), a cmdlet definition XML file (.cdxml), or a workflow (.xaml). When the module is imported, the members that are exported from the root module file are imported into the caller's session state. + +If a module has a manifest file and no root file has been specified in the RootModule key, the manifest becomes the primary file for the module. And, the module becomes a manifest module (ModuleType = Manifest). + +To export members from .psm1 or .dll files in a module that has a manifest, the names of those files must be specified in the values of the RootModule or NestedModules keys in the manifest. Otherwise, their members aren't exported. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ModuleVersion +Specifies the version of the module. + +```yaml +Type: System.Version +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description +Specifies a description of the module. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProcessorArchitecture +Specifies the processor architecture that the module requires. + +The acceptable values for this parameter are: + +* Amd64 +* Arm +* IA64 +* MSIL +* None (unknown or unspecified) +* X86 + +```yaml +Type: System.Reflection.ProcessorArchitecture +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CompatiblePSEditions +Specifies the compatible PSEditions of the module. For information about PSEdition, see: https://docs.microsoft.com/en-us/powershell/scripting/gallery/concepts/module-psedition-support + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: +Accepted Values: Desktop, Core + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PowerShellVersion +Specifies the minimum version of PowerShell that will work with this module. For example, you can specify 3.0, 4.0, or 5.0 as the value of this parameter. + +```yaml +Type: System.Version +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ClrVersion +Specifies the minimum version of the Common Language Runtime (CLR) of the Microsoft .NET Framework that the module requires. + +```yaml +Type: System.Version +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DotNetFrameworkVersion +Specifies the minimum version of the Microsoft .NET Framework that the module requires. + +```yaml +Type: System.Version +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PowerShellHostName +Specifies the name of the PowerShell host program that the module requires. Enter the name of the host program, such as PowerShell ISE Host or ConsoleHost. Wildcards aren't permitted. + +To find the name of a host program, in the program, type $Host.Name. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PowerShellHostVersion +Specifies the minimum version of the PowerShell host program that works with the module. Enter a version number, such as 1.1. + +```yaml +Type: System.Version +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RequiredModules +Specifies modules that must be in the global session state. If the required modules aren't in the global session state, PowerShell imports them. If the required modules aren't available, the Import-Module command fails. + +```yaml +Type: System.Object[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TypesToProcess +Specifies the type files (.ps1xml) that run when the module is imported. + +When you import the module, PowerShell runs the Update-TypeData cmdlet with the specified files. Because type files aren't scoped, they affect all session states in the session. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -FormatsToProcess +Specifies the formatting files (.ps1xml) that run when the module is imported. + +When you import a module, PowerShell runs the Update-FormatData cmdlet with the specified files. Because formatting files aren't scoped, they affect all session states in the session. + + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ScriptsToProcess +Specifies script (.ps1) files that run in the caller's session state when the module is imported. You can use these scripts to prepare an environment, just as you might use a login script. + +To specify scripts that run in the module's session state, use the NestedModules key. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RequiredAssemblies +Specifies the assembly (.dll) files that the module requires. Enter the assembly file names. PowerShell loads the specified assemblies before updating types or formats, importing nested modules, or importing the module file that is specified in the value of the RootModule key. + +Use this parameter to specify all the assemblies that the module requires, including assemblies that must be loaded to update any formatting or type files that are listed in the FormatsToProcess or TypesToProcess keys, even if those assemblies are also listed as binary modules in the NestedModules key. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -FileList +Specifies all items that are included in the module. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ModuleList +Specifies an array of modules that are included in the module. + +Enter each module name as a string or as a hash table with ModuleName and ModuleVersion keys. The hash table can also have an optional GUID key. You can combine strings and hash tables in the parameter value. + +This key is designed to act as a module inventory. The modules that are listed in the value of this key aren't automatically processed. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -FunctionsToExport +Specifies the functions that the module exports. Wildcards are permitted. + +Use this parameter to restrict the functions that are exported by the module. FunctionsToExport can remove functions from the list of exported aliases, but it can't add functions to the list. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: True +``` + +### -AliasesToExport +Specifies the aliases that the module exports. Wildcards are permitted. + +Use this parameter to restrict the aliases that are exported by the module. AliasesToExport can remove aliases from the list of exported aliases, but it can't add aliases to the list. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: True +``` + +### -VariablesToExport +Specifies the variables that the module exports. Wildcards are permitted. + +Use this parameter to restrict the variables that are exported by the module. VariablesToExport can remove variables from the list of exported variables, but it can't add variables to the list. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: True +``` + +### -CmdletsToExport +Specifies the cmdlets that the module exports. Wildcards are permitted. + +Use this parameter to restrict the cmdlets that are exported by the module. CmdletsToExport can remove cmdlets from the list of exported cmdlets, but it can't add cmdlets to the list. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: True +``` + +### -DscResourcesToExport +Specifies the Desired State Configuration (DSC) resources that the module exports. Wildcards are permitted. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: True +``` + +### -PrivateData +Specifies data that is passed to the module when it's imported. + +```yaml +Type: Hashtable +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Tags +Specifies an array of tags. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProjectUri +Specifies the URL of a web page about this project. + +```yaml +Type: Uri +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LicenseUri +Specifies the URL of licensing terms for the module. + +```yaml +Type: Uri +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -IconUri +Specifies the URL of an icon for the module. The specified icon is displayed on the gallery web page for the module. + +```yaml +Type: Uri +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ReleaseNotes +Specifies a string array that contains release notes or comments that you want available for this version of the script. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Prerelease +Specifies the prerelease tag that is appended to the module version. For example, if prerelease is "preview" and the module version is "1.0.0" the version of the module would be "1.0.0-preview". + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -HelpInfoUri +Specifies the internet address of the module's HelpInfo XML file. Enter a Uniform Resource Identifier (URI) that begins with http or https. + +The HelpInfo XML file supports the Updatable Help feature that was introduced in PowerShell version 3.0. It contains information about the location of the module's downloadable help files and the version numbers of the newest help files for each supported locale. + +For information about Updatable Help, see: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_updatable_help?view=powershell-7.2. For information about the HelpInfo XML file, see: https://docs.microsoft.com/en-us/powershell/scripting/developer/module/supporting-updatable-help. + +```yaml +Type: Uri +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DefaultCommandPrefix +Specifies the default command prefix. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExternalModuleDependencies +Specifies an array of external module dependencies. + +```yaml +Type: System.String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RequireLicenseAcceptance +Specifies that a license acceptance is required for the module. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### System.String[] + +## OUTPUTS +None + +## NOTES + +## RELATED LINKS + +[]() diff --git a/src/PowerShellGet.psd1 b/src/PowerShellGet.psd1 index 4029ab2bf..2c9b767b2 100644 --- a/src/PowerShellGet.psd1 +++ b/src/PowerShellGet.psd1 @@ -24,6 +24,7 @@ 'Publish-PSResource', 'Uninstall-PSResource', 'Unregister-PSResourceRepository', + 'Update-ModuleManifest', 'Update-PSResource') VariablesToExport = 'PSGetPath' diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index f10648451..752255eae 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -232,9 +232,21 @@ protected override void EndProcessing() } // validate that the module manifest has correct data - if (!IsValidModuleManifest(resourceFilePath)) + string[] errorMsgs = null; + try { - return; + Utils.ValidateModuleManifest(resourceFilePath, out errorMsgs); + + } + finally { + if (errorMsgs.Length > 0) + { + ThrowTerminatingError(new ErrorRecord( + new PSInvalidOperationException(errorMsgs.First()), + "InvalidModuleManifest", + ErrorCategory.InvalidOperation, + this)); + } } } @@ -404,65 +416,6 @@ protected override void EndProcessing() #region Private methods - private bool IsValidModuleManifest(string moduleManifestPath) - { - var isValid = true; - using (System.Management.Automation.PowerShell pwsh = System.Management.Automation.PowerShell.Create()) - { - // use PowerShell cmdlet Test-ModuleManifest - // TODO: Test-ModuleManifest will throw an error if RequiredModules specifies a module that does not exist - // locally on the machine. Consider adding a -Syntax param to Test-ModuleManifest so that it only checks that - // the syntax is correct. In build/release pipelines for example, the modules listed under RequiredModules may - // not be locally available, but we still want to allow the user to publish. - Collection results = null; - try - { - results = pwsh.AddCommand("Test-ModuleManifest").AddParameter("Path", moduleManifestPath).Invoke(); - } - catch (Exception e) - { - ThrowTerminatingError(new ErrorRecord( - new ArgumentException("Error occured while running 'Test-ModuleManifest': " + e.Message), - "ErrorExecutingTestModuleManifest", - ErrorCategory.InvalidArgument, - this)); - } - - if (pwsh.HadErrors) - { - var message = string.Empty; - - if (results.Any()) - { - if (string.IsNullOrWhiteSpace((results[0].BaseObject as PSModuleInfo).Author)) - { - message = "No author was provided in the module manifest. The module manifest must specify a version, author and description. Run 'Test-ModuleManifest' to validate the file."; - } - else if (string.IsNullOrWhiteSpace((results[0].BaseObject as PSModuleInfo).Description)) - { - message = "No description was provided in the module manifest. The module manifest must specify a version, author and description. Run 'Test-ModuleManifest' to validate the file."; - } - else if ((results[0].BaseObject as PSModuleInfo).Version == null) - { - message = "No version or an incorrectly formatted version was provided in the module manifest. The module manifest must specify a version, author and description. Run 'Test-ModuleManifest' to validate the file."; - } - } - - if (string.IsNullOrEmpty(message) && pwsh.Streams.Error.Count > 0) - { - // This will handle version errors - message = pwsh.Streams.Error[0].ToString() + "Run 'Test-ModuleManifest' to validate the module manifest."; - } - var ex = new ArgumentException(message); - var InvalidModuleManifest = new ErrorRecord(ex, "InvalidModuleManifest", ErrorCategory.InvalidData, null); - ThrowTerminatingError(InvalidModuleManifest); - isValid = false; - } - } - - return isValid; - } - private string CreateNuspec( string outputDir, string filePath, diff --git a/src/code/UpdateModuleManifest.cs b/src/code/UpdateModuleManifest.cs new file mode 100644 index 000000000..ee0200282 --- /dev/null +++ b/src/code/UpdateModuleManifest.cs @@ -0,0 +1,638 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.PowerShell.PowerShellGet.UtilClasses; +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Management.Automation; +using System.Reflection; + +namespace Microsoft.PowerShell.PowerShellGet.Cmdlets +{ + /// + /// Updates the module manifest (.psd1) for a resource. + /// + [Cmdlet(VerbsData.Update, "ModuleManifest")] + public sealed class UpdateModuleManifest : PSCmdlet + { + #region Parameters + + /// + /// Specifies the path and file name of the module manifest. + /// + [Parameter (Position = 0, Mandatory = true)] + [ValidateNotNullOrEmpty] + public string Path { get; set; } + + /// + /// Specifies script modules (.psm1) and binary modules (.dll) that are imported into the module's session state. + /// + [Parameter] + public object[] NestedModules { get; set; } + + /// + /// Specifies a unique identifier for the module, can be used to distinguish among modules with the same name. + /// + [Parameter] + public Guid Guid { get; set; } + + /// + /// Specifies the module author. + /// + [Parameter] + public string Author { get; set; } + + /// + /// Specifies the company or vendor who created the module. + /// + [Parameter] + public string CompanyName { get; set; } + + /// + /// Specifies a copyright statement for the module. + /// + [Parameter] + public string Copyright { get; set; } + + /// + /// Specifies the primary or root file of the module. + /// + [Parameter] + public string RootModule { get; set; } + + /// + /// Specifies the version of the module. + /// + [Parameter] + public Version ModuleVersion { get; set; } + + /// + /// Specifies a description of the module. + /// + [Parameter] + public string Description { get; set; } + + /// + /// Specifies the processor architecture that the module requires. + /// + [Parameter] + [ValidateNotNullOrEmpty] + public ProcessorArchitecture ProcessorArchitecture { get; set; } + + /// + /// Specifies the compatible PSEditions of the module. + /// + [Parameter] + public string[] CompatiblePSEditions { get; set; } + + /// + /// Specifies the minimum version of PowerShell that will work with this module. + /// + [Parameter] + public Version PowerShellVersion { get; set; } + + /// + /// Specifies the minimum version of the Common Language Runtime (CLR) of the Microsoft .NET Framework that the module requires. + /// + [Parameter] + public Version ClrVersion { get; set; } + + /// + /// Specifies the minimum version of the Microsoft .NET Framework that the module requires. + /// + [Parameter] + public Version DotNetFrameworkVersion { get; set; } + + /// + /// Specifies the name of the PowerShell host program that the module requires. + /// + [Parameter] + public string PowerShellHostName { get; set; } + + /// + /// Specifies the minimum version of the PowerShell host program that works with the module. + /// + [Parameter] + public Version PowerShellHostVersion { get; set; } + + /// + /// Specifies modules that must be in the global session state. + /// + [Parameter] + public Object[] RequiredModules { get; set; } + + /// + /// Specifies the type files (.ps1xml) that run when the module is imported. + /// + [Parameter] + public string[] TypesToProcess { get; set; } + + /// + /// Specifies the formatting files (.ps1xml) that run when the module is imported. + /// + [Parameter] + public string[] FormatsToProcess { get; set; } + + /// + /// Specifies script (.ps1) files that run in the caller's session state when the module is imported. + /// + [Parameter] + public string[] ScriptsToProcess { get; set; } + + /// + /// Specifies the assembly (.dll) files that the module requires. + /// + [Parameter] + public string[] RequiredAssemblies { get; set; } + + /// + /// Specifies all items that are included in the module. + /// + [Parameter] + public string[] FileList { get; set; } + + /// + /// Specifies an array of modules that are included in the module. + /// + [Parameter] + public Object[] ModuleList { get; set; } + + /// + /// Specifies the functions that the module exports. + /// + [Parameter] + public string[] FunctionsToExport { get; set; } + + /// + /// Specifies the aliases that the module exports. + /// + [Parameter] + public string[] AliasesToExport { get; set; } + + /// + /// Specifies the variables that the module exports. + /// + [Parameter] + public string[] VariablesToExport { get; set; } + + /// + /// Specifies the cmdlets that the module exports. + /// + [Parameter] + public string[] CmdletsToExport { get; set; } + + /// + /// Specifies the Desired State Configuration (DSC) resources that the module exports. + /// + [Parameter] + public string[] DscResourcesToExport { get; set; } + + /// + /// Specifies an array of tags. + /// + [Parameter] + public string[] Tags { get; set; } + + /// + /// Specifies the URL of a web page about this project. + /// + [Parameter] + public Uri ProjectUri { get; set; } + + /// + /// Specifies the URL of licensing terms for the module. + /// + [Parameter] + public Uri LicenseUri { get; set; } + + /// + /// Specifies the URL of an icon for the module. + /// + [Parameter] + public Uri IconUri { get; set; } + + /// + /// Specifies a string that contains release notes or comments that you want available for this version of the script. + + /// + [Parameter] + public string ReleaseNotes { get; set; } + + /// + /// Indicates the prerelease label of the module. + /// + [Parameter] + public string Prerelease { get; set; } + + /// + /// Specifies the internet address of the module's HelpInfo XML file. + /// + [Parameter] + public Uri HelpInfoUri { get; set; } + + /// + /// Returns an object representing the item with which you're working. + /// + [Parameter] + public SwitchParameter PassThru { get; set; } + + /// + /// Specifies the default command prefix. + /// + [Parameter] + public string DefaultCommandPrefix { get; set; } + + /// + /// Specifies an array of external module dependencies. + /// + [Parameter] + public string[] ExternalModuleDependencies { get; set; } + + /// + /// Specifies that a license acceptance is required for the module. + /// + [Parameter] + public SwitchParameter RequireLicenseAcceptance { get; set; } + + /// + /// Specifies data that is passed to the module when it's imported. + /// + [Parameter] + public Hashtable PrivateData { get; set; } + #endregion + + #region Methods + + protected override void EndProcessing() + { + string resolvedManifestPath = SessionState.Path.GetResolvedPSPathFromPSPath(Path).First().Path; + + // Test the path of the module manifest to see if the file exists + if (!File.Exists(resolvedManifestPath) || !resolvedManifestPath.EndsWith(".psd1")) + { + var message = $"The provided file path was not found: '{resolvedManifestPath}'. Please specify a valid module manifest (.psd1) file path."; + + ThrowTerminatingError( + new ErrorRecord( + new ArgumentException(message), + "moduleManifestPathNotFound", + ErrorCategory.ObjectNotFound, + this)); + } + + // Parse the module manifest + if(!Utils.TryReadManifestFile( + manifestFilePath: resolvedManifestPath, + manifestInfo: out Hashtable parsedMetadata, + error: out Exception manifestReadError)) + { + ThrowTerminatingError( + new ErrorRecord( + exception: manifestReadError, + errorId: "ModuleManifestParseFailure", + errorCategory: ErrorCategory.ParserError, + targetObject: this)); + } + + // Prerelease, ReleaseNotes, Tags, ProjectUri, LicenseUri, IconUri, RequireLicenseAcceptance, + // and ExternalModuleDependencies are all properties within a hashtable property called 'PSData' + // which is within another hashtable property called 'PrivateData' + // All of the properties mentioned above have their own parameter in 'New-ModuleManifest', so + // we will parse out these values from the parsedMetadata and create entries for each one in individualy. + // This way any values that were previously specified here will get transfered over to the new manifest. + // Example of the contents of PSData: + // PrivateData = @{ + // PSData = @{ + // # Tags applied to this module. These help with module discovery in online galleries. + // Tags = @('Tag1', 'Tag2') + // + // # A URL to the license for this module. + // LicenseUri = 'https://www.licenseurl.com/' + // + // # A URL to the main website for this project. + // ProjectUri = 'https://www.projecturi.com/' + // + // # A URL to an icon representing this module. + // IconUri = 'https://iconuri.com/' + // + // # ReleaseNotes of this module. + // ReleaseNotes = 'These are the release notes of this module.' + // + // # Prerelease string of this module. + // Prerelease = 'preview' + // + // # Flag to indicate whether the module requires explicit user acceptance for install/update/save. + // RequireLicenseAcceptance = $false + // + // # External dependent modules of this module + // ExternalModuleDependencies = @('ModuleDep1, 'ModuleDep2') + // + // } # End of PSData hashtable + // + // } # End of PrivateData hashtable + var PrivateData = parsedMetadata["PrivateData"] as Hashtable; + var PSData = PrivateData["PSData"] as Hashtable; + + if (PSData.ContainsKey("Prerelease")) + { + parsedMetadata["Prerelease"] = PSData["Prerelease"]; + } + + if (PSData.ContainsKey("ReleaseNotes")) + { + parsedMetadata["ReleaseNotes"] = PSData["ReleaseNotes"]; + } + + if (PSData.ContainsKey("Tags")) + { + parsedMetadata["Tags"] = PSData["Tags"]; + } + + if (PSData.ContainsKey("ProjectUri")) + { + parsedMetadata["ProjectUri"] = PSData["ProjectUri"]; + } + + if (PSData.ContainsKey("LicenseUri")) + { + parsedMetadata["LicenseUri"] = PSData["LicenseUri"]; + } + + if (PSData.ContainsKey("IconUri")) + { + parsedMetadata["IconUri"] = PSData["IconUri"]; + } + + if (PSData.ContainsKey("RequireLicenseAcceptance")) + { + parsedMetadata["RequireLicenseAcceptance"] = PSData["RequireLicenseAcceptance"]; + } + + if (PSData.ContainsKey("ExternalModuleDependencies")) + { + parsedMetadata["ExternalModuleDependencies"] = PSData["ExternalModuleDependencies"]; + } + + // Now we need to remove 'PSData' becaues if we leave this value in the hashtable, + // New-ModuleManifest will keep this value and also attempt to create a new value for 'PSData' + // and then complain that there's two keys within the PrivateData hashtable. + PrivateData.Remove("PSData"); + + // After getting the original module manifest contents, migrate all the fields to the new module manifest, + + // adding in any new values specified via cmdlet parameters. + // Set up params to pass to New-ModuleManifest module + // For now this will be parsedMetadata hashtable and we will just add to it as needed + + if (NestedModules != null) + { + parsedMetadata["NestedModules"] = NestedModules; + } + + if (Guid != Guid.Empty) + { + parsedMetadata["Guid"] = Guid; + } + + if (!string.IsNullOrWhiteSpace(Author)) + { + parsedMetadata["Author"] = Author; + } + + if (CompanyName != null) + { + parsedMetadata["CompanyName"] = CompanyName; + } + + if (Copyright != null) + { + parsedMetadata["Copyright"] = Copyright; + } + + if (RootModule != null) + { + parsedMetadata["RootModule"] = RootModule; + } + + if (ModuleVersion != null) + { + parsedMetadata["ModuleVersion"] = ModuleVersion; + } + + if (Description != null) + { + parsedMetadata["Description"] = Description; + } + + if (ProcessorArchitecture != ProcessorArchitecture.None) + { + parsedMetadata["ProcessorArchitecture"] = ProcessorArchitecture; + } + + if (PowerShellVersion != null) + { + parsedMetadata["PowerShellVersion"] = PowerShellVersion; + } + + if (ClrVersion != null) + { + parsedMetadata["ClrVersion"] = ClrVersion; + } + + if (DotNetFrameworkVersion != null) + { + parsedMetadata["DotNetFrameworkVersion"] = DotNetFrameworkVersion; + } + + if (PowerShellHostName != null) + { + parsedMetadata["PowerShellHostName"] = PowerShellHostName; + } + + if (PowerShellHostVersion != null) + { + parsedMetadata["PowerShellHostVersion"] = PowerShellHostVersion; + } + + if (RequiredModules != null) + { + parsedMetadata["RequiredModules"] = RequiredModules; + } + + if (TypesToProcess != null) + { + parsedMetadata["TypesToProcess"] = TypesToProcess; + } + + if (FormatsToProcess != null) + { + parsedMetadata["FormatsToProcess"] = FormatsToProcess; + } + + if (ScriptsToProcess != null) + { + parsedMetadata["ScriptsToProcess"] = ScriptsToProcess; + } + + if (RequiredAssemblies != null) + { + parsedMetadata["RequiredAssemblies"] = RequiredAssemblies; + } + + if (FileList != null) + { + parsedMetadata["FileList"] = FileList; + } + + if (ModuleList != null) + { + parsedMetadata["ModuleList"] = ModuleList; + } + + if (FunctionsToExport != null) + { + parsedMetadata["FunctionsToExport"] = FunctionsToExport; + } + + if (AliasesToExport != null) + { + parsedMetadata["AliasesToExport"] = AliasesToExport; + } + + if (VariablesToExport != null) + { + parsedMetadata["VariablesToExport"] = VariablesToExport; + } + + if (CmdletsToExport != null) + { + parsedMetadata["CmdletsToExport"] = CmdletsToExport; + } + + if (DscResourcesToExport != null) + { + parsedMetadata["DscResourcesToExport"] = DscResourcesToExport; + } + + if (CompatiblePSEditions != null) + { + parsedMetadata["CompatiblePSEditions"] = CompatiblePSEditions; + } + + if (HelpInfoUri != null) + { + parsedMetadata["HelpInfoUri"] = HelpInfoUri; + } + + if (DefaultCommandPrefix != null) + { + parsedMetadata["DefaultCommandPrefix"] = DefaultCommandPrefix; + } + + if (Tags != null) + { + parsedMetadata["Tags"] = Tags; + } + + if (LicenseUri != null) + { + parsedMetadata["LicenseUri"] = LicenseUri; + } + + if (ProjectUri != null) + { + parsedMetadata["ProjectUri"] = ProjectUri; + } + + if (IconUri != null) + { + parsedMetadata["IconUri"] = IconUri; + } + + if (ReleaseNotes != null) + { + parsedMetadata["ReleaseNotes"] = ReleaseNotes; + } + + if (Prerelease != null) + { + parsedMetadata["Prerelease"] = Prerelease; + } + + if (RequireLicenseAcceptance != null) + { + parsedMetadata["RequireLicenseAcceptance"] = RequireLicenseAcceptance; + } + + if (ExternalModuleDependencies != null) + { + parsedMetadata["ExternalModuleDependencies"] = ExternalModuleDependencies; + } + + // create a tmp path to create the module manifest + string tmpParentPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + Directory.CreateDirectory(tmpParentPath); + } + catch (Exception e) + { + ThrowTerminatingError( + new ErrorRecord( + new ArgumentException(e.Message), + "ErrorCreatingTempDir", + ErrorCategory.InvalidData, + this)); + } + + string tmpModuleManifestPath = System.IO.Path.Combine(tmpParentPath, System.IO.Path.GetFileName(resolvedManifestPath)); + parsedMetadata["Path"] = tmpModuleManifestPath; + WriteVerbose($"Temp path created for new module manifest is: {tmpModuleManifestPath}"); + + using (System.Management.Automation.PowerShell pwsh = System.Management.Automation.PowerShell.Create()) + { + try + { + var results = PowerShellInvoker.InvokeScriptWithHost( + cmdlet: this, + script: @" + param ( + [hashtable] $params + ) + + New-ModuleManifest @params + ", + args: new object[] { parsedMetadata }, + out Exception terminatingErrors); + } + catch (Exception e) + { + ThrowTerminatingError( + new ErrorRecord( + new ArgumentException($"Error occured while running 'New-ModuleManifest': {e.Message}"), + "ErrorExecutingNewModuleManifest", + ErrorCategory.InvalidArgument, + this)); + } + } + + try + { + // Move to the new module manifest back to the original location + WriteVerbose($"Moving '{tmpModuleManifestPath}' to '{resolvedManifestPath}'"); + Utils.MoveFiles(tmpModuleManifestPath, resolvedManifestPath, overwrite: true); + } + finally { + // Clean up temp file if move fails + if (File.Exists(tmpModuleManifestPath)) + { + File.Delete(tmpModuleManifestPath); + } + } + } + + #endregion + } +} diff --git a/src/code/Utils.cs b/src/code/Utils.cs index af3687a1f..ae72d6b21 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -818,6 +818,63 @@ private static bool TryReadPSDataFile( } } + public static void ValidateModuleManifest(string moduleManifestPath, out string[] errorMsgs) + { + List errorMsgsList = new List(); + using (System.Management.Automation.PowerShell pwsh = System.Management.Automation.PowerShell.Create()) + { + // use PowerShell cmdlet Test-ModuleManifest + // TODO: Test-ModuleManifest will throw an error if RequiredModules specifies a module that does not exist + // locally on the machine. Consider adding a -Syntax param to Test-ModuleManifest so that it only checks that + // the syntax is correct. In build/release pipelines for example, the modules listed under RequiredModules may + // not be locally available, but we still want to allow the user to publish. + Collection results = null; + try + { + results = pwsh.AddCommand("Test-ModuleManifest").AddParameter("Path", moduleManifestPath).Invoke(); + } + catch (Exception e) + { + errorMsgsList.Add($"Error occured while running 'Test-ModuleManifest': {e.Message}"); + + errorMsgs = errorMsgsList.ToArray(); + return; + } + + if (pwsh.HadErrors) + { + var message = string.Empty; + + if (results.Any()) + { + PSModuleInfo psModuleInfoObj = results[0].BaseObject as PSModuleInfo; + if (string.IsNullOrWhiteSpace(psModuleInfoObj.Author)) + { + message = "No author was provided in the module manifest. The module manifest must specify a version, author and description. Run 'Test-ModuleManifest' to validate the file."; + } + else if (string.IsNullOrWhiteSpace(psModuleInfoObj.Description)) + { + message = "No description was provided in the module manifest. The module manifest must specify a version, author and description. Run 'Test-ModuleManifest' to validate the file."; + } + else if (psModuleInfoObj.Version == null) + { + message = "No version or an incorrectly formatted version was provided in the module manifest. The module manifest must specify a version, author and description. Run 'Test-ModuleManifest' to validate the file."; + } + } + + if (string.IsNullOrEmpty(message) && pwsh.Streams.Error.Count > 0) + { + // This will handle version errors + message = $"{pwsh.Streams.Error[0].ToString()} Run 'Test-ModuleManifest' to validate the module manifest."; + } + + errorMsgsList.Add(message); + } + } + errorMsgs = errorMsgsList.ToArray(); + + } + #endregion #region Misc methods diff --git a/test/UpdateModuleManifest.Tests.ps1 b/test/UpdateModuleManifest.Tests.ps1 new file mode 100644 index 000000000..474607ae7 --- /dev/null +++ b/test/UpdateModuleManifest.Tests.ps1 @@ -0,0 +1,406 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$ProgressPreference = "SilentlyContinue" +Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force + +Describe 'Test Update-ModuleManifest' { + + BeforeEach { + # Create temp module manifest to be updated + $script:TempModulesPath = Join-Path $TestDrive "PSGet_$(Get-Random)" + $null = New-Item -Path $script:TempModulesPath -ItemType Directory -Force + + $script:UpdateModuleManifestName = "PSGetTestModule" + $script:UpdateModuleManifestBase = Join-Path $script:TempModulesPath $script:UpdateModuleManifestName + $null = New-Item -Path $script:UpdateModuleManifestBase -ItemType Directory -Force + + $script:testManifestPath = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath "$script:UpdateModuleManifestName.psd1" + } + + AfterEach { + RemoveItem "$script:TempModulesPath" + } + + It "Update module manifest given Path parameter" { + $description = "This is a PowerShellGet test" + New-ModuleManifest -Path $script:testManifestPath + Update-ModuleManifest -Path $script:testManifestPath -Description $description + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.Description | Should -Be $description + } + + It "Update module manifest given Guid parameter" { + $Guid = [guid]::NewGuid() + New-ModuleManifest -Path $script:testManifestPath + Update-ModuleManifest -Path $script:testManifestPath -Guid $Guid + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.Guid | Should -Be $Guid + } + + It "Update module manifest given Author parameter" { + $Author = "Test Author" + New-ModuleManifest -Path $script:testManifestPath + Update-ModuleManifest -Path $script:testManifestPath -Author $Author + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.Author | Should -Be $Author + } + + It "Update module manifest given Description parameter" { + $Description = "PowerShellGet test description" + New-ModuleManifest -Path $script:testManifestPath + Update-ModuleManifest -Path $script:testManifestPath -Description $Description + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.Description | Should -Be $Description + } + + It "Update module manifest given ModuleVersion parameter" { + $ModuleVersion = "7.0.0.0" + New-ModuleManifest -Path $script:testManifestPath + Update-ModuleManifest -Path $script:testManifestPath -ModuleVersion $ModuleVersion + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.Version.ToString() | Should -Be $ModuleVersion + } + + It "Update module manifest given RequiredModules parameter" { + $requiredModuleName = 'PackageManagement' + $requiredModuleVersion = '1.0.0.0' + $RequiredModules = @(@{ModuleName = $requiredModuleName; ModuleVersion = $requiredModuleVersion }) + New-ModuleManifest -Path $script:testManifestPath + Update-ModuleManifest -Path $script:testManifestPath -RequiredModules $RequiredModules -Description "test" + + $results = Test-ModuleManifest -Path $script:testManifestPath + foreach ($module in $results.RequiredModules) + { + $module | Should -Be $requiredModuleName + $module.Version | Should -Be $requiredModuleVersion + } + } + + It "Update module manifest given Prerelease parameter" { + $Description = "Test Description" + $ModuleVersion = "1.0.0" + $Prerelease = "preview" + New-ModuleManifest -Path $script:testManifestPath -Description $Description -ModuleVersion $ModuleVersion + Update-ModuleManifest -Path $script:testManifestPath -Prerelease $Prerelease + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.Prerelease | Should -Be $Prerelease + } + + It "Update module manifest given ReleaseNotes parameter" { + $Description = "Test Description" + $ReleaseNotes = "Release notes for module." + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -ReleaseNotes $ReleaseNotes + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.ReleaseNotes | Should -Be $ReleaseNotes + } + + It "Update module manifest given Tags parameter" { + $Description = "Test Description" + $Tag1 = "tag1" + $Tag2 = "tag2" + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -Tags $Tag1, $Tag2 + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.Tags | Should -Be @($Tag1, $Tag2) + } + + It "Update module manifest given ProjectUri parameter" { + $Description = "Test Description" + $ProjectUri = "https://www.testprojecturi.com/" + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -ProjectUri $ProjectUri + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.ProjectUri | Should -Be $ProjectUri + } + + It "Update module manifest given LicenseUri parameter" { + $Description = "Test Description" + $LicenseUri = "https://www.testlicenseuri.com/" + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -LicenseUri $LicenseUri + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.LicenseUri | Should -Be $LicenseUri + } + + It "Update module manifest given IconUri parameter" { + $Description = "Test Description" + $IconUri = "https://www.testiconuri.com/" + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -IconUri $IconUri + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.IconUri | Should -Be $IconUri + } + + It "Update module manifest given RequireLicenseAcceptance parameter" { + $Description = "PowerShellGet test description" + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -RequireLicenseAcceptance + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.RequireLicenseAcceptance | Should -Be $true + } + + It "Update module manifest given ExternalModuleDependencies parameter" { + $Description = "Test Description" + $ExternalModuleDep1 = "ExternalModuleDep1" + $ExternalModuleDep2 = "ExternalModuleDep2" + $ExternalModuleDep1FileName = "ExternalModuleDep1.psm1" + $ExternalModuleDep2FileName = "ExternalModuleDep2.psm1" + $ExternalModuleDepPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $ExternalModuleDep1FileName + $ExternalModuleDepPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $ExternalModuleDep2FileName + + $null = New-Item -Path $ExternalModuleDepPath1 -ItemType File -Force + $null = New-Item -Path $ExternalModuleDepPath2 -ItemType File -Force + + New-ModuleManifest -Path $script:testManifestPath -Description $Description -NestedModules $ExternalModuleDep1, $ExternalModuleDep2 + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.ExternalModuleDependencies | Should -Be $null + + Update-ModuleManifest -Path $script:testManifestPath -ExternalModuleDependencies $ExternalModuleDep1, $ExternalModuleDep2 + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.PrivateData.PSData.ExternalModuleDependencies | Should -Be @($ExternalModuleDep1, $ExternalModuleDep2) + } + + It "Update module manifest given PowerShellHostName parameter" { + $Description = "PowerShellGet test description" + $PowerShellHostName = $Host.Name + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -PowerShellHostName $PowerShellHostName + + $results = Test-ModuleManifest -Path $script:testManifestPath -ErrorAction SilentlyContinue + $results.PowerShellHostName | Should -Be $PowerShellHostName + } + + It "Update module manifest given DefaultCommandPrefix parameter" { + $Description = "PowerShellGet test description" + $DefaultCommandPrefix = "testprefix" + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -DefaultCommandPrefix $DefaultCommandPrefix + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.Prefix | Should -Be $DefaultCommandPrefix + } + + It "Update module manifest given RootModule parameter" { + $Description = "Test Description" + $RootModuleName = $script:UpdateModuleManifestName + ".psm1" + $RootModulePath = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $RootModuleName + $null = New-Item -Path $RootModulePath -ItemType File -Force + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -RootModule $RootModuleName + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.RootModule | Should -Be $RootModuleName + } + + It "Update module manifest given RequiredAssemblies parameter" { + $Description = "Test Description" + $RequiredAssembly1 = "RequiredAssembly1.dll" + $RequiredAssembly2 = "RequiredAssembly2.dll" + $RequiredAssemblyPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $RequiredAssembly1 + $RequiredAssemblyPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $RequiredAssembly2 + + $null = New-Item -Path $RequiredAssemblyPath1 -ItemType File -Force + $null = New-Item -Path $RequiredAssemblyPath2 -ItemType File -Force + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -RequiredAssemblies $RequiredAssembly1, $RequiredAssembly2 + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.RequiredAssemblies | Should -Be @($RequiredAssembly1, $RequiredAssembly2) + } + + It "Update module manifest given NestedModules parameter" { + $Description = "Test Description" + $NestedModule1 = "NestedModule1" + $NestedModule2 = "NestedModule2" + $NestModuleFileName1 = "NestedModule1.dll" + $NestModuleFileName2 = "NestedModule2.dll" + $NestedModulePath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $NestModuleFileName1 + $NestedModulePath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $NestModuleFileName2 + + $null = New-Item -Path $NestedModulePath1 -ItemType File -Force + $null = New-Item -Path $NestedModulePath2 -ItemType File -Force + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -NestedModules $NestedModule1, $NestedModule2 + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.NestedModules | Should -Be @($NestedModule1, $NestedModule2) + } + + It "Update module manifest given FileList parameter" { + $Description = "Test Description" + $FileList1 = "FileList1.cs" + $FileList2 = "FileList2.cs" + $FileListPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $FileList1 + $FileListPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $FileList2 + + $null = New-Item -Path $FileListPath1 -ItemType File -Force + $null = New-Item -Path $FileListPath2 -ItemType File -Force + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -FileList $FileList1, $FileList2 + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.FileList | Should -Be @($FileListPath1, $FileListPath2) + } + + It "Update module manifest given TypesToProcess parameter" { + $Description = "Test Description" + $TypeFile = "TypeFile.ps1xml" + $TypeFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $TypeFile + + $null = New-Item -Path $TypeFilePath -ItemType File -Force + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -TypesToProcess $TypeFile + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.ExportedTypeFiles | Should -Be $TypeFilePath + } + + It "Update module manifest given FormatsToProcess parameter" { + $Description = "Test Description" + $FormatFile = "FormatFile.ps1xml" + $FormatFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $FormatFile + + $null = New-Item -Path $FormatFilePath -ItemType File -Force + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -FormatsToProcess $FormatFile + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.ExportedFormatFiles | Should -Be $FormatFilePath + } + + It "Update module manifest given ScriptsToProcess parameter" { + $Description = "Test Description" + $Script1 = "Script1.ps1" + $Script2 = "Script2.ps1" + $ScriptPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $Script1 + $ScriptPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:UpdateModuleManifestBase -ChildPath $Script2 + + $null = New-Item -Path $ScriptPath1 -ItemType File -Force + $null = New-Item -Path $ScriptPath2 -ItemType File -Force + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -ScriptsToProcess $Script1, $Script2 + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.Scripts | Should -Be @($ScriptPath1, $ScriptPath2) + } + + It "Update module manifest given ProcessorArchitecture parameter" { + $Description = "Test Description" + $ProcessorArchitecture = [System.Reflection.ProcessorArchitecture]::Amd64 + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -ProcessorArchitecture $ProcessorArchitecture + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.ProcessorArchitecture | Should -Be $ProcessorArchitecture + } + + It "Update module manifest given ModuleList parameter" { + $Description = "Test Description" + $ModuleList1 = "PowerShellGet" + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -ModuleList $ModuleList1 + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.ModuleList | Should -Be $ModuleList1 + } + + It "Update module manifest given CompanyName, Copyright, PowerShellHostVersion, ClrVersion, DotnetFrameworkVersion, PowerShellVersion, HelpInfoUri, and CompatiblePSEditions" { + $Description = "Test Description" + $CompanyName = "Test CompanyName" + $Copyright = "Test Copyright" + $PowerShellHostVersion = "5.0" + $ClrVersion = "1.0" + $DotnetFrameworkVersion = "2.0" + $PowerShellVersion = "5.1" + $HelpInfoUri = "https://www.testhelpinfouri.com/" + $CompatiblePSEditions = @("Desktop", "Core") + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath ` + -CompanyName $CompanyName ` + -Copyright $Copyright ` + -PowerShellVersion $PowerShellVersion ` + -ClrVersion $ClrVersion ` + -DotNetFrameworkVersion $DotnetFrameworkVersion ` + -PowerShellHostVersion $PowerShellHostVersion ` + -HelpInfoUri $HelpInfoUri ` + -CompatiblePSEditions $CompatiblePSEditions + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.CompanyName | Should -Be $CompanyName + $results.Copyright | Should -Be $Copyright + $results.PowerShellVersion | Should -Be $PowerShellVersion + $results.ClrVersion | Should -Be $ClrVersion + $results.DotnetFrameworkVersion | Should -Be $DotnetFrameworkVersion + $results.PowerShellHostVersion | Should -Be $PowerShellHostVersion + $results.HelpInfoUri | Should -Be $HelpInfoUri + $results.CompatiblePSEditions | Should -Be $CompatiblePSEditions + } + + It "Update module manifest given FunctionsToExport, AliasesToExport, and VariablesToExport parameters" { + $Description = "Test Description" + $ExportedFunctions = "FunctionToExport1", "FunctionToExport2" + $ExportedAliases = "AliasToExport1", "AliasToExport2" + $ExportedVariables = "VariablesToExport1", "Variables2Export2" + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath ` + -FunctionsToExport $ExportedFunctions ` + -AliasesToExport $ExportedAliases ` + -VariablesToExport $ExportedVariables + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.ExportedFunctions.Keys | Should -Be $ExportedFunctions + $results.ExportedAliases.Keys | Should -Be $ExportedAliases + $results.ExportedVariables.Keys | Should -Be $ExportedVariables + } + + It "Update module manifest given CmdletsToExport parameters" { + $Description = "Test Description" + $CmdletToExport1 = "CmdletToExport1" + $CmdletToExport2 = "CmdletToExport2" + + New-ModuleManifest -Path $script:testManifestPath -Description $Description + Update-ModuleManifest -Path $script:testManifestPath -CmdletsToExport $CmdletToExport1, $CmdletToExport2 + + $results = Get-Content -Path $script:testManifestPath -Raw + $results.Contains($CmdletToExport1) | Should -Be $true + $results.Contains($CmdletToExport2) | Should -Be $true + } + + It "Update module manifest should not overwrite over old data unless explcitly specified" { + $Description = "Test Description" + $ModuleVersion = "2.0.0" + $Author = "Leto Atriedes" + $ProjectUri = "https://www.arrakis.gov/" + $Prerelease = "Preview" + New-ModuleManifest -Path $script:testManifestPath -Description $Description -ModuleVersion $ModuleVersion -Author $Author -ProjectUri $ProjectUri + Update-ModuleManifest -Path $script:testManifestPath -Prerelease $Prerelease + + $results = Test-ModuleManifest -Path $script:testManifestPath + $results.Author | Should -Be $Author + $results.PrivateData.PSData.ProjectUri | Should -Be $ProjectUri + $results.PrivateData.PSData.Prerelease | Should -Be $Prerelease + } +} +