diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 752255eae..714516e70 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -115,7 +115,7 @@ public PSCredential ProxyCredential { #region Members - private string _path; + private string resolvedPath; private CancellationToken _cancellationToken; private NuGetVersion _pkgVersion; private string _pkgName; @@ -123,6 +123,10 @@ public PSCredential ProxyCredential { 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 @@ -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)) @@ -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) @@ -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 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); @@ -235,7 +279,7 @@ protected override void EndProcessing() string[] errorMsgs = null; try { - Utils.ValidateModuleManifest(resourceFilePath, out errorMsgs); + Utils.ValidateModuleManifest(pathToModuleManifestToPublish, out errorMsgs); } finally { @@ -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 @@ -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); } @@ -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)) + { + 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)); } } @@ -419,7 +476,6 @@ protected override void EndProcessing() private string CreateNuspec( string outputDir, string filePath, - bool isScript, Hashtable parsedMetadataHash, out Hashtable requiredModules) { @@ -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( @@ -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) @@ -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 { @@ -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) diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 66beebdf5..1ddbdb226 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -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 GetPathsFromEnvVarAndScope( PSCmdlet psCmdlet, ScopeType? scope) - { - GetStandardPlatformPaths( - psCmdlet, - out string myDocumentsPath, + { + GetStandardPlatformPaths( + psCmdlet, + out string myDocumentsPath, out string programFilesPath); List resourcePaths = new List(); @@ -653,7 +653,7 @@ public static List GetPathsFromEnvVarAndScope( public static List GetAllResourcePaths( PSCmdlet psCmdlet, ScopeType? scope = null) - { + { List resourcePaths = GetPathsFromEnvVarAndScope(psCmdlet, scope); // resourcePaths should now contain, eg: @@ -703,7 +703,7 @@ public static List GetAllResourcePaths( public static List GetAllInstallationPaths( PSCmdlet psCmdlet, ScopeType? scope) - { + { List installationPaths = GetPathsFromEnvVarAndScope(psCmdlet, scope); installationPaths = installationPaths.Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1 index f2368978c..f1b1baf22 100644 --- a/test/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResource.Tests.ps1 @@ -67,6 +67,9 @@ Describe "Test Publish-PSResource" { $pkgsToDelete = Join-Path -Path "$script:repositoryPath2" -ChildPath "*" Remove-Item $pkgsToDelete -Recurse + + $pkgsToDelete = Join-Path -Path $script:PublishModuleBase -ChildPath "*" + Remove-Item $pkgsToDelete -Recurse -ErrorAction SilentlyContinue } It "Publish a module with -Path to the highest priority repo" { @@ -89,6 +92,52 @@ Describe "Test Publish-PSResource" { (Get-ChildItem $script:repositoryPath2).FullName | Should -Be $expectedPath } + It "Publish a module with -Path pointing to a module directory (parent directory has same name)" { + $version = "1.0.0" + New-ModuleManifest -Path (Join-Path -Path $script:PublishModuleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version -Description "$script:PublishModuleName module" + + Publish-PSResource -Path $script:PublishModuleBase -Repository $testRepository2 + + $expectedPath = Join-Path -Path $script:repositoryPath2 -ChildPath "$script:PublishModuleName.$version.nupkg" + (Get-ChildItem $script:repositoryPath2).FullName | Should -Be $expectedPath + } + + It "Publish a module with -Path pointing to a module directory (parent directory has different name)" { + $version = "1.0.0" + $newModuleRoot = Join-Path -Path $script:PublishModuleBase -ChildPath "NewTestParentDirectory" + New-Item -Path $newModuleRoot -ItemType Directory + New-ModuleManifest -Path (Join-Path -Path $newModuleRoot -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version -Description "$script:PublishModuleName module" + + Publish-PSResource -Path $newModuleRoot -Repository $testRepository2 + + $expectedPath = Join-Path -Path $script:repositoryPath2 -ChildPath "$script:PublishModuleName.$version.nupkg" + (Get-ChildItem $script:repositoryPath2).FullName | Should -Be $expectedPath + } + + It "Publish a module with -Path pointing to a .psd1 (parent directory has same name)" { + $version = "1.0.0" + $manifestPath = Join-Path -Path $script:PublishModuleBase -ChildPath "$script:PublishModuleName.psd1" + New-ModuleManifest -Path $manifestPath -ModuleVersion $version -Description "$script:PublishModuleName module" + + Publish-PSResource -Path $manifestPath -Repository $testRepository2 + + $expectedPath = Join-Path -Path $script:repositoryPath2 -ChildPath "$script:PublishModuleName.$version.nupkg" + (Get-ChildItem $script:repositoryPath2).FullName | Should -Be $expectedPath + } + + It "Publish a module with -Path pointing to a .psd1 (parent directory has different name)" { + $version = "1.0.0" + $newModuleRoot = Join-Path -Path $script:PublishModuleBase -ChildPath "NewTestParentDirectory" + New-Item -Path $newModuleRoot -ItemType Directory + $manifestPath = Join-Path -Path $newModuleRoot -ChildPath "$script:PublishModuleName.psd1" + New-ModuleManifest -Path $manifestPath -ModuleVersion $version -Description "$script:PublishModuleName module" + + Publish-PSResource -Path $manifestPath -Repository $testRepository2 + + $expectedPath = Join-Path -Path $script:repositoryPath2 -ChildPath "$script:PublishModuleName.$version.nupkg" + (Get-ChildItem $script:repositoryPath2).FullName | Should -Be $expectedPath + } + It "Publish a module with dependencies" { # Create dependency module $dependencyVersion = "2.0.0"