Skip to content

Expand acceptable Path arguments for Publish-PSResource #704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 18, 2022
187 changes: 121 additions & 66 deletions src/code/PublishPSResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,18 @@ public PSCredential ProxyCredential {

#region Members

private string _path;
private string resolvedPath;
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";
private string pathToScriptFileToPublish = string.Empty;
private string pathToModuleManifestToPublish = string.Empty;
private string pathToModuleDirToPublish = string.Empty;
private ResourceType resourceType = ResourceType.None;

#endregion

Expand All @@ -136,22 +140,49 @@ protected override void BeginProcessing()
// 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)))
try
{
// 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;
resolvedPath = SessionState.Path.GetResolvedPSPathFromPSPath(Path).First().Path;
}
else
catch (MethodInvocationException)
{
// path does not exist
ThrowTerminatingError(
new ErrorRecord(
new ArgumentException(
"The path to the resource to publish does not exist, point to an existing path or file of the module or script to publish."),
"SourcePathDoesNotExist",
ErrorCategory.InvalidArgument,
this));
}

// Condition 1: path is to the root directory of the module to be published
// Condition 2: path is to the .psd1 or .ps1 of the module/script to be published
if (string.IsNullOrEmpty(resolvedPath))
{
// 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);
ThrowTerminatingError(
new ErrorRecord(
new ArgumentException(
"The path to the resource to publish is not in the correct format, point to a path or file of the module or script to publish."),
"InvalidSourcePath",
ErrorCategory.InvalidArgument,
this));
}
else if (Directory.Exists(resolvedPath))
{
pathToModuleDirToPublish = resolvedPath;
resourceType = ResourceType.Module;
}
else if (resolvedPath.EndsWith(PSDataFileExt, StringComparison.OrdinalIgnoreCase))
{
pathToModuleManifestToPublish = resolvedPath;
resourceType = ResourceType.Module;
}
else if (resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase))
{
pathToScriptFileToPublish = resolvedPath;
resourceType = ResourceType.Script;
}

if (!String.IsNullOrEmpty(DestinationPath))
Expand Down Expand Up @@ -182,25 +213,19 @@ protected override void BeginProcessing()
protected override void EndProcessing()
{
// Returns the name of the file or the name of the directory, depending on path
var pkgFileOrDir = new DirectoryInfo(_path);
bool isScript = _path.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase);

if (!ShouldProcess(string.Format("Publish resource '{0}' from the machine", _path)))
if (!ShouldProcess(string.Format("Publish resource '{0}' from the machine", resolvedPath)))
{
WriteVerbose("ShouldProcess is set to false.");
return;
}

