diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs
index 8ff51d999..f80f0ca5c 100644
--- a/src/code/PublishPSResource.cs
+++ b/src/code/PublishPSResource.cs
@@ -18,6 +18,7 @@
using System.Management.Automation;
using System.Management.Automation.Language;
using System.Net.Http;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
@@ -50,28 +51,9 @@ public sealed class PublishPSResource : PSCmdlet
/// Specifies the path to the resource that you want to publish. This parameter accepts the path to the folder that contains the resource.
/// Specifies a path to one or more locations. Wildcards are permitted. The default location is the current directory (.).
///
- [Parameter]
+ [Parameter (Mandatory = true)]
[ValidateNotNullOrEmpty]
- public string Path
- {
- get
- { return _path; }
-
- set
- {
- string resolvedPath = SessionState.Path.GetResolvedPSPathFromPSPath(value).First().Path;
-
- if (Directory.Exists(resolvedPath))
- {
- _path = resolvedPath;
- }
- else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSDataFileExt, StringComparison.OrdinalIgnoreCase))
- {
- _path = resolvedPath;
- }
- }
- }
- private string _path;
+ public string Path { get; set; }
///
/// Specifies the path to where the resource (as a nupkg) should be saved to. This parameter can be used in conjunction with the
@@ -79,36 +61,7 @@ public string Path
///
[Parameter]
[ValidateNotNullOrEmpty]
- public string DestinationPath
- {
- get
- { return _destinationPath; }
-
- set
- {
- string resolvedPath = SessionState.Path.GetResolvedPSPathFromPSPath(value).First().Path;
-
- if (Directory.Exists(resolvedPath))
- {
- _destinationPath = resolvedPath;
- }
- else
- {
- try
- {
- Directory.CreateDirectory(value);
- }
- catch (Exception e)
- {
- var exMessage = string.Format("Destination path does not exist and cannot be created: {0}", e.Message);
- var ex = new ArgumentException(exMessage);
- var InvalidDestinationPath = new ErrorRecord(ex, "InvalidDestinationPath", ErrorCategory.InvalidArgument, null);
- ThrowTerminatingError(InvalidDestinationPath);
- }
- }
- }
- }
- private string _destinationPath;
+ public string DestinationPath { get; set; }
///
/// Specifies a user account that has rights to a specific repository (used for finding dependencies).
@@ -162,12 +115,14 @@ public PSCredential ProxyCredential {
#region Members
+ private string _path;
private CancellationToken _cancellationToken;
private NuGetVersion _pkgVersion;
private string _pkgName;
private static char[] _PathSeparators = new[] { System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar };
public const string PSDataFileExt = ".psd1";
public const string PSScriptFileExt = ".ps1";
+ private const string PSScriptInfoCommentString = "<#PSScriptInfo";
#endregion
@@ -180,9 +135,51 @@ protected override void BeginProcessing()
// Create a respository story (the PSResourceRepository.xml file) if it does not already exist
// This is to create a better experience for those who have just installed v3 and want to get up and running quickly
RepositorySettings.CheckRepositoryStore();
+
+ string resolvedPath = SessionState.Path.GetResolvedPSPathFromPSPath(Path).First().Path;
+
+ if (Directory.Exists(resolvedPath) ||
+ (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)))
+ {
+ // condition 1: we point to a folder when publishing a module
+ // condition 2: we point to a .ps1 file directly when publishing a script, but not to .psd1 file (for publishing a module)
+ _path = resolvedPath;
+ }
+ else
+ {
+ // unsupported file path
+ var exMessage = string.Format("Either the path to the resource to publish does not exist or is not in the correct format, for scripts point to .ps1 file and for modules point to folder containing .psd1");
+ var ex = new ArgumentException(exMessage);
+ var InvalidSourcePathError = new ErrorRecord(ex, "InvalidSourcePath", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(InvalidSourcePathError);
+ }
+
+ if (!String.IsNullOrEmpty(DestinationPath))
+ {
+ string resolvedDestinationPath = SessionState.Path.GetResolvedPSPathFromPSPath(DestinationPath).First().Path;
+
+ if (Directory.Exists(resolvedDestinationPath))
+ {
+ DestinationPath = resolvedDestinationPath;
+ }
+ else
+ {
+ try
+ {
+ Directory.CreateDirectory(resolvedDestinationPath);
+ }
+ catch (Exception e)
+ {
+ var exMessage = string.Format("Destination path does not exist and cannot be created: {0}", e.Message);
+ var ex = new ArgumentException(exMessage);
+ var InvalidDestinationPath = new ErrorRecord(ex, "InvalidDestinationPath", ErrorCategory.InvalidArgument, null);
+ ThrowTerminatingError(InvalidDestinationPath);
+ }
+ }
+ }
}
- protected override void ProcessRecord()
+ protected override void EndProcessing()
{
// Returns the name of the file or the name of the directory, depending on path
var pkgFileOrDir = new DirectoryInfo(_path);
@@ -195,41 +192,21 @@ protected override void ProcessRecord()
}
string resourceFilePath;
- Hashtable parsedMetadataHash = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
+ Hashtable parsedMetadata;
if (isScript)
{
resourceFilePath = pkgFileOrDir.FullName;
// Check that script metadata is valid
- // ParseScriptMetadata will write non-terminating error if it's unsuccessful in parsing
- parsedMetadataHash = ParseScriptMetadata(resourceFilePath);
-
- // Check that the value is valid input
- // If it does not contain 'Version' or the Version empty or whitespace, write error
- if (!parsedMetadataHash.ContainsKey("Version") || String.IsNullOrWhiteSpace(parsedMetadataHash["Version"].ToString()))
- {
- var message = "No version was provided in the script metadata. Script metadata must specify a version, author and description.";
- var ex = new ArgumentException(message);
- var InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null);
- WriteError(InvalidScriptMetadata);
-
- return;
- }
- if (!parsedMetadataHash.ContainsKey("Author") || String.IsNullOrWhiteSpace(parsedMetadataHash["Author"].ToString()))
+ if (!TryParseScriptMetadata(
+ out parsedMetadata,
+ resourceFilePath,
+ out ErrorRecord[] errors))
{
- var message = "No author was provided in the script metadata. Script metadata must specify a version, author and description.";
- var ex = new ArgumentException(message);
- var InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null);
- WriteError(InvalidScriptMetadata);
-
- return;
- }
- if (!parsedMetadataHash.ContainsKey("Description") || String.IsNullOrWhiteSpace(parsedMetadataHash["Description"].ToString()))
- {
- var message = "No description was provided in the script metadata. Script metadata must specify a version, author and description.";
- var ex = new ArgumentException(message);
- var InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null);
- WriteError(InvalidScriptMetadata);
+ foreach (ErrorRecord err in errors)
+ {
+ WriteError(err);
+ }
return;
}
@@ -241,6 +218,7 @@ protected override void ProcessRecord()
{
_pkgName = pkgFileOrDir.Name;
resourceFilePath = System.IO.Path.Combine(_path, _pkgName + PSDataFileExt);
+ parsedMetadata = new Hashtable();
// Validate that there's a module manifest
if (!File.Exists(resourceFilePath))
@@ -290,7 +268,7 @@ protected override void ProcessRecord()
outputDir: outputDir,
filePath: resourceFilePath,
isScript: isScript,
- parsedMetadataHash: parsedMetadataHash,
+ parsedMetadataHash: parsedMetadata,
requiredModules: out dependencies);
}
catch (Exception e)
@@ -321,6 +299,16 @@ protected override void ProcessRecord()
return;
}
+ else if(repository.Uri.Scheme == Uri.UriSchemeFile && !repository.Uri.IsUnc && !Directory.Exists(repository.Uri.LocalPath))
+ {
+ // this check to ensure valid local path is not for UNC paths (which are server based, instead of Drive based)
+ var message = String.Format("The repository '{0}' with uri: {1} is not a valid folder path which exists. If providing a file based repository, provide a repository with a path that exists.", Repository, repository.Uri.AbsoluteUri);
+ var ex = new ArgumentException(message);
+ var fileRepositoryPathDoesNotExistError = new ErrorRecord(ex, "repositoryPathDoesNotExist", ErrorCategory.ObjectNotFound, null);
+ WriteError(fileRepositoryPathDoesNotExistError);
+
+ return;
+ }
// Check if dependencies already exist within the repo if:
// 1) the resource to publish has dependencies and
@@ -337,7 +325,7 @@ protected override void ProcessRecord()
if (isScript)
{
// copy the script file to the temp directory
- File.Copy(_path, System.IO.Path.Combine(outputDir, _pkgName + PSDataFileExt), true);
+ File.Copy(_path, System.IO.Path.Combine(outputDir, _pkgName + PSScriptFileExt), true);
}
else
{
@@ -366,35 +354,24 @@ protected override void ProcessRecord()
var outputNupkgDir = System.IO.Path.Combine(outputDir, "nupkg");
- // pack into a nupkg
- try
+ // pack into .nupkg
+ if (!PackNupkg(outputDir, outputNupkgDir, nuspec, out ErrorRecord packNupkgError))
{
- if (!PackNupkg(outputDir, outputNupkgDir, nuspec))
- {
- return;
- }
- }
- catch (Exception e)
- {
- var message = string.Format("Error packing into .nupkg: '{0}'.", e.Message);
- var ex = new ArgumentException(message);
- var ErrorPackingIntoNupkg = new ErrorRecord(ex, "ErrorPackingIntoNupkg", ErrorCategory.NotSpecified, null);
- WriteError(ErrorPackingIntoNupkg);
-
- // exit process record
+ WriteError(packNupkgError);
+ // exit out of processing
return;
}
// If -DestinationPath is specified then also publish the .nupkg there
- if (!string.IsNullOrWhiteSpace(_destinationPath))
+ if (!string.IsNullOrWhiteSpace(DestinationPath))
{
try
{
var nupkgName = _pkgName + "." + _pkgVersion.ToNormalizedString() + ".nupkg";
- File.Copy(System.IO.Path.Combine(outputNupkgDir, nupkgName), System.IO.Path.Combine(_destinationPath, nupkgName));
+ File.Copy(System.IO.Path.Combine(outputNupkgDir, nupkgName), System.IO.Path.Combine(DestinationPath, nupkgName));
}
catch (Exception e) {
- var message = string.Format("Error moving .nupkg into destination path '{0}' due to: '{1}'.", _destinationPath, e.Message);
+ var message = string.Format("Error moving .nupkg into destination path '{0}' due to: '{1}'.", DestinationPath, e.Message);
var ex = new ArgumentException(message);
var ErrorMovingNupkg = new ErrorRecord(ex, "ErrorMovingNupkg", ErrorCategory.NotSpecified, null);
@@ -405,10 +382,15 @@ protected override void ProcessRecord()
}
}
- // This call does not throw any exceptions, but it will write unsuccessful responses to the console
string repositoryUri = repository.Uri.AbsoluteUri;
- PushNupkg(outputNupkgDir, repository.Name, repositoryUri);
-
+
+ // This call does not throw any exceptions, but it will write unsuccessful responses to the console
+ if (!PushNupkg(outputNupkgDir, repository.Name, repositoryUri, out ErrorRecord pushNupkgError))
+ {
+ WriteError(pushNupkgError);
+ // exit out of processing
+ return;
+ }
}
finally
{
@@ -416,8 +398,8 @@ protected override void ProcessRecord()
Utils.DeleteDirectory(outputDir);
}
- }
+ }
#endregion
#region Private methods
@@ -737,9 +719,15 @@ private Hashtable ParseRequiredModules(Hashtable parsedMetadataHash)
return dependenciesHash;
}
- private Hashtable ParseScriptMetadata(string filePath)
+ private bool TryParseScriptMetadata(
+ out Hashtable parsedMetadata,
+ string filePath,
+ out ErrorRecord[] errors)
{
- // parse .ps1 - example .ps1 metadata:
+ parsedMetadata = new Hashtable();
+ List parseMetadataErrors = new List();
+
+ // a valid example script will have this format:
/* <#PSScriptInfo
.VERSION 1.6
.GUID abf490023 - 9128 - 4323 - sdf9a - jf209888ajkl
@@ -748,8 +736,9 @@ .COMPANYNAME Microsoft
.COPYRIGHT
.TAGS Windows MacOS
#>
-
+
<#
+
.SYNOPSIS
Synopsis description here
.DESCRIPTION
@@ -757,54 +746,154 @@ Description here
.PARAMETER Name
.EXAMPLE
Example cmdlet here
+
#>
*/
- // We're retrieving all the comments within a script and grabbing all the key/value pairs
- // because there's no standard way to create metadata for a script.
- Hashtable parsedMetadataHash = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
- // parse comments out
- Parser.ParseFile(
+ // Parse the script file
+ var ast = Parser.ParseFile(
filePath,
out System.Management.Automation.Language.Token[] tokens,
- out ParseError[] errors);
+ out ParseError[] parserErrors);
- if (errors.Length > 0)
+ if (parserErrors.Length > 0)
{
- var message = String.Format("Could not parse '{0}' as a PowerShell data file.", filePath);
- var ex = new ArgumentException(message);
- var psdataParseError = new ErrorRecord(ex, "psdataParseError", ErrorCategory.ParserError, null);
- WriteError(psdataParseError);
- }
- else
- {
- // Parse the script metadata located in comments
- List parsedComments = new List();
- foreach (var token in tokens)
+ foreach (ParseError err in parserErrors)
{
- if (token.Kind == TokenKind.Comment)
+ // we ignore WorkFlowNotSupportedInPowerShellCore errors, as this is common in scripts currently on PSGallery
+ if (!String.Equals(err.ErrorId, "WorkflowNotSupportedInPowerShellCore", StringComparison.OrdinalIgnoreCase))
{
- // expecting only one or two comments
- var commentText = token.Text;
- parsedComments.AddRange(commentText.Split(new string[] { "\n\n" }, StringSplitOptions.RemoveEmptyEntries));
+ var message = String.Format("Could not parse '{0}' as a PowerShell script file due to {1}.", filePath, err.Message);
+ var ex = new ArgumentException(message);
+ var psScriptFileParseError = new ErrorRecord(ex, err.ErrorId, ErrorCategory.ParserError, null);
+ parseMetadataErrors.Add(psScriptFileParseError);
}
}
- foreach (var line in parsedComments)
+ errors = parseMetadataErrors.ToArray();
+ return false;
+ }
+
+ if (ast == null)
+ {
+ var astNullMessage = String.Format(".ps1 file was parsed but AST was null");
+ var astNullEx = new ArgumentException(astNullMessage);
+ var astCouldNotBeCreatedError = new ErrorRecord(astNullEx, "ASTCouldNotBeCreated", ErrorCategory.ParserError, null);
+
+ parseMetadataErrors.Add(astCouldNotBeCreatedError);
+ errors = parseMetadataErrors.ToArray();
+ return false;
+
+ }
+
+ // Get the block/group comment beginning with <#PSScriptInfo
+ List commentTokens = tokens.Where(a => String.Equals(a.Kind.ToString(), "Comment", StringComparison.OrdinalIgnoreCase)).ToList();
+ string commentPattern = PSScriptInfoCommentString;
+ Regex rg = new Regex(commentPattern);
+ List psScriptInfoCommentTokens = commentTokens.Where(a => rg.IsMatch(a.Extent.Text)).ToList();
+
+ if (psScriptInfoCommentTokens.Count() == 0 || psScriptInfoCommentTokens[0] == null)
+ {
+ var message = String.Format("PSScriptInfo comment was missing or could not be parsed");
+ var ex = new ArgumentException(message);
+ var psCommentMissingError = new ErrorRecord(ex, "psScriptInfoCommentMissingError", ErrorCategory.ParserError, null);
+ parseMetadataErrors.Add(psCommentMissingError);
+ errors = parseMetadataErrors.ToArray();
+ return false;
+ }
+
+ string[] commentLines = Regex.Split(psScriptInfoCommentTokens[0].Text, "[\r\n]").Where(x => !String.IsNullOrEmpty(x)).ToArray();
+ string keyName = String.Empty;
+ string value = String.Empty;
+
+ /**
+ If comment line count is not more than two, it doesn't have the any metadata property
+ comment block would look like:
+ <#PSScriptInfo
+ #>
+ */
+
+ if (commentLines.Count() > 2)
+ {
+ for (int i = 1; i < commentLines.Count(); i++)
{
- if (line.StartsWith("."))
+ string line = commentLines[i];
+ if (String.IsNullOrEmpty(line))
{
- char[] TrimBeginning = { '.', ' ' };
- var newlist = line.Split(new char[] { ' ' });
+ continue;
+ }
- var key = newlist[0].TrimStart(TrimBeginning);
- var value = newlist.Length > 1 ? newlist[1].Trim() : string.Empty;
- parsedMetadataHash.Add(key, value);
+ // A line is starting with . conveys a new metadata property
+ if (line.Trim().StartsWith("."))
+ {
+ string[] parts = line.Trim().TrimStart('.').Split();
+ keyName = parts[0].ToLower();
+ value = parts.Count() > 1 ? String.Join(" ", parts.Skip(1)) : String.Empty;
+ parsedMetadata.Add(keyName, value);
}
}
}
- return parsedMetadataHash;
+ // get .DESCRIPTION comment
+ CommentHelpInfo scriptCommentInfo = ast.GetHelpContent();
+ if (scriptCommentInfo == null)
+ {
+ var message = String.Format("PSScript file is missing the required Description comment block in the script contents.");
+ var ex = new ArgumentException(message);
+ var psScriptMissingHelpContentCommentBlockError = new ErrorRecord(ex, "PSScriptMissingHelpContentCommentBlock", ErrorCategory.ParserError, null);
+ parseMetadataErrors.Add(psScriptMissingHelpContentCommentBlockError);
+ errors = parseMetadataErrors.ToArray();
+ return false;
+ }
+
+ if (!String.IsNullOrEmpty(scriptCommentInfo.Description) && !scriptCommentInfo.Description.Contains("<#") && !scriptCommentInfo.Description.Contains("#>"))
+ {
+ parsedMetadata.Add("description", scriptCommentInfo.Description);
+ }
+ else
+ {
+ var message = String.Format("PSScript is missing the required Description property or Description value contains '<#' or '#>' which is invalid");
+ var ex = new ArgumentException(message);
+ var psScriptMissingDescriptionOrInvalidPropertyError = new ErrorRecord(ex, "MissingOrInvalidDescriptionInScriptMetadata", ErrorCategory.ParserError, null);
+ parseMetadataErrors.Add(psScriptMissingDescriptionOrInvalidPropertyError);
+ errors = parseMetadataErrors.ToArray();
+ return false;
+ }
+
+
+ // Check that the mandatory properites for a script are there (version, author, guid, in addition to description)
+ if (!parsedMetadata.ContainsKey("version") || String.IsNullOrWhiteSpace(parsedMetadata["version"].ToString()))
+ {
+ var message = "No version was provided in the script metadata. Script metadata must specify a version, author, description, and Guid.";
+ var ex = new ArgumentException(message);
+ var MissingVersionInScriptMetadataError = new ErrorRecord(ex, "MissingVersionInScriptMetadata", ErrorCategory.InvalidData, null);
+ parseMetadataErrors.Add(MissingVersionInScriptMetadataError);
+ errors = parseMetadataErrors.ToArray();
+ return false;
+ }
+
+ if (!parsedMetadata.ContainsKey("author") || String.IsNullOrWhiteSpace(parsedMetadata["author"].ToString()))
+ {
+ var message = "No author was provided in the script metadata. Script metadata must specify a version, author, description, and Guid.";
+ var ex = new ArgumentException(message);
+ var MissingAuthorInScriptMetadataError = new ErrorRecord(ex, "MissingAuthorInScriptMetadata", ErrorCategory.InvalidData, null);
+ parseMetadataErrors.Add(MissingAuthorInScriptMetadataError);
+ errors = parseMetadataErrors.ToArray();
+ return false;
+ }
+
+ if (!parsedMetadata.ContainsKey("guid") || String.IsNullOrWhiteSpace(parsedMetadata["guid"].ToString()))
+ {
+ var message = "No guid was provided in the script metadata. Script metadata must specify a version, author, description, and Guid.";
+ var ex = new ArgumentException(message);
+ var MissingGuidInScriptMetadataError = new ErrorRecord(ex, "MissingGuidInScriptMetadata", ErrorCategory.InvalidData, null);
+ parseMetadataErrors.Add(MissingGuidInScriptMetadataError);
+ errors = parseMetadataErrors.ToArray();
+ return false;
+ }
+
+ errors = parseMetadataErrors.ToArray();
+ return true;
}
private bool CheckDependenciesExist(Hashtable dependencies, string repositoryName)
@@ -851,11 +940,13 @@ private bool CheckDependenciesExist(Hashtable dependencies, string repositoryNam
return true;
}
- private bool PackNupkg(string outputDir, string outputNupkgDir, string nuspecFile)
+ private bool PackNupkg(string outputDir, string outputNupkgDir, string nuspecFile, out ErrorRecord error)
{
- // Pack the module or script into a nupkg given a nuspec.
+ // Pack the module or script into a nupkg given a nuspec.
var builder = new PackageBuilder();
- var runner = new PackCommandRunner(
+ try
+ {
+ var runner = new PackCommandRunner(
new PackArgs
{
CurrentDirectory = outputDir,
@@ -867,21 +958,37 @@ private bool PackNupkg(string outputDir, string outputNupkgDir, string nuspecFil
},
MSBuildProjectFactory.ProjectCreator,
builder);
-
- bool success = runner.RunPackageBuild();
- if (success)
- {
- WriteVerbose("Successfully packed the resource into a .nupkg");
+ bool success = runner.RunPackageBuild();
+
+ if (success)
+ {
+ WriteVerbose("Successfully packed the resource into a .nupkg");
+ }
+ else
+ {
+ var message = String.Format("Not able to successfully pack the resource into a .nupkg");
+ var ex = new InvalidOperationException(message);
+ var failedToPackIntoNupkgError = new ErrorRecord(ex, "failedToPackIntoNupkg", ErrorCategory.ObjectNotFound, null);
+ error = failedToPackIntoNupkgError;
+ return false;
+ }
}
- else
+ catch (Exception e)
{
- WriteVerbose("Not able to successfully pack the resource into a .nupkg");
+ var message = string.Format("Unexpectd error packing into .nupkg: '{0}'.", e.Message);
+ var ex = new ArgumentException(message);
+ var ErrorPackingIntoNupkg = new ErrorRecord(ex, "ErrorPackingIntoNupkg", ErrorCategory.NotSpecified, null);
+
+ error = ErrorPackingIntoNupkg;
+ // exit process record
+ return false;
}
- return success;
+ error = null;
+ return true;
}
- private void PushNupkg(string outputNupkgDir, string repoName, string repoUri)
+ private bool PushNupkg(string outputNupkgDir, string repoName, string repoUri, out ErrorRecord error)
{
// Push the nupkg to the appropriate repository
// Pkg version is parsed from .ps1 file or .psd1 file
@@ -895,7 +1002,7 @@ private void PushNupkg(string outputNupkgDir, string repoName, string repoUri)
var settings = NuGet.Configuration.Settings.LoadDefaultSettings(null, null, null);
ILogger log = new NuGetLogger();
- var success = true;
+ var success = false;
try
{
PushRunner.Run(
@@ -916,6 +1023,7 @@ private void PushNupkg(string outputNupkgDir, string repoName, string repoUri)
}
catch (HttpRequestException e)
{
+ WriteVerbose(string.Format("Not able to publish resource to '{0}'", repoUri));
// look in PS repo for how httpRequestExceptions are handled
// Unfortunately there is no response message are no status codes provided with the exception and no
@@ -927,49 +1035,48 @@ private void PushNupkg(string outputNupkgDir, string repoName, string repoUri)
var message = String.Format("{0} Please try running again with the -ApiKey parameter and specific API key for the repository specified.", e.Message);
ex = new ArgumentException(message);
var ApiKeyError = new ErrorRecord(ex, "ApiKeyError", ErrorCategory.AuthenticationError, null);
- WriteError(ApiKeyError);
+ error = ApiKeyError;
}
else
{
var Error401 = new ErrorRecord(ex, "401Error", ErrorCategory.PermissionDenied, null);
- WriteError(Error401);
+ error = Error401;
}
}
else if (e.Message.Contains("403"))
{
var Error403 = new ErrorRecord(ex, "403Error", ErrorCategory.PermissionDenied, null);
- WriteError(Error403);
+ error = Error403;
}
else if (e.Message.Contains("409"))
{
var Error409 = new ErrorRecord(ex, "409Error", ErrorCategory.PermissionDenied, null);
- WriteError(Error409);
+ error = Error409;
}
else
{
var HTTPRequestError = new ErrorRecord(ex, "HTTPRequestError", ErrorCategory.PermissionDenied, null);
- WriteError(HTTPRequestError);
+ error = HTTPRequestError;
}
- success = false;
+ return success;
}
catch (Exception e)
{
+ WriteVerbose(string.Format("Not able to publish resource to '{0}'", repoUri));
var ex = new ArgumentException(e.Message);
var PushNupkgError = new ErrorRecord(ex, "PushNupkgError", ErrorCategory.InvalidResult, null);
- WriteError(PushNupkgError);
+ error = PushNupkgError;
- success = false;
+ return success;
}
- if (success)
- {
- WriteVerbose(string.Format("Successfully published the resource to '{0}'", repoUri));
- }
- else
- {
- WriteVerbose(string.Format("Not able to publish resource to '{0}'", repoUri));
- }
+
+ WriteVerbose(string.Format("Successfully published the resource to '{0}'", repoUri));
+ error = null;
+ success = true;
+ return success;
+
}
}
diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1
index 8982e6af7..e72984f60 100644
--- a/test/PSGetTestUtils.psm1
+++ b/test/PSGetTestUtils.psm1
@@ -315,18 +315,22 @@ function Get-ScriptResourcePublishedToLocalRepoTestDrive
{
Param(
[string]
- $scriptName
+ $scriptName,
+
+ [string]
+ $scriptRepoName,
+
+ [string]
+ $scriptVersion
)
Get-TestDriveSetUp
$scriptFilePath = Join-Path -Path $script:testIndividualResourceFolder -ChildPath "$scriptName.ps1"
$null = New-Item -Path $scriptFilePath -ItemType File -Force
- $version = "1.0.0"
$params = @{
- #Path = $scriptFilePath
- Version = $version
- #GUID =
+ Version = $scriptVersion
+ GUID = [guid]::NewGuid()
Author = 'Jane'
CompanyName = 'Microsoft Corporation'
Copyright = '(c) 2020 Microsoft Corporation. All rights reserved.'
@@ -334,14 +338,13 @@ function Get-ScriptResourcePublishedToLocalRepoTestDrive
LicenseUri = "https://$scriptName.com/license"
IconUri = "https://$scriptName.com/icon"
ProjectUri = "https://$scriptName.com"
- Tags = @('Tag1','Tag2', "Tag-$scriptName-$version")
+ Tags = @('Tag1','Tag2', "Tag-$scriptName-$scriptVersion")
ReleaseNotes = "$scriptName release notes"
}
$scriptMetadata = Create-PSScriptMetadata @params
Set-Content -Path $scriptFilePath -Value $scriptMetadata
-
- Publish-PSResource -path $scriptFilePath -Repository psgettestlocal
+ Publish-PSResource -Path $scriptFilePath -Repository $scriptRepoName -Verbose
}
function Get-CommandResourcePublishedToLocalRepoTestDrive
@@ -507,8 +510,6 @@ function Create-PSScriptMetadata
.COPYRIGHT$(if ($Copyright) {" $Copyright"})
-.DESCRIPTION$(if ($Description) {" $Description"})
-
.TAGS$(if ($Tags) {" $Tags"})
.LICENSEURI$(if ($LicenseUri) {" $LicenseUri"})
@@ -528,6 +529,13 @@ $($ReleaseNotes -join "`r`n")
.PRIVATEDATA$(if ($PrivateData) {" $PrivateData"})
+#>
+
+<#
+
+.DESCRIPTION
+$(if ($Description) {" $Description"})
+
#>
"@
return $PSScriptInfoString
diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1
index 615271346..f2368978c 100644
--- a/test/PublishPSResource.Tests.ps1
+++ b/test/PublishPSResource.Tests.ps1
@@ -41,20 +41,32 @@ Describe "Test Publish-PSResource" {
$script:destinationPath = [IO.Path]::GetFullPath((Join-Path -Path $TestDrive -ChildPath "tmpDestinationPath"))
New-Item $script:destinationPath -ItemType directory -Force
- # Path to folder, within our test folder, where we store invalid module files used for testing
+ #Create folder where we shall place all script files to be published for these tests
+ $script:tmpScriptsFolderPath = Join-Path -Path $TestDrive -ChildPath "tmpScriptsPath"
+ if(!(Test-Path $script:tmpScriptsFolderPath))
+ {
+ New-Item -Path $script:tmpScriptsFolderPath -ItemType Directory -Force
+ }
+
+ # 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 modules folder
$script:testModulesFolderPath = Join-Path $testFilesFolderPath -ChildPath "testModules"
+
+ # Path to specifically to that invalid test scripts folder
+ $script:testScriptsFolderPath = Join-Path $testFilesFolderPath -ChildPath "testScripts"
}
AfterAll {
- # Get-RevertPSResourceRepositoryFile
+ Get-RevertPSResourceRepositoryFile
}
AfterEach {
- # Delete all contents of the repository without deleting the repository directory itself
- # $pkgsToDelete = Join-Path -Path "$script:repositoryPath" -ChildPath "*"
- # Remove-Item $pkgsToDelete -Recurse
+ # Delete all contents of the repository without deleting the repository directory itself
+ $pkgsToDelete = Join-Path -Path "$script:repositoryPath" -ChildPath "*"
+ Remove-Item $pkgsToDelete -Recurse
- # $pkgsToDelete = Join-Path -Path "$script:repositoryPath2" -ChildPath "*"
- # Remove-Item $pkgsToDelete -Recurse
+ $pkgsToDelete = Join-Path -Path "$script:repositoryPath2" -ChildPath "*"
+ Remove-Item $pkgsToDelete -Recurse
}
It "Publish a module with -Path to the highest priority repo" {
@@ -290,6 +302,98 @@ Describe "Test Publish-PSResource" {
(Get-ChildItem $script:repositoryPath2).FullName | Should -Be $expectedPath
}
+ It "publish a script locally"{
+ $scriptName = "PSGetTestScript"
+ $scriptVersion = "1.0.0"
+
+ $params = @{
+ Version = $scriptVersion
+ GUID = [guid]::NewGuid()
+ Author = 'Jane'
+ CompanyName = 'Microsoft Corporation'
+ Copyright = '(c) 2020 Microsoft Corporation. All rights reserved.'
+ Description = "Description for the $scriptName script"
+ LicenseUri = "https://$scriptName.com/license"
+ IconUri = "https://$scriptName.com/icon"
+ ProjectUri = "https://$scriptName.com"
+ Tags = @('Tag1','Tag2', "Tag-$scriptName-$scriptVersion")
+ ReleaseNotes = "$scriptName release notes"
+ }
+
+ $scriptPath = (Join-Path -Path $script:tmpScriptsFolderPath -ChildPath "$scriptName.ps1")
+ New-ScriptFileInfo @params -Path $scriptPath
+
+ Publish-PSResource -Path $scriptPath
+
+ $expectedPath = Join-Path -Path $script:repositoryPath -ChildPath "$scriptName.$scriptVersion.nupkg"
+ (Get-ChildItem $script:repositoryPath).FullName | Should -Be $expectedPath
+ }
+
+ It "should write error and not publish script when Author property is missing" {
+ $scriptName = "InvalidScriptMissingAuthor.ps1"
+ $scriptVersion = "1.0.0"
+
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+ Publish-PSResource -Path $scriptFilePath -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "MissingAuthorInScriptMetadata,Microsoft.PowerShell.PowerShellGet.Cmdlets.PublishPSResource"
+
+ $publishedPath = Join-Path -Path $script:repositoryPath -ChildPath "$scriptName.$scriptVersion.nupkg"
+ Test-Path -Path $publishedPath | Should -Be $false
+ }
+
+ It "should write error and not publish script when Version property is missing" {
+ $scriptName = "InvalidScriptMissingVersion.ps1"
+
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+ Publish-PSResource -Path $scriptFilePath -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "MissingVersionInScriptMetadata,Microsoft.PowerShell.PowerShellGet.Cmdlets.PublishPSResource"
+
+ $publishedPkgs = Get-ChildItem -Path $script:repositoryPath -Filter *.nupkg
+ $publishedPkgs.Count | Should -Be 0
+ }
+
+ It "should write error and not publish script when Guid property is missing" {
+ $scriptName = "InvalidScriptMissingGuid.ps1"
+ $scriptVersion = "1.0.0"
+
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+ Publish-PSResource -Path $scriptFilePath -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "MissingGuidInScriptMetadata,Microsoft.PowerShell.PowerShellGet.Cmdlets.PublishPSResource"
+
+ $publishedPath = Join-Path -Path $script:repositoryPath -ChildPath "$scriptName.$scriptVersion.nupkg"
+ Test-Path -Path $publishedPath | Should -Be $false
+ }
+
+ It "should write error and not publish script when Description property is missing" {
+ $scriptName = "InvalidScriptMissingDescription.ps1"
+ $scriptVersion = "1.0.0"
+
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+ Publish-PSResource -Path $scriptFilePath -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "MissingOrInvalidDescriptionInScriptMetadata,Microsoft.PowerShell.PowerShellGet.Cmdlets.PublishPSResource"
+
+ $publishedPath = Join-Path -Path $script:repositoryPath -ChildPath "$scriptName.$scriptVersion.nupkg"
+ Test-Path -Path $publishedPath | Should -Be $false
+ }
+
+ It "should write error and not publish script when Description block altogether is missing" {
+ # we expect .ps1 files to have a separate comment block for .DESCRIPTION property, not to be included in the PSScriptInfo commment block
+ $scriptName = "InvalidScriptMissingDescriptionCommentBlock.ps1"
+ $scriptVersion = "1.0.0"
+
+ $scriptFilePath = Join-Path $script:testScriptsFolderPath -ChildPath $scriptName
+ Publish-PSResource -Path $scriptFilePath -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -Not -Be 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "PSScriptMissingHelpContentCommentBlock,Microsoft.PowerShell.PowerShellGet.Cmdlets.PublishPSResource"
+
+ $publishedPath = Join-Path -Path $script:repositoryPath -ChildPath "$scriptName.$scriptVersion.nupkg"
+ Test-Path -Path $publishedPath | Should -Be $false
+ }
+
It "Publish a module with that has an invalid version format, should throw" {
$moduleName = "incorrectmoduleversion"
$incorrectmoduleversion = Join-Path -Path $script:testModulesFolderPath -ChildPath $moduleName
diff --git a/test/testFiles/testScripts/InvalidScriptMissingAuthor.ps1 b/test/testFiles/testScripts/InvalidScriptMissingAuthor.ps1
new file mode 100644
index 000000000..11ecd964c
--- /dev/null
+++ b/test/testFiles/testScripts/InvalidScriptMissingAuthor.ps1
@@ -0,0 +1,43 @@
+
+<#PSScriptInfo
+
+.VERSION 1.0
+
+.GUID 3951be04-bd06-4337-8dc3-a620bf539fbd
+
+.AUTHOR
+
+.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()
+
+
diff --git a/test/testFiles/testScripts/InvalidScriptMissingDescription.ps1 b/test/testFiles/testScripts/InvalidScriptMissingDescription.ps1
new file mode 100644
index 000000000..cdfed58dc
--- /dev/null
+++ b/test/testFiles/testScripts/InvalidScriptMissingDescription.ps1
@@ -0,0 +1,42 @@
+
+<#PSScriptInfo
+
+.VERSION 1.0
+
+.GUID 3951be04-bd06-4337-8dc3-a620bf539fbd
+
+.AUTHOR annavied
+
+.COMPANYNAME
+
+.COPYRIGHT
+
+.TAGS
+
+.LICENSEURI
+
+.PROJECTURI
+
+.ICONURI
+
+.EXTERNALMODULEDEPENDENCIES
+
+.REQUIREDSCRIPTS
+
+.EXTERNALSCRIPTDEPENDENCIES
+
+.RELEASENOTES
+
+
+.PRIVATEDATA
+
+#>
+
+<#
+
+.DESCRIPTION
+
+#>
+Param()
+
+
diff --git a/test/testFiles/testScripts/InvalidScriptMissingDescriptionCommentBlock.ps1 b/test/testFiles/testScripts/InvalidScriptMissingDescriptionCommentBlock.ps1
new file mode 100644
index 000000000..b3cd98e25
--- /dev/null
+++ b/test/testFiles/testScripts/InvalidScriptMissingDescriptionCommentBlock.ps1
@@ -0,0 +1,37 @@
+
+<#PSScriptInfo
+
+.VERSION 1.0
+
+.GUID 3951be04-bd06-4337-8dc3-a620bf539fbd
+
+.AUTHOR annavied
+
+.COMPANYNAME
+
+.COPYRIGHT
+
+.TAGS
+
+.LICENSEURI
+
+.PROJECTURI
+
+.ICONURI
+
+.EXTERNALMODULEDEPENDENCIES
+
+.REQUIREDSCRIPTS
+
+.EXTERNALSCRIPTDEPENDENCIES
+
+.RELEASENOTES
+
+
+.PRIVATEDATA
+
+#>
+
+Param()
+
+
diff --git a/test/testFiles/testScripts/InvalidScriptMissingGuid.ps1 b/test/testFiles/testScripts/InvalidScriptMissingGuid.ps1
new file mode 100644
index 000000000..7cf637efc
--- /dev/null
+++ b/test/testFiles/testScripts/InvalidScriptMissingGuid.ps1
@@ -0,0 +1,43 @@
+
+<#PSScriptInfo
+
+.VERSION 1.0
+
+.GUID
+
+.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()
+
+
diff --git a/test/testFiles/testScripts/InvalidScriptMissingVersion.ps1 b/test/testFiles/testScripts/InvalidScriptMissingVersion.ps1
new file mode 100644
index 000000000..1c05323f3
--- /dev/null
+++ b/test/testFiles/testScripts/InvalidScriptMissingVersion.ps1
@@ -0,0 +1,43 @@
+
+<#PSScriptInfo
+
+.VERSION
+
+.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()
+
+