diff --git a/src/PowerShellGet.psd1 b/src/PowerShellGet.psd1
index 2c9b767b2..2b4953d39 100644
--- a/src/PowerShellGet.psd1
+++ b/src/PowerShellGet.psd1
@@ -21,6 +21,9 @@
'Register-PSResourceRepository',
'Save-PSResource',
'Set-PSResourceRepository',
+ 'New-PSScriptFileInfo',
+ 'Test-PSScriptFileInfo',
+ 'Update-PSScriptFileInfo',
'Publish-PSResource',
'Uninstall-PSResource',
'Unregister-PSResourceRepository',
diff --git a/src/code/NewPSScriptFileInfo.cs b/src/code/NewPSScriptFileInfo.cs
new file mode 100644
index 000000000..0e1bf0031
--- /dev/null
+++ b/src/code/NewPSScriptFileInfo.cs
@@ -0,0 +1,258 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Management.Automation;
+using Microsoft.PowerShell.Commands;
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ ///
+ /// Creates a new .ps1 file with script information required for publishing a script.
+ ///
+ [Cmdlet(VerbsCommon.New, "PSScriptFileInfo")]
+ public sealed class NewPSScriptFileInfo : PSCmdlet
+ {
+ #region Parameters
+
+ ///
+ /// The path the .ps1 script info file will be created at.
+ ///
+ [Parameter(Position = 0, Mandatory = true)]
+ [ValidateNotNullOrEmpty]
+ public string FilePath { get; set; }
+
+ ///
+ /// The version of the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string Version { get; set; }
+
+ ///
+ /// The author of the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string Author { get; set; }
+
+ ///
+ /// The description of the script.
+ ///
+ [Parameter(Mandatory = true)]
+ [ValidateNotNullOrEmpty()]
+ public string Description { get; set; }
+
+ ///
+ /// A unique identifier for the script. The GUID can be used to distinguish among scripts with the same name.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public Guid Guid { get; set; }
+
+ ///
+ /// The name of the company owning the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string CompanyName { get; set; }
+
+ ///
+ /// The copyright statement for the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string Copyright { get; set; }
+
+ ///
+ /// The list of modules required by the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public Hashtable[] RequiredModules { get; set; }
+
+ ///
+ /// The list of external module dependencies taken by this script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string[] ExternalModuleDependencies { get; set; }
+
+ ///
+ /// The list of scripts required by the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string[] RequiredScripts { get; set; }
+
+ ///
+ /// The list of external script dependencies taken by this script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string[] ExternalScriptDependencies { get; set; }
+
+ ///
+ /// The tags associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string[] Tags { get; set; }
+
+ ///
+ /// The Uri for the project associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string ProjectUri { get; set; }
+
+ ///
+ /// The Uri for the license associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string LicenseUri { get; set; }
+
+ ///
+ /// The Uri for the icon associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string IconUri { get; set; }
+
+ ///
+ /// The release notes for the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string ReleaseNotes { get; set; }
+
+ ///
+ /// The private data associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string PrivateData { get; set; }
+
+ ///
+ /// If used with Path parameter and .ps1 file specified at the path exists, it rewrites the file.
+ ///
+ [Parameter]
+ public SwitchParameter Force { get; set; }
+
+ #endregion
+
+ #region Methods
+
+ protected override void EndProcessing()
+ {
+ // validate Uri related parameters passed in as strings
+ Uri projectUri = null;
+ if (!String.IsNullOrEmpty(ProjectUri) && !Utils.TryCreateValidUri(uriString: ProjectUri,
+ cmdletPassedIn: this,
+ uriResult: out projectUri,
+ errorRecord: out ErrorRecord projectErrorRecord))
+ {
+ ThrowTerminatingError(projectErrorRecord);
+ }
+
+ Uri licenseUri = null;
+ if (!String.IsNullOrEmpty(LicenseUri) && !Utils.TryCreateValidUri(uriString: LicenseUri,
+ cmdletPassedIn: this,
+ uriResult: out licenseUri,
+ errorRecord: out ErrorRecord licenseErrorRecord))
+ {
+ ThrowTerminatingError(licenseErrorRecord);
+ }
+
+ Uri iconUri = null;
+ if (!String.IsNullOrEmpty(IconUri) && !Utils.TryCreateValidUri(uriString: IconUri,
+ cmdletPassedIn: this,
+ uriResult: out iconUri,
+ errorRecord: out ErrorRecord iconErrorRecord))
+ {
+ ThrowTerminatingError(iconErrorRecord);
+ }
+
+ if (!FilePath.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase))
+ {
+ var exMessage = "Path needs to end with a .ps1 file. Example: C:/Users/john/x/MyScript.ps1";
+ var ex = new ArgumentException(exMessage);
+ var InvalidPathError = new ErrorRecord(ex, "InvalidPath", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(InvalidPathError);
+ }
+
+ var resolvedFilePath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(FilePath);
+ if (String.IsNullOrEmpty(resolvedFilePath))
+ {
+ var exMessage = "Error: Could not resolve provided Path argument into a single path.";
+ var ex = new PSArgumentException(exMessage);
+ var InvalidPathArgumentError = new ErrorRecord(ex, "InvalidPathArgumentError", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(InvalidPathArgumentError);
+ }
+
+ if (File.Exists(resolvedFilePath) && !Force)
+ {
+ // .ps1 file at specified location already exists and Force parameter isn't used to rewrite the file
+ var exMessage = ".ps1 file at specified path already exists. Specify a different location or use -Force parameter to overwrite the .ps1 file.";
+ var ex = new ArgumentException(exMessage);
+ var ScriptAtPathAlreadyExistsError = new ErrorRecord(ex, "ScriptAtPathAlreadyExists", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(ScriptAtPathAlreadyExistsError);
+ }
+
+ ModuleSpecification[] validatedRequiredModuleSpecifications = Array.Empty();
+ if (RequiredModules != null && RequiredModules.Length > 0)
+ {
+ if (!Utils.TryCreateModuleSpecification(
+ moduleSpecHashtables: RequiredModules,
+ out validatedRequiredModuleSpecifications,
+ out ErrorRecord[] moduleSpecErrors))
+ {
+ foreach (ErrorRecord err in moduleSpecErrors)
+ {
+ WriteError(err);
+ }
+
+ return;
+ }
+ }
+
+ PSScriptFileInfo scriptInfo = new PSScriptFileInfo(
+ version: Version,
+ guid: Guid,
+ author: Author,
+ companyName: CompanyName,
+ copyright: Copyright,
+ tags: Tags,
+ licenseUri: licenseUri,
+ projectUri: projectUri,
+ iconUri: iconUri,
+ requiredModules: validatedRequiredModuleSpecifications,
+ externalModuleDependencies: ExternalModuleDependencies,
+ requiredScripts: RequiredScripts,
+ externalScriptDependencies: ExternalScriptDependencies,
+ releaseNotes: ReleaseNotes,
+ privateData: PrivateData,
+ description: Description);
+
+ if (!scriptInfo.TryCreateScriptFileInfoString(
+ psScriptFileContents: out string[] psScriptFileContents,
+ errors: out ErrorRecord[] errors))
+ {
+ foreach (ErrorRecord err in errors)
+ {
+ WriteError(err);
+ }
+
+ return;
+ }
+
+ File.WriteAllLines(resolvedFilePath, psScriptFileContents);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/code/PSScriptContents.cs b/src/code/PSScriptContents.cs
new file mode 100644
index 000000000..c04f1dc49
--- /dev/null
+++ b/src/code/PSScriptContents.cs
@@ -0,0 +1,117 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+
+namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
+{
+ ///
+ /// This class contains information for a PSScriptFileInfo (representing a .ps1 file contents).
+ ///
+ public sealed class PSScriptContents
+ {
+ #region Properties
+
+ ///
+ /// End of file contents for the .ps1 file.
+ ///
+ public string[] EndOfFileContents { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// End of file contents for the .ps1 file.
+ ///
+ public bool ContainsSignature { get; set; } = false;
+
+ #endregion
+
+ #region Private Members
+
+ private const string signatureStartString = "# SIG # Begin signature block";
+ private int _signatureStartIndex = -1;
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// This constructor takes end of file contents as a string and checks if it has a signature.
+ ///
+ public PSScriptContents(string[] endOfFileContents)
+ {
+ EndOfFileContents = endOfFileContents;
+ ContainsSignature = CheckForSignature();
+ }
+
+ ///
+ /// This constructor creates a PSScriptContents instance with default values for its properties.
+ /// The calling method, like PSScriptContents.ParseContent() could then populate the properties.
+ ///
+ internal PSScriptContents() {}
+
+ #endregion
+
+ #region Internal Methods
+
+ ///
+ /// Parses end of file contents as a string from the file lines passed in
+ /// and sets property indicating whether those contents contain a signature.
+ ///
+ internal void ParseContent(string[] commentLines)
+ {
+ if (commentLines.Length != 0)
+ {
+ EndOfFileContents = commentLines;
+ ContainsSignature = CheckForSignature();
+ }
+ }
+
+ ///
+ /// This function is called by PSScriptFileInfo.TryCreateScriptFileInfoString(),
+ /// by the New-PSScriptFileInfo cmdlet (in which case EndOfFileContents is an empty string so there's no signature that'll get removed)
+ /// or by Update-PSScriptFileInfo cmdlet (in which case EndOfFileContents may not be empty and may contain a signature.
+ /// When emitting contents, any file signature is always removed because it is invalidated when the content is updated.
+ ///
+ internal string[] EmitContent()
+ {
+ RemoveSignatureString();
+ return EndOfFileContents;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ ///
+ /// Checks if the end of file contents contain a signature.
+ ///
+ private bool CheckForSignature()
+ {
+ for (int i = 0; i < EndOfFileContents.Length; i++)
+ {
+ if (String.Equals(EndOfFileContents[i], signatureStartString, StringComparison.InvariantCultureIgnoreCase))
+ {
+ _signatureStartIndex = i;
+ }
+ }
+
+ return _signatureStartIndex != -1;
+ }
+
+ ///
+ /// Removes the signature from EndOfFileContents property
+ /// as the signature would be invalidated during update.
+ ///
+ private void RemoveSignatureString()
+ {
+ if (ContainsSignature)
+ {
+ string[] newEndOfFileContents = new string[EndOfFileContents.Length - _signatureStartIndex];
+ Array.Copy(EndOfFileContents, newEndOfFileContents, _signatureStartIndex);
+ EndOfFileContents = newEndOfFileContents;
+
+ ContainsSignature = false;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/src/code/PSScriptFileInfo.cs b/src/code/PSScriptFileInfo.cs
new file mode 100644
index 000000000..f3c4f3b5b
--- /dev/null
+++ b/src/code/PSScriptFileInfo.cs
@@ -0,0 +1,492 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Management.Automation;
+using System.Linq;
+using Microsoft.PowerShell.Commands;
+
+namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
+{
+ ///
+ /// This class contains information for a PSScriptFileInfo (representing a .ps1 file contents).
+ ///
+ public sealed class PSScriptFileInfo
+ {
+ #region Properties
+ public PSScriptMetadata ScriptMetadataComment { get; set; }
+
+ public PSScriptHelp ScriptHelpComment { get; set; }
+
+ public PSScriptRequires ScriptRequiresComment { get; set; }
+
+ public PSScriptContents ScriptContent { get; set; }
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// This constructor takes metadata values that could have been passed in by the calling cmdlet
+ /// and uses those to create associated script class properties (PSScriptMetadata, PSScriptHelp, PSScriptRequires, PSScriptContents)
+ ///
+ public PSScriptFileInfo(
+ string version,
+ Guid guid,
+ string author,
+ string companyName,
+ string copyright,
+ string[] tags,
+ Uri licenseUri,
+ Uri projectUri,
+ Uri iconUri,
+ ModuleSpecification[] requiredModules,
+ string[] externalModuleDependencies,
+ string[] requiredScripts,
+ string[] externalScriptDependencies,
+ string releaseNotes,
+ string privateData,
+ string description)
+ {
+ PSScriptMetadata scriptMetadataComment = new PSScriptMetadata(
+ version,
+ guid,
+ author,
+ companyName,
+ copyright,
+ tags,
+ licenseUri,
+ projectUri,
+ iconUri,
+ externalModuleDependencies,
+ requiredScripts,
+ externalScriptDependencies,
+ releaseNotes,
+ privateData);
+
+ PSScriptHelp scriptHelpComment = new PSScriptHelp(description);
+ PSScriptRequires scriptRequiresComment = new PSScriptRequires(requiredModules);
+ PSScriptContents scriptRemainingContent = new PSScriptContents(Utils.EmptyStrArray);
+
+ ScriptMetadataComment = scriptMetadataComment;
+ ScriptHelpComment = scriptHelpComment;
+ ScriptRequiresComment = scriptRequiresComment;
+ ScriptContent = scriptRemainingContent;
+ }
+
+ ///
+ /// This constructor takes script class properties' values that could have been passed in by the calling internal methods.
+ ///
+ public PSScriptFileInfo(
+ PSScriptMetadata scriptMetadataComment,
+ PSScriptHelp scriptHelpComment,
+ PSScriptRequires scriptRequiresComment,
+ PSScriptContents scriptRemainingContent
+ )
+ {
+ ScriptMetadataComment = scriptMetadataComment;
+ ScriptHelpComment = scriptHelpComment;
+ ScriptRequiresComment = scriptRequiresComment;
+ ScriptContent = scriptRemainingContent;
+ }
+
+ #endregion
+
+ #region Internal Static Methods
+
+ ///
+ /// Parses .ps1 file contents for PSScriptInfo, PSHelpInfo, Requires comments
+ ///
+ internal static bool TryParseScriptFileContents(
+ string scriptFileInfoPath,
+ ref List psScriptInfoCommentContent,
+ ref List helpInfoCommentContent,
+ ref List requiresCommentContent,
+ ref string[] remainingFileContent,
+ out ErrorRecord error)
+ {
+ error= null;
+
+ psScriptInfoCommentContent = new List();
+ helpInfoCommentContent = new List();
+ requiresCommentContent = new List();
+ remainingFileContent = Utils.EmptyStrArray;
+
+ string[] fileContents = File.ReadAllLines(scriptFileInfoPath);
+
+ bool reachedPSScriptInfoCommentEnd = false;
+ bool reachedHelpInfoCommentEnd = false;
+
+ int i = 0;
+ int endOfFileContentsStartIndex = 0;
+ while (i < fileContents.Length)
+ {
+ string line = fileContents[i];
+
+ if (line.StartsWith("<#PSScriptInfo"))
+ {
+ int j = i + 1; // start at the next line
+ // keep grabbing lines until we get to closing #>
+ while (j < fileContents.Length)
+ {
+ string blockLine = fileContents[j];
+ if (blockLine.StartsWith("#>"))
+ {
+ reachedPSScriptInfoCommentEnd = true;
+ i = j + 1;
+ break;
+ }
+
+ psScriptInfoCommentContent.Add(blockLine);
+ j++;
+ }
+
+ if (!reachedPSScriptInfoCommentEnd)
+ {
+ var message = String.Format("Could not parse '{0}' as a PowerShell script file due to missing the closing '#>' for <#PSScriptInfo comment block", scriptFileInfoPath);
+ var ex = new InvalidOperationException(message);
+ error = new ErrorRecord(ex, "MissingEndBracketToPSScriptInfoParseError", ErrorCategory.ParserError, null);
+ return false;
+ }
+ }
+ else if (line.StartsWith("<#"))
+ {
+ // The next comment block must be the help comment block (containing description)
+ // keep grabbing lines until we get to closing #>
+ int j = i + 1;
+ while (j < fileContents.Length)
+ {
+ string blockLine = fileContents[j];
+ if (blockLine.StartsWith("#>"))
+ {
+ reachedHelpInfoCommentEnd = true;
+ i = j + 1;
+ endOfFileContentsStartIndex = i;
+ break;
+ }
+
+ helpInfoCommentContent.Add(blockLine);
+ j++;
+ }
+
+ if (!reachedHelpInfoCommentEnd)
+ {
+ var message = String.Format("Could not parse '{0}' as a PowerShell script file due to missing the closing '#>' for HelpInfo comment block", scriptFileInfoPath);
+ var ex = new InvalidOperationException(message);
+ error = new ErrorRecord(ex, "MissingEndBracketToHelpInfoCommentParseError", ErrorCategory.ParserError, null);
+ return false;
+ }
+ }
+ else if (line.StartsWith("#Requires"))
+ {
+ requiresCommentContent.Add(line);
+ i++;
+ }
+ else if (endOfFileContentsStartIndex != 0)
+ {
+ break;
+ }
+ else
+ {
+ // this would be newlines between blocks, or if there was other (unexpected) data between PSScriptInfo, Requires, and HelpInfo blocks
+ i++;
+ }
+ }
+
+ if (endOfFileContentsStartIndex != 0 && (endOfFileContentsStartIndex < fileContents.Length))
+ {
+ // from this line to fileContents.Length is the endOfFileContents, if any
+ remainingFileContent = new string[fileContents.Length - endOfFileContentsStartIndex];
+
+ Array.Copy(fileContents, endOfFileContentsStartIndex, remainingFileContent, 0, (fileContents.Length - endOfFileContentsStartIndex));
+ }
+
+ if (psScriptInfoCommentContent.Count() == 0)
+ {
+ // check for file not containing '<#PSScriptInfo ... #>' comment
+ var message = String.Format("Could not parse '{0}' as a PowerShell script due to it missing '<#PSScriptInfo #> block", scriptFileInfoPath);
+ var ex = new InvalidOperationException(message);
+ error = new ErrorRecord(ex, "MissingEndBracketToHelpInfoCommentParseError", ErrorCategory.ParserError, null);
+ return false;
+ }
+
+ if (helpInfoCommentContent.Count() == 0)
+ {
+ // check for file not containing HelpInfo comment
+ var message = String.Format("Could not parse '{0}' as a PowerShell script due to it missing HelpInfo comment block", scriptFileInfoPath);
+ var ex = new InvalidOperationException(message);
+ error = new ErrorRecord(ex, "missingHelpInfoCommentError", ErrorCategory.ParserError, null);
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Populates script info classes (PSScriptMetadata, PSScriptHelp, PSScriptRequires, PSScriptContents) with previosuly
+ /// parsed metadata from the ps1 file.
+ ///
+ internal static bool TryPopulateScriptClassesWithParsedContent(
+ List psScriptInfoCommentContent,
+ List helpInfoCommentContent,
+ List requiresCommentContent,
+ string[] remainingFileContent,
+ out PSScriptMetadata currentMetadata,
+ out PSScriptHelp currentHelpInfo,
+ out PSScriptRequires currentRequiresComment,
+ out PSScriptContents currentEndOfFileContents,
+ out ErrorRecord[] errors,
+ out string[] verboseMsgs)
+ {
+ List errorsList = new List();
+
+ bool parsedContentSuccessfully = true;
+
+ currentMetadata = new PSScriptMetadata();
+ if (!currentMetadata.ParseContentIntoObj(
+ commentLines: psScriptInfoCommentContent.ToArray(),
+ out ErrorRecord[] metadataErrors,
+ out verboseMsgs))
+ {
+ errorsList.AddRange(metadataErrors);
+ parsedContentSuccessfully = false;
+ }
+
+ currentHelpInfo = new PSScriptHelp();
+ if (!currentHelpInfo.ParseContentIntoObj(
+ commentLines: helpInfoCommentContent.ToArray(),
+ out ErrorRecord helpError))
+ {
+ errorsList.Add(helpError);
+ parsedContentSuccessfully = false;
+ }
+
+ currentRequiresComment = new PSScriptRequires();
+ if (!currentRequiresComment.ParseContentIntoObj(
+ commentLines: requiresCommentContent.ToArray(),
+ out ErrorRecord[] requiresErrors))
+ {
+ errorsList.AddRange(requiresErrors);
+ parsedContentSuccessfully = false;
+ }
+
+ currentEndOfFileContents = new PSScriptContents();
+ currentEndOfFileContents.ParseContent(commentLines: remainingFileContent);
+
+ errors = errorsList.ToArray();
+ return parsedContentSuccessfully;
+ }
+
+ ///
+ /// Tests .ps1 file for validity
+ ///
+ internal static bool TryTestPSScriptFile(
+ string scriptFileInfoPath,
+ out PSScriptFileInfo parsedScript,
+ out ErrorRecord[] errors,
+ // this is for Uri errors, which aren't required by script but we check if those in the script aren't valid Uri's.
+ out string[] verboseMsgs)
+ {
+ verboseMsgs = Utils.EmptyStrArray;
+ List errorsList = new List();
+ parsedScript = null;
+
+ List psScriptInfoCommentContent = new List();
+ List helpInfoCommentContent = new List();
+ List requiresCommentContent = new List();
+ string[] remainingFileContent = Utils.EmptyStrArray;
+
+ // Parse .ps1 contents out of file into list objects
+ if (!TryParseScriptFileContents(
+ scriptFileInfoPath: scriptFileInfoPath,
+ psScriptInfoCommentContent: ref psScriptInfoCommentContent,
+ helpInfoCommentContent: ref helpInfoCommentContent,
+ requiresCommentContent: ref requiresCommentContent,
+ remainingFileContent: ref remainingFileContent,
+ out ErrorRecord parseError))
+ {
+ errors = new ErrorRecord[]{parseError};
+ return false;
+ }
+
+ // Populate PSScriptFileInfo object by first creating instances for the property objects
+ // i.e (PSScriptMetadata, PSScriptHelp, PSScriptRequires, PSScriptContents)
+ if (!TryPopulateScriptClassesWithParsedContent(
+ psScriptInfoCommentContent: psScriptInfoCommentContent,
+ helpInfoCommentContent: helpInfoCommentContent,
+ requiresCommentContent: requiresCommentContent,
+ remainingFileContent: remainingFileContent,
+ currentMetadata: out PSScriptMetadata currentMetadata,
+ currentHelpInfo: out PSScriptHelp currentHelpInfo,
+ currentRequiresComment: out PSScriptRequires currentRequiresComment,
+ currentEndOfFileContents: out PSScriptContents currentEndOfFileContents,
+ errors: out errors,
+ out verboseMsgs))
+ {
+ return false;
+ }
+
+ // Create PSScriptFileInfo instance with script metadata class instances (PSScriptMetadata, PSScriptHelp, PSScriptRequires, PSScriptContents)
+ try
+ {
+ parsedScript = new PSScriptFileInfo(
+ scriptMetadataComment: currentMetadata,
+ scriptHelpComment: currentHelpInfo,
+ scriptRequiresComment: currentRequiresComment,
+ scriptRemainingContent: currentEndOfFileContents);
+ }
+ catch (Exception e)
+ {
+ var message = String.Format("PSScriptFileInfo object could not be created from passed in file due to {0}", e.Message);
+ var ex = new ArgumentException(message);
+ var PSScriptFileInfoObjectNotCreatedFromFileError = new ErrorRecord(ex, "PSScriptFileInfoObjectNotCreatedFromFileError", ErrorCategory.ParserError, null);
+ errors = new ErrorRecord[]{PSScriptFileInfoObjectNotCreatedFromFileError};
+ return false;
+ }
+
+ errors = errorsList.ToArray();
+ return true;
+ }
+
+ ///
+ /// Updates .ps1 file.
+ /// Caller must check that the file to update doesn't have a signature or if it does permission to remove signature has been granted
+ /// as this method will remove original signature, as updating would have invalidated it.
+ ///
+ internal static bool TryUpdateScriptFileContents(
+ PSScriptFileInfo scriptInfo,
+ out string[] updatedPSScriptFileContents,
+ out ErrorRecord[] errors,
+ string version,
+ Guid guid,
+ string author,
+ string companyName,
+ string copyright,
+ string[] tags,
+ Uri licenseUri,
+ Uri projectUri,
+ Uri iconUri,
+ ModuleSpecification[] requiredModules,
+ string[] externalModuleDependencies,
+ string[] requiredScripts,
+ string[] externalScriptDependencies,
+ string releaseNotes,
+ string privateData,
+ string description)
+ {
+ updatedPSScriptFileContents = Utils.EmptyStrArray;
+ List errorsList = new List();
+ bool successfullyUpdated = true;
+
+ if (scriptInfo == null)
+ {
+ throw new ArgumentNullException(nameof(scriptInfo));
+ }
+
+ if (!scriptInfo.ScriptMetadataComment.UpdateContent(
+ version: version,
+ guid: guid,
+ author: author,
+ companyName: companyName,
+ copyright: copyright,
+ tags: tags,
+ licenseUri: licenseUri,
+ projectUri: projectUri,
+ iconUri: iconUri,
+ externalModuleDependencies: externalModuleDependencies,
+ requiredScripts: requiredScripts,
+ externalScriptDependencies: externalScriptDependencies,
+ releaseNotes: releaseNotes,
+ privateData: privateData,
+ out ErrorRecord metadataUpdateError))
+ {
+ errorsList.Add(metadataUpdateError);
+ successfullyUpdated = false;
+ }
+
+ if (!scriptInfo.ScriptHelpComment.UpdateContent(
+ description: description,
+ out ErrorRecord helpUpdateError))
+ {
+ errorsList.Add(helpUpdateError);
+ successfullyUpdated = false;
+ }
+
+ // this doesn't produce errors, as ModuleSpecification creation is already validated before param passed in
+ // and user can't update endOfFileContents
+ scriptInfo.ScriptRequiresComment.UpdateContent(requiredModules: requiredModules);
+
+ if (!successfullyUpdated)
+ {
+ errors = errorsList.ToArray();
+ return successfullyUpdated;
+ }
+
+ // create string contents for .ps1 file
+ if (!scriptInfo.TryCreateScriptFileInfoString(
+ psScriptFileContents: out updatedPSScriptFileContents,
+ errors: out ErrorRecord[] createUpdatedFileContentErrors))
+ {
+ errorsList.AddRange(createUpdatedFileContentErrors);
+ successfullyUpdated = false;
+ }
+
+ errors = errorsList.ToArray();
+ return successfullyUpdated;
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ ///
+ /// Creates .ps1 file content string representation for the PSScriptFileInfo object this called upon, which is used by the caller to write the .ps1 file.
+ ///
+ internal bool TryCreateScriptFileInfoString(
+ out string[] psScriptFileContents,
+ out ErrorRecord[] errors
+ )
+ {
+ psScriptFileContents = Utils.EmptyStrArray;
+ List fileContentsList = new List();
+ errors = Array.Empty();
+ List errorsList = new List();
+
+ bool fileContentsSuccessfullyCreated = true;
+
+ // Step 1: validate object properties for required script properties.
+ if (!ScriptMetadataComment.ValidateContent(out ErrorRecord[] metadataValidationErrors))
+ {
+ errorsList.AddRange(metadataValidationErrors);
+ fileContentsSuccessfullyCreated = false;
+ }
+
+ if (!ScriptHelpComment.ValidateContent(out ErrorRecord helpValidationError))
+ {
+ errorsList.Add(helpValidationError);
+ fileContentsSuccessfullyCreated = false;
+ }
+
+ if (!fileContentsSuccessfullyCreated)
+ {
+ errors = errorsList.ToArray();
+ return fileContentsSuccessfullyCreated;
+ }
+
+ // Step 2: create string [] that will be used to write to file later
+ fileContentsList.AddRange(ScriptMetadataComment.EmitContent());
+
+ // string psRequiresCommentBlock = ScriptRequiresComment.EmitContent();
+ fileContentsList.AddRange(ScriptRequiresComment.EmitContent());
+ fileContentsList.AddRange(ScriptHelpComment.EmitContent());
+ fileContentsList.AddRange(ScriptContent.EmitContent());
+
+ psScriptFileContents = fileContentsList.ToArray();
+ return fileContentsSuccessfullyCreated;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/code/PSScriptHelp.cs b/src/code/PSScriptHelp.cs
new file mode 100644
index 000000000..9964d2159
--- /dev/null
+++ b/src/code/PSScriptHelp.cs
@@ -0,0 +1,325 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Management.Automation;
+
+namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
+{
+ ///
+ /// This class contains information for a PSScriptFileInfo (representing a .ps1 file contents).
+ ///
+ public sealed class PSScriptHelp
+ {
+ #region Properties
+
+ ///
+ /// The description of the script.
+ ///
+ public string Description { get; private set; }
+
+ ///
+ /// The synopsis of the script.
+ ///
+ public string Synopsis { get; private set; }
+
+ ///
+ /// The example(s) relating to the script's usage.
+ ///
+ public string[] Example { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// The inputs to the script.
+ ///
+ public string[] Inputs { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// The outputs to the script.
+ ///
+ public string[] Outputs { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// The notes for the script.
+ ///
+ public string[] Notes { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// The links for the script.
+ ///
+ public string[] Links { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// The components for the script.
+ ///
+ public string[] Component { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// The roles for the script.
+ ///
+ public string[] Role { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// The functionality components for the script.
+ ///
+ public string[] Functionality { get; private set; } = Utils.EmptyStrArray;
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// This constructor takes a value for description and creates a new PSScriptHelp instance.
+ ///
+ public PSScriptHelp (string description)
+ {
+ this.Description = description;
+ }
+
+ ///
+ /// This constructor takes values for description as well as other properties and creates a new PSScriptHelp instance.
+ /// Currently, the New-PSScriptFileInfo and Update-PSScriptFileInfo cmdlets don't support the user providing these values.
+ ///
+ public PSScriptHelp (
+ string description,
+ string synopsis,
+ string[] example,
+ string[] inputs,
+ string[] outputs,
+ string[] notes,
+ string[] links,
+ string[] component,
+ string[] role,
+ string[] functionality)
+ {
+ this.Description = description;
+ this.Synopsis = synopsis;
+ this.Example = example;
+ this.Inputs = inputs;
+ this.Outputs = outputs;
+ this.Notes = notes;
+ this.Links = links;
+ this.Component = component;
+ this.Role = role;
+ this.Functionality = functionality;
+ }
+
+ ///
+ /// This constructor is called by internal cmdlet methods and creates a PSScriptHelp with default values
+ /// for the parameters. Calling a method like PSScriptHelp.ParseConentIntoObj() would then populate those properties.
+ ///
+ internal PSScriptHelp() {}
+
+ #endregion
+
+ #region Internal Methods
+
+ ///
+ /// Parses HelpInfo metadata out of the HelpInfo comment lines found while reading the file
+ /// and populates PSScriptHelp properties from that metadata.
+ ///
+ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord error)
+ {
+ bool successfullyParsed = true;
+ string[] spaceDelimeter = new string[]{" "};
+ string[] newlineDelimeter = new string[]{Environment.NewLine};
+
+ // parse content into a hashtable
+ Hashtable parsedHelpMetadata = Utils.ParseCommentBlockContent(commentLines);
+
+ if (!ValidateParsedContent(parsedHelpMetadata, out error))
+ {
+ return false;
+ }
+
+ // populate object
+ Description = (string) parsedHelpMetadata["DESCRIPTION"];
+ Synopsis = (string) parsedHelpMetadata["SYNOPSIS"] ?? String.Empty;
+ Example = Utils.GetStringArrayFromString(newlineDelimeter, (string) parsedHelpMetadata["EXAMPLE"]);
+ Inputs = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["INPUT"]);
+ Outputs = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["OUTPUTS"]);
+ Notes = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["NOTES"]);
+ Links = Utils.GetStringArrayFromString(newlineDelimeter, (string) parsedHelpMetadata["LINKS"]);
+ Component = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["COMPONENT"]);
+ Role = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["ROLE"]);
+ Functionality = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedHelpMetadata["FUNCTIONALITY"]);
+
+ return successfullyParsed;
+ }
+
+ ///
+ /// Valides parsed help info content from the hashtable to ensure required help metadata (Description) is present
+ /// and does not contain empty values.
+ ///
+ internal bool ValidateParsedContent(Hashtable parsedHelpMetadata, out ErrorRecord error)
+ {
+ error = null;
+ if (!parsedHelpMetadata.ContainsKey("DESCRIPTION") || String.IsNullOrEmpty((string) parsedHelpMetadata["DESCRIPTION"]) || String.Equals(((string) parsedHelpMetadata["DESCRIPTION"]).Trim(), String.Empty))
+ {
+ var exMessage = "PSScript file must contain value for Description. Ensure value for Description is passed in and try again.";
+ var ex = new ArgumentException(exMessage);
+ var PSScriptInfoMissingDescriptionError = new ErrorRecord(ex, "PSScriptInfoMissingDescription", ErrorCategory.InvalidArgument, null);
+ error = PSScriptInfoMissingDescriptionError;
+ return false;
+ }
+
+ if (StringContainsComment((string) parsedHelpMetadata["DESCRIPTION"]))
+ {
+ var exMessage = "PSScript file's value for Description cannot contain '<#' or '#>'. Pass in a valid value for Description and try again.";
+ var ex = new ArgumentException(exMessage);
+ var DescriptionContainsCommentError = new ErrorRecord(ex, "DescriptionContainsComment", ErrorCategory.InvalidArgument, null);
+ error = DescriptionContainsCommentError;
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Validates help info properties contain required script Help properties
+ /// i.e Description.
+ ///
+ internal bool ValidateContent(out ErrorRecord error)
+ {
+ error = null;
+ if (String.IsNullOrEmpty(Description))
+ {
+ var exMessage = "PSScript file must contain value for Description. Ensure value for Description is passed in and try again.";
+ var ex = new ArgumentException(exMessage);
+ var PSScriptInfoMissingDescriptionError = new ErrorRecord(ex, "PSScriptInfoMissingDescription", ErrorCategory.InvalidArgument, null);
+ error = PSScriptInfoMissingDescriptionError;
+ return false;
+ }
+
+ if (StringContainsComment(Description))
+ {
+ var exMessage = "PSScript file's value for Description cannot contain '<#' or '#>'. Pass in a valid value for Description and try again.";
+ var ex = new ArgumentException(exMessage);
+ var DescriptionContainsCommentError = new ErrorRecord(ex, "DescriptionContainsComment", ErrorCategory.InvalidArgument, null);
+ error = DescriptionContainsCommentError;
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Emits string representation of 'HelpInfo <# ... #>' comment and its metadata contents.
+ ///
+ internal string[] EmitContent()
+ {
+ // Note: we add a newline to the end of each property entry in HelpInfo so that there's an empty line separating them.
+ List psHelpInfoLines = new List();
+
+ psHelpInfoLines.Add($"<#{Environment.NewLine}");
+ psHelpInfoLines.Add($".DESCRIPTION");
+ psHelpInfoLines.Add($"{Description}{Environment.NewLine}");
+
+ if (!String.IsNullOrEmpty(Synopsis))
+ {
+ psHelpInfoLines.Add($".SYNOPSIS");
+ psHelpInfoLines.Add($"{Synopsis}{Environment.NewLine}");
+ }
+
+ foreach (string currentExample in Example)
+ {
+ psHelpInfoLines.Add($".EXAMPLE");
+ psHelpInfoLines.Add($"{currentExample}{Environment.NewLine}");
+ }
+
+ foreach (string input in Inputs)
+ {
+ psHelpInfoLines.Add($".INPUTS");
+ psHelpInfoLines.Add($"{input}{Environment.NewLine}");
+ }
+
+ foreach (string output in Outputs)
+ {
+ psHelpInfoLines.Add($".OUTPUTS");
+ psHelpInfoLines.Add($"{output}{Environment.NewLine}");
+ }
+
+ if (Notes.Length > 0)
+ {
+ psHelpInfoLines.Add($".NOTES");
+ psHelpInfoLines.Add($"{String.Join(Environment.NewLine, Notes)}{Environment.NewLine}");
+ }
+
+ foreach (string link in Links)
+ {
+ psHelpInfoLines.Add($".LINK");
+ psHelpInfoLines.Add($"{link}{Environment.NewLine}");
+ }
+
+ if (Component.Length > 0)
+ {
+ psHelpInfoLines.Add($".COMPONENT");
+ psHelpInfoLines.Add($"{String.Join(Environment.NewLine, Component)}{Environment.NewLine}");
+ }
+
+ if (Role.Length > 0)
+ {
+ psHelpInfoLines.Add($".ROLE");
+ psHelpInfoLines.Add($"{String.Join(Environment.NewLine, Role)}{Environment.NewLine}");
+ }
+
+ if (Functionality.Length > 0)
+ {
+ psHelpInfoLines.Add($".FUNCTIONALITY");
+ psHelpInfoLines.Add($"{String.Join(Environment.NewLine, Functionality)}{Environment.NewLine}");
+ }
+
+ psHelpInfoLines.Add("#>");
+
+ return psHelpInfoLines.ToArray();
+ }
+
+ ///
+ /// Updates contents of the HelpInfo properties from any (non-default) values passed in.
+ ///
+ internal bool UpdateContent(string description, out ErrorRecord error)
+ {
+ error = null;
+
+ if (!String.IsNullOrEmpty(description))
+ {
+ if (String.Equals(description.Trim(), String.Empty))
+ {
+ var exMessage = "Description value can't be updated to whitespace as this would invalidate the script.";
+ var ex = new ArgumentException(exMessage);
+ var descriptionUpdateValueIsWhitespaceError = new ErrorRecord(ex, "descriptionUpdateValueIsWhitespaceError", ErrorCategory.InvalidArgument, null);
+ error = descriptionUpdateValueIsWhitespaceError;
+ return false;
+ }
+
+ if (StringContainsComment(description))
+ {
+ var exMessage = "Description value can't be updated to value containing comment '<#' or '#>' as this would invalidate the script.";
+ var ex = new ArgumentException(exMessage);
+ var descriptionUpdateValueContainsCommentError = new ErrorRecord(ex, "descriptionUpdateValueContainsCommentError", ErrorCategory.InvalidArgument, null);
+ error = descriptionUpdateValueContainsCommentError;
+ return false;
+ }
+
+ Description = description;
+ }
+
+ return true;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ ///
+ /// Ensure description field (passed as stringToValidate) does not contain '<#' or '#>'.
+ ///
+ private bool StringContainsComment(string stringToValidate)
+ {
+ return stringToValidate.Contains("<#") || stringToValidate.Contains("#>");
+ }
+
+ #endregion
+ }
+}
diff --git a/src/code/PSScriptMetadata.cs b/src/code/PSScriptMetadata.cs
new file mode 100644
index 000000000..ded8088e7
--- /dev/null
+++ b/src/code/PSScriptMetadata.cs
@@ -0,0 +1,458 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Management.Automation;
+using NuGet.Versioning;
+
+namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
+{
+ ///
+ /// This class contains information for a PSScriptFileInfo (representing a .ps1 file contents).
+ ///
+ public sealed class PSScriptMetadata
+ {
+ #region Properties
+
+ ///
+ /// the version of the script.
+ ///
+ public NuGetVersion Version { get; private set; }
+
+ ///
+ /// the GUID for the script.
+ ///
+ public Guid Guid { get; private set; }
+
+ ///
+ /// the author for the script.
+ ///
+ public string Author { get; private set; }
+
+ ///
+ /// the name of the company owning the script.
+ ///
+ public string CompanyName { get; private set; }
+
+ ///
+ /// the copyright statement for the script.
+ ///
+ public string Copyright { get; private set; }
+
+ ///
+ /// the tags for the script.
+ ///
+ public string[] Tags { get; private set; }
+
+ ///
+ /// the Uri for the license of the script.
+ ///
+ public Uri LicenseUri { get; private set; }
+
+ ///
+ /// the Uri for the project relating to the script.
+ ///
+ public Uri ProjectUri { get; private set; }
+
+ ///
+ /// the Uri for the icon relating to the script.
+ ///
+ public Uri IconUri { get; private set; }
+
+ ///
+ /// the list of external module dependencies for the script.
+ ///
+ public string[] ExternalModuleDependencies { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// the list of required scripts for the parent script.
+ ///
+ public string[] RequiredScripts { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// the list of external script dependencies for the script.
+ ///
+ public string[] ExternalScriptDependencies { get; private set; } = Utils.EmptyStrArray;
+
+ ///
+ /// the release notes relating to the script.
+ ///
+ public string ReleaseNotes { get; private set; } = String.Empty;
+
+ ///
+ /// The private data associated with the script.
+ ///
+ public string PrivateData { get; private set; }
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// This constructor takes metadata properties and creates PSScriptMetadata instance.
+ ///
+ public PSScriptMetadata(
+ string version,
+ Guid guid,
+ string author,
+ string companyName,
+ string copyright,
+ string[] tags,
+ Uri licenseUri,
+ Uri projectUri,
+ Uri iconUri,
+ string[] externalModuleDependencies,
+ string[] requiredScripts,
+ string[] externalScriptDependencies,
+ string releaseNotes,
+ string privateData)
+ {
+ if (String.IsNullOrEmpty(author))
+ {
+ author = Environment.UserName;
+ }
+
+ Version = !String.IsNullOrEmpty(version) ? new NuGetVersion (version) : new NuGetVersion("1.0.0.0");
+ Guid = (guid == null || guid == Guid.Empty) ? Guid.NewGuid() : guid;
+ Author = !String.IsNullOrEmpty(author) ? author : Environment.UserName;
+ CompanyName = companyName;
+ Copyright = copyright;
+ Tags = tags ?? Utils.EmptyStrArray;
+ LicenseUri = licenseUri;
+ ProjectUri = projectUri;
+ IconUri = iconUri;
+ ExternalModuleDependencies = externalModuleDependencies ?? Utils.EmptyStrArray;
+ RequiredScripts = requiredScripts ?? Utils.EmptyStrArray;
+ ExternalScriptDependencies = externalScriptDependencies ?? Utils.EmptyStrArray;
+ ReleaseNotes = releaseNotes;
+ PrivateData = privateData;
+ }
+
+ ///
+ /// This constructor is called by internal cmdlet methods and creates a PSScriptFileInfo with default values
+ /// for the parameters. Calling a method like PSScriptMetadata.ParseConentIntoObj() would then populate those properties.
+ ///
+ internal PSScriptMetadata() {}
+
+ #endregion
+
+ #region Internal Methods
+
+ ///
+ /// Parses script metadata comment (passed in as its lines) into PSScriptMetadata instance's properties
+ /// Also validates that this metadata has required script properties.
+ ///
+ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] errors, out string[] msgs)
+ {
+ msgs = Utils.EmptyStrArray;
+ List msgsList = new List();
+
+ // parse content into a hashtable
+ Hashtable parsedMetadata = Utils.ParseCommentBlockContent(commentLines);
+
+ if (parsedMetadata.Count == 0)
+ {
+ var message = String.Format("PowerShell script '<#PSScriptInfo .. #>' comment block contains no metadata");
+ var ex = new InvalidOperationException(message);
+ var psScriptInfoBlockMissingMetadata = new ErrorRecord(ex, "psScriptInfoBlockMissingMetadataError", ErrorCategory.ParserError, null);
+ errors = new ErrorRecord[]{psScriptInfoBlockMissingMetadata};
+ return false;
+ }
+
+ // check parsed metadata contains required Author, Version, Guid key values
+ if (!ValidateParsedContent(parsedMetadata, out errors))
+ {
+ return false;
+ }
+
+ // now populate the object instance
+ string[] spaceDelimeter = new string[]{" "};
+
+ Uri parsedLicenseUri = null;
+ if (!String.IsNullOrEmpty((string) parsedMetadata["LICENSEURI"]))
+ {
+ if (!Uri.TryCreate((string) parsedMetadata["LICENSEURI"], UriKind.Absolute, out parsedLicenseUri))
+ {
+ msgsList.Add($"LicenseUri property {(string) parsedMetadata["LICENSEURI"]} could not be created as a Uri");
+ }
+ }
+
+ Uri parsedProjectUri = null;
+ if (!String.IsNullOrEmpty((string) parsedMetadata["PROJECTURI"]))
+ {
+ if (!Uri.TryCreate((string) parsedMetadata["PROJECTURI"], UriKind.Absolute, out parsedProjectUri))
+ {
+ msgsList.Add($"ProjectUri property {(string) parsedMetadata["PROJECTURI"]} could not be created as Uri");
+ }
+ }
+
+ Uri parsedIconUri = null;
+ if (!String.IsNullOrEmpty((string) parsedMetadata["ICONURI"]))
+ {
+ if (!Uri.TryCreate((string) parsedMetadata["ICONURI"], UriKind.Absolute, out parsedIconUri))
+ {
+ msgsList.Add($"IconUri property {(string) parsedMetadata["ICONURI"]} could not be created as Uri");
+ }
+ }
+
+ // now populate PSScriptMetadata object properties with parsed metadata
+ Author = (string) parsedMetadata["AUTHOR"];
+ Version = new NuGetVersion((string) parsedMetadata["VERSION"]);
+ Guid = new Guid((string) parsedMetadata["GUID"]);
+
+ CompanyName = (string) parsedMetadata["COMPANYNAME"] ?? String.Empty;
+ Copyright = (string) parsedMetadata["COPYRIGHT"] ?? String.Empty;
+
+ LicenseUri = parsedLicenseUri;
+ ProjectUri = parsedProjectUri;
+ IconUri = parsedIconUri;
+
+ Tags = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedMetadata["TAGS"]);;
+ ExternalModuleDependencies = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedMetadata["EXTERNALMODULEDEPENDENCIES"]);
+ RequiredScripts = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedMetadata["REQUIREDSCRIPTS"]);
+ ExternalScriptDependencies = Utils.GetStringArrayFromString(spaceDelimeter, (string) parsedMetadata["EXTERNALSCRIPTDEPENDENCIES"]);
+ ReleaseNotes = (string) parsedMetadata["RELEASENOTES"] ?? String.Empty;
+ PrivateData = (string) parsedMetadata["PRIVATEDATA"] ?? String.Empty;
+
+ msgs = msgsList.ToArray();
+ return true;
+ }
+
+ ///
+ /// Valides parsed metadata content from the hashtable to ensure required metadata (Author, Version, Guid) is present
+ /// and does not contain empty values.
+ ///
+ internal bool ValidateParsedContent(Hashtable parsedMetadata, out ErrorRecord[] errors)
+ {
+ List errorsList = new List();
+
+ if (!parsedMetadata.ContainsKey("VERSION") || String.IsNullOrEmpty((string) parsedMetadata["VERSION"]) || String.Equals(((string) parsedMetadata["VERSION"]).Trim(), String.Empty))
+ {
+ var message = String.Format("PSScript file is missing the required Version property");
+ var ex = new ArgumentException(message);
+ var psScriptMissingVersionError = new ErrorRecord(ex, "psScriptMissingVersion", ErrorCategory.ParserError, null);
+ errorsList.Add(psScriptMissingVersionError);
+ }
+
+ if (!parsedMetadata.ContainsKey("AUTHOR") || String.IsNullOrEmpty((string) parsedMetadata["AUTHOR"]) || String.Equals(((string) parsedMetadata["AUTHOR"]).Trim(), String.Empty))
+ {
+ var message = String.Format("PSScript file is missing the required Author property");
+ var ex = new ArgumentException(message);
+ var psScriptMissingAuthorError = new ErrorRecord(ex, "psScriptMissingAuthor", ErrorCategory.ParserError, null);
+ errorsList.Add(psScriptMissingAuthorError);
+ }
+
+ if (!parsedMetadata.ContainsKey("GUID") || String.IsNullOrEmpty((string) parsedMetadata["GUID"]) || String.Equals(((string) parsedMetadata["GUID"]).Trim(), String.Empty))
+ {
+ var message = String.Format("PSScript file is missing the required Guid property");
+ var ex = new ArgumentException(message);
+ var psScriptMissingGuidError = new ErrorRecord(ex, "psScriptMissingGuid", ErrorCategory.ParserError, null);
+ errorsList.Add(psScriptMissingGuidError);
+ }
+
+ errors = errorsList.ToArray();
+ return errors.Length == 0;
+ }
+ ///
+ /// Validates metadata properties are valid and contains required script properties
+ /// i.e Author, Version, Guid.
+ ///
+ internal bool ValidateContent(out ErrorRecord[] errors)
+ {
+ bool validPSScriptInfo = true;
+ List errorsList = new List();
+
+ if (Version == null || String.IsNullOrEmpty(Version.ToString()))
+ {
+ var message = String.Format("PSScript file is missing the required Version property");
+ var ex = new ArgumentException(message);
+ var psScriptMissingVersionError = new ErrorRecord(ex, "psScriptMissingVersion", ErrorCategory.ParserError, null);
+ errorsList.Add(psScriptMissingVersionError);
+ validPSScriptInfo = false;
+ }
+
+ if (String.IsNullOrEmpty(Author))
+ {
+ var message = String.Format("PSScript file is missing the required Author property");
+ var ex = new ArgumentException(message);
+ var psScriptMissingAuthorError = new ErrorRecord(ex, "psScriptMissingAuthor", ErrorCategory.ParserError, null);
+ errorsList.Add(psScriptMissingAuthorError);
+ validPSScriptInfo = false;
+ }
+
+ if (Guid == Guid.Empty)
+ {
+ var message = String.Format("PSScript file is missing the required Guid property");
+ var ex = new ArgumentException(message);
+ var psScriptMissingGuidError = new ErrorRecord(ex, "psScriptMissingGuid", ErrorCategory.ParserError, null);
+ errorsList.Add(psScriptMissingGuidError);
+ validPSScriptInfo = false;
+ }
+
+ errors = errorsList.ToArray();
+ return validPSScriptInfo;
+ }
+
+ ///
+ /// Emits string representation of '<#PSScriptInfo ... #>' comment and its metadata contents.
+ ///
+ internal string[] EmitContent()
+ {
+ /**
+ PSScriptInfo comment will be in following format:
+ <#PSScriptInfo
+ .VERSION 1.0
+ .GUID 544238e3-1751-4065-9227-be105ff11636
+ .AUTHOR manikb
+ .COMPANYNAME Microsoft Corporation
+ .COPYRIGHT (c) 2015 Microsoft Corporation. All rights reserved.
+ .TAGS Tag1 Tag2 Tag3
+ .LICENSEURI https://contoso.com/License
+ .PROJECTURI https://contoso.com/
+ .ICONURI https://contoso.com/Icon
+ .EXTERNALMODULEDEPENDENCIES ExternalModule1
+ .REQUIREDSCRIPTS Start-WFContosoServer,Stop-ContosoServerScript
+ .EXTERNALSCRIPTDEPENDENCIES Stop-ContosoServerScript
+ .RELEASENOTES
+ contoso script now supports following features
+ Feature 1
+ Feature 2
+ Feature 3
+ Feature 4
+ Feature 5
+ .PRIVATEDATA
+ #>
+ */
+
+ string liceseUriString = LicenseUri == null ? String.Empty : LicenseUri.ToString();
+ string projectUriString = ProjectUri == null ? String.Empty : ProjectUri.ToString();
+ string iconUriString = IconUri == null ? String.Empty : IconUri.ToString();
+
+ string tagsString = String.Join(" ", Tags);
+ string externalModuleDependenciesString = String.Join(" ", ExternalModuleDependencies);
+ string requiredScriptsString = String.Join(" ", RequiredScripts);
+ string externalScriptDependenciesString = String.Join(" ", ExternalScriptDependencies);
+
+ List psScriptInfoLines = new List();
+
+ // Note: we add a newline to the end of each property entry in HelpInfo so that there's an empty line separating them.
+ psScriptInfoLines.Add($"<#PSScriptInfo{Environment.NewLine}");
+ psScriptInfoLines.Add($".VERSION {Version.ToString()}{Environment.NewLine}");
+ psScriptInfoLines.Add($".GUID {Guid.ToString()}{Environment.NewLine}");
+ psScriptInfoLines.Add($".AUTHOR {Author}{Environment.NewLine}");
+ psScriptInfoLines.Add($".COMPANYNAME {CompanyName}{Environment.NewLine}");
+ psScriptInfoLines.Add($".COPYRIGHT {Copyright}{Environment.NewLine}");
+ psScriptInfoLines.Add($".TAGS {tagsString}{Environment.NewLine}");
+ psScriptInfoLines.Add($".LICENSEURI {liceseUriString}{Environment.NewLine}");
+ psScriptInfoLines.Add($".PROJECTURI {projectUriString}{Environment.NewLine}");
+ psScriptInfoLines.Add($".ICONURI {iconUriString}{Environment.NewLine}");
+ psScriptInfoLines.Add($".EXTERNALMODULEDEPENDENCIES {externalModuleDependenciesString}{Environment.NewLine}");
+ psScriptInfoLines.Add($".REQUIREDSCRIPTS {requiredScriptsString}{Environment.NewLine}");
+ psScriptInfoLines.Add($".EXTERNALSCRIPTDEPENDENCIES {externalScriptDependenciesString}{Environment.NewLine}");
+ psScriptInfoLines.Add($".RELEASENOTES{Environment.NewLine}{ReleaseNotes}{Environment.NewLine}");
+ psScriptInfoLines.Add($".PRIVATEDATA{Environment.NewLine}{PrivateData}{Environment.NewLine}");
+ psScriptInfoLines.Add("#>");
+
+ return psScriptInfoLines.ToArray();
+ }
+
+ ///
+ /// Updates contents of the script metadata properties from any (non-default) values passed in.
+ ///
+ internal bool UpdateContent(
+ string version,
+ Guid guid,
+ string author,
+ string companyName,
+ string copyright,
+ string[] tags,
+ Uri licenseUri,
+ Uri projectUri,
+ Uri iconUri,
+ string[] externalModuleDependencies,
+ string[] requiredScripts,
+ string[] externalScriptDependencies,
+ string releaseNotes,
+ string privateData,
+ out ErrorRecord error)
+ {
+ error = null;
+ if (!String.IsNullOrEmpty(version))
+ {
+ if (!NuGetVersion.TryParse(version, out NuGetVersion updatedVersion))
+ {
+ var message = String.Format("Version provided for update could not be parsed successfully into NuGetVersion");
+ var ex = new ArgumentException(message);
+ var versionParseIntoNuGetVersionError = new ErrorRecord(ex, "VersionParseIntoNuGetVersion", ErrorCategory.ParserError, null);
+ error = versionParseIntoNuGetVersionError;
+ return false;
+ }
+
+ Version = updatedVersion;
+ }
+
+ if (guid != Guid.Empty)
+ {
+ Guid = guid;
+ }
+
+ if (!String.IsNullOrEmpty(author))
+ {
+ Author = author;
+ }
+
+ if (!String.IsNullOrEmpty(companyName)){
+ CompanyName = companyName;
+ }
+
+ if (!String.IsNullOrEmpty(copyright)){
+ Copyright = copyright;
+ }
+
+ if (tags != null && tags.Length != 0){
+ Tags = tags;
+ }
+
+ if (licenseUri != null && !licenseUri.Equals(default(Uri))){
+ LicenseUri = licenseUri;
+ }
+
+ if (projectUri != null && !projectUri.Equals(default(Uri))){
+ ProjectUri = projectUri;
+ }
+
+ if (iconUri != null && !iconUri.Equals(default(Uri))){
+ IconUri = iconUri;
+ }
+
+ if (externalModuleDependencies != null && externalModuleDependencies.Length != 0){
+ ExternalModuleDependencies = externalModuleDependencies;
+ }
+
+ if (requiredScripts != null && requiredScripts.Length != 0)
+ {
+ RequiredScripts = requiredScripts;
+ }
+
+ if (externalScriptDependencies != null && externalScriptDependencies.Length != 0){
+ ExternalScriptDependencies = externalScriptDependencies;
+ }
+
+ if (!String.IsNullOrEmpty(releaseNotes))
+ {
+ ReleaseNotes = releaseNotes;
+ }
+
+ if (!String.IsNullOrEmpty(privateData))
+ {
+ PrivateData = privateData;
+ }
+
+ return true;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/code/PSScriptRequires.cs b/src/code/PSScriptRequires.cs
new file mode 100644
index 000000000..4c2e551d4
--- /dev/null
+++ b/src/code/PSScriptRequires.cs
@@ -0,0 +1,144 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Management.Automation;
+using System.Management.Automation.Language;
+using System.Linq;
+using System.Collections.ObjectModel;
+using Microsoft.PowerShell.Commands;
+
+namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
+{
+ ///
+ /// This class contains information for a PSScriptFileInfo (representing a .ps1 file contents).
+ ///
+ public sealed class PSScriptRequires
+ {
+ #region Properties
+
+ ///
+ /// The list of modules required by the script.
+ /// Hashtable keys: GUID, MaxVersion, ModuleName (Required), RequiredVersion, Version.
+ ///
+ public ModuleSpecification[] RequiredModules { get; private set; } = Array.Empty();
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// This constructor creates a new PSScriptRequires instance with specified required modules.
+ ///
+ public PSScriptRequires(ModuleSpecification[] requiredModules)
+ {
+ RequiredModules = requiredModules ?? Array.Empty();
+ }
+
+ ///
+ /// This constructor is called by internal cmdlet methods and creates a PSScriptHelp with default values
+ /// for the parameters. Calling a method like PSScriptRequires.ParseConentIntoObj() would then populate those properties.
+ ///
+ internal PSScriptRequires() {}
+
+ #endregion
+
+ #region Internal Methods
+
+ ///
+ /// Parses RequiredModules out of comment lines and validates during parse process.
+ ///
+ internal bool ParseContentIntoObj(string[] commentLines, out ErrorRecord[] errors)
+ {
+ /**
+ When Requires comment lines are obtained from .ps1 file they will have this format:
+
+ #Requires -Module RequiredModule1
+ #Requires -Module @{ ModuleName = 'RequiredModule2'; ModuleVersion = '2.0' }
+ #Requires -Module @{ ModuleName = 'RequiredModule3'; RequiredVersion = '2.5' }
+ #Requires -Module @{ ModuleName = 'RequiredModule4'; ModuleVersion = '1.1'; MaximumVersion = '2.0' }
+ #Requires -Module @{ ModuleName = 'RequiredModule5'; MaximumVersion = '1.5' }
+
+ */
+
+ errors = Array.Empty();
+ List errorsList = new List();
+ string requiresComment = String.Join(Environment.NewLine, commentLines);
+
+ try
+ {
+ var ast = Parser.ParseInput(
+ requiresComment,
+ out Token[] tokens,
+ out ParseError[] parserErrors);
+
+ if (parserErrors.Length > 0)
+ {
+ foreach (ParseError err in parserErrors)
+ {
+ var message = String.Format("Could not requires comments as valid PowerShell input due to {0}.", err.Message);
+ var ex = new InvalidOperationException(message);
+ var requiresCommentParseError = new ErrorRecord(ex, err.ErrorId, ErrorCategory.ParserError, null);
+ errorsList.Add(requiresCommentParseError);
+ }
+
+ errors = errorsList.ToArray();
+ return false;
+ }
+
+ // get .REQUIREDMODULES property, by accessing the System.Management.Automation.Language.ScriptRequirements object ScriptRequirements.RequiredModules property
+ ScriptRequirements parsedScriptRequirements = ast.ScriptRequirements;
+ ReadOnlyCollection parsedModules = new List().AsReadOnly();
+
+ if (parsedScriptRequirements != null && parsedScriptRequirements.RequiredModules != null)
+ {
+ RequiredModules = parsedScriptRequirements.RequiredModules.ToArray();
+ }
+ }
+ catch (Exception e)
+ {
+ var message = $"Parsing RequiredModules failed due to {e.Message}";
+ var ex = new ArgumentException(message);
+ var requiredModulesAstParseError = new ErrorRecord(ex, "requiredModulesAstParseThrewError", ErrorCategory.ParserError, null);
+ errorsList.Add(requiredModulesAstParseError);
+ errors = errorsList.ToArray();
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Emits string representation of '#Requires ...' comment(s).
+ ///
+ internal string[] EmitContent()
+ {
+ List psRequiresLines = new List();
+ if (RequiredModules.Length > 0)
+ {
+ psRequiresLines.Add(String.Empty);
+ foreach (ModuleSpecification moduleSpec in RequiredModules)
+ {
+ psRequiresLines.Add(String.Format("#Requires -Module {0}", moduleSpec.ToString()));
+ }
+
+ psRequiresLines.Add(String.Empty);
+ }
+
+ return psRequiresLines.ToArray();
+ }
+
+ ///
+ /// Updates the current Requires content with another (passed in), effectively replaces it.
+ ///
+ internal void UpdateContent(ModuleSpecification[] requiredModules)
+ {
+ if (requiredModules != null && requiredModules.Length != 0){
+ RequiredModules = requiredModules;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/code/TestPSScriptFileInfo.cs b/src/code/TestPSScriptFileInfo.cs
new file mode 100644
index 000000000..931d04c7c
--- /dev/null
+++ b/src/code/TestPSScriptFileInfo.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.IO;
+using System.Management.Automation;
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ ///
+ /// Tests the contents of a .ps1 file to see if it has all properties and is in correct format
+ /// for publishing the script with the file.
+ ///
+ [Cmdlet(VerbsDiagnostic.Test, "PSScriptFileInfo")]
+ [OutputType(typeof(bool))]
+ public sealed class TestPSScriptFileInfo : PSCmdlet
+ {
+ #region Parameters
+
+ ///
+ /// The path to the .ps1 file to test.
+ ///
+ [Parameter(Position = 0, Mandatory = true)]
+ [ValidateNotNullOrEmpty]
+ public string FilePath { get; set; }
+
+ #endregion
+
+ #region Methods
+
+ protected override void EndProcessing()
+ {
+ if (!FilePath.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase))
+ {
+ var exMessage = "Path needs to end with a .ps1 file. Example: C:/Users/john/x/MyScript.ps1";
+ var ex = new ArgumentException(exMessage);
+ var InvalidPathError = new ErrorRecord(ex, "InvalidPath", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(InvalidPathError);
+ }
+
+ var resolvedPaths = SessionState.Path.GetResolvedPSPathFromPSPath(FilePath);
+ if (resolvedPaths.Count != 1)
+ {
+ var exMessage = "Error: Could not resolve provided Path argument into a single path.";
+ var ex = new PSArgumentException(exMessage);
+ var InvalidPathArgumentError = new ErrorRecord(ex, "InvalidPathArgumentError", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(InvalidPathArgumentError);
+ }
+
+ var resolvedFilePath = resolvedPaths[0].Path;
+
+ if (!File.Exists(resolvedFilePath))
+ {
+ var exMessage = "A .ps1 file does not exist at the location specified.";
+ var ex = new ArgumentException(exMessage);
+ var FileDoesNotExistError = new ErrorRecord(ex, "FileDoesNotExistAtPath", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(FileDoesNotExistError);
+ }
+
+ bool isValidScript = PSScriptFileInfo.TryTestPSScriptFile(
+ scriptFileInfoPath: resolvedFilePath,
+ parsedScript: out PSScriptFileInfo _,
+ errors: out ErrorRecord[] errors,
+ out string[] verboseMsgs);
+
+ if (!isValidScript)
+ {
+ foreach (ErrorRecord error in errors)
+ {
+ WriteVerbose("The .ps1 script file passed in was not valid due to: " + error.Exception.Message);
+ }
+ }
+
+ foreach (string msg in verboseMsgs)
+ {
+ WriteVerbose(msg);
+ }
+
+ WriteObject(isValidScript);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/code/UpdatePSScriptFileInfo.cs b/src/code/UpdatePSScriptFileInfo.cs
new file mode 100644
index 000000000..8803873d7
--- /dev/null
+++ b/src/code/UpdatePSScriptFileInfo.cs
@@ -0,0 +1,319 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Management.Automation;
+using Microsoft.PowerShell.Commands;
+using Microsoft.PowerShell.PowerShellGet.UtilClasses;
+
+namespace Microsoft.PowerShell.PowerShellGet.Cmdlets
+{
+ ///
+ /// Updates a .ps1 file with specified properties.
+ ///
+ [Cmdlet(VerbsData.Update, "PSScriptFileInfo")]
+ public sealed class UpdatePSScriptFileInfo : PSCmdlet
+ {
+ #region Parameters
+
+ ///
+ /// The author of the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string Author { get; set; }
+
+ ///
+ /// The name of the company owning the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string CompanyName { get; set; }
+
+ ///
+ /// The copyright statement for the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string Copyright { get; set; }
+
+ ///
+ /// The description of the script.
+ ///
+ [Parameter()]
+ [ValidateNotNullOrEmpty()]
+ public string Description { get; set; }
+
+ ///
+ /// The list of external module dependencies taken by this script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string[] ExternalModuleDependencies { get; set; }
+
+ ///
+ /// The list of external script dependencies taken by this script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string[] ExternalScriptDependencies { get; set; }
+
+ ///
+ /// The unique identifier for the script. The GUID can be used to distinguish among scripts with the same name.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public Guid Guid { get; set; }
+
+ ///
+ /// The Uri for the icon associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string IconUri { get; set; }
+
+ ///
+ /// The Uri for the license associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string LicenseUri { get; set; }
+
+ ///
+ /// The path the .ps1 script info file will be created at.
+ ///
+ [Parameter(Position = 0, Mandatory = true)]
+ [ValidateNotNullOrEmpty]
+ public string FilePath { get; set; }
+
+ ///
+ /// The private data associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string PrivateData { get; set; }
+
+ ///
+ /// The Uri for the project associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string ProjectUri { get; set; }
+
+ ///
+ /// The release notes for the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string ReleaseNotes { get; set; }
+
+ ///
+ /// Remove signature from signed .ps1 (if present) thereby allowing update of script to happen
+ /// User should re-sign the updated script afterwards.
+ ///
+ [Parameter]
+ public SwitchParameter RemoveSignature { get; set; }
+
+ ///
+ /// The list of modules required by the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public Hashtable[] RequiredModules { get; set; }
+
+ ///
+ /// The list of scripts required by the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string[] RequiredScripts { get; set; }
+
+ ///
+ /// The tags associated with the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string[] Tags { get; set; }
+
+ ///
+ /// The version of the script.
+ ///
+ [Parameter]
+ [ValidateNotNullOrEmpty()]
+ public string Version { get; set; }
+
+ #endregion
+
+ #region Methods
+
+ protected override void EndProcessing()
+ {
+ Uri projectUri = null;
+ if (!String.IsNullOrEmpty(ProjectUri) && !Utils.TryCreateValidUri(uriString: ProjectUri,
+ cmdletPassedIn: this,
+ uriResult: out projectUri,
+ errorRecord: out ErrorRecord projectErrorRecord))
+ {
+ ThrowTerminatingError(projectErrorRecord);
+ }
+
+ Uri licenseUri = null;
+ if (!String.IsNullOrEmpty(LicenseUri) && !Utils.TryCreateValidUri(uriString: LicenseUri,
+ cmdletPassedIn: this,
+ uriResult: out licenseUri,
+ errorRecord: out ErrorRecord licenseErrorRecord))
+ {
+ ThrowTerminatingError(licenseErrorRecord);
+ }
+
+ Uri iconUri = null;
+ if (!String.IsNullOrEmpty(IconUri) && !Utils.TryCreateValidUri(uriString: IconUri,
+ cmdletPassedIn: this,
+ uriResult: out iconUri,
+ errorRecord: out ErrorRecord iconErrorRecord))
+ {
+ ThrowTerminatingError(iconErrorRecord);
+ }
+
+ if (!FilePath.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase))
+ {
+ var exMessage = "File path needs to end with a .ps1 extension. Example: C:/Users/john/x/MyScript.ps1";
+ var ex = new ArgumentException(exMessage);
+ var InvalidOrNonExistantPathError = new ErrorRecord(ex, "InvalidOrNonExistantPath", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(InvalidOrNonExistantPathError);
+ }
+
+ var resolvedPaths = SessionState.Path.GetResolvedPSPathFromPSPath(FilePath);
+ if (resolvedPaths.Count != 1)
+ {
+ var exMessage = "Error: Could not resolve provided Path argument into a single path.";
+ var ex = new PSArgumentException(exMessage);
+ var InvalidPathArgumentError = new ErrorRecord(ex, "InvalidPathArgumentError", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(InvalidPathArgumentError);
+ }
+
+ string resolvedFilePath = resolvedPaths[0].Path;
+
+ if (!File.Exists(resolvedFilePath))
+ {
+ var exMessage = "A script file does not exist at the location specified";
+ var ex = new ArgumentException(exMessage);
+ var FileDoesNotExistError = new ErrorRecord(ex, "FileDoesNotExistAtPath", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(FileDoesNotExistError);
+ }
+
+ ModuleSpecification[] validatedRequiredModuleSpecifications = Array.Empty();
+ if (RequiredModules != null && RequiredModules.Length > 0)
+ {
+ if (!Utils.TryCreateModuleSpecification(
+ moduleSpecHashtables: RequiredModules,
+ out validatedRequiredModuleSpecifications,
+ out ErrorRecord[] moduleSpecErrors))
+ {
+ foreach (ErrorRecord err in moduleSpecErrors)
+ {
+ WriteError(err);
+ }
+
+ return;
+ }
+ }
+
+ if (!PSScriptFileInfo.TryTestPSScriptFile(
+ scriptFileInfoPath: resolvedFilePath,
+ parsedScript: out PSScriptFileInfo parsedScriptInfo,
+ errors: out ErrorRecord[] errors,
+ out string[] verboseMsgs))
+ {
+ foreach (string msg in verboseMsgs)
+ {
+ WriteVerbose(msg);
+ }
+
+ foreach (ErrorRecord error in errors)
+ {
+ WriteError(error);
+ }
+
+ return;
+ }
+
+ bool signatureRemoved = false;
+ if (parsedScriptInfo.ScriptContent.ContainsSignature)
+ {
+ if (!RemoveSignature)
+ {
+ var exMessage = "Cannot update the script file because the file contains a signature block and updating will invalidate the signature. Use -RemoveSignature to remove the signature block, and then re-sign the file after it is updated.";
+ var ex = new PSInvalidOperationException(exMessage);
+ var ScriptToBeUpdatedContainsSignatureError = new ErrorRecord(ex, "ScriptToBeUpdatedContainsSignature", ErrorCategory.InvalidOperation, null);
+ ThrowTerminatingError(ScriptToBeUpdatedContainsSignatureError);
+ }
+
+ signatureRemoved = true;
+ }
+
+ if (!PSScriptFileInfo.TryUpdateScriptFileContents(
+ scriptInfo: parsedScriptInfo,
+ updatedPSScriptFileContents: out string[] updatedPSScriptFileContents,
+ errors: out ErrorRecord[] updateErrors,
+ version: Version,
+ guid: Guid,
+ author: Author,
+ companyName: CompanyName,
+ copyright: Copyright,
+ tags: Tags,
+ licenseUri: licenseUri,
+ projectUri: projectUri,
+ iconUri: iconUri,
+ requiredModules: validatedRequiredModuleSpecifications,
+ externalModuleDependencies: ExternalModuleDependencies,
+ requiredScripts: RequiredScripts,
+ externalScriptDependencies: ExternalScriptDependencies,
+ releaseNotes: ReleaseNotes,
+ privateData: PrivateData,
+ description: Description))
+ {
+ WriteWarning("Updating the specified script file failed due to the following error(s):");
+ foreach (ErrorRecord error in updateErrors)
+ {
+ WriteError(error);
+ }
+
+ return;
+ }
+
+ string tempScriptFilePath = null;
+ try
+ {
+ tempScriptFilePath = Path.GetTempFileName();
+
+ File.WriteAllLines(tempScriptFilePath, updatedPSScriptFileContents);
+ File.Copy(tempScriptFilePath, resolvedFilePath, overwrite: true);
+ }
+ catch(Exception e)
+ {
+ WriteError(new ErrorRecord(
+ new PSInvalidOperationException($"Could not update .ps1 file due to: {e.Message}"),
+ "FileIOErrorDuringUpdate",
+ ErrorCategory.InvalidArgument,
+ this));
+ }
+ finally
+ {
+ if (tempScriptFilePath != null)
+ {
+ File.Delete(tempScriptFilePath);
+ }
+ }
+
+ if (signatureRemoved)
+ {
+ WriteWarning("Re-sign this script, as the original signature was removed during update.");
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/code/Utils.cs b/src/code/Utils.cs
index f19861847..977d0be8a 100644
--- a/src/code/Utils.cs
+++ b/src/code/Utils.cs
@@ -16,6 +16,7 @@
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography.X509Certificates;
+using Microsoft.PowerShell.Commands;
namespace Microsoft.PowerShell.PowerShellGet.UtilClasses
{
@@ -97,6 +98,17 @@ public static string QuoteName(string name)
return "'" + CodeGeneration.EscapeSingleQuotedStringContent(name) + "'";
}
+ public static string[] GetStringArrayFromString(string[] delimeter, string stringToConvertToArray)
+ {
+ // This will be a string where entries are separated by space.
+ if (String.IsNullOrEmpty(stringToConvertToArray))
+ {
+ return Utils.EmptyStrArray;
+ }
+
+ return stringToConvertToArray.Split(delimeter, StringSplitOptions.RemoveEmptyEntries);
+ }
+
///
/// Converts an ArrayList of object types to a string array.
///
@@ -914,6 +926,185 @@ public static Hashtable ConvertJsonToHashtable(
return (results.Count == 1 && results[0] != null) ? (Hashtable)results[0].BaseObject : null;
}
+ public static bool TryCreateModuleSpecification(
+ Hashtable[] moduleSpecHashtables,
+ out ModuleSpecification[] validatedModuleSpecs,
+ out ErrorRecord[] errors)
+ {
+ bool moduleSpecCreatedSuccessfully = true;
+ List errorList = new List();
+ validatedModuleSpecs = Array.Empty();
+ List moduleSpecsList = new List();
+
+ foreach(Hashtable moduleSpec in moduleSpecHashtables)
+ {
+ // ModuleSpecification(string) constructor for creating a ModuleSpecification when only ModuleName is provided.
+ if (!moduleSpec.ContainsKey("ModuleName") || String.IsNullOrEmpty((string) moduleSpec["ModuleName"]))
+ {
+ var exMessage = $"RequiredModules Hashtable entry {moduleSpec.ToString()} is missing a key 'ModuleName' and associated value, which is required for each module specification entry";
+ var ex = new ArgumentException(exMessage);
+ var NameMissingModuleSpecError = new ErrorRecord(ex, "NameMissingInModuleSpecification", ErrorCategory.InvalidArgument, null);
+ errorList.Add(NameMissingModuleSpecError);
+ moduleSpecCreatedSuccessfully = false;
+ continue;
+ }
+
+ // At this point it must contain ModuleName key.
+ string moduleSpecName = (string) moduleSpec["ModuleName"];
+ ModuleSpecification currentModuleSpec = null;
+ if (!moduleSpec.ContainsKey("MaximumVersion") && !moduleSpec.ContainsKey("ModuleVersion") && !moduleSpec.ContainsKey("RequiredVersion"))
+ {
+ // Pass to ModuleSpecification(string) constructor.
+ // This constructor method would only throw for a null/empty string, which we've already validated against above.
+ currentModuleSpec = new ModuleSpecification(moduleSpecName);
+
+ if (currentModuleSpec != null)
+ {
+ moduleSpecsList.Add(currentModuleSpec);
+ }
+ else
+ {
+ var exMessage = $"ModuleSpecification object was not able to be created for {moduleSpecName}";
+ var ex = new ArgumentException(exMessage);
+ var ModuleSpecNotCreatedError = new ErrorRecord(ex, "ModuleSpecificationNotCreated", ErrorCategory.InvalidArgument, null);
+ errorList.Add(ModuleSpecNotCreatedError);
+ moduleSpecCreatedSuccessfully = false;
+ continue;
+ }
+ }
+ else
+ {
+ // ModuleSpecification(Hashtable) constructor for when ModuleName + {Required,Maximum,Module}Version value is also provided.
+ string moduleSpecMaxVersion = moduleSpec.ContainsKey("MaximumVersion") ? (string) moduleSpec["MaximumVersion"] : String.Empty;
+ string moduleSpecModuleVersion = moduleSpec.ContainsKey("ModuleVersion") ? (string) moduleSpec["ModuleVersion"] : String.Empty;
+ string moduleSpecRequiredVersion = moduleSpec.ContainsKey("RequiredVersion") ? (string) moduleSpec["RequiredVersion"] : String.Empty;
+ Guid moduleSpecGuid = moduleSpec.ContainsKey("Guid") ? (Guid) moduleSpec["Guid"] : Guid.Empty;
+
+ if (String.IsNullOrEmpty(moduleSpecMaxVersion) && String.IsNullOrEmpty(moduleSpecModuleVersion) && String.IsNullOrEmpty(moduleSpecRequiredVersion))
+ {
+ var exMessage = $"ModuleSpecification hashtable requires one of the following keys: MaximumVersion, ModuleVersion, RequiredVersion and failed to be created for {moduleSpecName}";
+ var ex = new ArgumentException(exMessage);
+ var MissingModuleSpecificationMemberError = new ErrorRecord(ex, "MissingModuleSpecificationMember", ErrorCategory.InvalidArgument, null);
+ errorList.Add(MissingModuleSpecificationMemberError);
+ moduleSpecCreatedSuccessfully = false;
+ continue;
+ }
+
+ Hashtable moduleSpecHash = new Hashtable();
+
+ moduleSpecHash.Add("ModuleName", moduleSpecName);
+ if (moduleSpecGuid != Guid.Empty)
+ {
+ moduleSpecHash.Add("Guid", moduleSpecGuid);
+ }
+
+ if (!String.IsNullOrEmpty(moduleSpecMaxVersion))
+ {
+ moduleSpecHash.Add("MaximumVersion", moduleSpecMaxVersion);
+ }
+
+ if (!String.IsNullOrEmpty(moduleSpecModuleVersion))
+ {
+ moduleSpecHash.Add("ModuleVersion", moduleSpecModuleVersion);
+ }
+
+ if (!String.IsNullOrEmpty(moduleSpecRequiredVersion))
+ {
+ moduleSpecHash.Add("RequiredVersion", moduleSpecRequiredVersion);
+ }
+
+ try
+ {
+ currentModuleSpec = new ModuleSpecification(moduleSpecHash);
+ }
+ catch (Exception e)
+ {
+ var ex = new ArgumentException($"ModuleSpecification instance was not able to be created with hashtable constructor due to: {e.Message}");
+ var ModuleSpecNotCreatedError = new ErrorRecord(ex, "ModuleSpecificationNotCreated", ErrorCategory.InvalidArgument, null);
+ errorList.Add(ModuleSpecNotCreatedError);
+ moduleSpecCreatedSuccessfully = false;
+ }
+
+ if (currentModuleSpec != null)
+ {
+ moduleSpecsList.Add(currentModuleSpec);
+ }
+ }
+ }
+
+ errors = errorList.ToArray();
+ validatedModuleSpecs = moduleSpecsList.ToArray();
+ return moduleSpecCreatedSuccessfully;
+ }
+
+ ///
+ /// Parses metadata out of a comment block's lines (which are passed in) into a hashtable.
+ ///
+ public static Hashtable ParseCommentBlockContent(string[] commentLines)
+ {
+ /**
+ Comment lines can look like this:
+
+ .KEY1 value
+
+ .KEY2 value
+
+ .KEY3
+ value
+
+ .KEY4 value
+ value continued
+
+ */
+
+ Hashtable parsedHelpMetadata = new Hashtable();
+ string keyName = "";
+ string value = "";
+
+ for (int i = 1; i < commentLines.Count(); i++)
+ {
+ string line = commentLines[i];
+
+ // scenario where line is: .KEY VALUE
+ // this line contains a new metadata property.
+ if (line.Trim().StartsWith("."))
+ {
+ // check if keyName was previously populated, if so add this key value pair to the metadata hashtable
+ if (!String.IsNullOrEmpty(keyName))
+ {
+ parsedHelpMetadata.Add(keyName, value);
+ }
+
+ string[] parts = line.Trim().TrimStart('.').Split();
+ keyName = parts[0];
+ value = parts.Count() > 1 ? String.Join(" ", parts.Skip(1)) : String.Empty;
+ }
+ else if (!String.IsNullOrEmpty(line))
+ {
+ // scenario where line contains text that is a continuation of value from previously recorded key
+ // this line does not starting with .KEY, and is also not an empty line.
+ if (value.Equals(String.Empty))
+ {
+ value += line;
+ }
+ else
+ {
+ value += Environment.NewLine + line;
+ }
+ }
+ }
+
+ // this is the case where last key value had multi-line value.
+ // and we've captured it, but still need to add it to hashtable.
+ if (!String.IsNullOrEmpty(keyName) && !parsedHelpMetadata.ContainsKey(keyName))
+ {
+ // only add this key value if it hasn't already been added
+ parsedHelpMetadata.Add(keyName, value);
+ }
+
+ return parsedHelpMetadata;
+ }
+
#endregion
#region Directory and File
diff --git a/test/NewPSScriptFileInfo.Tests.ps1 b/test/NewPSScriptFileInfo.Tests.ps1
index 3ba34acf3..4b48ccd57 100644
--- a/test/NewPSScriptFileInfo.Tests.ps1
+++ b/test/NewPSScriptFileInfo.Tests.ps1
@@ -5,108 +5,107 @@ Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
Describe "Test New-PSScriptFileInfo" {
BeforeAll {
- $script:TempPath = Get-TempPath
+ $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1"
+ $tmpDirPaths = @($tmpDir1Path)
+ Get-NewTestDirs($tmpDirPaths)
}
BeforeEach {
- # Create temp script path
- $script:TempScriptPath = Join-Path $script:TempPath "PSGet_$(Get-Random)"
- $null = New-Item -Path $script:TempScriptPath -ItemType Directory -Force
-
- $script:PSScriptInfoName = "PSGetTestScript"
- $script:testPSScriptInfoPath = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath "$script:PSScriptInfoName.psd1"
+ $script:PSScriptInfoName = "test_script"
+ $script:testScriptFilePath = Join-Path -Path $tmpDir1Path -ChildPath "$script:PSScriptInfoName.ps1"
}
AfterEach {
- RemoveItem "$script:TempScriptPath"
+ if (Test-Path -Path $script:testScriptFilePath)
+ {
+ Remove-Item $script:testScriptFilePath
+ }
}
- ### TODO: Add tests for -Force and -WhatIf if those parameters are applicable
-<#
It "Create .ps1 file with minimal required fields" {
- $Description = "this is a test script"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $Description
-
- Test-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath | Should -BeTrue
+ $description = "Test description"
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -Description $description
+ Test-PSScriptFileInfo -FilePath $script:testScriptFilePath | Should -BeTrue
}
It "Create .ps1 file with relative path" {
- $RelativeCurrentPath = Get-Location
- $ScriptFilePath = Join-Path -Path $relativeCurrentPath -ChildPath "$script:PSScriptInfoName.ps1"
- $Description = "this is a test script"
- New-PSScriptFileInfo -FilePath $ScriptFilePath -Description $Description
+ $relativeCurrentPath = Get-Location
+ $scriptFilePath = Join-Path -Path $relativeCurrentPath -ChildPath "$script:PSScriptInfoName.ps1"
+
+ $description = "Test description"
+ New-PSScriptFileInfo -FilePath $scriptFilePath -Description $description
- Test-PSScriptFileInfo -FilePath $ScriptFilePath | Should -BeTrue
- Remove-Item -Path $ScriptFilePath
+ Test-PSScriptFileInfo -FilePath $scriptFilePath | Should -BeTrue
+ Remove-Item -Path $scriptFilePath
}
It "Create new .ps1 given Version parameter" {
- $Version = "2.0.0.0"
- $Description = "Test description"
+ $version = "2.0.0.0"
+ $description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Version $Version -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -Version $version -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($Version) | Should -BeTrue
- $results.Contains(".VERSION $Version") | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($version) | Should -BeTrue
+ $results.Contains(".VERSION $version") | Should -BeTrue
}
It "Create new .ps1 given Guid parameter" {
- $Guid = [guid]::NewGuid()
- $Description = "Test description"
+ $guid = [guid]::NewGuid()
+ $description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Guid $Guid -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -Guid $guid -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($Guid) | Should -BeTrue
- $results.Contains(".GUID $Guid") | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($guid) | Should -BeTrue
+ $results.Contains(".GUID $guid") | Should -BeTrue
}
It "Create new .ps1 given Author parameter" {
- $Author = "Test Author"
- $Description = "Test description"
+ $author = "Test Author"
+ $description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Author $Author -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -Author $author -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($Author) | Should -BeTrue
- $results.Contains(".AUTHOR $Author") | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($author) | Should -BeTrue
+ $results.Contains(".AUTHOR $author") | Should -BeTrue
}
It "Create new .ps1 given Description parameter" {
- $Description = "PowerShellGet test description"
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $Description
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($Description) | Should -BeTrue
- $results -like ".DESCRIPTION*$Description" | Should -BeTrue
+ $description = "PowerShellGet test description"
+
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -Description $description
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($description) | Should -BeTrue
+ $results -like "*.DESCRIPTION$script:newline*$description*" | Should -BeTrue
}
It "Create new .ps1 given CompanyName parameter" {
- $CompanyName = "Microsoft"
- $Description = "Test description"
+ $companyName = "Microsoft"
+ $description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -CompanyName $CompanyName -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -CompanyName $companyName -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($CompanyName) | Should -BeTrue
- $results.Contains(".COMPANYNAME $Companyname") | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($companyName) | Should -BeTrue
+ $results.Contains(".COMPANYNAME $companyname") | Should -BeTrue
}
It "Create new .ps1 given Copyright parameter" {
- $Copyright = "(c) Test Corporation"
- $Description = "Test description"
+ $copyright = "(c) Test Corporation"
+ $description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Copyright $Copyright -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -Copyright $copyright -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($Copyright) | Should -BeTrue
- $results.Contains(".COPYRIGHT $Copyright") | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($copyright) | Should -BeTrue
+ $results.Contains(".COPYRIGHT $copyright") | Should -BeTrue
}
It "Create new .ps1 given RequiredModules parameter" {
@@ -114,186 +113,129 @@ Describe "Test New-PSScriptFileInfo" {
$requiredModuleVersion = '1.0.0.0'
$RequiredModules = @(@{ModuleName = $requiredModuleName; ModuleVersion = $requiredModuleVersion })
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredModules $RequiredModules -Description $Description
+ $description = "Test description"
+
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -RequiredModules $RequiredModules -Description $Description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
$results.Contains($requiredModuleName) | Should -BeTrue
$results.Contains($requiredModuleVersion) | Should -BeTrue
- $results -like ".REQUIREDMODULES*$requiredModuleName*$requiredModuleVersion" | Should -BeTrue
+ $results -like "*#Requires*$requiredModuleName*$requiredModuleVersion*" | Should -BeTrue
}
It "Create new .ps1 given ReleaseNotes parameter" {
- $Description = "Test Description"
- $ReleaseNotes = "Release notes for script."
+ $description = "Test Description"
+ $releaseNotes = "Release notes for script."
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ReleaseNotes $ReleaseNotes -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -ReleaseNotes $releaseNotes -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($ReleaseNotes) | Should -BeTrue
- $results -like ".RELEASENOTES*$ReleaseNotes" | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($releaseNotes) | Should -BeTrue
+ $results -like "*.RELEASENOTES$script:newline*$ReleaseNotes*" | Should -BeTrue
}
It "Create new .ps1 given Tags parameter" {
- $Description = "Test Description"
- $Tag1 = "tag1"
- $Tag2 = "tag2"
+ $description = "Test Description"
+ $tag1 = "tag1"
+ $tag2 = "tag2"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Tags $Tag1, $Tag2 -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -Tags $tag1, $tag2 -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($Tag1) | Should -BeTrue
- $results.Contains($Tag2) | Should -BeTrue
- $results.Contains(".TAGS $Tag1 $Tag2") | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($tag1) | Should -BeTrue
+ $results.Contains($tag2) | Should -BeTrue
+ $results.Contains(".TAGS $tag1 $tag2") | Should -BeTrue
}
It "Create new .ps1 given ProjectUri parameter" {
- $Description = "Test Description"
- $ProjectUri = "https://www.testprojecturi.com/"
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ProjectUri $ProjectUri -Description $Description
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($ProjectUri) | Should -BeTrue
- $results.Contains(".PROJECTURI $ProjectUri") | Should -BeTrue
+ $description = "Test Description"
+ $projectUri = "https://www.testprojecturi.com/"
+
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -ProjectUri $projectUri -Description $description
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($projectUri) | Should -BeTrue
+ $results.Contains(".PROJECTURI $projectUri") | Should -BeTrue
}
It "Create new .ps1 given LicenseUri parameter" {
- $Description = "Test Description"
- $LicenseUri = "https://www.testlicenseuri.com/"
+ $description = "Test Description"
+ $licenseUri = "https://www.testlicenseuri.com/"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -LicenseUri $LicenseUri -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -LicenseUri $licenseUri -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($LicenseUri) | Should -BeTrue
- $results.Contains(".LICENSEURI $LicenseUri") | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($licenseUri) | Should -BeTrue
+ $results.Contains(".LICENSEURI $licenseUri") | Should -BeTrue
}
It "Create new .ps1 given IconUri parameter" {
- $Description = "Test Description"
- $IconUri = "https://www.testiconuri.com/"
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -IconUri $IconUri -Description $Description
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($IconUri) | Should -BeTrue
- $results.Contains(".ICONURI $IconUri") | Should -BeTrue
- }
+ $description = "Test Description"
+ $iconUri = "https://www.testiconuri.com/"
- It "Create new .ps1 given ExternalModuleDependencies parameter" {
- $Description = "Test Description"
- $ExternalModuleDep1 = "ExternalModuleDep1"
- $ExternalModuleDep2 = "ExternalModuleDep2"
- $ExternalModuleDep1FileName = "ExternalModuleDep1.psm1"
- $ExternalModuleDep2FileName = "ExternalModuleDep2.psm1"
- $ExternalModuleDepPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $ExternalModuleDep1FileName
- $ExternalModuleDepPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $ExternalModuleDep2FileName
-
- $null = New-Item -Path $ExternalModuleDepPath1 -ItemType File -Force
- $null = New-Item -Path $ExternalModuleDepPath2 -ItemType File -Force
-
- # NOTE: you may need to add the -NestedModules parameter here as well
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ExternalModuleDependencies $ExternalModuleDep1, $ExternalModuleDep2 -Description $Description
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($ExternalModuleDep1) | Should -BeTrue
- $results.Contains($ExternalModuleDep2) | Should -BeTrue
- $results -like ".EXTERNALMODULEDEPENDENCIES*$ExternalModuleDep1*$ExternalModuleDep2" | Should -BeTrue
- }
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -IconUri $iconUri -Description $description
- It "Create new .ps1 given RequiredAssemblies parameter" {
- $Description = "Test Description"
- $RequiredAssembly1 = "RequiredAssembly1.dll"
- $RequiredAssembly2 = "RequiredAssembly2.dll"
- $RequiredAssemblyPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $RequiredAssembly1
- $RequiredAssemblyPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $RequiredAssembly2
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($iconUri) | Should -BeTrue
+ $results.Contains(".ICONURI $iconUri") | Should -BeTrue
+ }
- $null = New-Item -Path $RequiredAssemblyPath1 -ItemType File -Force
- $null = New-Item -Path $RequiredAssemblyPath2 -ItemType File -Force
+ It "Create new .ps1 given ExternalModuleDependencies parameter" {
+ $description = "Test Description"
+ $externalModuleDep1 = "ExternalModuleDep1"
+ $externalModuleDep2 = "ExternalModuleDep2"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredAssemblies $RequiredAssembly1, $RequiredAssembly2 -Description $Description
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -ExternalModuleDependencies $externalModuleDep1, $externalModuleDep2 -Description $description
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($RequiredAssembly1) | Should -BeTrue
- $results.Contains($RequiredAssembly2) | Should -BeTrue
- $results -like ".REQUIREDASSEMBLIES*$RequiredAssembly1*$RequiredAssembly2" | Should -BeTrue
- }
-
- It "Create new .ps1 given NestedModules parameter" {
- $Description = "Test Description"
- $NestedModule1 = "NestedModule1"
- $NestedModule2 = "NestedModule2"
- $NestModuleFileName1 = "NestedModule1.dll"
- $NestModuleFileName2 = "NestedModule2.dll"
- $NestedModulePath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NestModuleFileName1
- $NestedModulePath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NestModuleFileName2
-
- $null = New-Item -Path $NestedModulePath1 -ItemType File -Force
- $null = New-Item -Path $NestedModulePath2 -ItemType File -Force
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -NestedModules $NestedModule1, $NestedModule2 -Description $Description
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NestedModule1) | Should -BeTrue
- $results.Contains($NestedModule2) | Should -BeTrue
- $results -like ".NESTEDMODULES*$NestedModule1*$NestedModule2" | Should -BeTrue
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($externalModuleDep1) | Should -BeTrue
+ $results.Contains($externalModuleDep2) | Should -BeTrue
+ $results -like "*.EXTERNALMODULEDEPENDENCIES*$externalModuleDep1*$externalModuleDep2*" | Should -BeTrue
}
It "Create new .ps1 given RequiredScripts parameter" {
- $Description = "Test Description"
- $RequiredScript1 = "NestedModule1.ps1"
- $RequiredScript2 = "NestedModule2.ps1"
- $RequiredScript1Path = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $RequiredScript1
- $RequiredScript2Path = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $RequiredScript2
-
- $null = New-Item -Path $RequiredScript1Path -ItemType File -Force
- $null = New-Item -Path $RequiredScript2Path -ItemType File -Force
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredScripts $RequiredScript1, $RequiredScript2
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($RequiredScript1) | Should -BeTrue
- $results.Contains($RequiredScript2) | Should -BeTrue
- $results -like ".REQUIREDSCRIPTS*$RequiredScript1*$RequiredScript2" | Should -BeTrue
+ $description = "Test Description"
+ $requiredScript1 = "RequiredScript1"
+ $requiredScript2 = "RequiredScript2"
+
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -RequiredScripts $requiredScript1, $requiredScript2 -Description $description
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($requiredScript1) | Should -BeTrue
+ $results.Contains($requiredScript2) | Should -BeTrue
+ $results -like "*.REQUIREDSCRIPTS*$requiredScript1*$requiredScript2*" | Should -BeTrue
}
It "Create new .ps1 given ExternalScriptDependencies parameter" {
- $Description = "Test Description"
- $ExternalScriptDep1 = "ExternalScriptDep1.ps1"
- $ExternalScriptDep2 = "ExternalScriptDep2.ps1"
- $ExternalScriptDepPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $ExternalScriptDep1
- $ExternalScriptDepPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $ExternalScriptDep2
-
- $null = New-Item -Path $ExternalScriptDepPath1 -ItemType File -Force
- $null = New-Item -Path $ExternalScriptDepPath2 -ItemType File -Force
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ExternalModuleDependencies $ExternalModuleDep1, $ExternalModuleDep2 -Description $Description
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($ExternalModuleDep1) | Should -BeTrue
- $results.Contains($ExternalModuleDep2) | Should -BeTrue
- $results -like ".EXTERNALSCRIPTDEPENDENCIES*$ExternalScriptDep1*$ExternalScriptDep2" | Should -BeTrue
+ $description = "Test Description"
+ $externalScriptDep1 = "ExternalScriptDep1"
+ $externalScriptDep2 = "ExternalScriptDep2"
+
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -ExternalScriptDependencies $externalScriptDep1, $externalScriptDep2 -Description $description
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($externalScriptDep1) | Should -BeTrue
+ $results.Contains($externalScriptDep2) | Should -BeTrue
+ $results -like "*.EXTERNALSCRIPTDEPENDENCIES*$externalScriptDep1*$externalScriptDep2*" | Should -BeTrue
}
It "Create new .ps1 given PrivateData parameter" {
- $Description = "Test Description"
- $PrivateData = @{"PrivateDataEntry1" = "PrivateDataValue1"}
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -PrivateData $PrivateData -Description $Description
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($PrivateData) | Should -BeTrue
- $results -like ".PRIVATEDATA*$PrivateData" | Should -BeTrue
+ $description = "Test Description"
+ $privateData = @{"PrivateDataEntry1" = "PrivateDataValue1"}
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -PrivateData $privateData -Description $description
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($privateData) | Should -BeTrue
+ $results -like "*.PRIVATEDATA*$privateData*" | Should -BeTrue
}
-#>
-}
\ No newline at end of file
+}
diff --git a/test/TestPSScriptFileInfo.Tests.ps1 b/test/TestPSScriptFileInfo.Tests.ps1
new file mode 100644
index 000000000..30adae47b
--- /dev/null
+++ b/test/TestPSScriptFileInfo.Tests.ps1
@@ -0,0 +1,60 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
+
+Describe "Test Test-PSScriptFileInfo" {
+ BeforeAll {
+ $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1"
+ $tmpDirPaths = @($tmpDir1Path)
+ Get-NewTestDirs($tmpDirPaths)
+
+ # Path to folder, within our test folder, where we store invalid module and script files used for testing
+ $script:testFilesFolderPath = Join-Path $psscriptroot -ChildPath "testFiles"
+
+ # Path to specifically to that invalid test scripts folder
+ $script:testScriptsFolderPath = Join-Path $testFilesFolderPath -ChildPath "testScripts"
+ }
+
+ It "determine script file with minimal required fields as valid" {
+ $scriptFilePath = Join-Path -Path $tmpDir1Path -ChildPath "testscript.ps1"
+ $scriptDescription = "this is a test script"
+ New-PSScriptFileInfo -FilePath $scriptFilePath -Description $scriptDescription
+ Test-PSScriptFileInfo $scriptFilePath | Should -Be $true
+ }
+
+ It "not determine script file with Author field missing as valid" {
+ $scriptName = "InvalidScriptMissingAuthor.ps1"
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+
+ Test-PSScriptFileInfo $scriptFilePath | Should -Be $false
+ }
+
+ It "not determine script file with Description field missing as valid" {
+ $scriptName = "InvalidScriptMissingDescription.ps1"
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+
+ Test-PSScriptFileInfo $scriptFilePath | Should -Be $false
+ }
+
+ It "not determine script that is missing Description block altogether as valid" {
+ $scriptName = "InvalidScriptMissingDescriptionCommentBlock.ps1"
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+
+ Test-PSScriptFileInfo $scriptFilePath | Should -Be $false
+ }
+
+ It "not determine script file Guid as valid" {
+ $scriptName = "InvalidScriptMissingGuid.ps1"
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+
+ Test-PSScriptFileInfo $scriptFilePath | Should -Be $false
+ }
+
+ It "not determine script file missing Version as valid" {
+ $scriptName = "InvalidScriptMissingVersion.ps1"
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+
+ Test-PSScriptFileInfo $scriptFilePath | Should -Be $false
+ }
+}
diff --git a/test/TestPSScriptInfo.Tests.ps1 b/test/TestPSScriptInfo.Tests.ps1
deleted file mode 100644
index fa79f8bf9..000000000
--- a/test/TestPSScriptInfo.Tests.ps1
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (c) Microsoft Corporation.
-# Licensed under the MIT License.
-
-Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
-
-Describe "Test Test-PSScriptFileInfo" {
-
- BeforeAll {
- $script:TempPath = Get-TempPath
- }
- BeforeEach {
- # Create temp script path
- $script:TempScriptPath = Join-Path $script:TempPath "PSGet_$(Get-Random)"
- $null = New-Item -Path $script:TempScriptPath -ItemType Directory -Force
-
- $script:PSScriptInfoName = "PSGetTestScript"
- $script:testPSScriptInfoPath = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath "$script:PSScriptInfoName.psd1"
- }
- AfterEach {
- RemoveItem "$script:TempScriptPath"
- }
-
- ### TODO: Add tests for -Force and -WhatIf if those parameters are applicable
- <#
- It "Test .ps1 file with minimal required fields" {
- $Description = "This is a test script"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $Description
-
- Test-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- }
-
- It "Test .ps1 file with relative path" {
- $RelativeCurrentPath = Get-Location
- $ScriptFilePath = Join-Path -Path $relativeCurrentPath -ChildPath "$script:PSScriptInfoName.ps1"
- $Description = "this is a test script"
- New-PSScriptFileInfo -FilePath $ScriptFilePath -Description $Description
-
- Test-PSScriptFileInfo -FilePath $ScriptFilePath | Should -BeTrue
- Remove-Item -Path $ScriptFilePath
- }
- #>
-}
diff --git a/test/UpdatePSScriptFileInfo.Tests.ps1 b/test/UpdatePSScriptFileInfo.Tests.ps1
new file mode 100644
index 000000000..6eac2d1d0
--- /dev/null
+++ b/test/UpdatePSScriptFileInfo.Tests.ps1
@@ -0,0 +1,314 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
+
+Describe "Test Update-PSScriptFileInfo" {
+ BeforeAll {
+ $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1"
+ $tmpDirPaths = @($tmpDir1Path)
+ Get-NewTestDirs($tmpDirPaths)
+
+ # Path to folder, within our test folder, where we store invalid module and script files used for testing
+ $script:testFilesFolderPath = Join-Path $psscriptroot -ChildPath "testFiles"
+
+ # Path to specifically to that invalid test scripts folder
+ $script:testScriptsFolderPath = Join-Path $testFilesFolderPath -ChildPath "testScripts"
+
+ $script:newline = [System.Environment]::NewLine;
+ }
+
+ BeforeEach {
+ $script:psScriptInfoName = "test_script"
+ $scriptDescription = "this is a test script"
+ $script:testScriptFilePath = Join-Path -Path $tmpDir1Path -ChildPath "$script:psScriptInfoName.ps1"
+ New-PSScriptFileInfo -FilePath $script:testScriptFilePath -Description $scriptDescription
+ }
+
+ AfterEach {
+ if (Test-Path -Path $script:testScriptFilePath)
+ {
+ Remove-Item $script:testScriptFilePath
+ }
+ }
+
+ It "Update .ps1 file with relative path" {
+ $relativeCurrentPath = Get-Location
+ $scriptFilePath = Join-Path -Path $relativeCurrentPath -ChildPath "$script:psScriptInfoName.ps1"
+ $oldDescription = "Old description for test script"
+ $newDescription = "New description for test script"
+ New-PSScriptFileInfo -FilePath $scriptFilePath -Description $oldDescription
+
+ Update-PSScriptFileInfo -FilePath $scriptFilePath -Description $newDescription
+ Test-PSScriptFileInfo -FilePath $scriptFilePath | Should -BeTrue
+
+ Test-Path -Path $scriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $scriptFilePath -Raw
+ $results.Contains($newDescription) | Should -BeTrue
+ $results -like "*.DESCRIPTION$script:newline*$newDescription*" | Should -BeTrue
+
+ Remove-Item -Path $scriptFilePath -Force
+ }
+
+ It "Update script should not overwrite old script data unless that property is specified" {
+ $description = "Test Description"
+ $version = "3.0.0"
+ $author = "John Doe"
+ $newAuthor = "Jane Doe"
+ $projectUri = "https://testscript.com/"
+
+ $relativeCurrentPath = Get-Location
+ $scriptFilePath = Join-Path -Path $relativeCurrentPath -ChildPath "$script:psScriptInfoName.ps1"
+
+ New-PSScriptFileInfo -FilePath $scriptFilePath -Description $description -Version $version -Author $author -ProjectUri $projectUri
+ Update-PSScriptFileInfo -FilePath $scriptFilePath -Author $newAuthor
+
+ Test-PSScriptFileInfo -FilePath $scriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $scriptFilePath -Raw
+ $results.Contains($newAuthor) | Should -BeTrue
+ $results.Contains(".AUTHOR $newAuthor") | Should -BeTrue
+
+ # rest should be original data used when creating the script
+ $results.Contains($projectUri) | Should -BeTrue
+ $results.Contains(".PROJECTURI $projectUri") | Should -BeTrue
+
+ $results.Contains($version) | Should -BeTrue
+ $results.Contains(".VERSION $version") | Should -BeTrue
+
+ $results.Contains($description) | Should -BeTrue
+ $results -like "*.DESCRIPTION$script:newline*$description*" | Should -BeTrue
+
+ Remove-Item -Path $scriptFilePath -Force
+ }
+
+ It "update script file Author property" {
+ $author = "New Author"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -Author $author
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($author) | Should -BeTrue
+ $results.Contains(".AUTHOR $author") | Should -BeTrue
+ }
+
+ It "update script file Version property" {
+ $version = "2.0.0.0"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -Version $version
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($version) | Should -BeTrue
+ $results.Contains(".VERSION $version") | Should -BeTrue
+ }
+
+ It "update script file Version property with prerelease version" {
+ $version = "3.0.0-alpha"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -Version $version
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($version) | Should -BeTrue
+ $results.Contains(".VERSION $version") | Should -BeTrue
+ }
+
+ It "not update script file with invalid version" {
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -Version "4.0.0.0.0" -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "VersionParseIntoNuGetVersion,Microsoft.PowerShell.PowerShellGet.Cmdlets.UpdatePSScriptFileInfo"
+ }
+
+ It "update script file Description property" {
+ $description = "this is an updated test script"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -Description $description
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($description) | Should -BeTrue
+ $results -like "*.DESCRIPTION$script:newline*$description*" | Should -BeTrue
+ }
+
+ It "update script file Guid property" {
+ $guid = [Guid]::NewGuid();
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -Guid $guid
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($guid) | Should -BeTrue
+ $results.Contains(".GUID $guid") | Should -BeTrue
+ }
+
+ It "update script file CompanyName property" {
+ $companyName = "New Corporation"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -CompanyName $companyName
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($companyName) | Should -BeTrue
+ $results.Contains(".COMPANYNAME $companyName") | Should -BeTrue
+ }
+
+ It "update script file Copyright property" {
+ $copyright = "(c) 2022 New Corporation. All rights reserved"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -Copyright $copyright
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($copyright) | Should -BeTrue
+ $results.Contains(".COPYRIGHT $copyright") | Should -BeTrue
+ }
+
+ It "update script file ExternalModuleDependencies property" {
+ $externalModuleDep1 = "ExternalModuleDep1"
+ $externalModuleDep2 = "ExternalModuleDep2"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -ExternalModuleDependencies $externalModuleDep1,$externalModuleDep2
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($externalModuleDep1) | Should -BeTrue
+ $results.Contains($externalModuleDep2) | Should -BeTrue
+ $results -like "*.EXTERNALMODULEDEPENDENCIES*$externalModuleDep1*$externalModuleDep2*" | Should -BeTrue
+ }
+
+ It "update script file ExternalScriptDependencies property" {
+ $externalScriptDep1 = "ExternalScriptDep1"
+ $externalScriptDep2 = "ExternalScriptDep2"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -ExternalScriptDependencies $externalScriptDep1,$externalScriptDep2
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($externalScriptDep1) | Should -BeTrue
+ $results.Contains($externalScriptDep2) | Should -BeTrue
+ $results -like "*.EXTERNALMODULEDEPENDENCIES*$externalScriptDep1*$externalScriptDep2*" | Should -BeTrue
+ }
+
+ It "update script file IconUri property" {
+ $iconUri = "https://testscript.com/icon"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -IconUri $iconUri
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($iconUri) | Should -BeTrue
+ $results.Contains(".ICONURI $iconUri") | Should -BeTrue
+ }
+
+ It "update script file LicenseUri property" {
+ $licenseUri = "https://testscript.com/license"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -LicenseUri $licenseUri
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($licenseUri) | Should -BeTrue
+ $results.Contains(".LICENSEURI $licenseUri") | Should -BeTrue
+ }
+
+ It "update script file ProjectUri property" {
+ $projectUri = "https://testscript.com/"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -ProjectUri $projectUri
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($projectUri) | Should -BeTrue
+ $results.Contains(".PROJECTURI $projectUri") | Should -BeTrue
+ }
+
+ It "update script file PrivateData property" {
+ $privateData = "this is some private data"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -PrivateData $privateData
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($privateData) | Should -BeTrue
+ $results -like "*.PRIVATEDATA*$privateData*" | Should -BeTrue
+ }
+
+ It "update script file ReleaseNotes property" {
+ $releaseNotes = "Release notes for script."
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -ReleaseNotes $releaseNotes
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($releaseNotes) | Should -BeTrue
+ $results -like "*.RELEASENOTES$script:newline*$releaseNotes*" | Should -BeTrue
+ }
+
+ It "update script file RequiredModules property" {
+ $hashtable1 = @{ModuleName = "RequiredModule1"}
+ $hashtable2 = @{ModuleName = "RequiredModule2"; ModuleVersion = "1.0.0.0"}
+ $hashtable3 = @{ModuleName = "RequiredModule3"; RequiredVersion = "2.5.0.0"}
+ $hashtable4 = @{ModuleName = "RequiredModule4"; ModuleVersion = "1.1.0.0"; MaximumVersion = "2.0.0.0"}
+ $requiredModules = $hashtable1, $hashtable2, $hashtable3, $hashtable4
+
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -RequiredModules $requiredModules
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains("#Requires -Module RequiredModule1") | Should -BeTrue
+ $results -like "*#Requires*ModuleName*Version*" | Should -BeTrue
+ }
+
+ It "update script file RequiredScripts property" {
+ $requiredScript1 = "RequiredScript1"
+ $requiredScript2 = "RequiredScript2"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -RequiredScripts $requiredScript1, $requiredScript2
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($requiredScript1) | Should -BeTrue
+ $results.Contains($requiredScript2) | Should -BeTrue
+ $results -like "*.REQUIREDSCRIPTS*$requiredScript1*$requiredScript2*" | Should -BeTrue
+ }
+
+ It "update script file Tags property" {
+ $tag1 = "tag1"
+ $tag2 = "tag2"
+ Update-PSScriptFileInfo -FilePath $script:testScriptFilePath -Tags $tag1, $tag2
+ Test-PSScriptFileInfo $script:testScriptFilePath | Should -Be $true
+
+ Test-Path -Path $script:testScriptFilePath | Should -BeTrue
+ $results = Get-Content -Path $script:testScriptFilePath -Raw
+ $results.Contains($tag1) | Should -BeTrue
+ $results.Contains($tag2) | Should -BeTrue
+ $results.Contains(".TAGS $tag1 $tag2") | Should -BeTrue
+ }
+
+ It "throw error when attempting to update a signed script without -RemoveSignature parameter" {
+ # Note: user should sign the script again once it's been updated
+
+ $scriptName = "ScriptWithSignature.ps1"
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+
+ # use a copy of the signed script file so we can re-use for other tests
+ $null = Copy-Item -Path $scriptFilePath -Destination $TestDrive
+ $tmpScriptFilePath = Join-Path -Path $TestDrive -ChildPath $scriptName
+
+ { Update-PSScriptFileInfo -FilePath $tmpScriptFilePath -Version "2.0.0.0" } | Should -Throw -ErrorId "ScriptToBeUpdatedContainsSignature,Microsoft.PowerShell.PowerShellGet.Cmdlets.UpdatePSScriptFileInfo"
+ }
+
+ It "update signed script when using RemoveSignature parameter" {
+ $scriptName = "ScriptWithSignature.ps1"
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+
+ # use a copy of the signed script file so we can re-use for other tests
+ $null = Copy-Item -Path $scriptFilePath -Destination $TestDrive
+ $tmpScriptFilePath = Join-Path -Path $TestDrive -ChildPath $scriptName
+
+ Update-PSScriptFileInfo -FilePath $tmpScriptFilePath -Version "2.0.0.0" -RemoveSignature
+ Test-PSScriptFileInfo -FilePath $tmpScriptFilePath | Should -Be $true
+ }
+}
diff --git a/test/UpdatePSScriptInfo.Tests.ps1 b/test/UpdatePSScriptInfo.Tests.ps1
deleted file mode 100644
index eb1450eaf..000000000
--- a/test/UpdatePSScriptInfo.Tests.ps1
+++ /dev/null
@@ -1,415 +0,0 @@
-# Copyright (c) Microsoft Corporation.
-# Licensed under the MIT License.
-
-Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force
-
-Describe "Test Update-PSScriptFileInfo" {
-
- BeforeAll {
- $script:TempPath = Get-TempPath
- }
- BeforeEach {
- # Create temp script path
- $script:TempScriptPath = Join-Path $script:TempPath "PSGet_$(Get-Random)"
- $null = New-Item -Path $script:TempScriptPath -ItemType Directory -Force
-
- $script:PSScriptInfoName = "PSGetTestScript"
- $script:testPSScriptInfoPath = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath "$script:PSScriptInfoName.psd1"
- }
- AfterEach {
- RemoveItem "$script:TempScriptPath"
- }
-
- ### TODO: Add tests for -Force and -WhatIf if those parameters are applicable
-<#
- It "Update .ps1 file with relative path" {
- $RelativeCurrentPath = Get-Location
- $ScriptFilePath = Join-Path -Path $relativeCurrentPath -ChildPath "$script:PSScriptInfoName.ps1"
- $OldDescription = "Old description for test script"
- $NewDescription = "Old description for test script"
- New-PSScriptFileInfo -FilePath $ScriptFilePath -Description $OldDescription
-
- Update-PSScriptFileInfo -FilePath $ScriptFilePath -Description $NewDescription
-
- Test-PSScriptFileInfo -FilePath $ScriptFilePath | Should -BeTrue
- Remove-Item -Path $ScriptFilePath
- }
-
- It "Update .ps1 given Version parameter" {
- $Version = "2.0.0.0"
- $Description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Version $Version
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($Version) | Should -BeTrue
- $results.Contains(".VERSION $Version") | Should -BeTrue
- }
-
- It "Update .ps1 given prerelease version" {
- $Version = "2.0.0.0-alpha"
- $Description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Version $Version
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($Version) | Should -BeTrue
- $results.Contains(".VERSION $Version") | Should -BeTrue
- }
-
- It "Should not update .ps1 with invalid version" {
- $Version = "4.0.0.0.0"
- $Description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Version $Version -ErrorVariable err -ErrorAction SilentlyContinue
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeFalse
- $err.Count | Should -Not -Be 0
- $err[0].FullyQualifiedErrorId | Should -BeExactly "VersionParseIntoNuGetVersion,Microsoft.PowerShell.PowerShellGet.Cmdlets.UpdatePSScriptFileInfo"
- }
-
- It "Update .ps1 given Guid parameter" {
- $Guid = [guid]::NewGuid()
- $Description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Guid $Guid
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($Guid) | Should -BeTrue
- $results.Contains(".GUID $Guid") | Should -BeTrue
- }
-
- It "Update .ps1 given Author parameter" {
- $Author = "New Author"
- $Description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Author $Author
-
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($Author) | Should -BeTrue
- $results.Contains(".AUTHOR $Author") | Should -BeTrue
- }
-
- It "Update .ps1 given Description parameter" {
- $OldDescription = "Old description for test script."
- $NewDescription = "New description for test script."
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $OldDescription
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Description $NewDescription
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewDescription) | Should -BeTrue
- $results -like ".DESCRIPTION*$NewDescription" | Should -BeTrue
- }
-
- It "Update .ps1 given CompanyName parameter" {
- $OldCompanyName = "Old company name"
- $NewCompanyName = "New company name"
- $Description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -CompanyName $OldCompanyName -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -CompanyName $NewCompanyName
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($NewCompanyName) | Should -BeTrue
- $results.Contains(".COMPANYNAME $NewCompanyName") | Should -BeTrue
- }
-
- It "Update .ps1 given Copyright parameter" {
- $OldCopyright = "(c) Old Test Corporation"
- $NewCopyright = "(c) New Test Corporation"
- $Description = "Test description"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Copyright $OldCopyright -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Copyright $NewCopyright
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testPSScriptInfoPath -Raw
- $results.Contains($NewCopyright) | Should -BeTrue
- $results.Contains(".COPYRIGHT $NewCopyright") | Should -BeTrue
- }
-
- It "Update .ps1 given RequiredModules parameter" {
- $RequiredModuleName = 'PackageManagement'
- $OldrequiredModuleVersion = '1.0.0.0'
- $OldRequiredModules = @(@{ModuleName = $RequiredModuleName; ModuleVersion = $OldrequiredModuleVersion })
- $NewrequiredModuleVersion = '2.0.0.0'
- $NewRequiredModules = @(@{ModuleName = $RequiredModuleName; ModuleVersion = $NewrequiredModuleVersion })
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredModules $OldRequiredModules -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredModules $NewRequiredModules
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($RequiredModuleName) | Should -BeTrue
- $results.Contains($NewrequiredModuleVersion) | Should -BeTrue
- $results -like ".REQUIREDMODULES*$RequiredModuleName*$NewrequiredModuleVersion" | Should -BeTrue
- }
-
- It "Update .ps1 given ReleaseNotes parameter" {
- $Description = "Test Description"
- $OldReleaseNotes = "Old release notes for script."
- $NewReleaseNotes = "New release notes for script."
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ReleaseNotes $OldReleaseNotes -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ReleaseNotes $NewReleaseNotes
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewReleaseNotes) | Should -BeTrue
- $results -like ".RELEASENOTES*$NewReleaseNotes" | Should -BeTrue
- }
-
- It "Update .ps1 given Tags parameter" {
- $Description = "Test Description"
- $OldTag1 = "Tag1"
- $OldTag2 = "Tag2"
- $NewTag1 = "NewTag1"
- $NewTag2 = "NewTag2"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Tags $OldTag1, $OldTag2 -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -Tags $NewTag1, $NewTag2
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewTag1) | Should -BeTrue
- $results.Contains($NewTag2) | Should -BeTrue
- $results.Contains(".TAGS $NewTag1 $NewTag2") | Should -BeTrue
- }
-
- It "Update .ps1 given ProjectUri parameter" {
- $Description = "Test Description"
- $OldProjectUri = "https://www.oldtestprojecturi.com/"
- $NewProjectUri = "https://www.newtestprojecturi.com/"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ProjectUri $OldProjectUri -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ProjectUri $NewProjectUri
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewProjectUri) | Should -BeTrue
- $results.Contains(".PROJECTURI $NewProjectUri") | Should -BeTrue
- }
-
- It "Update .ps1 given LicenseUri parameter" {
- $Description = "Test Description"
- $OldLicenseUri = "https://www.oldtestlicenseuri.com/"
- $NewLicenseUri = "https://www.newtestlicenseuri.com/"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -LicenseUri $OldLicenseUri -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -LicenseUri $NewLicenseUri
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewLicenseUri) | Should -BeTrue
- $results.Contains(".LICENSEURI $NewLicenseUri") | Should -BeTrue
- }
-
- It "Update .ps1 given IconUri parameter" {
- $Description = "Test Description"
- $OldIconUri = "https://www.oldtesticonuri.com/"
- $NewIconUri = "https://www.newtesticonuri.com/"
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -IconUri $OldIconUri -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -IconUri $NewIconUri
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewIconUri) | Should -BeTrue
- $results.Contains(".ICONURI $NewIconUri") | Should -BeTrue
- }
-
- It "Update .ps1 given ExternalModuleDependencies parameter" {
- $Description = "Test Description"
- $OldExternalModuleDep1 = "OldExternalModuleDep1"
- $OldExternalModuleDep2 = "OldExternalModuleDep2"
- $OldExternalModuleDep1FileName = "OldExternalModuleDep1.psm1"
- $OldExternalModuleDep2FileName = "OldExternalModuleDep2.psm1"
- $OldExternalModuleDepPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldExternalModuleDep1FileName
- $OldExternalModuleDepPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldExternalModuleDep2FileName
- $null = New-Item -Path $OldExternalModuleDepPath1 -ItemType File -Force
- $null = New-Item -Path $OldExternalModuleDepPath2 -ItemType File -Force
-
- $NewExternalModuleDep1 = "NewExternalModuleDep1"
- $NewExternalModuleDep2 = "NewExternalModuleDep2"
- $NewExternalModuleDep1FileName = "NewExternalModuleDep1.psm1"
- $NewExternalModuleDep2FileName = "NewExternalModuleDep2.psm1"
- $NewExternalModuleDepPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewExternalModuleDep1FileName
- $NewExternalModuleDepPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewExternalModuleDep2FileName
- $null = New-Item -Path $NewExternalModuleDepPath1 -ItemType File -Force
- $null = New-Item -Path $NewExternalModuleDepPath2 -ItemType File -Force
-
- # NOTE: you may need to add the -NestedModules parameter here as well
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ExternalModuleDependencies $OldExternalModuleDep1, $OldExternalModuleDep2 -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ExternalModuleDependencies $NewExternalModuleDep1, $NewExternalModuleDep2
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewExternalModuleDep1) | Should -BeTrue
- $results.Contains($NewExternalModuleDep2) | Should -BeTrue
- $results -like ".EXTERNALMODULEDEPENDENCIES*$NewExternalModuleDep1*$NewExternalModuleDep2" | Should -BeTrue
- }
-
- It "Update .ps1 given RequiredAssemblies parameter" {
- $Description = "Test Description"
- $OldRequiredAssembly1 = "OldRequiredAssembly1.dll"
- $OldRequiredAssembly2 = "OldRequiredAssembly2.dll"
- $OldRequiredAssemblyPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldRequiredAssembly1
- $OldRequiredAssemblyPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldRequiredAssembly2
- $null = New-Item -Path $OldRequiredAssemblyPath1 -ItemType File -Force
- $null = New-Item -Path $OldRequiredAssemblyPath2 -ItemType File -Force
-
- $NewRequiredAssembly1 = "NewRequiredAssembly1.dll"
- $NewRequiredAssembly2 = "NewRequiredAssembly2.dll"
- $NewRequiredAssemblyPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewRequiredAssembly1
- $NewRequiredAssemblyPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewRequiredAssembly2
- $null = New-Item -Path $NewRequiredAssemblyPath1 -ItemType File -Force
- $null = New-Item -Path $NewRequiredAssemblyPath2 -ItemType File -Force
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredAssemblies $OldRequiredAssembly1, $OldRequiredAssembly2 -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredAssemblies $NewRequiredAssembly1, $NewRequiredAssembly2
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewRequiredAssembly1) | Should -BeTrue
- $results.Contains($NewRequiredAssembly2) | Should -BeTrue
- $results -like ".REQUIREDASSEMBLIES*$NewRequiredAssembly1*$NewRequiredAssembly2" | Should -BeTrue
- }
-
- It "Update .ps1 given NestedModules parameter" {
- $Description = "Test Description"
- $OldNestedModule1 = "OldNestedModule1"
- $OldNestedModule2 = "OldNestedModule2"
- $OldNestModuleFileName1 = "OldNestedModule1.dll"
- $OldNestModuleFileName2 = "OldNestedModule2.dll"
- $OldNestedModulePath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldNestModuleFileName1
- $OldNestedModulePath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldNestModuleFileName2
- $null = New-Item -Path $OldNestedModulePath1 -ItemType File -Force
- $null = New-Item -Path $OldNestedModulePath2 -ItemType File -Force
-
- $NewNestedModule1 = "NewNestedModule1"
- $NewNestedModule2 = "NewNestedModule2"
- $NewNestModuleFileName1 = "NewNestedModule1.dll"
- $NewNestModuleFileName2 = "NewNestedModule2.dll"
- $NewNestedModulePath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewNestModuleFileName1
- $NewNestedModulePath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewNestModuleFileName2
- $null = New-Item -Path $NewNestedModulePath1 -ItemType File -Force
- $null = New-Item -Path $NewNestedModulePath2 -ItemType File -Force
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -NestedModules $OldNestedModule1, $OldNestedModule2 -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -NestedModules $NewNestedModule1, $NewNestedModule2
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewNestedModule1) | Should -BeTrue
- $results.Contains($NewNestedModule2) | Should -BeTrue
- $results -like ".NESTEDMODULES*$NewNestedModule1*$NewNestedModule2" | Should -BeTrue
- }
-
- It "Update .ps1 given RequiredScripts parameter" {
- $Description = "Test Description"
- $OldRequiredScript1 = "OldNestedModule1.ps1"
- $OldRequiredScript2 = "OldNestedModule2.ps1"
- $OldRequiredScript1Path = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldRequiredScript1
- $OldRequiredScript2Path = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldRequiredScript2
- $null = New-Item -Path $OldRequiredScript1Path -ItemType File -Force
- $null = New-Item -Path $OldRequiredScript2Path -ItemType File -Force
-
- $NewRequiredScript1 = "NewNestedModule1.ps1"
- $NewRequiredScript2 = "NewNestedModule2.ps1"
- $NewRequiredScript1Path = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewRequiredScript1
- $NewRequiredScript2Path = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewRequiredScript2
- $null = New-Item -Path $NewRequiredScript1Path -ItemType File -Force
- $null = New-Item -Path $NewRequiredScript2Path -ItemType File -Force
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredScripts $OldRequiredScript1, $OldRequiredScript2 -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -RequiredScripts $NewRequiredScript1, $NewRequiredScript2
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewRequiredScript1) | Should -BeTrue
- $results.Contains($NewRequiredScript2) | Should -BeTrue
- $results -like ".REQUIREDSCRIPTS*$NewRequiredScript1*$NewRequiredScript2" | Should -BeTrue
- }
-
- It "Update .ps1 given ExternalScriptDependencies parameter" {
- $Description = "Test Description"
- $OldExternalScriptDep1 = "OldExternalScriptDep1.ps1"
- $OldExternalScriptDep2 = "OldExternalScriptDep2.ps1"
- $OldExternalScriptDepPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldExternalScriptDep1
- $OldExternalScriptDepPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $OldExternalScriptDep2
- $null = New-Item -Path $OldExternalScriptDepPath1 -ItemType File -Force
- $null = New-Item -Path $OldExternalScriptDepPath2 -ItemType File -Force
-
- $NewExternalScriptDep1 = "NewExternalScriptDep1.ps1"
- $NewExternalScriptDep2 = "NewExternalScriptDep2.ps1"
- $NewExternalScriptDepPath1 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewExternalScriptDep1
- $NewExternalScriptDepPath2 = Microsoft.PowerShell.Management\Join-Path -Path $script:TempScriptPath -ChildPath $NewExternalScriptDep2
- $null = New-Item -Path $NewExternalScriptDepPath1 -ItemType File -Force
- $null = New-Item -Path $NewExternalScriptDepPath2 -ItemType File -Force
-
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ExternalModuleDependencies $OldExternalModuleDep1, $OldExternalModuleDep2 -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -ExternalModuleDependencies $NewExternalModuleDep1, $NewExternalModuleDep2
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewExternalModuleDep1) | Should -BeTrue
- $results.Contains($NewExternalModuleDep2) | Should -BeTrue
- $results -like ".EXTERNALSCRIPTDEPENDENCIES*$NewExternalModuleDep1*$NewExternalModuleDep2" | Should -BeTrue
- }
-
- It "Update .ps1 given PrivateData parameter" {
- $Description = "Test Description"
- $OldPrivateData = @{"OldPrivateDataEntry1" = "OldPrivateDataValue1"}
- $NewPrivateData = @{"NewPrivateDataEntry1" = "NewPrivateDataValue1"}
- New-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -PrivateData $OldPrivateData -Description $Description
-
- Update-PSScriptFileInfo -FilePath $script:testPSScriptInfoPath -PrivateData $NewPrivateData
-
- Test-Path -FilePath $script:testPSScriptInfoPath | Should -BeTrue
- $results = Get-Content -Path $script:testManifestPath -Raw
- $results.Contains($NewPrivateData) | Should -BeTrue
- $results -like ".PRIVATEDATA*$NewPrivateData" | Should -BeTrue
- }
-
- It "Update signed script when using RemoveSignature parameter" {
- $scriptName = "ScriptWithSignature.ps1"
- $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
-
- # use a copy of the signed script file so we can re-use for other tests
- $null = Copy-Item -Path $scriptFilePath -Destination $TestDrive
- $tmpScriptFilePath = Join-Path -Path $TestDrive -ChildPath $scriptName
-
- Update-PSScriptFileInfo -FilePath $tmpScriptFilePath -Version "2.0.0.0" -RemoveSignature
- Test-PSScriptFileInfo -FilePath $tmpScriptFilePath | Should -Be $true
- }
-
- It "Throw error when attempting to update a signed script without -RemoveSignature parameter" {
- $scriptName = "ScriptWithSignature.ps1"
- $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
-
- # use a copy of the signed script file so we can re-use for other tests
- $null = Copy-Item -Path $scriptFilePath -Destination $TestDrive
- $tmpScriptFilePath = Join-Path -Path $TestDrive -ChildPath $scriptName
-
- { Update-PSScriptFileInfo -FilePath $tmpScriptFilePath -Version "2.0.0.0" } | Should -Throw -ErrorId "ScriptToBeUpdatedContainsSignature,Microsoft.PowerShell.PowerShellGet.Cmdlets.UpdatePSScriptFileInfo"
- }
-#>
-}
\ No newline at end of file
diff --git a/test/testFiles/testScripts/ScriptWithSignature.ps1 b/test/testFiles/testScripts/ScriptWithSignature.ps1
new file mode 100644
index 000000000..9bd193af7
--- /dev/null
+++ b/test/testFiles/testScripts/ScriptWithSignature.ps1
@@ -0,0 +1,75 @@
+
+<#PSScriptInfo
+
+.VERSION 1.0
+
+.GUID 3951be04-bd06-4337-8dc3-a620bf539fbd
+
+.AUTHOR annavied
+
+.COMPANYNAME
+
+.COPYRIGHT
+
+.TAGS
+
+.LICENSEURI
+
+.PROJECTURI
+
+.ICONURI
+
+.EXTERNALMODULEDEPENDENCIES
+
+.REQUIREDSCRIPTS
+
+.EXTERNALSCRIPTDEPENDENCIES
+
+.RELEASENOTES
+
+.PRIVATEDATA
+
+#>
+
+<#
+
+.DESCRIPTION
+ this is a test for a script that will be published remotely
+
+#>
+Param()
+
+
+
+# SIG # Begin signature block
+# MIIFbQYJKoZIhvcNAQcCoIIFXjCCBVoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
+# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
+# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUhY04RvNe0Q8hliL7qS3/X9kr
+# QVugggMIMIIDBDCCAeygAwIBAgIQN+zCRZRKiphJ5gGoRKvpeTANBgkqhkiG9w0B
+# AQsFADAaMRgwFgYDVQQDDA9Db2RlU2lnbmluZ0NlcnQwHhcNMjIwNjIyMTgyODUx
+# WhcNMjQwNjIyMTgzODUwWjAaMRgwFgYDVQQDDA9Db2RlU2lnbmluZ0NlcnQwggEi
+# MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCf7gQAR4AVpVc4/4OlffaEQ6uE
+# klG01+ga7sZbV7z9UkJFIDbntapCoXV85w/bNbmWSI+IUDisVBS7BIoicKagHskE
+# YhRJv6WL/zxD2lWP21MRkEJBEMicbrj38F2R/khGDq/T5/a1XH+7QVAsf1kOG/oU
+# d0CUDqgsR5+JdpaMt/QRM/jFLEUdvs+7zCvduciEEQRFFUbYYqy9RfmxMpPxZ6CM
+# RjLVr5k4tirbg1YyBK6l7xPvT3BUejGvEYPOdAskPXMVbMO37DyEszudqOz9eEvp
+# yHCKOgePLeq+9DbOQ+fAy30c79YNU5JfvgaDY+3c99WQXSeQuLYNDUeDDPGxAgMB
+# AAGjRjBEMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNV
+# HQ4EFgQUrmtlJTMGV5h8ksEMzPTPYk04g3IwDQYJKoZIhvcNAQELBQADggEBAAR3
+# sIiiVgSxUiPzGS/Ivwgjvqfsb6HXioE9VIJxQPwXc63LqC01TGJpeGayjr5zQ4p5
+# vt9q8WsiZvoUMofWzabz4BdprGWVDrO8hwksIixF8ojbfLuAra1cZ4qkDZtJH2Sn
+# 0dUhvXabZqLuVghMiyqcSvs2hN8OiVI+tLzW8VQKzbFdj77c+lHxKBTkcKVpLiSI
+# V2V8P4zRxyYE+CMlpTr58ErOGVxP1zITou7fwCAXdWEKWo5nlU22VNF6oGE9tghm
+# S3M5PQT8lFCjZOPPKx+0oLDxwjluHENXZzH+61ugrszzRjK1rG3D3emrRYh/4BcG
+# Wy7J1H41povt21JlzEExggHPMIIBywIBATAuMBoxGDAWBgNVBAMMD0NvZGVTaWdu
+# aW5nQ2VydAIQN+zCRZRKiphJ5gGoRKvpeTAJBgUrDgMCGgUAoHgwGAYKKwYBBAGC
+# NwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
+# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUbfriqhB/
+# EKzgoXnVu2UFtaTb040wDQYJKoZIhvcNAQEBBQAEggEAiQa/HUhDP1HyrPh7mC5H
+# 6IwOdxL4p3EIkGeuUh3ZqWRNFLNz0ob24vqmKBtaKTfJqqrxTIBYeoBKB3Y8Wcx2
+# rEaH31WqQM2U7mFvM2cVv6dcrdWmLcMwi3LSEMxJf6VbWpbmWZK6zMRW2H76P5wQ
+# cs6BUOwKZq/5eQcQLjJ3h+Mh5dsENZ7scB4U1yihD7Ggvrgxf54+J/TS8XuDsx2o
+# g0czxIjMBwT5wGh8BqbC50izZ3D0WRFe7UNnhMk7zKG/bvIRBxah+JV25hdoGYaR
+# 2tdmgr4EMPoB1ti8DOFmYAicckDWfX7/X4NzeM234LSMLtOxO2lVj5jhkmJJdjKh
+# WA==
+# SIG # End signature block
\ No newline at end of file