string resourceFilePath;
Hashtable parsedMetadata;
if (isScript)
if (resourceType == ResourceType.Script)
{
resourceFilePath = pkgFileOrDir.FullName;

// Check that script metadata is valid
if (!TryParseScriptMetadata(
out parsedMetadata,
resourceFilePath,
pathToScriptFileToPublish,
out ErrorRecord[] errors))
{
foreach (ErrorRecord err in errors)
Expand All @@ -211,19 +236,38 @@ protected override void EndProcessing()
return;
}

// remove '.ps1' extension from file name
_pkgName = pkgFileOrDir.Name.Remove(pkgFileOrDir.Name.Length - 4);
_pkgName = System.IO.Path.GetFileNameWithoutExtension(pathToScriptFileToPublish);
}
else
{
_pkgName = pkgFileOrDir.Name;
resourceFilePath = System.IO.Path.Combine(_path, _pkgName + PSDataFileExt);
// parsedMetadata needs to be initialized for modules, will later be passed in to create nuspec
parsedMetadata = new Hashtable();
if (!string.IsNullOrEmpty(pathToModuleManifestToPublish))
{
_pkgName = System.IO.Path.GetFileNameWithoutExtension(pathToModuleManifestToPublish);
}
else {
// directory
// search for module manifest
List<FileInfo> childFiles = new DirectoryInfo(pathToModuleDirToPublish).EnumerateFiles().ToList();

foreach (FileInfo file in childFiles)
{
if (file.Name.EndsWith(PSDataFileExt, StringComparison.OrdinalIgnoreCase))
{
pathToModuleManifestToPublish = file.FullName;
_pkgName = System.IO.Path.GetFileNameWithoutExtension(file.Name);

break;
}
}
}

// Validate that there's a module manifest
if (!File.Exists(resourceFilePath))
if (!File.Exists(pathToModuleManifestToPublish))
{
var message = String.Format("No file with a .psd1 extension was found in {0}. Please specify a path to a valid modulemanifest.", resourceFilePath);
var message = String.Format("No file with a .psd1 extension was found in {0}. Please specify a path to a valid modulemanifest.", pathToModuleManifestToPublish);

var ex = new ArgumentException(message);
var moduleManifestNotFound = new ErrorRecord(ex, "moduleManifestNotFound", ErrorCategory.ObjectNotFound, null);
WriteError(moduleManifestNotFound);
Expand All @@ -235,7 +279,7 @@ protected override void EndProcessing()
string[] errorMsgs = null;
try
{
Utils.ValidateModuleManifest(resourceFilePath, out errorMsgs);
Utils.ValidateModuleManifest(pathToModuleManifestToPublish, out errorMsgs);

}
finally {
Expand All @@ -252,22 +296,19 @@ protected override void EndProcessing()

// Create a temp folder to push the nupkg to and delete it later
string outputDir = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString());
if (!Directory.Exists(outputDir))
try
{
try
{
Directory.CreateDirectory(outputDir);
}
catch (Exception e)
{
var ex = new ArgumentException(e.Message);
var ErrorCreatingTempDir = new ErrorRecord(ex, "ErrorCreatingTempDir", ErrorCategory.InvalidData, null);
WriteError(ErrorCreatingTempDir);

return;
}
Directory.CreateDirectory(outputDir);
}
catch (Exception e)
{
var ex = new ArgumentException(e.Message);
var ErrorCreatingTempDir = new ErrorRecord(ex, "ErrorCreatingTempDir", ErrorCategory.InvalidData, null);
WriteError(ErrorCreatingTempDir);

return;
}

try
{
// Create a nuspec
Expand All @@ -278,8 +319,7 @@ protected override void EndProcessing()
{
nuspec = CreateNuspec(
outputDir: outputDir,
filePath: resourceFilePath,
isScript: isScript,
filePath: (resourceType == ResourceType.Script) ? pathToScriptFileToPublish : pathToModuleManifestToPublish,
parsedMetadataHash: parsedMetadata,
requiredModules: out dependencies);
}
Expand Down Expand Up @@ -334,33 +374,50 @@ protected override void EndProcessing()
}
}

if (isScript)
if (resourceType == ResourceType.Script)
{
// copy the script file to the temp directory
File.Copy(_path, System.IO.Path.Combine(outputDir, _pkgName + PSScriptFileExt), true);
File.Copy(pathToScriptFileToPublish, System.IO.Path.Combine(outputDir, _pkgName + PSScriptFileExt), true);

}
else
{
// Create subdirectory structure in temp folder
foreach (string dir in System.IO.Directory.GetDirectories(_path, "*", System.IO.SearchOption.AllDirectories))
try
{
var dirName = dir.Substring(_path.Length).Trim(_PathSeparators);
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(outputDir, dirName));
}
// If path is pointing to a file, get the parent directory, otherwise assumption is that path is pointing to the root directory
string rootModuleDir = !string.IsNullOrEmpty(pathToModuleManifestToPublish) ? System.IO.Path.GetDirectoryName(pathToModuleManifestToPublish) : pathToModuleDirToPublish;

// Copy files over to temp folder
foreach (string fileNamePath in System.IO.Directory.GetFiles(_path, "*", System.IO.SearchOption.AllDirectories))
{
var fileName = fileNamePath.Substring(_path.Length).Trim(_PathSeparators);
// Create subdirectory structure in temp folder
foreach (string dir in System.IO.Directory.GetDirectories(rootModuleDir, "*", System.IO.SearchOption.AllDirectories))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should make this CopyDir Utility method public and use it here? It would be nice to not duplicate code if we can help it.

https://github.com/PowerShell/PowerShellGet/blob/115343118fb9eb794b927bb78aa20c031768d900/src/code/Utils.cs#L1135

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a small issue with the Utility method, which is that it doesn't ignore the .nuspec file if it sees it. The way the publish 'copy' function currently works is that it prioritizes any .nuspec file that it sees in the publish folder, so that if the user specifically wants to use their own nuspec file they can. We can get rid of this and only use the PSGet generated .nuspec file and if there's feedback that the community wants that functionality, add it back in at that point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the utility method is not suitable then it is fine to have your own custom code.

{
DirectoryInfo dirInfo = new DirectoryInfo(dir);
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(outputDir, dirInfo.Name));
}

// The user may have a .nuspec defined in the module directory
// If that's the case, we will not use that file and use the .nuspec that is generated via PSGet
// The .nuspec that is already in in the output directory is the one that was generated via the CreateNuspec method
var newFilePath = System.IO.Path.Combine(outputDir, fileName);
if (!File.Exists(newFilePath))
// Copy files over to temp folder
foreach (string fileNamePath in System.IO.Directory.GetFiles(rootModuleDir, "*", System.IO.SearchOption.AllDirectories))
{
System.IO.File.Copy(fileNamePath, newFilePath);
FileInfo fileInfo = new FileInfo(fileNamePath);

var newFilePath = System.IO.Path.Combine(outputDir, fileInfo.Name);
// The user may have a .nuspec defined in the module directory
// If that's the case, we will not use that file and use the .nuspec that is generated via PSGet
// The .nuspec that is already in in the output directory is the one that was generated via the CreateNuspec method
if (!File.Exists(newFilePath))
{
System.IO.File.Copy(fileNamePath, newFilePath);
}
}


}
catch (Exception e)
{
ThrowTerminatingError(new ErrorRecord(
new ArgumentException("Error occured while creating directory to publish: " + e.Message),
"ErrorCreatingDirectoryToPublish",
ErrorCategory.InvalidOperation,
this));
}
}

