From 560835de46db00c9b34719806f4ccd33fd35f4da Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Mon, 2 May 2022 23:11:31 -0400 Subject: [PATCH 01/22] uninstall specify version for perf increase --- test/UninstallPSResource.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/UninstallPSResource.Tests.ps1 b/test/UninstallPSResource.Tests.ps1 index 54cfeb062..20992250a 100644 --- a/test/UninstallPSResource.Tests.ps1 +++ b/test/UninstallPSResource.Tests.ps1 @@ -16,11 +16,11 @@ Describe 'Test Uninstall-PSResource for Modules' { } BeforeEach { - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue + $null = Install-PSResource $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue } AfterEach { - Uninstall-PSResource -Name $testModuleName -Version "*" + Uninstall-PSResource -Name $testModuleName -Version "5.0.0.0" } AfterAll { @@ -28,7 +28,7 @@ Describe 'Test Uninstall-PSResource for Modules' { } It "Uninstall a specific module by name" { - Uninstall-PSResource -name $testModuleName + Uninstall-PSResource -name $testModuleName -Version "5.0.0.0" Get-PSResource $testModuleName | Should -BeNullOrEmpty } From b015489ff4b8d76af392f0bdf2ba2d56d97215ee Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Thu, 5 May 2022 00:34:11 -0400 Subject: [PATCH 02/22] support for publishing scripts with correct ps1 parsing --- src/code/PublishPSResource.cs | 172 ++++++++++- test/PSGetTestUtils.psm1 | 52 +++- test/UninstallPSResource.Tests.ps1 | 477 +++++++++++++++-------------- 3 files changed, 444 insertions(+), 257 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index bd9f46dd6..3470f5e5b 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -17,6 +17,7 @@ using System.Management.Automation; using System.Management.Automation.Language; using System.Net.Http; +using System.Text.RegularExpressions; using System.Xml; namespace Microsoft.PowerShell.PowerShellGet.Cmdlets @@ -61,10 +62,12 @@ public string Path if (Directory.Exists(resolvedPath)) { + // we point to a folder when publishing a module _path = resolvedPath; } - else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSDataFileExt, StringComparison.OrdinalIgnoreCase)) + else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)) { + // we can point to .ps1 file directly when publishing a script, but not to .psd1 file (for publishing a module) _path = resolvedPath; } } @@ -197,31 +200,41 @@ protected override void ProcessRecord() // Check that script metadata is valid // ParseScriptMetadata will write non-terminating error if it's unsuccessful in parsing - parsedMetadataHash = ParseScriptMetadata(resourceFilePath); + // parsedMetadataHash = ParseScriptMetadata(resourceFilePath); + parsedMetadataHash = ParseScriptMetadataIntoHash(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())) + 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 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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); WriteError(InvalidScriptMetadata); return; } - if (!parsedMetadataHash.ContainsKey("Author") || String.IsNullOrWhiteSpace(parsedMetadataHash["Author"].ToString())) + if (!parsedMetadataHash.ContainsKey("author") || String.IsNullOrWhiteSpace(parsedMetadataHash["author"].ToString())) { - var message = "No author was provided in the script metadata. Script metadata must specify a version, author and description."; + 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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); WriteError(InvalidScriptMetadata); return; } - if (!parsedMetadataHash.ContainsKey("Description") || String.IsNullOrWhiteSpace(parsedMetadataHash["Description"].ToString())) + 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 message = "No description was provided in the script metadata. Script metadata must specify a version, author, description, and Guid."; + var ex = new ArgumentException(message); + var InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); + WriteError(InvalidScriptMetadata); + + return; + } + if (!parsedMetadataHash.ContainsKey("guid") || String.IsNullOrWhiteSpace(parsedMetadataHash["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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); WriteError(InvalidScriptMetadata); @@ -334,7 +347,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,10 +379,10 @@ protected override void ProcessRecord() // pack into a nupkg try { - if (!PackNupkg(outputDir, outputNupkgDir, nuspec)) - { - return; - } + if (!PackNupkg(outputDir, outputNupkgDir, nuspec)) + { + return; + } } catch (Exception e) { @@ -762,6 +775,7 @@ Example cmdlet here { // expecting only one or two comments var commentText = token.Text; + Console.WriteLine(commentText); parsedComments.AddRange(commentText.Split(new string[] { "\n\n" }, StringSplitOptions.RemoveEmptyEntries) ); } } @@ -783,6 +797,138 @@ Example cmdlet here return parsedMetadataHash; } + private Hashtable ParseScriptMetadataIntoHash(string filePath) + { + // public static bool TryParseScriptFile( + // string scriptFileInfoPath, + // out PSScriptFileInfo parsedScript, + // out ErrorRecord[] errors) + // { + Hashtable parsedPSScriptInfoHashtable = new Hashtable(); + // Parse the script file + var ast = Parser.ParseFile( + filePath, + out System.Management.Automation.Language.Token[] tokens, + out ParseError[] parserErrors); + + if (parserErrors.Length > 0 && !String.Equals(parserErrors[0].ErrorId, "WorkflowNotSupportedInPowerShellCore", StringComparison.OrdinalIgnoreCase)) + { + // TODO: do we want to completely ignore WorkFlowNotSupportedInPowerShellCore error (not even write) + // or do we want to write, but not return if it's just that error? + foreach (ParseError err in parserErrors) + { + 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); + WriteError(psScriptFileParseError); + return parsedPSScriptInfoHashtable; + + } + + } + else if (ast != null) + { + // Get the block/group comment beginning with <#PSScriptInfo + List commentTokens = tokens.Where(a => String.Equals(a.Kind.ToString(), "Comment", StringComparison.OrdinalIgnoreCase)).ToList(); + string commentPattern = "<#PSScriptInfo"; + 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); + WriteError(psCommentMissingError); + return parsedPSScriptInfoHashtable; + } + + 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) + { + // TODO: is it an error if the metadata property is empty? + for (int i = 1; i < commentLines.Count(); i++) + { + string line = commentLines[i]; + if (String.IsNullOrEmpty(line)) + { + continue; + } + + // 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; + parsedPSScriptInfoHashtable.Add(keyName, value); + } + } + } + + // get .DESCRIPTION comment + CommentHelpInfo scriptCommentInfo = ast.GetHelpContent(); + if (scriptCommentInfo != null) + { + if (!String.IsNullOrEmpty(scriptCommentInfo.Description) && !scriptCommentInfo.Description.Contains("<#") && !scriptCommentInfo.Description.Contains("#>")) + { + parsedPSScriptInfoHashtable.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, "psScriptDescriptionMissingOrInvalidDescription", ErrorCategory.ParserError, null); + WriteError(psScriptMissingDescriptionOrInvalidPropertyError); + return new Hashtable(); // TODO: Anam, or return hashtable populated with PSScriptCommentInfo metadata? + } + } + + return parsedPSScriptInfoHashtable; + + // // get RequiredModules + // ScriptRequirements parsedScriptRequirements = ast.ScriptRequirements; + // ReadOnlyCollection parsedModules = new List().AsReadOnly(); + + // if (parsedScriptRequirements != null && parsedScriptRequirements.RequiredModules != null) + // { + // parsedModules = parsedScriptRequirements.RequiredModules; + // parsedPSScriptInfoHashtable.Add("RequiredModules", parsedModules); + // } + + // string parsedVersion = (string) parsedPSScriptInfoHashtable["VERSION"]; + // string parsedAuthor = (string) parsedPSScriptInfoHashtable["AUTHOR"]; + // Guid parsedGuid = String.IsNullOrEmpty((string)parsedPSScriptInfoHashtable["GUID"]) ? Guid.NewGuid() : new Guid((string) parsedPSScriptInfoHashtable["GUID"]); + // if (String.IsNullOrEmpty(parsedVersion) || String.IsNullOrEmpty(parsedAuthor) || parsedGuid == Guid.Empty) + // { + // var message = String.Format("PSScript file is missing one of the following required properties: Version, Author, Guid"); + // var ex = new ArgumentException(message); + // var psScriptMissingRequiredPropertyError = new ErrorRecord(ex, "psScriptMissingRequiredProperty", ErrorCategory.ParserError, null); + // errorsList.Add(psScriptMissingRequiredPropertyError); + // successfullyParsed = false; + // return successfullyParsed; + // } + + + } + + 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); + WriteError(astCouldNotBeCreatedError); + return parsedPSScriptInfoHashtable; + } + private bool CheckDependenciesExist(Hashtable dependencies, string repositoryUri) { // Check to see that all dependencies are in the repository diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1 index 8982e6af7..965455cef 100644 --- a/test/PSGetTestUtils.psm1 +++ b/test/PSGetTestUtils.psm1 @@ -315,18 +315,27 @@ function Get-ScriptResourcePublishedToLocalRepoTestDrive { Param( [string] - $scriptName + $scriptName, + + [string] + $scriptRepoName, + + [string] + $scriptVersion ) Get-TestDriveSetUp + # $publishScriptBase = Join-Path $script:testIndividualResourceFolder $scriptName + # $null = New-Item -Path $publishScriptBase -ItemType Directory -Force + $scriptFilePath = Join-Path -Path $script:testIndividualResourceFolder -ChildPath "$scriptName.ps1" $null = New-Item -Path $scriptFilePath -ItemType File -Force - $version = "1.0.0" + # $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 +343,24 @@ 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 + Write-Host "metadata:" + Write-Host $scriptMetadata Set-Content -Path $scriptFilePath -Value $scriptMetadata - - Publish-PSResource -path $scriptFilePath -Repository psgettestlocal + Write-Host "in Utils" $scriptName + Write-Host $scriptFilePath + Write-Host $scriptRepoName + Write-Host $scriptVersion + $a = Get-PSResourceRepository $scriptRepoName + Write-Host "repo name" $a.Name + Write-Host "repo url:" $a.Uri + Write-Host "file dump:" + Write-Host (Get-Content $scriptFilePath) + Publish-PSResource -Path $scriptFilePath -Repository $scriptRepoName -Verbose } function Get-CommandResourcePublishedToLocalRepoTestDrive @@ -369,7 +388,10 @@ function Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName, [string] - $repoName + $repoName, + + [string] + $moduleVersion ) Get-TestDriveSetUp @@ -377,8 +399,8 @@ function Get-ModuleResourcePublishedToLocalRepoTestDrive $publishModuleBase = Join-Path $script:testIndividualResourceFolder $publishModuleName $null = New-Item -Path $publishModuleBase -ItemType Directory -Force - $version = "1.0" - New-ModuleManifest -Path (Join-Path -Path $publishModuleBase -ChildPath "$publishModuleName.psd1") -ModuleVersion $version -Description "$publishModuleName module" + # $version = "1.0" + New-ModuleManifest -Path (Join-Path -Path $publishModuleBase -ChildPath "$publishModuleName.psd1") -ModuleVersion $moduleVersion -Description "$publishModuleName module" Publish-PSResource -Path $publishModuleBase -Repository $repoName } @@ -507,8 +529,6 @@ function Create-PSScriptMetadata .COPYRIGHT$(if ($Copyright) {" $Copyright"}) -.DESCRIPTION$(if ($Description) {" $Description"}) - .TAGS$(if ($Tags) {" $Tags"}) .LICENSEURI$(if ($LicenseUri) {" $LicenseUri"}) @@ -525,9 +545,15 @@ function Create-PSScriptMetadata .RELEASENOTES $($ReleaseNotes -join "`r`n") - .PRIVATEDATA$(if ($PrivateData) {" $PrivateData"}) +#> + +<# + +.DESCRIPTION +$(if ($Description) {" $Description"}) + #> "@ return $PSScriptInfoString diff --git a/test/UninstallPSResource.Tests.ps1 b/test/UninstallPSResource.Tests.ps1 index 20992250a..d256c445d 100644 --- a/test/UninstallPSResource.Tests.ps1 +++ b/test/UninstallPSResource.Tests.ps1 @@ -13,14 +13,29 @@ Describe 'Test Uninstall-PSResource for Modules' { Get-NewPSResourceRepositoryFile Uninstall-PSResource -Name $testModuleName -Version "*" Uninstall-PSResource -Name $testScriptName -Version "*" + Register-LocalRepos + $publishModuleName = "TestMyLocalModule" + $repoName = "psgettestlocal" + $moduleVersion = "1.0.0.0" + Get-ModuleResourcePublishedToLocalRepoTestDrive $publishModuleName $repoName $moduleVersion + + $publishScriptName = "TestMyLocalScript" + $scriptVersion = "1.0.0.0" + Write-Host $publishScriptName + Write-Host $repoName + Write-Host $scriptVersion + Get-ScriptResourcePublishedToLocalRepoTestDrive $publishScriptName $repoName $scriptVersion } BeforeEach { - $null = Install-PSResource $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue + $null = Install-PSResource "TestMyLocalModule" -Version "1.0.0.0" -Repository "psgettestlocal" -TrustRepository -WarningAction SilentlyContinue + $null = Install-PSResource "TestMyLocalScript" -Version "1.0.0.0" -Repository "psgettestlocal" -TrustRepository -WarningAction SilentlyContinue } AfterEach { - Uninstall-PSResource -Name $testModuleName -Version "5.0.0.0" + Uninstall-PSResource -Name "TestMyLocalModule" -Version "1.0.0.0" + Uninstall-PSResource -Name "TestMyLocalScript" -Version "1.0.0.0" } AfterAll { @@ -28,243 +43,243 @@ Describe 'Test Uninstall-PSResource for Modules' { } It "Uninstall a specific module by name" { - Uninstall-PSResource -name $testModuleName -Version "5.0.0.0" + Uninstall-PSResource -name "TestMyLocalModule" -Version "1.0.0.0" Get-PSResource $testModuleName | Should -BeNullOrEmpty } - $testCases = @{Name="Test?Module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"}, - @{Name="Test[Module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"} - - It "not uninstall module given Name with invalid wildcard characters" -TestCases $testCases { - param($Name, $ErrorId) - Uninstall-PSResource -Name $Name -ErrorVariable err -ErrorAction SilentlyContinue - $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PowerShellGet.Cmdlets.UninstallPSResource" - } - - It "Uninstall a list of modules by name" { - $null = Install-PSResource "RequiredModule1" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue -SkipDependencyCheck - - Uninstall-PSResource -Name $testModuleName, "RequiredModule1" - Get-PSResource $testModuleName, "RequiredModule1" | Should -BeNullOrEmpty - } - - It "Uninstall a specific script by name" { - $null = Install-PSResource $testScriptName -Repository $PSGalleryName -TrustRepository - $res = Get-PSResource -Name $testScriptName - $res.Name | Should -Be $testScriptName - - Uninstall-PSResource -Name $testScriptName - $res = Get-PSResource -Name $testScriptName - $res | Should -BeNullOrEmpty - } - - It "Uninstall a list of scripts by name" { - $null = Install-PSResource $testScriptName, "Required-Script1" -Repository $PSGalleryName -TrustRepository - $res = Get-PSResource -Name $testScriptName - $res.Name | Should -Be $testScriptName - $res2 = Get-PSResource -Name "Required-Script1" - $res2.Name | Should -Be "Required-Script1" - - Uninstall-PSResource -Name $testScriptName, "Required-Script1" - $res = Get-PSResource -Name $testScriptName - $res | Should -BeNullOrEmpty - $res2 = Get-PSResource -Name "Required-Script1" - $res2 | Should -BeNullOrEmpty - } - - It "Uninstall a module when given name and specifying all versions" { - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue - - Uninstall-PSResource -Name $testModuleName -version "*" - $pkgs = Get-PSResource $testModuleName - $pkgs.Version | Should -Not -Contain "1.0.0" - $pkgs.Version | Should -Not -Contain "3.0.0" - $pkgs.Version | Should -Not -Contain "5.0.0" - } - - It "Uninstall a module when given name and using the default version (ie all versions, not explicitly specified)" { - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue - - Uninstall-PSResource -Name $testModuleName - $pkgs = Get-PSResource $testModuleName - $pkgs.Version | Should -Not -Contain "1.0.0" - $pkgs.Version | Should -Not -Contain "3.0.0" - $pkgs.Version | Should -Not -Contain "5.0.0" - } - - It "Uninstall module when given Name and specifying exact version" { - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue - - Uninstall-PSResource -Name $testModuleName -Version "3.0.0" - $pkgs = Get-PSResource -Name $testModuleName - $pkgs.Version | Should -Not -Contain "1.0.0" - } - - $testCases = @{Version="[1.0.0.0]"; ExpectedVersion="1.0.0.0"; Reason="validate version, exact match"}, - @{Version="1.0.0.0"; ExpectedVersion="1.0.0.0"; Reason="validate version, exact match without bracket syntax"}, - @{Version="[1.0.0.0, 5.0.0.0]"; ExpectedVersion="5.0.0.0"; Reason="validate version, exact range inclusive"}, - @{Version="(1.0.0.0, 5.0.0.0)"; ExpectedVersion="3.0.0.0"; Reason="validate version, exact range exclusive"}, - @{Version="(1.0.0.0,)"; ExpectedVersion="5.0.0.0"; Reason="validate version, minimum version exclusive"}, - @{Version="[1.0.0.0,)"; ExpectedVersion="5.0.0.0"; Reason="validate version, minimum version inclusive"}, - @{Version="(,3.0.0.0)"; ExpectedVersion="1.0.0.0"; Reason="validate version, maximum version exclusive"}, - @{Version="(,3.0.0.0]"; ExpectedVersion="1.0.0.0"; Reason="validate version, maximum version inclusive"}, - @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersion="3.0.0.0"; Reason="validate version, mixed inclusive minimum and exclusive maximum version"} + # $testCases = @{Name="Test?Module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"}, + # @{Name="Test[Module"; ErrorId="ErrorFilteringNamesForUnsupportedWildcards"} + + # It "not uninstall module given Name with invalid wildcard characters" -TestCases $testCases { + # param($Name, $ErrorId) + # Uninstall-PSResource -Name $Name -ErrorVariable err -ErrorAction SilentlyContinue + # $err.Count | Should -Not -Be 0 + # $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PowerShellGet.Cmdlets.UninstallPSResource" + # } + + # It "Uninstall a list of modules by name" { + # $null = Install-PSResource "test_module2" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue -SkipDependencyCheck + + # Uninstall-PSResource -Name "TestMyLocalModule", "test_module2" + # Get-PSResource "TestMyLocalModule", "test_module2" | Should -BeNullOrEmpty + # } + + # It "Uninstall a specific script by name" { + # # $null = Install-PSResource $testScriptName -Repository $PSGalleryName -TrustRepository + # $res = Get-PSResource -Name "TestMyLocalScript" + # $res.Name | Should -Be "TestMyLocalScript" + + # Uninstall-PSResource -Name "TestMyLocalScript" + # $res = Get-PSResource -Name "TestMyLocalScript" + # $res | Should -BeNullOrEmpty + # } + + # It "Uninstall a list of scripts by name" { + # $null = Install-PSResource $testScriptName, "Required-Script1" -Repository $PSGalleryName -TrustRepository + # $res = Get-PSResource -Name $testScriptName + # $res.Name | Should -Be $testScriptName + # $res2 = Get-PSResource -Name "Required-Script1" + # $res2.Name | Should -Be "Required-Script1" + + # Uninstall-PSResource -Name $testScriptName, "Required-Script1" + # $res = Get-PSResource -Name $testScriptName + # $res | Should -BeNullOrEmpty + # $res2 = Get-PSResource -Name "Required-Script1" + # $res2 | Should -BeNullOrEmpty + # } + + # It "Uninstall a module when given name and specifying all versions" { + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue + + # Uninstall-PSResource -Name $testModuleName -version "*" + # $pkgs = Get-PSResource $testModuleName + # $pkgs.Version | Should -Not -Contain "1.0.0" + # $pkgs.Version | Should -Not -Contain "3.0.0" + # $pkgs.Version | Should -Not -Contain "5.0.0" + # } + + # It "Uninstall a module when given name and using the default version (ie all versions, not explicitly specified)" { + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue + + # Uninstall-PSResource -Name $testModuleName + # $pkgs = Get-PSResource $testModuleName + # $pkgs.Version | Should -Not -Contain "1.0.0" + # $pkgs.Version | Should -Not -Contain "3.0.0" + # $pkgs.Version | Should -Not -Contain "5.0.0" + # } + + # It "Uninstall module when given Name and specifying exact version" { + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue + + # Uninstall-PSResource -Name $testModuleName -Version "3.0.0" + # $pkgs = Get-PSResource -Name $testModuleName + # $pkgs.Version | Should -Not -Contain "1.0.0" + # } + + # $testCases = @{Version="[1.0.0.0]"; ExpectedVersion="1.0.0.0"; Reason="validate version, exact match"}, + # @{Version="1.0.0.0"; ExpectedVersion="1.0.0.0"; Reason="validate version, exact match without bracket syntax"}, + # @{Version="[1.0.0.0, 5.0.0.0]"; ExpectedVersion="5.0.0.0"; Reason="validate version, exact range inclusive"}, + # @{Version="(1.0.0.0, 5.0.0.0)"; ExpectedVersion="3.0.0.0"; Reason="validate version, exact range exclusive"}, + # @{Version="(1.0.0.0,)"; ExpectedVersion="5.0.0.0"; Reason="validate version, minimum version exclusive"}, + # @{Version="[1.0.0.0,)"; ExpectedVersion="5.0.0.0"; Reason="validate version, minimum version inclusive"}, + # @{Version="(,3.0.0.0)"; ExpectedVersion="1.0.0.0"; Reason="validate version, maximum version exclusive"}, + # @{Version="(,3.0.0.0]"; ExpectedVersion="1.0.0.0"; Reason="validate version, maximum version inclusive"}, + # @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersion="3.0.0.0"; Reason="validate version, mixed inclusive minimum and exclusive maximum version"} - It "Uninstall module when given Name to " -TestCases $testCases { - param($Version, $ExpectedVersion) - Uninstall-PSResource -Name $testModuleName -Version "*" - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue - - Uninstall-PSResource -Name $testModuleName -Version $Version - $pkgs = Get-PSResource $testModuleName - $pkgs.Version | Should -Not -Contain $Version - } - - $testCases2 = @{Version='[1.*.0]'; Description="version with wilcard in middle"}, - @{Version='[*.0.0.0]'; Description="version with wilcard at start"}, - @{Version='[1.*.0.0]'; Description="version with wildcard at second digit"}, - @{Version='[1.0.*.0]'; Description="version with wildcard at third digit"} - @{Version='[1.0.0.*]'; Description="version with wildcard at end"}, - @{Version='[1..0.0]'; Description="version with missing digit in middle"}, - @{Version='[1.0.0.]'; Description="version with missing digit at end"}, - @{Version='[1.0.0.0.0]'; Description="version with more than 4 digits"} - - It "Do not uninstall module with incorrectly formatted version such as " -TestCases $testCases2 { - param($Version, $Description) - - {Uninstall-PSResource -Name $testModuleName -Version $Version} | Should -Throw "Argument for -Version parameter is not in the proper format." - } - - $testCases3 = @{Version='(1.0.0.0)'; Description="exclusive version (1.0.0.0)"}, - @{Version='[1-0-0-0]'; Description="version formatted with invalid delimiter"} - - It "Do not uninstall module with incorrectly formatted version such as " -TestCases $testCases3 { - param($Version, $Description) - - Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository - - Uninstall-PSResource -Name $testModuleName -Version $Version - $pkg = Get-PSResource $testModuleName -Version "1.0.0.0" - $pkg.Version | Should -Be "1.0.0.0" - } - - It "Uninstall prerelease version module when prerelease version specified" { - Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository - Uninstall-PSResource -Name $testModuleName -Version "5.2.5-alpha001" - $res = Get-PSResource $testModuleName -Version "5.2.5-alpha001" - $res | Should -BeNullOrEmpty - } - - It "Not uninstall non-prerelease version module when similar prerelease version is specified" { - # test_module has a version 5.0.0.0, but no version 5.0.0-preview. - # despite the core version part being the same this uninstall on a nonexistant prerelease version should not be successful - Install-PSResource -Name $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository - Uninstall-PSResource -Name $testModuleName -Version "5.0.0-preview" - $res = Get-PSResource -Name $testModuleName -Version "5.0.0.0" - $res.Name | Should -Be $testModuleName - $res.Version | Should -Be "5.0.0.0" - } - - It "Uninstall prerelease version script when prerelease version specified" { - Install-PSResource -Name $testScriptName -Version "3.0.0-alpha" -Repository $PSGalleryName -TrustRepository - Uninstall-PSResource -Name $testScriptName -Version "3.0.0-alpha" - $res = Get-PSResource -Name $testScriptName - $res | Should -BeNullOrEmpty - } - - It "Not uninstall non-prerelease version module when prerelease version specified" { - Install-PSResource -Name $testScriptName -Version "2.5.0.0" -Repository $PSGalleryName -TrustRepository - Uninstall-PSResource -Name $testScriptName -Version "2.5.0-alpha001" - $res = Get-PSResource -Name $testScriptName -Version "2.5.0.0" - $res.Name | Should -Be $testScriptName - $res.Version | Should -Be "2.5.0.0" - } - - It "uninstall all prerelease versions (which satisfy the range) when -Version '*' and -Prerelease parameter is specified" { - Uninstall-PSResource -Name $testModuleName -Version "*" - Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository - Install-PSResource -Name $testModuleName -Version "3.0.0" -Repository $PSGalleryName -TrustRepository - Install-PSResource -Name $testModuleName -Version "5.0.0" -Repository $PSGalleryName -TrustRepository - Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository - $res = Get-PSResource -Name $testModuleName - $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} - $prereleaseVersionPkgs.Count | Should -Be 2 - - Uninstall-PSResource -Name $testModuleName -Version "*" -Prerelease - $res = Get-PSResource -Name $testModuleName - $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} - $prereleaseVersionPkgs.Count | Should -Be 0 - $stableVersionPkgs = $res | Where-Object {$_.IsPrerelease -ne $true} - $stableVersionPkgs.Count | Should -Be 2 - } - - It "uninstall all prerelease versions (which satisfy the range) when -Version range and -Prerelease parameter is specified" { - Uninstall-PSResource -Name $testModuleName -Version "*" - Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository - Install-PSResource -Name $testModuleName -Version "3.0.0" -Repository $PSGalleryName -TrustRepository - Install-PSResource -Name $testModuleName -Version "5.0.0" -Repository $PSGalleryName -TrustRepository - Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository - - $res = Get-PSResource -Name $testModuleName - $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} - $prereleaseVersionPkgs.Count | Should -Be 2 - - Uninstall-PSResource -Name $testModuleName -Version "[2.0.0, 5.0.0]" -Prerelease - $res = Get-PSResource -Name $testModuleName - # should only uninstall 2.5.0-beta, 5.2.5-alpha001 is out of range and should remain installed - $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} - $prereleaseVersionPkgs.Count | Should -Be 1 - $stableVersionPkgs = $res | Where-Object {$_.IsPrerelease -ne $true} - # versions 3.0.0 falls in range but should not be uninstalled as Prerelease parameter only selects prerelease versions to uninstall - $stableVersionPkgs.Count | Should -Be 2 - } - - It "Uninstall module using -WhatIf, should not uninstall the module" { - Uninstall-PSResource -Name $testModuleName -WhatIf - $pkg = Get-PSResource $testModuleName -Version "5.0.0.0" - $pkg.Version | Should -Be "5.0.0.0" - } - - It "Do not Uninstall module that is a dependency for another module" { - $null = Install-PSResource "test_module" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue + # It "Uninstall module when given Name to " -TestCases $testCases { + # param($Version, $ExpectedVersion) + # Uninstall-PSResource -Name $testModuleName -Version "*" + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue + + # Uninstall-PSResource -Name $testModuleName -Version $Version + # $pkgs = Get-PSResource $testModuleName + # $pkgs.Version | Should -Not -Contain $Version + # } + + # $testCases2 = @{Version='[1.*.0]'; Description="version with wilcard in middle"}, + # @{Version='[*.0.0.0]'; Description="version with wilcard at start"}, + # @{Version='[1.*.0.0]'; Description="version with wildcard at second digit"}, + # @{Version='[1.0.*.0]'; Description="version with wildcard at third digit"} + # @{Version='[1.0.0.*]'; Description="version with wildcard at end"}, + # @{Version='[1..0.0]'; Description="version with missing digit in middle"}, + # @{Version='[1.0.0.]'; Description="version with missing digit at end"}, + # @{Version='[1.0.0.0.0]'; Description="version with more than 4 digits"} + + # It "Do not uninstall module with incorrectly formatted version such as " -TestCases $testCases2 { + # param($Version, $Description) + + # {Uninstall-PSResource -Name $testModuleName -Version $Version} | Should -Throw "Argument for -Version parameter is not in the proper format." + # } + + # $testCases3 = @{Version='(1.0.0.0)'; Description="exclusive version (1.0.0.0)"}, + # @{Version='[1-0-0-0]'; Description="version formatted with invalid delimiter"} + + # It "Do not uninstall module with incorrectly formatted version such as " -TestCases $testCases3 { + # param($Version, $Description) + + # Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository + + # Uninstall-PSResource -Name $testModuleName -Version $Version + # $pkg = Get-PSResource $testModuleName -Version "1.0.0.0" + # $pkg.Version | Should -Be "1.0.0.0" + # } + + # It "Uninstall prerelease version module when prerelease version specified" { + # Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository + # Uninstall-PSResource -Name $testModuleName -Version "5.2.5-alpha001" + # $res = Get-PSResource $testModuleName -Version "5.2.5-alpha001" + # $res | Should -BeNullOrEmpty + # } + + # It "Not uninstall non-prerelease version module when similar prerelease version is specified" { + # # test_module has a version 5.0.0.0, but no version 5.0.0-preview. + # # despite the core version part being the same this uninstall on a nonexistant prerelease version should not be successful + # Install-PSResource -Name $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository + # Uninstall-PSResource -Name $testModuleName -Version "5.0.0-preview" + # $res = Get-PSResource -Name $testModuleName -Version "5.0.0.0" + # $res.Name | Should -Be $testModuleName + # $res.Version | Should -Be "5.0.0.0" + # } + + # It "Uninstall prerelease version script when prerelease version specified" { + # Install-PSResource -Name $testScriptName -Version "3.0.0-alpha" -Repository $PSGalleryName -TrustRepository + # Uninstall-PSResource -Name $testScriptName -Version "3.0.0-alpha" + # $res = Get-PSResource -Name $testScriptName + # $res | Should -BeNullOrEmpty + # } + + # It "Not uninstall non-prerelease version module when prerelease version specified" { + # Install-PSResource -Name $testScriptName -Version "2.5.0.0" -Repository $PSGalleryName -TrustRepository + # Uninstall-PSResource -Name $testScriptName -Version "2.5.0-alpha001" + # $res = Get-PSResource -Name $testScriptName -Version "2.5.0.0" + # $res.Name | Should -Be $testScriptName + # $res.Version | Should -Be "2.5.0.0" + # } + + # It "uninstall all prerelease versions (which satisfy the range) when -Version '*' and -Prerelease parameter is specified" { + # Uninstall-PSResource -Name $testModuleName -Version "*" + # Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository + # Install-PSResource -Name $testModuleName -Version "3.0.0" -Repository $PSGalleryName -TrustRepository + # Install-PSResource -Name $testModuleName -Version "5.0.0" -Repository $PSGalleryName -TrustRepository + # Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository + # $res = Get-PSResource -Name $testModuleName + # $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} + # $prereleaseVersionPkgs.Count | Should -Be 2 + + # Uninstall-PSResource -Name $testModuleName -Version "*" -Prerelease + # $res = Get-PSResource -Name $testModuleName + # $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} + # $prereleaseVersionPkgs.Count | Should -Be 0 + # $stableVersionPkgs = $res | Where-Object {$_.IsPrerelease -ne $true} + # $stableVersionPkgs.Count | Should -Be 2 + # } + + # It "uninstall all prerelease versions (which satisfy the range) when -Version range and -Prerelease parameter is specified" { + # Uninstall-PSResource -Name $testModuleName -Version "*" + # Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository + # Install-PSResource -Name $testModuleName -Version "3.0.0" -Repository $PSGalleryName -TrustRepository + # Install-PSResource -Name $testModuleName -Version "5.0.0" -Repository $PSGalleryName -TrustRepository + # Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository + + # $res = Get-PSResource -Name $testModuleName + # $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} + # $prereleaseVersionPkgs.Count | Should -Be 2 + + # Uninstall-PSResource -Name $testModuleName -Version "[2.0.0, 5.0.0]" -Prerelease + # $res = Get-PSResource -Name $testModuleName + # # should only uninstall 2.5.0-beta, 5.2.5-alpha001 is out of range and should remain installed + # $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} + # $prereleaseVersionPkgs.Count | Should -Be 1 + # $stableVersionPkgs = $res | Where-Object {$_.IsPrerelease -ne $true} + # # versions 3.0.0 falls in range but should not be uninstalled as Prerelease parameter only selects prerelease versions to uninstall + # $stableVersionPkgs.Count | Should -Be 2 + # } + + # It "Uninstall module using -WhatIf, should not uninstall the module" { + # Uninstall-PSResource -Name $testModuleName -WhatIf + # $pkg = Get-PSResource $testModuleName -Version "5.0.0.0" + # $pkg.Version | Should -Be "5.0.0.0" + # } + + # It "Do not Uninstall module that is a dependency for another module" { + # $null = Install-PSResource "test_module" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue - Uninstall-PSResource -Name "RequiredModule1" -ErrorVariable ev -ErrorAction SilentlyContinue + # Uninstall-PSResource -Name "RequiredModule1" -ErrorVariable ev -ErrorAction SilentlyContinue - $pkg = Get-PSResource "RequiredModule1" - $pkg | Should -Not -Be $null + # $pkg = Get-PSResource "RequiredModule1" + # $pkg | Should -Not -Be $null - $ev.FullyQualifiedErrorId | Should -BeExactly 'UninstallPSResourcePackageIsaDependency,Microsoft.PowerShell.PowerShellGet.Cmdlets.UninstallPSResource' - } + # $ev.FullyQualifiedErrorId | Should -BeExactly 'UninstallPSResourcePackageIsaDependency,Microsoft.PowerShell.PowerShellGet.Cmdlets.UninstallPSResource' + # } - It "Uninstall module that is a dependency for another module using -SkipDependencyCheck" { - $null = Install-PSResource $testModuleName -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue + # It "Uninstall module that is a dependency for another module using -SkipDependencyCheck" { + # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue - Uninstall-PSResource -Name "RequiredModule1" -SkipDependencyCheck + # Uninstall-PSResource -Name "RequiredModule1" -SkipDependencyCheck - $pkg = Get-PSResource "RequiredModule1" - $pkg | Should -BeNullOrEmpty - } - - It "Uninstall PSResourceInfo object piped in" { - Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository - Get-PSResource -Name $testModuleName -Version "1.0.0.0" | Uninstall-PSResource - $res = Get-PSResource -Name "ContosoServer" -Version "1.0.0.0" - $res | Should -BeNullOrEmpty - } - - It "Uninstall PSResourceInfo object piped in for prerelease version object" { - Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository - Get-PSResource -Name $testModuleName -Version "2.5.0-beta" | Uninstall-PSResource - $res = Get-PSResource -Name $testModuleName -Version "2.5.0-beta" - $res | Should -BeNullOrEmpty - } + # $pkg = Get-PSResource "RequiredModule1" + # $pkg | Should -BeNullOrEmpty + # } + + # It "Uninstall PSResourceInfo object piped in" { + # Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository + # Get-PSResource -Name $testModuleName -Version "1.0.0.0" | Uninstall-PSResource + # $res = Get-PSResource -Name "ContosoServer" -Version "1.0.0.0" + # $res | Should -BeNullOrEmpty + # } + + # It "Uninstall PSResourceInfo object piped in for prerelease version object" { + # Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository + # Get-PSResource -Name $testModuleName -Version "2.5.0-beta" | Uninstall-PSResource + # $res = Get-PSResource -Name $testModuleName -Version "2.5.0-beta" + # $res | Should -BeNullOrEmpty + # } } From 8a808c495741e362be7f5f6c25e958b69680e88e Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 11 May 2022 10:54:58 -0400 Subject: [PATCH 03/22] check for local repo Uri not being valid before attempting to publish --- src/code/PublishPSResource.cs | 437 ++++++++++++++++++++++++------- test/PublishPSResource.Tests.ps1 | 44 +++- 2 files changed, 375 insertions(+), 106 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 3470f5e5b..81173dc5e 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -49,30 +49,31 @@ 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)) - { - // we point to a folder when publishing a module - _path = resolvedPath; - } - else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)) - { - // we can point to .ps1 file directly when publishing a script, but not to .psd1 file (for publishing a module) - _path = resolvedPath; - } - } - } - private string _path; + public string Path { get; set; } + // public string Path + // { + // get + // { return _path; } + + // set + // { + // // string resolvedPath = SessionState.Path.GetResolvedPSPathFromPSPath(value).First().Path; + + // // if (Directory.Exists(resolvedPath)) + // // { + // // // we point to a folder when publishing a module + // // _path = resolvedPath; + // // } + // // else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)) + // // { + // // // we can point to .ps1 file directly when publishing a script, but not to .psd1 file (for publishing a module) + // // _path = resolvedPath; + // // } + // } + // } + // private string _path; /// /// Specifies the path to where the resource (as a nupkg) should be saved to. This parameter can be used in conjunction with the @@ -163,6 +164,7 @@ public PSCredential ProxyCredential { #region Members + private string _path; private NuGetVersion _pkgVersion; private string _pkgName; private static char[] _PathSeparators = new [] { System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }; @@ -178,8 +180,46 @@ 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)) + { + // we point to a folder when publishing a module + _path = resolvedPath; + } + else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)) + { + // we can point to .ps1 file directly when publishing a script, but not to .psd1 file (for publishing a module) + _path = resolvedPath; + } + + 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() { // Returns the name of the file or the name of the directory, depending on path @@ -193,15 +233,17 @@ protected override void ProcessRecord() } string resourceFilePath; - Hashtable parsedMetadataHash = new Hashtable(StringComparer.InvariantCultureIgnoreCase); + // TODO: name parsedMetadata + Hashtable parsedMetadataHash; if (isScript) { - resourceFilePath = pkgFileOrDir.FullName; + resourceFilePath = pkgFileOrDir.FullName; // TODO: Anam is this already resolved? Needed? // Check that script metadata is valid // ParseScriptMetadata will write non-terminating error if it's unsuccessful in parsing // parsedMetadataHash = ParseScriptMetadata(resourceFilePath); parsedMetadataHash = ParseScriptMetadataIntoHash(resourceFilePath); + // TODO return boolean, take out Hashtable, filePath, error[] string[] and name it TryPArseMetadata() // Check that the value is valid input // If it does not contain 'Version' or the Version empty or whitespace, write error @@ -249,6 +291,7 @@ protected override void ProcessRecord() { _pkgName = pkgFileOrDir.Name; resourceFilePath = System.IO.Path.Combine(_path, _pkgName + PSDataFileExt); + // TODO: Anam assign hashtable variable here like in "if" // Validate that there's a module manifest if (!File.Exists(resourceFilePath)) @@ -426,7 +469,267 @@ protected override void ProcessRecord() Utils.DeleteDirectory(outputDir); } } + */ + 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))) + { + WriteVerbose("ShouldProcess is set to false."); + return; + } + + string resourceFilePath; + Hashtable parsedMetadata; + if (isScript) + { + resourceFilePath = pkgFileOrDir.FullName; // TODO: Anam is this already resolved? Needed? + + // Check that script metadata is valid + // ParseScriptMetadata will write non-terminating error if it's unsuccessful in parsing + // parsedMetadataHash = ParseScriptMetadata(resourceFilePath); + parsedMetadata = ParseScriptMetadata(resourceFilePath); + // TODO return boolean, take out Hashtable, filePath, error[] string[] and name it TryPArseMetadata() + + // Check that the value is valid input + // If it does not contain 'Version' or the Version empty or whitespace, write error + 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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); + WriteError(InvalidScriptMetadata); + + return; + } + 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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); + WriteError(InvalidScriptMetadata); + + return; + } + if (!parsedMetadata.ContainsKey("description") || String.IsNullOrWhiteSpace(parsedMetadata["description"].ToString())) + { + var message = "No description was provided in the script metadata. Script metadata must specify a version, author, description, and Guid."; + var ex = new ArgumentException(message); + var InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); + WriteError(InvalidScriptMetadata); + + return; + } + 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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); + WriteError(InvalidScriptMetadata); + + return; + } + + // remove '.ps1' extension from file name + _pkgName = pkgFileOrDir.Name.Remove(pkgFileOrDir.Name.Length - 4); + } + else + { + _pkgName = pkgFileOrDir.Name; + resourceFilePath = System.IO.Path.Combine(_path, _pkgName + PSDataFileExt); + parsedMetadata = new Hashtable(); + // TODO: Anam assign hashtable variable here like in "if" + + // Validate that there's a module manifest + if (!File.Exists(resourceFilePath)) + { + var message = String.Format("No file with a .psd1 extension was found in {0}. Please specify a path to a valid modulemanifest.", resourceFilePath); + var ex = new ArgumentException(message); + var moduleManifestNotFound = new ErrorRecord(ex, "moduleManifestNotFound", ErrorCategory.ObjectNotFound, null); + WriteError(moduleManifestNotFound); + + return; + } + + // validate that the module manifest has correct data + if (!IsValidModuleManifest(resourceFilePath)) + { + return; + } + } + + // 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 + { + 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 + // Right now parsedMetadataHash will be empty for modules and will contain metadata for scripts + Hashtable dependencies; + string nuspec = string.Empty; + try + { + nuspec = CreateNuspec( + outputDir: outputDir, + filePath: resourceFilePath, + isScript: isScript, + parsedMetadataHash: parsedMetadata, + requiredModules: out dependencies); + } + catch (Exception e) + { + var message = string.Format("Nuspec creation failed: {0}", e.Message); + var ex = new ArgumentException(message); + var nuspecCreationFailed = new ErrorRecord(ex, "NuspecCreationFailed", ErrorCategory.ObjectNotFound, null); + WriteError(nuspecCreationFailed); + + return; + } + + if (string.IsNullOrEmpty(nuspec)) + { + // nuspec creation failed. + WriteVerbose("Nuspec creation failed."); + return; + } + + // Find repository + PSRepositoryInfo repository = RepositorySettings.Read(new[] { Repository }, out string[] _).FirstOrDefault(); + if (repository == null) + { + var message = String.Format("The resource repository '{0}' is not a registered. Please run 'Register-PSResourceRepository' in order to publish to this repository.", Repository); + var ex = new ArgumentException(message); + var repositoryNotFound = new ErrorRecord(ex, "repositoryNotFound", ErrorCategory.ObjectNotFound, null); + WriteError(repositoryNotFound); + + return; + } + else if(repository.Uri.Scheme == Uri.UriSchemeFile && !Directory.Exists(repository.Uri.AbsoluteUri)) + { + //TODO: would this include localhost tho? + 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; + } + + string repositoryUri = repository.Uri.AbsoluteUri; + + // Check if dependencies already exist within the repo if: + // 1) the resource to publish has dependencies and + // 2) the -SkipDependenciesCheck flag is not passed in + if (dependencies != null && !SkipDependenciesCheck) + { + // If error gets thrown, exit process record + if (!CheckDependenciesExist(dependencies, repositoryUri)) + { + return; + } + } + + if (isScript) + { + // copy the script file to the temp directory + File.Copy(_path, 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)) + { + var dirName = dir.Substring(_path.Length).Trim(_PathSeparators); + System.IO.Directory.CreateDirectory(System.IO.Path.Combine(outputDir, dirName)); + } + + // 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); + + // 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)) + { + System.IO.File.Copy(fileNamePath, newFilePath); + } + } + } + + var outputNupkgDir = System.IO.Path.Combine(outputDir, "nupkg"); + // pack into a nupkg + try + { + 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 + return; + } + + // If -DestinationPath is specified then also publish the .nupkg there + 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)); + } + catch (Exception e) { + 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); + WriteError(ErrorMovingNupkg); + + // exit process record + return; + } + } + + // This call does not throw any exceptions, but it will write unsuccessful responses to the console + PushNupkg(outputNupkgDir, repository.Name, repositoryUri); + + } + finally + { + WriteVerbose(string.Format("Deleting temporary directory '{0}'", outputDir)); + + Utils.DeleteDirectory(outputDir); + } + + } #endregion #region Private methods @@ -728,82 +1031,6 @@ private Hashtable ParseRequiredModules(Hashtable parsedMetadataHash) private Hashtable ParseScriptMetadata(string filePath) { - // parse .ps1 - example .ps1 metadata: - /* <#PSScriptInfo - .VERSION 1.6 - .GUID abf490023 - 9128 - 4323 - sdf9a - jf209888ajkl - .AUTHOR Jane Doe - .COMPANYNAME Microsoft - .COPYRIGHT - .TAGS Windows MacOS - #> - - <# - .SYNOPSIS - Synopsis description here - .DESCRIPTION - 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( - filePath, - out System.Management.Automation.Language.Token[] tokens, - out ParseError[] errors); - - if (errors.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) - { - if (token.Kind == TokenKind.Comment) - { - // expecting only one or two comments - var commentText = token.Text; - Console.WriteLine(commentText); - parsedComments.AddRange(commentText.Split(new string[] { "\n\n" }, StringSplitOptions.RemoveEmptyEntries) ); - } - } - - foreach (var line in parsedComments) - { - if (line.StartsWith(".")) - { - char[] TrimBeginning = { '.', ' ' }; - var newlist = line.Split(new char[] { ' ' }); - - var key = newlist[0].TrimStart(TrimBeginning); - var value = newlist.Length > 1 ? newlist[1].Trim() : string.Empty; - parsedMetadataHash.Add(key,value); - } - } - } - - return parsedMetadataHash; - } - - private Hashtable ParseScriptMetadataIntoHash(string filePath) - { - // public static bool TryParseScriptFile( - // string scriptFileInfoPath, - // out PSScriptFileInfo parsedScript, - // out ErrorRecord[] errors) - // { Hashtable parsedPSScriptInfoHashtable = new Hashtable(); // Parse the script file var ast = Parser.ParseFile( @@ -813,8 +1040,10 @@ private Hashtable ParseScriptMetadataIntoHash(string filePath) if (parserErrors.Length > 0 && !String.Equals(parserErrors[0].ErrorId, "WorkflowNotSupportedInPowerShellCore", StringComparison.OrdinalIgnoreCase)) { + // TODO: Anam iterate errors in a loop and check each for this error. Emit all errors // TODO: do we want to completely ignore WorkFlowNotSupportedInPowerShellCore error (not even write) // or do we want to write, but not return if it's just that error? + // just write errors here as we are in a class & return boolean afterwrds foreach (ParseError err in parserErrors) { var message = String.Format("Could not parse '{0}' as a PowerShell script file due to {1}.", filePath, err.Message); @@ -825,6 +1054,12 @@ private Hashtable ParseScriptMetadataIntoHash(string filePath) } + } + if (ast == null) + { + // TODO: early out with boolean + return parsedPSScriptInfoHashtable; + } else if (ast != null) { diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1 index 3ad06aba6..b3bf6fad6 100644 --- a/test/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResource.Tests.ps1 @@ -40,17 +40,24 @@ Describe "Test Publish-PSResource" { # Create temp destination path $script:destinationPath = [IO.Path]::GetFullPath((Join-Path -Path $TestDrive -ChildPath "tmpDestinationPath")) New-Item $script:destinationPath -ItemType directory -Force + + #Create folder where we shall place all script files used in these tests + $script:tmpScriptsFolderPath = Join-Path -Path $TestDrive -ChildPath "tmpScriptsPath" + if(!(Test-Path $script:tmpScriptsFolderPath)) + { + New-Item -Path $script:tmpScriptsFolderPath -ItemType Directory -Force + } } AfterAll { # 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 } @@ -290,4 +297,31 @@ Describe "Test Publish-PSResource" { $expectedPath = Join-Path -Path $script:repositoryPath2 -ChildPath "$script:PublishModuleName.$version.nupkg" (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 + } } From 560e8f76900f64a825f2ab79d74d1b8cd01c82e0 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 11 May 2022 11:34:20 -0400 Subject: [PATCH 04/22] have ParseScript() return boolean and take out variables as params --- src/code/PublishPSResource.cs | 212 +++++++++++++++++----------------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 81173dc5e..26f4b5181 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -490,9 +490,18 @@ protected override void EndProcessing() // Check that script metadata is valid // ParseScriptMetadata will write non-terminating error if it's unsuccessful in parsing - // parsedMetadataHash = ParseScriptMetadata(resourceFilePath); - parsedMetadata = ParseScriptMetadata(resourceFilePath); - // TODO return boolean, take out Hashtable, filePath, error[] string[] and name it TryPArseMetadata() + if (!TryParseScriptMetadata( + out parsedMetadata, + resourceFilePath, + out ErrorRecord[] errors)) + { + foreach (ErrorRecord err in errors) + { + WriteError(err); + } + + return; + } // Check that the value is valid input // If it does not contain 'Version' or the Version empty or whitespace, write error @@ -505,6 +514,7 @@ protected override void EndProcessing() return; } + 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"; @@ -514,6 +524,7 @@ protected override void EndProcessing() return; } + if (!parsedMetadata.ContainsKey("description") || String.IsNullOrWhiteSpace(parsedMetadata["description"].ToString())) { var message = "No description was provided in the script metadata. Script metadata must specify a version, author, description, and Guid."; @@ -523,6 +534,7 @@ protected override void EndProcessing() return; } + 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."; @@ -541,7 +553,6 @@ protected override void EndProcessing() _pkgName = pkgFileOrDir.Name; resourceFilePath = System.IO.Path.Combine(_path, _pkgName + PSDataFileExt); parsedMetadata = new Hashtable(); - // TODO: Anam assign hashtable variable here like in "if" // Validate that there's a module manifest if (!File.Exists(resourceFilePath)) @@ -1029,9 +1040,13 @@ private Hashtable ParseRequiredModules(Hashtable parsedMetadataHash) return dependenciesHash; } - private Hashtable ParseScriptMetadata(string filePath) + private bool TryParseScriptMetadata( + out Hashtable parsedMetadata, + string filePath, + out ErrorRecord[] errors) { - Hashtable parsedPSScriptInfoHashtable = new Hashtable(); + parsedMetadata = new Hashtable(); + List parseMetadataErrors = new List(); // Parse the script file var ast = Parser.ParseFile( filePath, @@ -1040,128 +1055,113 @@ private Hashtable ParseScriptMetadata(string filePath) if (parserErrors.Length > 0 && !String.Equals(parserErrors[0].ErrorId, "WorkflowNotSupportedInPowerShellCore", StringComparison.OrdinalIgnoreCase)) { - // TODO: Anam iterate errors in a loop and check each for this error. Emit all errors - // TODO: do we want to completely ignore WorkFlowNotSupportedInPowerShellCore error (not even write) - // or do we want to write, but not return if it's just that error? - // just write errors here as we are in a class & return boolean afterwrds foreach (ParseError err in parserErrors) { - 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); - WriteError(psScriptFileParseError); - return parsedPSScriptInfoHashtable; - + // we ignore WorkFlowNotSupportedInPowerShellCore errors, as this is common in scripts currently on PSGallery + if (!String.Equals(err.ErrorId, "WorkflowNotSupportedInPowerShellCore", StringComparison.OrdinalIgnoreCase)) + { + 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); + } } - + errors = parseMetadataErrors.ToArray(); + return false; } + if (ast == null) { - // TODO: early out with boolean - return parsedPSScriptInfoHashtable; + 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); - } - else if (ast != null) - { - // Get the block/group comment beginning with <#PSScriptInfo - List commentTokens = tokens.Where(a => String.Equals(a.Kind.ToString(), "Comment", StringComparison.OrdinalIgnoreCase)).ToList(); - string commentPattern = "<#PSScriptInfo"; - Regex rg = new Regex(commentPattern); - List psScriptInfoCommentTokens = commentTokens.Where(a => rg.IsMatch(a.Extent.Text)).ToList(); + parseMetadataErrors.Add(astCouldNotBeCreatedError); + errors = parseMetadataErrors.ToArray(); + return false; - 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); - WriteError(psCommentMissingError); - return parsedPSScriptInfoHashtable; - } + } - string[] commentLines = Regex.Split(psScriptInfoCommentTokens[0].Text, "[\r\n]").Where(x => !String.IsNullOrEmpty(x)).ToArray(); - string keyName = String.Empty; - string value = String.Empty; + // Get the block/group comment beginning with <#PSScriptInfo + List commentTokens = tokens.Where(a => String.Equals(a.Kind.ToString(), "Comment", StringComparison.OrdinalIgnoreCase)).ToList(); + string commentPattern = "<#PSScriptInfo"; + Regex rg = new Regex(commentPattern); + List psScriptInfoCommentTokens = commentTokens.Where(a => rg.IsMatch(a.Extent.Text)).ToList(); - /** - If comment line count is not more than two, it doesn't have the any metadata property - comment block would look like: - <#PSScriptInfo - #> - */ + 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; + } - if (commentLines.Count() > 2) - { - // TODO: is it an error if the metadata property is empty? - for (int i = 1; i < commentLines.Count(); i++) - { - string line = commentLines[i]; - if (String.IsNullOrEmpty(line)) - { - continue; - } + string[] commentLines = Regex.Split(psScriptInfoCommentTokens[0].Text, "[\r\n]").Where(x => !String.IsNullOrEmpty(x)).ToArray(); + string keyName = String.Empty; + string value = String.Empty; - // 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; - parsedPSScriptInfoHashtable.Add(keyName, value); - } - } - } + /** + If comment line count is not more than two, it doesn't have the any metadata property + comment block would look like: + <#PSScriptInfo + #> + */ - // get .DESCRIPTION comment - CommentHelpInfo scriptCommentInfo = ast.GetHelpContent(); - if (scriptCommentInfo != null) + if (commentLines.Count() > 2) + { + // TODO: is it an error if the metadata property is empty? + for (int i = 1; i < commentLines.Count(); i++) { - if (!String.IsNullOrEmpty(scriptCommentInfo.Description) && !scriptCommentInfo.Description.Contains("<#") && !scriptCommentInfo.Description.Contains("#>")) + string line = commentLines[i]; + if (String.IsNullOrEmpty(line)) { - parsedPSScriptInfoHashtable.Add("description", scriptCommentInfo.Description); + continue; } - else + + // A line is starting with . conveys a new metadata property + if (line.Trim().StartsWith(".")) { - 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, "psScriptDescriptionMissingOrInvalidDescription", ErrorCategory.ParserError, null); - WriteError(psScriptMissingDescriptionOrInvalidPropertyError); - return new Hashtable(); // TODO: Anam, or return hashtable populated with PSScriptCommentInfo metadata? + 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 parsedPSScriptInfoHashtable; - - // // get RequiredModules - // ScriptRequirements parsedScriptRequirements = ast.ScriptRequirements; - // ReadOnlyCollection parsedModules = new List().AsReadOnly(); - - // if (parsedScriptRequirements != null && parsedScriptRequirements.RequiredModules != null) - // { - // parsedModules = parsedScriptRequirements.RequiredModules; - // parsedPSScriptInfoHashtable.Add("RequiredModules", parsedModules); - // } - - // string parsedVersion = (string) parsedPSScriptInfoHashtable["VERSION"]; - // string parsedAuthor = (string) parsedPSScriptInfoHashtable["AUTHOR"]; - // Guid parsedGuid = String.IsNullOrEmpty((string)parsedPSScriptInfoHashtable["GUID"]) ? Guid.NewGuid() : new Guid((string) parsedPSScriptInfoHashtable["GUID"]); - // if (String.IsNullOrEmpty(parsedVersion) || String.IsNullOrEmpty(parsedAuthor) || parsedGuid == Guid.Empty) - // { - // var message = String.Format("PSScript file is missing one of the following required properties: Version, Author, Guid"); - // var ex = new ArgumentException(message); - // var psScriptMissingRequiredPropertyError = new ErrorRecord(ex, "psScriptMissingRequiredProperty", ErrorCategory.ParserError, null); - // errorsList.Add(psScriptMissingRequiredPropertyError); - // successfullyParsed = false; - // return successfullyParsed; - // } - - + // get .DESCRIPTION comment + CommentHelpInfo scriptCommentInfo = ast.GetHelpContent(); + if (scriptCommentInfo != null) + { + 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, "psScriptDescriptionMissingOrInvalidDescription", ErrorCategory.ParserError, null); + parseMetadataErrors.Add(psScriptMissingDescriptionOrInvalidPropertyError); + errors = parseMetadataErrors.ToArray(); + return false; + } } - 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); - WriteError(astCouldNotBeCreatedError); - return parsedPSScriptInfoHashtable; + errors = parseMetadataErrors.ToArray(); + return true; + + // // get RequiredModules + // ScriptRequirements parsedScriptRequirements = ast.ScriptRequirements; + // ReadOnlyCollection parsedModules = new List().AsReadOnly(); + + // if (parsedScriptRequirements != null && parsedScriptRequirements.RequiredModules != null) + // { + // parsedModules = parsedScriptRequirements.RequiredModules; + // parsedPSScriptInfoHashtable.Add("RequiredModules", parsedModules); + // } } private bool CheckDependenciesExist(Hashtable dependencies, string repositoryUri) From 0d7db4cac93d499864b24aed01359ad108d7476e Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 11 May 2022 12:36:46 -0400 Subject: [PATCH 05/22] check for local repo uri existing must be done against Uri.LocalPath --- src/code/PublishPSResource.cs | 4 ++-- test/PublishPSResource.Tests.ps1 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 26f4b5181..143adf6af 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -633,9 +633,9 @@ protected override void EndProcessing() return; } - else if(repository.Uri.Scheme == Uri.UriSchemeFile && !Directory.Exists(repository.Uri.AbsoluteUri)) + else if(repository.Uri.Scheme == Uri.UriSchemeFile && !Directory.Exists(repository.Uri.LocalPath)) { - //TODO: would this include localhost tho? + //TODO: would this include localhost? 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); diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1 index b3bf6fad6..db4f6d7a7 100644 --- a/test/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResource.Tests.ps1 @@ -49,7 +49,7 @@ Describe "Test Publish-PSResource" { } } AfterAll { - # Get-RevertPSResourceRepositoryFile + Get-RevertPSResourceRepositoryFile } AfterEach { # Delete all contents of the repository without deleting the repository directory itself From 61649e6a6b015200e3ff0d166c6a03b69fb89be5 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 11 May 2022 12:38:08 -0400 Subject: [PATCH 06/22] typo fix --- src/code/PublishPSResource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 143adf6af..69da0cb95 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -635,7 +635,7 @@ protected override void EndProcessing() } else if(repository.Uri.Scheme == Uri.UriSchemeFile && !Directory.Exists(repository.Uri.LocalPath)) { - //TODO: would this include localhost? + //TODO: Anam would this include localhost? 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); From 4255975f3882ff08afd5a15089252f0d96e20dc3 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 11 May 2022 12:53:58 -0400 Subject: [PATCH 07/22] error handling for unsupported source paths --- src/code/PublishPSResource.cs | 260 ++-------------------------------- 1 file changed, 8 insertions(+), 252 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 69da0cb95..2efb1f7f9 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -193,6 +193,14 @@ protected override void BeginProcessing() // we can point to .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)) { @@ -218,258 +226,6 @@ protected override void BeginProcessing() } } } - - /** - protected override void ProcessRecord() - { - // 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))) - { - WriteVerbose("ShouldProcess is set to false."); - return; - } - - string resourceFilePath; - // TODO: name parsedMetadata - Hashtable parsedMetadataHash; - if (isScript) - { - resourceFilePath = pkgFileOrDir.FullName; // TODO: Anam is this already resolved? Needed? - - // Check that script metadata is valid - // ParseScriptMetadata will write non-terminating error if it's unsuccessful in parsing - // parsedMetadataHash = ParseScriptMetadata(resourceFilePath); - parsedMetadataHash = ParseScriptMetadataIntoHash(resourceFilePath); - // TODO return boolean, take out Hashtable, filePath, error[] string[] and name it TryPArseMetadata() - - // 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, description, and Guid."; - 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())) - { - 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 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, description, and Guid."; - var ex = new ArgumentException(message); - var InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); - WriteError(InvalidScriptMetadata); - - return; - } - if (!parsedMetadataHash.ContainsKey("guid") || String.IsNullOrWhiteSpace(parsedMetadataHash["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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); - WriteError(InvalidScriptMetadata); - - return; - } - - // remove '.ps1' extension from file name - _pkgName = pkgFileOrDir.Name.Remove(pkgFileOrDir.Name.Length - 4); - } - else - { - _pkgName = pkgFileOrDir.Name; - resourceFilePath = System.IO.Path.Combine(_path, _pkgName + PSDataFileExt); - // TODO: Anam assign hashtable variable here like in "if" - - // Validate that there's a module manifest - if (!File.Exists(resourceFilePath)) - { - var message = String.Format("No file with a .psd1 extension was found in {0}. Please specify a path to a valid modulemanifest.", resourceFilePath); - var ex = new ArgumentException(message); - var moduleManifestNotFound = new ErrorRecord(ex, "moduleManifestNotFound", ErrorCategory.ObjectNotFound, null); - WriteError(moduleManifestNotFound); - - return; - } - - // validate that the module manifest has correct data - if (!IsValidModuleManifest(resourceFilePath)) - { - return; - } - } - - // 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 - { - 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 - // Right now parsedMetadataHash will be empty for modules and will contain metadata for scripts - Hashtable dependencies; - string nuspec = string.Empty; - try - { - nuspec = CreateNuspec( - outputDir: outputDir, - filePath: resourceFilePath, - isScript: isScript, - parsedMetadataHash: parsedMetadataHash, - requiredModules: out dependencies); - } - catch (Exception e) - { - var message = string.Format("Nuspec creation failed: {0}", e.Message); - var ex = new ArgumentException(message); - var nuspecCreationFailed = new ErrorRecord(ex, "NuspecCreationFailed", ErrorCategory.ObjectNotFound, null); - WriteError(nuspecCreationFailed); - - return; - } - - if (string.IsNullOrEmpty(nuspec)) - { - // nuspec creation failed. - WriteVerbose("Nuspec creation failed."); - return; - } - - // Find repository - PSRepositoryInfo repository = RepositorySettings.Read(new[] { Repository }, out string[] _).FirstOrDefault(); - if (repository == null) - { - var message = String.Format("The resource repository '{0}' is not a registered. Please run 'Register-PSResourceRepository' in order to publish to this repository.", Repository); - var ex = new ArgumentException(message); - var repositoryNotFound = new ErrorRecord(ex, "repositoryNotFound", ErrorCategory.ObjectNotFound, null); - WriteError(repositoryNotFound); - - return; - } - - string repositoryUri = repository.Uri.AbsoluteUri; - - // Check if dependencies already exist within the repo if: - // 1) the resource to publish has dependencies and - // 2) the -SkipDependenciesCheck flag is not passed in - if (dependencies != null && !SkipDependenciesCheck) - { - // If error gets thrown, exit process record - if (!CheckDependenciesExist(dependencies, repositoryUri)) - { - return; - } - } - - if (isScript) - { - // copy the script file to the temp directory - File.Copy(_path, 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)) - { - var dirName = dir.Substring(_path.Length).Trim(_PathSeparators); - System.IO.Directory.CreateDirectory(System.IO.Path.Combine(outputDir, dirName)); - } - - // 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); - - // 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)) - { - System.IO.File.Copy(fileNamePath, newFilePath); - } - } - } - - var outputNupkgDir = System.IO.Path.Combine(outputDir, "nupkg"); - - // pack into a nupkg - try - { - 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 - return; - } - - // If -DestinationPath is specified then also publish the .nupkg there - 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)); - } - catch (Exception e) { - 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); - WriteError(ErrorMovingNupkg); - - // exit process record - return; - } - } - - // This call does not throw any exceptions, but it will write unsuccessful responses to the console - PushNupkg(outputNupkgDir, repository.Name, repositoryUri); - - } - finally - { - WriteVerbose(string.Format("Deleting temporary directory '{0}'", outputDir)); - - Utils.DeleteDirectory(outputDir); - } - } - */ protected override void EndProcessing() { // Returns the name of the file or the name of the directory, depending on path From 51c4ed70495f00de7476e126268bc1e0d5aa8294 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 11 May 2022 13:03:17 -0400 Subject: [PATCH 08/22] use constant string instad of hardcoded value in code --- src/code/PublishPSResource.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 2efb1f7f9..4c7ab9051 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -170,6 +170,7 @@ public PSCredential ProxyCredential { 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 @@ -449,6 +450,7 @@ protected override void EndProcessing() // pack into a nupkg try { + // TODO: Anam, per Paul's code review comments, what does false mean here, specifically? Can we write error? if (!PackNupkg(outputDir, outputNupkgDir, nuspec)) { return; @@ -822,6 +824,7 @@ private bool TryParseScriptMetadata( parseMetadataErrors.Add(psScriptFileParseError); } } + errors = parseMetadataErrors.ToArray(); return false; } @@ -840,7 +843,7 @@ private bool TryParseScriptMetadata( // Get the block/group comment beginning with <#PSScriptInfo List commentTokens = tokens.Where(a => String.Equals(a.Kind.ToString(), "Comment", StringComparison.OrdinalIgnoreCase)).ToList(); - string commentPattern = "<#PSScriptInfo"; + string commentPattern = PSScriptInfoCommentString; Regex rg = new Regex(commentPattern); List psScriptInfoCommentTokens = commentTokens.Where(a => rg.IsMatch(a.Extent.Text)).ToList(); From 4e8fa9bbcd88ff84e24a0a5f1d8d918a1f356c0b Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Thu, 12 May 2022 10:21:13 -0400 Subject: [PATCH 09/22] clean up --- src/code/PublishPSResource.cs | 164 +++++++++---------------------- test/PublishPSResource.Tests.ps1 | 10 +- 2 files changed, 49 insertions(+), 125 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 4c7ab9051..1214d8584 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -52,28 +52,6 @@ public sealed class PublishPSResource : PSCmdlet [Parameter (Mandatory = true)] [ValidateNotNullOrEmpty] public string Path { get; set; } - // public string Path - // { - // get - // { return _path; } - - // set - // { - // // string resolvedPath = SessionState.Path.GetResolvedPSPathFromPSPath(value).First().Path; - - // // if (Directory.Exists(resolvedPath)) - // // { - // // // we point to a folder when publishing a module - // // _path = resolvedPath; - // // } - // // else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)) - // // { - // // // we can point to .ps1 file directly when publishing a script, but not to .psd1 file (for publishing a module) - // // _path = resolvedPath; - // // } - // } - // } - // private string _path; /// /// Specifies the path to where the resource (as a nupkg) should be saved to. This parameter can be used in conjunction with the @@ -81,36 +59,7 @@ public sealed class PublishPSResource : PSCmdlet /// [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). @@ -184,14 +133,11 @@ protected override void BeginProcessing() string resolvedPath = SessionState.Path.GetResolvedPSPathFromPSPath(Path).First().Path; - if (Directory.Exists(resolvedPath)) + if (Directory.Exists(resolvedPath) || + (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase))) { - // we point to a folder when publishing a module - _path = resolvedPath; - } - else if (File.Exists(resolvedPath) && resolvedPath.EndsWith(PSScriptFileExt, StringComparison.OrdinalIgnoreCase)) - { - // we can point to .ps1 file directly when publishing a script, but not to .psd1 file (for publishing a module) + // 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 @@ -209,7 +155,7 @@ protected override void BeginProcessing() if (Directory.Exists(resolvedDestinationPath)) { - _destinationPath = resolvedDestinationPath; + DestinationPath = resolvedDestinationPath; } else { @@ -227,6 +173,7 @@ protected override void BeginProcessing() } } } + protected override void EndProcessing() { // Returns the name of the file or the name of the directory, depending on path @@ -243,10 +190,9 @@ protected override void EndProcessing() Hashtable parsedMetadata; if (isScript) { - resourceFilePath = pkgFileOrDir.FullName; // TODO: Anam is this already resolved? Needed? + resourceFilePath = pkgFileOrDir.FullName; // Check that script metadata is valid - // ParseScriptMetadata will write non-terminating error if it's unsuccessful in parsing if (!TryParseScriptMetadata( out parsedMetadata, resourceFilePath, @@ -260,48 +206,6 @@ protected override void EndProcessing() return; } - // Check that the value is valid input - // If it does not contain 'Version' or the Version empty or whitespace, write error - 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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); - WriteError(InvalidScriptMetadata); - - return; - } - - 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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); - WriteError(InvalidScriptMetadata); - - return; - } - - if (!parsedMetadata.ContainsKey("description") || String.IsNullOrWhiteSpace(parsedMetadata["description"].ToString())) - { - var message = "No description was provided in the script metadata. Script metadata must specify a version, author, description, and Guid."; - var ex = new ArgumentException(message); - var InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); - WriteError(InvalidScriptMetadata); - - return; - } - - 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 InvalidScriptMetadata = new ErrorRecord(ex, "InvalidScriptMetadata", ErrorCategory.InvalidData, null); - WriteError(InvalidScriptMetadata); - - return; - } - // remove '.ps1' extension from file name _pkgName = pkgFileOrDir.Name.Remove(pkgFileOrDir.Name.Length - 4); } @@ -392,7 +296,7 @@ protected override void EndProcessing() } else if(repository.Uri.Scheme == Uri.UriSchemeFile && !Directory.Exists(repository.Uri.LocalPath)) { - //TODO: Anam would this include localhost? + //TODO: Anam would this include localhost? Test this. 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); @@ -468,15 +372,15 @@ protected override void EndProcessing() } // 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); @@ -811,7 +715,7 @@ private bool TryParseScriptMetadata( out System.Management.Automation.Language.Token[] tokens, out ParseError[] parserErrors); - if (parserErrors.Length > 0 && !String.Equals(parserErrors[0].ErrorId, "WorkflowNotSupportedInPowerShellCore", StringComparison.OrdinalIgnoreCase)) + if (parserErrors.Length > 0) { foreach (ParseError err in parserErrors) { @@ -870,7 +774,6 @@ private bool TryParseScriptMetadata( if (commentLines.Count() > 2) { - // TODO: is it an error if the metadata property is empty? for (int i = 1; i < commentLines.Count(); i++) { string line = commentLines[i]; @@ -909,18 +812,39 @@ private bool TryParseScriptMetadata( } } - errors = parseMetadataErrors.ToArray(); - return true; + // 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; + } - // // get RequiredModules - // ScriptRequirements parsedScriptRequirements = ast.ScriptRequirements; - // ReadOnlyCollection parsedModules = new List().AsReadOnly(); + 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 (parsedScriptRequirements != null && parsedScriptRequirements.RequiredModules != null) - // { - // parsedModules = parsedScriptRequirements.RequiredModules; - // parsedPSScriptInfoHashtable.Add("RequiredModules", parsedModules); - // } + 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 repositoryUri) diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1 index db4f6d7a7..056df39f6 100644 --- a/test/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResource.Tests.ps1 @@ -316,12 +316,12 @@ Describe "Test Publish-PSResource" { ReleaseNotes = "$scriptName release notes" } - $scriptPath = (Join-Path -Path $script:tmpScriptsFolderPath -ChildPath "$scriptName.ps1") - New-ScriptFileInfo @params -Path $scriptPath + $scriptPath = (Join-Path -Path $script:tmpScriptsFolderPath -ChildPath "$scriptName.ps1") + New-ScriptFileInfo @params -Path $scriptPath - Publish-PSResource -Path $scriptPath + Publish-PSResource -Path $scriptPath - $expectedPath = Join-Path -Path $script:repositoryPath -ChildPath "$scriptName.$scriptVersion.nupkg" - (Get-ChildItem $script:repositoryPath).FullName | Should -Be $expectedPath + $expectedPath = Join-Path -Path $script:repositoryPath -ChildPath "$scriptName.$scriptVersion.nupkg" + (Get-ChildItem $script:repositoryPath).FullName | Should -Be $expectedPath } } From e01429586f68cabcb78c869795998a372a449d5c Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Thu, 12 May 2022 10:24:49 -0400 Subject: [PATCH 10/22] add comment --- src/code/PublishPSResource.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 1214d8584..ffd578d7f 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -190,6 +190,8 @@ protected override void EndProcessing() Hashtable parsedMetadata; if (isScript) { + // this converts from System.IO.DirectoryInfo to string. resourceFilePath needs to be set for later use though + // alternatively could use resourceFilePath = _path resourceFilePath = pkgFileOrDir.FullName; // Check that script metadata is valid From 0034accdfb1a6110f047beadcfe31054953ed2cf Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Thu, 12 May 2022 11:06:09 -0400 Subject: [PATCH 11/22] better error handling and method definition for PackNupkg() --- src/code/PublishPSResource.cs | 60 +++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index ffd578d7f..8c37426b9 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -353,23 +353,11 @@ protected override void EndProcessing() var outputNupkgDir = System.IO.Path.Combine(outputDir, "nupkg"); - // pack into a nupkg - try + // pack into .nupkg + if (!PackNupkg(outputDir, outputNupkgDir, nuspec, out ErrorRecord error)) { - // TODO: Anam, per Paul's code review comments, what does false mean here, specifically? Can we write error? - 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(error); + // exit out of processing return; } @@ -880,11 +868,13 @@ private bool CheckDependenciesExist(Hashtable dependencies, string repositoryUri 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, @@ -896,18 +886,34 @@ 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) From 88ed85aaef356903473d288ae60dcc5ca51ea2ca Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Thu, 12 May 2022 14:28:48 -0400 Subject: [PATCH 12/22] revert changes to Uninstall tests and take from master --- test/UninstallPSResource.Tests.ps1 | 277 ++++++++++++++--------------- 1 file changed, 131 insertions(+), 146 deletions(-) diff --git a/test/UninstallPSResource.Tests.ps1 b/test/UninstallPSResource.Tests.ps1 index acf1fa487..e4ea4babb 100644 --- a/test/UninstallPSResource.Tests.ps1 +++ b/test/UninstallPSResource.Tests.ps1 @@ -13,29 +13,14 @@ Describe 'Test Uninstall-PSResource for Modules' { Get-NewPSResourceRepositoryFile Uninstall-PSResource -Name $testModuleName -Version "*" Uninstall-PSResource -Name $testScriptName -Version "*" - Register-LocalRepos - $publishModuleName = "TestMyLocalModule" - $repoName = "psgettestlocal" - $moduleVersion = "1.0.0.0" - Get-ModuleResourcePublishedToLocalRepoTestDrive $publishModuleName $repoName $moduleVersion - - $publishScriptName = "TestMyLocalScript" - $scriptVersion = "1.0.0.0" - Write-Host $publishScriptName - Write-Host $repoName - Write-Host $scriptVersion - Get-ScriptResourcePublishedToLocalRepoTestDrive $publishScriptName $repoName $scriptVersion } BeforeEach { - # $null = Install-PSResource $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource "TestMyLocalModule" -Version "1.0.0.0" -Repository "psgettestlocal" -TrustRepository -WarningAction SilentlyContinue - $null = Install-PSResource "TestMyLocalScript" -Version "1.0.0.0" -Repository "psgettestlocal" -TrustRepository -WarningAction SilentlyContinue + $null = Install-PSResource $testModuleName -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue } AfterEach { - Uninstall-PSResource -Name "TestMyLocalModule" -Version "1.0.0.0" - Uninstall-PSResource -Name "TestMyLocalScript" -Version "1.0.0.0" + Uninstall-PSResource -Name $testModuleName -Version "*" } AfterAll { @@ -43,7 +28,7 @@ Describe 'Test Uninstall-PSResource for Modules' { } It "Uninstall a specific module by name" { - Uninstall-PSResource -name "TestMyLocalModule" -Version "1.0.0.0" + Uninstall-PSResource -name $testModuleName Get-PSResource $testModuleName | Should -BeNullOrEmpty } @@ -132,138 +117,138 @@ Describe 'Test Uninstall-PSResource for Modules' { @{Version="(,3.0.0.0]"; ExpectedVersion="1.0.0.0"; Reason="validate version, maximum version inclusive"}, @{Version="[1.0.0.0, 5.0.0.0)"; ExpectedVersion="3.0.0.0"; Reason="validate version, mixed inclusive minimum and exclusive maximum version"} - # It "Uninstall module when given Name to " -TestCases $testCases { - # param($Version, $ExpectedVersion) - # Uninstall-PSResource -Name $testModuleName -Version "*" - # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue - # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue - # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue - - # Uninstall-PSResource -Name $testModuleName -Version $Version - # $pkgs = Get-PSResource $testModuleName - # $pkgs.Version | Should -Not -Contain $Version - # } - - # $testCases2 = @{Version='[1.*.0]'; Description="version with wilcard in middle"}, - # @{Version='[*.0.0.0]'; Description="version with wilcard at start"}, - # @{Version='[1.*.0.0]'; Description="version with wildcard at second digit"}, - # @{Version='[1.0.*.0]'; Description="version with wildcard at third digit"} - # @{Version='[1.0.0.*]'; Description="version with wildcard at end"}, - # @{Version='[1..0.0]'; Description="version with missing digit in middle"}, - # @{Version='[1.0.0.]'; Description="version with missing digit at end"}, - # @{Version='[1.0.0.0.0]'; Description="version with more than 4 digits"} - - # It "Do not uninstall module with incorrectly formatted version such as " -TestCases $testCases2 { - # param($Version, $Description) - - # {Uninstall-PSResource -Name $testModuleName -Version $Version} | Should -Throw "Argument for -Version parameter is not in the proper format." - # } - - # $testCases3 = @{Version='(1.0.0.0)'; Description="exclusive version (1.0.0.0)"}, - # @{Version='[1-0-0-0]'; Description="version formatted with invalid delimiter"} - - # It "Do not uninstall module with incorrectly formatted version such as " -TestCases $testCases3 { - # param($Version, $Description) - - # Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository - - # Uninstall-PSResource -Name $testModuleName -Version $Version - # $pkg = Get-PSResource $testModuleName -Version "1.0.0.0" - # $pkg.Version | Should -Be "1.0.0.0" - # } - - # It "Uninstall prerelease version module when prerelease version specified" { - # Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository - # Uninstall-PSResource -Name $testModuleName -Version "5.2.5-alpha001" - # $res = Get-PSResource $testModuleName -Version "5.2.5-alpha001" - # $res | Should -BeNullOrEmpty - # } - - # It "Not uninstall non-prerelease version module when similar prerelease version is specified" { - # # test_module has a version 5.0.0.0, but no version 5.0.0-preview. - # # despite the core version part being the same this uninstall on a nonexistant prerelease version should not be successful - # Install-PSResource -Name $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository - # Uninstall-PSResource -Name $testModuleName -Version "5.0.0-preview" - # $res = Get-PSResource -Name $testModuleName -Version "5.0.0.0" - # $res.Name | Should -Be $testModuleName - # $res.Version | Should -Be "5.0.0.0" - # } - - # It "Uninstall prerelease version script when prerelease version specified" { - # Install-PSResource -Name $testScriptName -Version "3.0.0-alpha" -Repository $PSGalleryName -TrustRepository - # Uninstall-PSResource -Name $testScriptName -Version "3.0.0-alpha" - # $res = Get-PSResource -Name $testScriptName - # $res | Should -BeNullOrEmpty - # } - - # It "Not uninstall non-prerelease version module when prerelease version specified" { - # Install-PSResource -Name $testScriptName -Version "2.5.0.0" -Repository $PSGalleryName -TrustRepository - # Uninstall-PSResource -Name $testScriptName -Version "2.5.0-alpha001" - # $res = Get-PSResource -Name $testScriptName -Version "2.5.0.0" - # $res.Name | Should -Be $testScriptName - # $res.Version | Should -Be "2.5.0.0" - # } - - # It "uninstall all prerelease versions (which satisfy the range) when -Version '*' and -Prerelease parameter is specified" { - # Uninstall-PSResource -Name $testModuleName -Version "*" - # Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository - # Install-PSResource -Name $testModuleName -Version "3.0.0" -Repository $PSGalleryName -TrustRepository - # Install-PSResource -Name $testModuleName -Version "5.0.0" -Repository $PSGalleryName -TrustRepository - # Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository - # $res = Get-PSResource -Name $testModuleName - # $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} - # $prereleaseVersionPkgs.Count | Should -Be 2 - - # Uninstall-PSResource -Name $testModuleName -Version "*" -Prerelease - # $res = Get-PSResource -Name $testModuleName - # $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} - # $prereleaseVersionPkgs.Count | Should -Be 0 - # $stableVersionPkgs = $res | Where-Object {$_.IsPrerelease -ne $true} - # $stableVersionPkgs.Count | Should -Be 2 - # } - - # It "uninstall all prerelease versions (which satisfy the range) when -Version range and -Prerelease parameter is specified" { - # Uninstall-PSResource -Name $testModuleName -Version "*" - # Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository - # Install-PSResource -Name $testModuleName -Version "3.0.0" -Repository $PSGalleryName -TrustRepository - # Install-PSResource -Name $testModuleName -Version "5.0.0" -Repository $PSGalleryName -TrustRepository - # Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository - - # $res = Get-PSResource -Name $testModuleName - # $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} - # $prereleaseVersionPkgs.Count | Should -Be 2 - - # Uninstall-PSResource -Name $testModuleName -Version "[2.0.0, 5.0.0]" -Prerelease - # $res = Get-PSResource -Name $testModuleName - # # should only uninstall 2.5.0-beta, 5.2.5-alpha001 is out of range and should remain installed - # $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} - # $prereleaseVersionPkgs.Count | Should -Be 1 - # $stableVersionPkgs = $res | Where-Object {$_.IsPrerelease -ne $true} - # # versions 3.0.0 falls in range but should not be uninstalled as Prerelease parameter only selects prerelease versions to uninstall - # $stableVersionPkgs.Count | Should -Be 2 - # } - - # It "Uninstall module using -WhatIf, should not uninstall the module" { - # Uninstall-PSResource -Name $testModuleName -WhatIf - # $pkg = Get-PSResource $testModuleName -Version "5.0.0.0" - # $pkg.Version | Should -Be "5.0.0.0" - # } - - # It "Do not Uninstall module that is a dependency for another module" { - # $null = Install-PSResource "test_module" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue + It "Uninstall module when given Name to " -TestCases $testCases { + param($Version, $ExpectedVersion) + Uninstall-PSResource -Name $testModuleName -Version "*" + $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "1.0.0" -TrustRepository -WarningAction SilentlyContinue + $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "3.0.0" -TrustRepository -WarningAction SilentlyContinue + $null = Install-PSResource $testModuleName -Repository $PSGalleryName -Version "5.0.0" -TrustRepository -WarningAction SilentlyContinue + + Uninstall-PSResource -Name $testModuleName -Version $Version + $pkgs = Get-PSResource $testModuleName + $pkgs.Version | Should -Not -Contain $Version + } + + $testCases2 = @{Version='[1.*.0]'; Description="version with wilcard in middle"}, + @{Version='[*.0.0.0]'; Description="version with wilcard at start"}, + @{Version='[1.*.0.0]'; Description="version with wildcard at second digit"}, + @{Version='[1.0.*.0]'; Description="version with wildcard at third digit"} + @{Version='[1.0.0.*]'; Description="version with wildcard at end"}, + @{Version='[1..0.0]'; Description="version with missing digit in middle"}, + @{Version='[1.0.0.]'; Description="version with missing digit at end"}, + @{Version='[1.0.0.0.0]'; Description="version with more than 4 digits"} + + It "Do not uninstall module with incorrectly formatted version such as " -TestCases $testCases2 { + param($Version, $Description) + + {Uninstall-PSResource -Name $testModuleName -Version $Version} | Should -Throw "Argument for -Version parameter is not in the proper format." + } + + $testCases3 = @{Version='(1.0.0.0)'; Description="exclusive version (1.0.0.0)"}, + @{Version='[1-0-0-0]'; Description="version formatted with invalid delimiter"} + + It "Do not uninstall module with incorrectly formatted version such as " -TestCases $testCases3 { + param($Version, $Description) + + Install-PSResource -Name $testModuleName -Version "1.0.0.0" -Repository $PSGalleryName -TrustRepository + + Uninstall-PSResource -Name $testModuleName -Version $Version + $pkg = Get-PSResource $testModuleName -Version "1.0.0.0" + $pkg.Version | Should -Be "1.0.0.0" + } + + It "Uninstall prerelease version module when prerelease version specified" { + Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository + Uninstall-PSResource -Name $testModuleName -Version "5.2.5-alpha001" + $res = Get-PSResource $testModuleName -Version "5.2.5-alpha001" + $res | Should -BeNullOrEmpty + } + + It "Not uninstall non-prerelease version module when similar prerelease version is specified" { + # test_module has a version 5.0.0.0, but no version 5.0.0-preview. + # despite the core version part being the same this uninstall on a nonexistant prerelease version should not be successful + Install-PSResource -Name $testModuleName -Version "5.0.0.0" -Repository $PSGalleryName -TrustRepository + Uninstall-PSResource -Name $testModuleName -Version "5.0.0-preview" + $res = Get-PSResource -Name $testModuleName -Version "5.0.0.0" + $res.Name | Should -Be $testModuleName + $res.Version | Should -Be "5.0.0.0" + } + + It "Uninstall prerelease version script when prerelease version specified" { + Install-PSResource -Name $testScriptName -Version "3.0.0-alpha" -Repository $PSGalleryName -TrustRepository + Uninstall-PSResource -Name $testScriptName -Version "3.0.0-alpha" + $res = Get-PSResource -Name $testScriptName + $res | Should -BeNullOrEmpty + } + + It "Not uninstall non-prerelease version module when prerelease version specified" { + Install-PSResource -Name $testScriptName -Version "2.5.0.0" -Repository $PSGalleryName -TrustRepository + Uninstall-PSResource -Name $testScriptName -Version "2.5.0-alpha001" + $res = Get-PSResource -Name $testScriptName -Version "2.5.0.0" + $res.Name | Should -Be $testScriptName + $res.Version | Should -Be "2.5.0.0" + } + + It "uninstall all prerelease versions (which satisfy the range) when -Version '*' and -Prerelease parameter is specified" { + Uninstall-PSResource -Name $testModuleName -Version "*" + Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository + Install-PSResource -Name $testModuleName -Version "3.0.0" -Repository $PSGalleryName -TrustRepository + Install-PSResource -Name $testModuleName -Version "5.0.0" -Repository $PSGalleryName -TrustRepository + Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository + $res = Get-PSResource -Name $testModuleName + $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} + $prereleaseVersionPkgs.Count | Should -Be 2 + + Uninstall-PSResource -Name $testModuleName -Version "*" -Prerelease + $res = Get-PSResource -Name $testModuleName + $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} + $prereleaseVersionPkgs.Count | Should -Be 0 + $stableVersionPkgs = $res | Where-Object {$_.IsPrerelease -ne $true} + $stableVersionPkgs.Count | Should -Be 2 + } + + It "uninstall all prerelease versions (which satisfy the range) when -Version range and -Prerelease parameter is specified" { + Uninstall-PSResource -Name $testModuleName -Version "*" + Install-PSResource -Name $testModuleName -Version "2.5.0-beta" -Repository $PSGalleryName -TrustRepository + Install-PSResource -Name $testModuleName -Version "3.0.0" -Repository $PSGalleryName -TrustRepository + Install-PSResource -Name $testModuleName -Version "5.0.0" -Repository $PSGalleryName -TrustRepository + Install-PSResource -Name $testModuleName -Version "5.2.5-alpha001" -Repository $PSGalleryName -TrustRepository + + $res = Get-PSResource -Name $testModuleName + $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} + $prereleaseVersionPkgs.Count | Should -Be 2 + + Uninstall-PSResource -Name $testModuleName -Version "[2.0.0, 5.0.0]" -Prerelease + $res = Get-PSResource -Name $testModuleName + # should only uninstall 2.5.0-beta, 5.2.5-alpha001 is out of range and should remain installed + $prereleaseVersionPkgs = $res | Where-Object {$_.IsPrerelease -eq $true} + $prereleaseVersionPkgs.Count | Should -Be 1 + $stableVersionPkgs = $res | Where-Object {$_.IsPrerelease -ne $true} + # versions 3.0.0 falls in range but should not be uninstalled as Prerelease parameter only selects prerelease versions to uninstall + $stableVersionPkgs.Count | Should -Be 2 + } + + It "Uninstall module using -WhatIf, should not uninstall the module" { + Uninstall-PSResource -Name $testModuleName -WhatIf + $pkg = Get-PSResource $testModuleName -Version "5.0.0.0" + $pkg.Version | Should -Be "5.0.0.0" + } + + It "Do not Uninstall module that is a dependency for another module" { + $null = Install-PSResource "test_module" -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue - # Uninstall-PSResource -Name "RequiredModule1" -ErrorVariable ev -ErrorAction SilentlyContinue + Uninstall-PSResource -Name "RequiredModule1" -ErrorVariable ev -ErrorAction SilentlyContinue - # $pkg = Get-PSResource "RequiredModule1" - # $pkg | Should -Not -Be $null + $pkg = Get-PSResource "RequiredModule1" + $pkg | Should -Not -Be $null - # $ev.FullyQualifiedErrorId | Should -BeExactly 'UninstallPSResourcePackageIsaDependency,Microsoft.PowerShell.PowerShellGet.Cmdlets.UninstallPSResource' - # } + $ev.FullyQualifiedErrorId | Should -BeExactly 'UninstallPSResourcePackageIsaDependency,Microsoft.PowerShell.PowerShellGet.Cmdlets.UninstallPSResource' + } - # It "Uninstall module that is a dependency for another module using -SkipDependencyCheck" { - # $null = Install-PSResource $testModuleName -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue + It "Uninstall module that is a dependency for another module using -SkipDependencyCheck" { + $null = Install-PSResource $testModuleName -Repository $PSGalleryName -TrustRepository -WarningAction SilentlyContinue - # Uninstall-PSResource -Name "RequiredModule1" -SkipDependencyCheck + Uninstall-PSResource -Name "RequiredModule1" -SkipDependencyCheck $pkg = Get-PSResource "RequiredModule1" $pkg | Should -BeNullOrEmpty @@ -302,4 +287,4 @@ Describe 'Test Uninstall-PSResource for Modules' { $pkg.Name | Should -Be $testModuleName $pkg.Path.ToString().Contains("Documents") | Should -Be $true } -} +} \ No newline at end of file From 20610af89f83fd76e6649fac9a5cd20d623865de Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Thu, 12 May 2022 15:46:20 -0400 Subject: [PATCH 13/22] add test files for invalid script test files to test against --- src/code/PublishPSResource.cs | 36 ++++++---- test/PublishPSResource.Tests.ps1 | 65 +++++++++++++++++++ .../InvalidScriptMissingAuthor.ps1 | 43 ++++++++++++ .../InvalidScriptMissingDescription.ps1 | 42 ++++++++++++ ...idScriptMissingDescriptionCommentBlock.ps1 | 37 +++++++++++ .../testScripts/InvalidScriptMissingGuid.ps1 | 43 ++++++++++++ .../InvalidScriptMissingVersion.ps1 | 43 ++++++++++++ 7 files changed, 295 insertions(+), 14 deletions(-) create mode 100644 test/testFiles/testScripts/InvalidScriptMissingAuthor.ps1 create mode 100644 test/testFiles/testScripts/InvalidScriptMissingDescription.ps1 create mode 100644 test/testFiles/testScripts/InvalidScriptMissingDescriptionCommentBlock.ps1 create mode 100644 test/testFiles/testScripts/InvalidScriptMissingGuid.ps1 create mode 100644 test/testFiles/testScripts/InvalidScriptMissingVersion.ps1 diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 8c37426b9..5035fda97 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -785,22 +785,30 @@ private bool TryParseScriptMetadata( // get .DESCRIPTION comment CommentHelpInfo scriptCommentInfo = ast.GetHelpContent(); - if (scriptCommentInfo != null) + if (scriptCommentInfo == null) { - 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, "psScriptDescriptionMissingOrInvalidDescription", ErrorCategory.ParserError, null); - parseMetadataErrors.Add(psScriptMissingDescriptionOrInvalidPropertyError); - errors = parseMetadataErrors.ToArray(); - return false; - } + 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())) diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1 index 056df39f6..abd00ec28 100644 --- a/test/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResource.Tests.ps1 @@ -324,4 +324,69 @@ Describe "Test Publish-PSResource" { $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 $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$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 $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$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 $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$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 $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$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 $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$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 + } } 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() + + From b609ce6a3e44c21d8a23d7f835798fc4193bacb1 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Fri, 13 May 2022 13:40:38 -0400 Subject: [PATCH 14/22] revert changes to Get-ModuleResourcePublishedToLocalDrive() in PSGetTestUtils --- src/code/PublishPSResource.cs | 49 +++++++++++++++++++++-------------- test/PSGetTestUtils.psm1 | 20 +++----------- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 5035fda97..63e3ec3f5 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -296,7 +296,7 @@ protected override void EndProcessing() return; } - else if(repository.Uri.Scheme == Uri.UriSchemeFile && !Directory.Exists(repository.Uri.LocalPath)) + else if(repository.Uri.Scheme == Uri.UriSchemeFile && !repository.Uri.IsUnc && !Directory.Exists(repository.Uri.LocalPath)) { //TODO: Anam would this include localhost? Test this. 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); @@ -354,9 +354,9 @@ protected override void EndProcessing() var outputNupkgDir = System.IO.Path.Combine(outputDir, "nupkg"); // pack into .nupkg - if (!PackNupkg(outputDir, outputNupkgDir, nuspec, out ErrorRecord error)) + if (!PackNupkg(outputDir, outputNupkgDir, nuspec, out ErrorRecord packNupkgError)) { - WriteError(error); + WriteError(packNupkgError); // exit out of processing return; } @@ -382,7 +382,12 @@ protected override void EndProcessing() } // This call does not throw any exceptions, but it will write unsuccessful responses to the console - PushNupkg(outputNupkgDir, repository.Name, repositoryUri); + if (!PushNupkg(outputNupkgDir, repository.Name, repositoryUri, out ErrorRecord pushNupkgError)) + { + WriteError(packNupkgError); + // exit out of processing + return; + } } finally @@ -924,7 +929,7 @@ private bool PackNupkg(string outputDir, string outputNupkgDir, string nuspecFil 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 @@ -938,7 +943,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( @@ -959,6 +964,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 @@ -970,49 +976,54 @@ 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; + // WriteError(ApiKeyError); } else { var Error401 = new ErrorRecord(ex, "401Error", ErrorCategory.PermissionDenied, null); - WriteError(Error401); + error = Error401; + // WriteError(Error401); } } else if (e.Message.Contains("403")) { var Error403 = new ErrorRecord(ex, "403Error", ErrorCategory.PermissionDenied, null); + error = Error403; WriteError(Error403); } else if (e.Message.Contains("409")) { var Error409 = new ErrorRecord(ex, "409Error", ErrorCategory.PermissionDenied, null); + error = Error409; WriteError(Error409); } else { var HTTPRequestError = new ErrorRecord(ex, "HTTPRequestError", ErrorCategory.PermissionDenied, null); + error = HTTPRequestError; WriteError(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; + // WriteError(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 965455cef..8faeb2b7f 100644 --- a/test/PSGetTestUtils.psm1 +++ b/test/PSGetTestUtils.psm1 @@ -348,18 +348,7 @@ function Get-ScriptResourcePublishedToLocalRepoTestDrive } $scriptMetadata = Create-PSScriptMetadata @params - Write-Host "metadata:" - Write-Host $scriptMetadata Set-Content -Path $scriptFilePath -Value $scriptMetadata - Write-Host "in Utils" $scriptName - Write-Host $scriptFilePath - Write-Host $scriptRepoName - Write-Host $scriptVersion - $a = Get-PSResourceRepository $scriptRepoName - Write-Host "repo name" $a.Name - Write-Host "repo url:" $a.Uri - Write-Host "file dump:" - Write-Host (Get-Content $scriptFilePath) Publish-PSResource -Path $scriptFilePath -Repository $scriptRepoName -Verbose } @@ -388,10 +377,7 @@ function Get-ModuleResourcePublishedToLocalRepoTestDrive $moduleName, [string] - $repoName, - - [string] - $moduleVersion + $repoName ) Get-TestDriveSetUp @@ -399,8 +385,8 @@ function Get-ModuleResourcePublishedToLocalRepoTestDrive $publishModuleBase = Join-Path $script:testIndividualResourceFolder $publishModuleName $null = New-Item -Path $publishModuleBase -ItemType Directory -Force - # $version = "1.0" - New-ModuleManifest -Path (Join-Path -Path $publishModuleBase -ChildPath "$publishModuleName.psd1") -ModuleVersion $moduleVersion -Description "$publishModuleName module" + $version = "1.0" + New-ModuleManifest -Path (Join-Path -Path $publishModuleBase -ChildPath "$publishModuleName.psd1") -ModuleVersion $version -Description "$publishModuleName module" Publish-PSResource -Path $publishModuleBase -Repository $repoName } From 415ca94f744c932ca32e587ec6589023b1116343 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Fri, 13 May 2022 16:13:48 -0400 Subject: [PATCH 15/22] fix typo --- src/code/PublishPSResource.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 63e3ec3f5..17b535a69 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -384,7 +384,7 @@ protected override void EndProcessing() // 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(packNupkgError); + WriteError(pushNupkgError); // exit out of processing return; } @@ -977,32 +977,27 @@ private bool PushNupkg(string outputNupkgDir, string repoName, string repoUri, o ex = new ArgumentException(message); var ApiKeyError = new ErrorRecord(ex, "ApiKeyError", ErrorCategory.AuthenticationError, null); error = ApiKeyError; - // WriteError(ApiKeyError); } else { var Error401 = new ErrorRecord(ex, "401Error", ErrorCategory.PermissionDenied, null); error = Error401; - // WriteError(Error401); } } else if (e.Message.Contains("403")) { var Error403 = new ErrorRecord(ex, "403Error", ErrorCategory.PermissionDenied, null); error = Error403; - WriteError(Error403); } else if (e.Message.Contains("409")) { var Error409 = new ErrorRecord(ex, "409Error", ErrorCategory.PermissionDenied, null); error = Error409; - WriteError(Error409); } else { var HTTPRequestError = new ErrorRecord(ex, "HTTPRequestError", ErrorCategory.PermissionDenied, null); error = HTTPRequestError; - WriteError(HTTPRequestError); } return success; @@ -1013,7 +1008,6 @@ private bool PushNupkg(string outputNupkgDir, string repoName, string repoUri, o var ex = new ArgumentException(e.Message); var PushNupkgError = new ErrorRecord(ex, "PushNupkgError", ErrorCategory.InvalidResult, null); error = PushNupkgError; - // WriteError(PushNupkgError); return success; } From 3a1842bb5e7f969d7b5bda908ec1f0b3b2a06308 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Fri, 13 May 2022 16:19:06 -0400 Subject: [PATCH 16/22] update comment --- src/code/PublishPSResource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 17b535a69..41b83516c 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -298,7 +298,7 @@ protected override void EndProcessing() } else if(repository.Uri.Scheme == Uri.UriSchemeFile && !repository.Uri.IsUnc && !Directory.Exists(repository.Uri.LocalPath)) { - //TODO: Anam would this include localhost? Test this. + // 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); From b103ca6189fd1d0155ae1fe9fe437923265b26b7 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Mon, 16 May 2022 11:00:55 -0400 Subject: [PATCH 17/22] cant use -AdditionalChildPath for windowsPowerShell --- test/PublishPSResource.Tests.ps1 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1 index abd00ec28..34c6bfc46 100644 --- a/test/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResource.Tests.ps1 @@ -41,12 +41,16 @@ Describe "Test Publish-PSResource" { $script:destinationPath = [IO.Path]::GetFullPath((Join-Path -Path $TestDrive -ChildPath "tmpDestinationPath")) New-Item $script:destinationPath -ItemType directory -Force - #Create folder where we shall place all script files used in these tests + #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 script files used for testing + $script:testFilesFolderPath = Join-Path $psscriptroot -ChildPath "testFiles" + $script:testScriptFolderPath = Join-Path $testFilesFolderPath -ChildPath "testScripts" } AfterAll { Get-RevertPSResourceRepositoryFile @@ -329,7 +333,7 @@ Describe "Test Publish-PSResource" { $scriptName = "InvalidScriptMissingAuthor.ps1" $scriptVersion = "1.0.0" - $scriptFilePath = Join-Path $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$scriptName + $scriptFilePath = Join-Path $script:testScriptFolderPath -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" @@ -341,7 +345,7 @@ Describe "Test Publish-PSResource" { It "should write error and not publish script when Version property is missing" { $scriptName = "InvalidScriptMissingVersion.ps1" - $scriptFilePath = Join-Path $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$scriptName + $scriptFilePath = Join-Path $script:testScriptFolderPath -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" @@ -354,7 +358,7 @@ Describe "Test Publish-PSResource" { $scriptName = "InvalidScriptMissingGuid.ps1" $scriptVersion = "1.0.0" - $scriptFilePath = Join-Path $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$scriptName + $scriptFilePath = Join-Path $script:testScriptFolderPath -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" @@ -367,7 +371,7 @@ Describe "Test Publish-PSResource" { $scriptName = "InvalidScriptMissingDescription.ps1" $scriptVersion = "1.0.0" - $scriptFilePath = Join-Path $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$scriptName + $scriptFilePath = Join-Path $script:testScriptFolderPath -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" @@ -381,7 +385,7 @@ Describe "Test Publish-PSResource" { $scriptName = "InvalidScriptMissingDescriptionCommentBlock.ps1" $scriptVersion = "1.0.0" - $scriptFilePath = Join-Path $psscriptroot -ChildPath "testFiles" -AdditionalChildPath "testScripts",$scriptName + $scriptFilePath = Join-Path $script:testScriptFolderPath -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" From 3952af916aa1311a3a5fdb69dd991b5e74f05ed7 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 18 May 2022 17:07:37 -0400 Subject: [PATCH 18/22] PR feedback- add example script comment back --- src/code/PublishPSResource.cs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index 1601db07a..a43ffab9a 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -728,6 +728,30 @@ private bool TryParseScriptMetadata( { 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 + .AUTHOR Jane Doe + .COMPANYNAME Microsoft + .COPYRIGHT + .TAGS Windows MacOS + #> + + <# + + .SYNOPSIS + Synopsis description here + .DESCRIPTION + Description here + .PARAMETER Name + .EXAMPLE + Example cmdlet here + + #> + */ + // Parse the script file var ast = Parser.ParseFile( filePath, From f96ce55d062fa4f60a2dcca3e105c2bbd49422c0 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 18 May 2022 17:16:54 -0400 Subject: [PATCH 19/22] PR feedback- remove comment --- src/code/PublishPSResource.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/code/PublishPSResource.cs b/src/code/PublishPSResource.cs index a43ffab9a..f80f0ca5c 100644 --- a/src/code/PublishPSResource.cs +++ b/src/code/PublishPSResource.cs @@ -195,8 +195,6 @@ protected override void EndProcessing() Hashtable parsedMetadata; if (isScript) { - // this converts from System.IO.DirectoryInfo to string. resourceFilePath needs to be set for later use though - // alternatively could use resourceFilePath = _path resourceFilePath = pkgFileOrDir.FullName; // Check that script metadata is valid From c284f4de01ffde43493e136b0e2e0f0533802b04 Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 18 May 2022 17:33:15 -0400 Subject: [PATCH 20/22] remove unncessary comments in PSGEtTestUtils --- test/PSGetTestUtils.psm1 | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1 index 8faeb2b7f..f9364ff20 100644 --- a/test/PSGetTestUtils.psm1 +++ b/test/PSGetTestUtils.psm1 @@ -325,15 +325,10 @@ function Get-ScriptResourcePublishedToLocalRepoTestDrive ) Get-TestDriveSetUp - # $publishScriptBase = Join-Path $script:testIndividualResourceFolder $scriptName - # $null = New-Item -Path $publishScriptBase -ItemType Directory -Force - $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 = $scriptVersion GUID = [guid]::NewGuid() Author = 'Jane' From 81bcb3e1411314bacb0283f5a985e78d009da02e Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 18 May 2022 17:52:10 -0400 Subject: [PATCH 21/22] add space after .PRIVATEDATA key in test script that gets created --- test/PSGetTestUtils.psm1 | 1 + 1 file changed, 1 insertion(+) diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1 index f9364ff20..e72984f60 100644 --- a/test/PSGetTestUtils.psm1 +++ b/test/PSGetTestUtils.psm1 @@ -526,6 +526,7 @@ function Create-PSScriptMetadata .RELEASENOTES $($ReleaseNotes -join "`r`n") + .PRIVATEDATA$(if ($PrivateData) {" $PrivateData"}) #> From b7091b0fcadb6a95c06696b7b41f472e7e49f0ef Mon Sep 17 00:00:00 2001 From: Anam Navied Date: Wed, 18 May 2022 18:02:38 -0400 Subject: [PATCH 22/22] revert unintentional change to Uninstall tests --- test/UninstallPSResource.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/UninstallPSResource.Tests.ps1 b/test/UninstallPSResource.Tests.ps1 index e4ea4babb..0c0850e60 100644 --- a/test/UninstallPSResource.Tests.ps1 +++ b/test/UninstallPSResource.Tests.ps1 @@ -287,4 +287,4 @@ Describe 'Test Uninstall-PSResource for Modules' { $pkg.Name | Should -Be $testModuleName $pkg.Path.ToString().Contains("Documents") | Should -Be $true } -} \ No newline at end of file +}