Expand Down Expand Up @@ -419,7 +476,6 @@ protected override void EndProcessing()
private string CreateNuspec(
string outputDir,
string filePath,
bool isScript,
Hashtable parsedMetadataHash,
out Hashtable requiredModules)
{
Expand All @@ -428,7 +484,7 @@ private string CreateNuspec(

// A script will already have the metadata parsed into the parsedMetadatahash,
// a module will still need the module manifest to be parsed.
if (!isScript)
if (resourceType == ResourceType.Module)
{
// Use the parsed module manifest data as 'parsedMetadataHash' instead of the passed-in data.
if (!Utils.TryReadManifestFile(
Expand Down Expand Up @@ -545,7 +601,7 @@ private string CreateNuspec(
metadataElementsDictionary.Add("copyright", parsedMetadataHash["copyright"].ToString().Trim());
}

string tags = isScript ? "PSScript" : "PSModule";
string tags = (resourceType == ResourceType.Script) ? "PSScript" : "PSModule";
if (parsedMetadataHash.ContainsKey("tags"))
{
if (parsedMetadataHash["tags"] != null)
Expand Down Expand Up @@ -961,7 +1017,6 @@ private bool PushNupkg(string outputNupkgDir, string repoName, string repoUri, o
string publishLocation = repoUri.EndsWith("/v2", StringComparison.OrdinalIgnoreCase) ? repoUri + "/package" : repoUri;

var settings = NuGet.Configuration.Settings.LoadDefaultSettings(null, null, null);
ILogger log = new NuGetLogger();
var success = false;
try
{
Expand All @@ -978,7 +1033,7 @@ private bool PushNupkg(string outputNupkgDir, string repoName, string repoUri, o
noSymbols: false,
noServiceEndpoint: false, // enable server endpoint
skipDuplicate: false, // if true-- if a package and version already exists, skip it and continue with the next package in the push, if any.
logger: log // nuget logger
logger: NullLogger.Instance // nuget logger
).GetAwaiter().GetResult();
}
catch (HttpRequestException e)
Expand Down
18 changes: 9 additions & 9 deletions src/code/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -614,16 +614,16 @@ public static string GetInstalledPackageName(string pkgPath)
// expecting the full version module path
// ex: ./PowerShell/Modules/TestModule/1.0.0
return new DirectoryInfo(pkgPath).Parent.Name;
}
// Find all potential resource paths
}

// Find all potential resource paths
public static List<string> GetPathsFromEnvVarAndScope(
PSCmdlet psCmdlet,
ScopeType? scope)
{
GetStandardPlatformPaths(
psCmdlet,
out string myDocumentsPath,
{
GetStandardPlatformPaths(
psCmdlet,
out string myDocumentsPath,
out string programFilesPath);

List<string> resourcePaths = new List<string>();
Expand Down Expand Up @@ -653,7 +653,7 @@ public static List<string> GetPathsFromEnvVarAndScope(
public static List<string> GetAllResourcePaths(
PSCmdlet psCmdlet,
ScopeType? scope = null)
{
{
List<String> resourcePaths = GetPathsFromEnvVarAndScope(psCmdlet, scope);

// resourcePaths should now contain, eg:
Expand Down Expand Up @@ -703,7 +703,7 @@ public static List<string> GetAllResourcePaths(
public static List<string> GetAllInstallationPaths(
PSCmdlet psCmdlet,
ScopeType? scope)
{
{
List<String> installationPaths = GetPathsFromEnvVarAndScope(psCmdlet, scope);

installationPaths = installationPaths.Distinct(StringComparer.InvariantCultureIgnoreCase).ToList();
Expand Down
Loading