From 93a81a5f6812e3cd15c52677816d8c87ad53c046 Mon Sep 17 00:00:00 2001 From: alerickson <25858831+alerickson@users.noreply.github.com> Date: Mon, 18 Mar 2024 13:54:11 -0700 Subject: [PATCH] ACR: Parse 'ModuleList' from ACR server metadata to populate 'Dependencies' in PSResourceInfo object (#1604) Create OneBranch build and release pipeline (#1605) Add verbose and debug messages for Container Registry Server (#1615) Add Name and Repository pipeline by property name (#1451) Bugfix script parse whitespace (#1457) Bug fix for Import-PSGetRepository in Windows PS (#1460) Update README.md (#1458) update chANGELOG, psd1, csproj for release 1.0.1 (#1473) Bump System.Text.Json from 6.0.0 to 8.0.0 in /src/code (#1475) Verify whether SourceLocation is a UNC path and select the appropriate ApiVersion (#1479) TryConvertFromXml: Prevent NRE when NormalizedVersion is missing (#1503) Update InstallHelper.cs (#1510) Bump BenchmarkDotNet.Diagnostics.Windows in /test/perf/benchmarks (#1528) enable isJFrogRepo flag for domains containing `artifactory` (#1532) Fix 'name' bug with v2 JFrog Artifactory (#1535) Bugfix Update-ModuleManifest throws null pointer exception (#1538) Add tests for ADO v2 server (#1539) Bugfix - Test if InstalledScriptInfos folder exists and create if needed (#1542) * Test InstalledScriptInfos folder and create if needed * Update src/code/InstallHelper.cs Co-authored-by: Aditya Patwardhan * Update src/code/InstallHelper.cs Co-authored-by: Aditya Patwardhan --------- Co-authored-by: Aditya Patwardhan Remove redeclaration of s_tempHome (#1544) Bug fix for Update-PSResource not updating from correct repository (#1549) Update changelog, version, releasenotes --- .config/tsaoptions.json | 10 + .pipelines/PSResourceGet-Official.yml | 334 ++++++++++++++++++ CHANGELOG.md | 2 +- global.json | 5 + src/Microsoft.PowerShell.PSResourceGet.psd1 | 27 +- src/code/ContainerRegistryServerAPICalls.cs | 34 +- .../Microsoft.PowerShell.PSResourceGet.csproj | 6 +- src/code/PSResourceInfo.cs | 10 + src/code/UpdateModuleManifest.cs | 6 - .../PublishPSResource.Tests.ps1 | 4 +- 10 files changed, 422 insertions(+), 16 deletions(-) create mode 100644 .config/tsaoptions.json create mode 100644 .pipelines/PSResourceGet-Official.yml create mode 100644 global.json diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json new file mode 100644 index 000000000..692eaec1f --- /dev/null +++ b/.config/tsaoptions.json @@ -0,0 +1,10 @@ +{ + "instanceUrl": "https://msazure.visualstudio.com", + "projectName": "One", + "areaPath": "One\\MGMT\\Compute\\Powershell\\Powershell\\PowerShell Core", + "notificationAliases": [ + "adityap@microsoft.com", + "americks@microsoft.com", + "annavied@microsoft.com" + ] +} diff --git a/.pipelines/PSResourceGet-Official.yml b/.pipelines/PSResourceGet-Official.yml new file mode 100644 index 000000000..c4b39f20b --- /dev/null +++ b/.pipelines/PSResourceGet-Official.yml @@ -0,0 +1,334 @@ +################################################################################# +# OneBranch Pipelines # +# This pipeline was created by EasyStart from a sample located at: # +# https://aka.ms/obpipelines/easystart/samples # +# Documentation: https://aka.ms/obpipelines # +# Yaml Schema: https://aka.ms/obpipelines/yaml/schema # +# Retail Tasks: https://aka.ms/obpipelines/tasks # +# Support: https://aka.ms/onebranchsup # +################################################################################# +name: PSResourceGet-Release-$(Build.BuildId) +trigger: none # https://aka.ms/obpipelines/triggers +pr: + branches: + include: + - main + - release* +parameters: # parameters are shown up in ADO UI in a build queue time +- name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false + +variables: + - name: DOTNET_CLI_TELEMETRY_OPTOUT + value: 1 + - name: POWERSHELL_TELEMETRY_OPTOUT + value: 1 + - name: WindowsContainerImage + value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest # Docker image which is used to build the project https://aka.ms/obpipelines/containers + +resources: + repositories: + - repository: onebranchTemplates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + template: v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates # https://aka.ms/obpipelines/templates + parameters: + featureFlags: + WindowsHostVersion: '1ESWindows2022' + customTags: 'ES365AIMigrationTooling' + globalSdl: + disableLegacyManifest: true + sbom: + enabled: true + packageName: Microsoft.PowerShell.PSResourceGet + codeql: + compiled: + enabled: true + asyncSdl: # https://aka.ms/obpipelines/asyncsdl + enabled: true + forStages: [stagebuild] + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory)\PSResourceGet + binskim: + enabled: true + apiscan: + enabled: false + + stages: + - stage: stagebuild + displayName: Build and Package Microsoft.PowerShell.PSResourceGet + jobs: + - job: jobbuild + displayName: Build Microsoft.PowerShell.PSResourceGet Files + variables: # More settings at https://aka.ms/obpipelines/yaml/jobs + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: repoRoot + value: $(Build.SourcesDirectory)\PSResourceGet + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PSResourceGet\.config\tsaoptions.json + - name: signSrcPath + value: $(repoRoot)/out + - name: depsPath + value: $(signSrcPath)\Microsoft.PowerShell.PSResourceGet\Dependencies + - name: ob_sdl_sbom_enabled + value: true + - name: ob_signing_setup_enabled + value: true + #CodeQL tasks added manually to workaround signing failures + - name: ob_sdl_codeql_compiled_enabled + value: false + pool: + type: windows + steps: + - checkout: self + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - pwsh: | + if (-not (Test-Path $(repoRoot)/.config/tsaoptions.json)) { + Get-ChildItem $(Build.SourcesDirectory) -recurse -ErrorAction SilentlyContinue + throw "tsaoptions.json does not exist under $(repoRoot)/.config" + } + displayName: Test if tsaoptions.json exists + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - pwsh: | + Get-ChildItem env: + displayName: Capture Environment + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: UseDotNet@2 + displayName: 'Install .NET dependencies' + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + inputs: + packageType: 'sdk' + useGlobalJson: true + # this is to ensure that we are installing the dotnet at the same location as container by default install the dotnet sdks + installationPath: 'C:\Program Files\dotnet\' + workingDirectory: $(repoRoot) + + - task: CodeQL3000Init@0 # Add CodeQL Init task right before your 'Build' step. + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + inputs: + Enabled: true + AnalyzeInPipeline: true + Language: csharp + + - pwsh: | + $module = 'Microsoft.PowerShell.PSResourceGet' + Write-Verbose "installing $module..." -verbose + $ProgressPreference = 'SilentlyContinue' + Install-Module $module -AllowClobber -Force + displayName: Install PSResourceGet 0.9.0 or above for build.psm1 + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + # this is installing .NET + - pwsh: | + Set-Location "$(repoRoot)" + try { ./build.ps1 -Build -Clean -BuildConfiguration Release -BuildFramework 'net472'} catch { throw $_ } + displayName: Execute build + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: CodeQL3000Finalize@0 # Add CodeQL Finalize task right after your 'Build' step. + condition: always() + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: onebranch.pipeline.signing@1 + displayName: Sign 1st party files + inputs: + command: 'sign' + signing_profile: external_distribution + files_to_sign: '**\*.psd1;**\*.psm1;**\*.ps1xml;**\Microsoft*.dll' + search_root: $(signSrcPath) + + - pwsh: | + $unsignedDepsPath = Join-Path -Path $(signSrcPath) -ChildPath "Microsoft.PowerShell.PSResourceGet" -AdditionalChildPath "UnsignedDependencies" + New-Item -Path $unsignedDepsPath -ItemType Directory -Force + + Get-ChildItem -Path $(depsPath) -Filter '*.dll' | Foreach-Object { + $sig = Get-AuthenticodeSignature -FilePath $_.FullName + if ($sig.Status -ne 'Valid' -or $sig.SignerCertificate.Subject -notlike '*Microsoft*' -or $sig.SignerCertificate.Issuer -notlike '*Microsoft Code Signing PCA*') { + # Copy for third party signing + Copy-Item -Path $_.FullName -Dest $unsignedDepsPath -Force -Verbose + } + } + displayName: Find all 3rd party files that need to be signed + + - task: onebranch.pipeline.signing@1 + displayName: Sign 3rd Party files + inputs: + command: 'sign' + signing_profile: 135020002 + files_to_sign: '*.dll' + search_root: $(signSrcPath)/Microsoft.PowerShell.PSResourceGet/UnsignedDependencies + + - pwsh: | + $newlySignedDepsPath = Join-Path -Path $(signSrcPath) -ChildPath "Microsoft.PowerShell.PSResourceGet" -AdditionalChildPath "UnsignedDependencies" + Get-ChildItem -Path $newlySignedDepsPath -Filter '*.dll' | Foreach-Object { + $sig = Get-AuthenticodeSignature -FilePath $_.FullName + if ($sig.Status -ne 'Valid' -or $sig.SignerCertificate.Subject -notlike '*Microsoft*' -or $sig.SignerCertificate.Issuer -notlike '*Microsoft Windows Production PCA*') { + Write-Error "File $($_.FileName) is not signed by Microsoft" + } + else { + Copy-Item -Path $_.FullName -Dest $(depsPath) -Force -Verbose + } + } + Remove-Item -Path $newlySignedDepsPath -Recurse -Force + displayName: Validate 3rd party files were signed + + - task: CopyFiles@2 + displayName: "Copy signed files to ob_outputDirectory - '$(ob_outputDirectory)'" + inputs: + SourceFolder: "$(signSrcPath)" + Contents: '**' + TargetFolder: $(ob_outputDirectory) + + - pwsh: | + Write-Host "Displaying contents of signSrcPath:" + Get-ChildItem $(signSrcPath) -Recurse + Write-Host "Displaying contents of ob_outputDirectory:" + Get-ChildItem $(ob_outputDirectory) -Recurse + displayName: Get contents of dirs with signed files + + - job: nupkg + dependsOn: jobbuild + displayName: Package Microsoft.PowerShell.PSResourceGet + variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: repoRoot + value: $(Build.SourcesDirectory)\PSResourceGet + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PSResourceGet\.config\tsaoptions.json + # Disable because SBOM was already built in the previous job + - name: ob_sdl_sbom_enabled + value: false + - name: signOutPath + value: $(repoRoot)/signed + - name: ob_signing_setup_enabled + value: true + # This job is not compiling code, so disable codeQL + - name: ob_sdl_codeql_compiled_enabled + value: false + + pool: + type: windows + steps: + - checkout: self + + - pwsh: | + if (-not (Test-Path $(repoRoot)/.config/tsaoptions.json)) { + Get-ChildItem $(Build.SourcesDirectory) -recurse -ErrorAction SilentlyContinue + throw "tsaoptions.json does not exist under $(repoRoot)/.config" + } + displayName: Test if tsaoptions.json exists + + - task: DownloadPipelineArtifact@2 + displayName: 'Download build files' + inputs: + targetPath: $(signOutPath) + artifact: drop_stagebuild_jobbuild + + - pwsh: | + Set-Location "$(signOutPath)" + Write-Host "Contents of signOutPath:" + Get-ChildItem $(signOutPath) -Recurse + displayName: Capture artifacts directory structure + + - pwsh: | + $module = 'Microsoft.PowerShell.PSResourceGet' + Write-Verbose "installing $module..." -verbose + $ProgressPreference = 'SilentlyContinue' + Install-Module $module -AllowClobber -Force + displayName: Install PSResourceGet 0.9.0 or above for build.psm1 + + - pwsh: | + Set-Location "$(signOutPath)\Microsoft.PowerShell.PSResourceGet" + New-Item -ItemType Directory -Path "$(signOutPath)\PublishedNupkg" -Force + Register-PSResourceRepository -Name 'localRepo' -Uri "$(signOutPath)\PublishedNupkg" + Publish-PSResource -Path "$(signOutPath)\Microsoft.PowerShell.PSResourceGet" -Repository 'localRepo' -Verbose + displayName: Create nupkg for publishing + + - task: onebranch.pipeline.signing@1 + displayName: Sign nupkg + inputs: + command: 'sign' + signing_profile: external_distribution + files_to_sign: '**\*.nupkg' + search_root: "$(signOutPath)\PublishedNupkg" + + - pwsh: | + Set-Location "$(signOutPath)\PublishedNupkg" + Write-Host "Contents of signOutPath:" + Get-ChildItem "$(signOutPath)" -Recurse + displayName: Find Nupkg + + - task: CopyFiles@2 + displayName: "Copy nupkg to ob_outputDirectory - '$(ob_outputDirectory)'" + inputs: + Contents: $(signOutPath)\PublishedNupkg\Microsoft.PowerShell.PSResourceGet.*.nupkg + TargetFolder: $(ob_outputDirectory) + + - pwsh: | + Write-Host "Contents of ob_outputDirectory:" + Get-ChildItem "$(ob_outputDirectory)" -Recurse + displayName: Find Signed Nupkg + + - stage: release + displayName: Release PSResourceGet + dependsOn: stagebuild + variables: + version: $[ stageDependencies.build.main.outputs['package.version'] ] + drop: $(Pipeline.Workspace)/drop_build_main + jobs: + - job: validation + displayName: Manual validation + pool: + type: agentless + timeoutInMinutes: 1440 + steps: + - task: ManualValidation@0 + displayName: Wait 24 hours for validation + inputs: + instructions: Please validate the release + timeoutInMinutes: 1440 + - job: PSGalleryPublish + displayName: Publish to PSGallery + dependsOn: validation + pool: + type: windows + variables: + ob_outputDirectory: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + steps: + - download: current + displayName: Download artifact + + - pwsh: | + Get-ChildItem $(Pipeline.Workspace) -Recurse + displayName: Capture environment + + - pwsh: | + Get-ChildItem "$(Pipeline.Workspace)/drop_stagebuild_nupkg" -Recurse + displayName: Find signed Nupkg + + - task: NuGetCommand@2 + displayName: Push PowerShellGet module artifacts to PSGallery feed + inputs: + command: push + packagesToPush: '$(Pipeline.Workspace)\drop_stagebuild_nupkg\PSResourceGet\signed\PublishedNupkg\Microsoft.PowerShell.PSResourceGet.*.nupkg' + nuGetFeedType: external + publishFeedCredentials: PSGet-PSGalleryPush diff --git a/CHANGELOG.md b/CHANGELOG.md index 08dacccbd..622bae039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1 @@ -The change logs have been split by version and moved to [CHANGELOG](./CHANGELOG). \ No newline at end of file +The change logs have been split by version and moved to [CHANGELOG](./CHANGELOG). diff --git a/global.json b/global.json new file mode 100644 index 000000000..65405ec9c --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "8.0.202" + } +} diff --git a/src/Microsoft.PowerShell.PSResourceGet.psd1 b/src/Microsoft.PowerShell.PSResourceGet.psd1 index e4e01cfbb..bac80a5da 100644 --- a/src/Microsoft.PowerShell.PSResourceGet.psd1 +++ b/src/Microsoft.PowerShell.PSResourceGet.psd1 @@ -4,7 +4,7 @@ @{ RootModule = './Microsoft.PowerShell.PSResourceGet.dll' NestedModules = @('./Microsoft.PowerShell.PSResourceGet.psm1') - ModuleVersion = '1.0.0' + ModuleVersion = '1.0.2' CompatiblePSEditions = @('Core', 'Desktop') GUID = 'e4e0bda1-0703-44a5-b70d-8fe704cd0643' Author = 'Microsoft Corporation' @@ -55,6 +55,31 @@ ProjectUri = 'https://go.microsoft.com/fwlink/?LinkId=828955' LicenseUri = 'https://go.microsoft.com/fwlink/?LinkId=829061' ReleaseNotes = @' +## 1.0.2 + +### Bug Fixes +- Bug fix for `Update-PSResource` not updating from correct repository (#1549) +- Bug fix for creating temp home directory on Unix (#1544) +- Bug fix for creating `InstalledScriptInfos` directory when it does not exist (#1542) +- Bug fix for `Update-ModuleManifest` throwing null pointer exception (#1538) +- Bug fix for `name` property not populating in `PSResourceInfo` object when using `Find-PSResource` with JFrog Artifactory (#1535) +- Bug fix for incorrect configuration of requests to JFrog Artifactory v2 endpoints (#1533 Thanks @sean-r-williams!) +- Bug fix for determining JFrog Artifactory repositories (#1532 Thanks @sean-r-williams!) +- Bug fix for v2 server repositories incorrectly adding script endpoint (1526) +- Bug fixes for null references (#1525) +- Typo fixes in message prompts in `Install-PSResource` (#1510 Thanks @NextGData!) +- Bug fix to add `NormalizedVersion` property to `AdditionalMetadata` only when it exists (#1503 Thanks @sean-r-williams!) +- Bug fix to verify whether `Uri` is a UNC path and set respective `ApiVersion` (#1479 Thanks @kborowinski!) + +## 1.0.1 + +### Bug Fixes +- Bugfix to update Unix local user installation paths to be compatible with .NET 7 and .NET 8 (#1464) +- Bugfix for Import-PSGetRepository in Windows PowerShell (#1460) +- Bugfix for Test-PSScriptFileInfo to be less sensitive to whitespace (#1457) +- Bugfix to overwrite rels/rels directory on net472 when extracting nupkg to directory (#1456) +- Bugfix to add pipeline by property name support for Name and Repository properties for Find-PSResource (#1451 Thanks @ThomasNieto!) + ## 1.0.0 ### New Features diff --git a/src/code/ContainerRegistryServerAPICalls.cs b/src/code/ContainerRegistryServerAPICalls.cs index 0a7360109..5bf5872ad 100644 --- a/src/code/ContainerRegistryServerAPICalls.cs +++ b/src/code/ContainerRegistryServerAPICalls.cs @@ -301,6 +301,7 @@ private Stream InstallVersion( string packageVersion, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::InstallVersion()"); errRecord = null; string packageNameLowercase = packageName.ToLower(); string accessToken = string.Empty; @@ -371,6 +372,7 @@ private Stream InstallVersion( /// internal string GetContainerRegistryAccessToken(out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetContainerRegistryAccessToken()"); string accessToken = string.Empty; string containerRegistryAccessToken = string.Empty; string tenantID = string.Empty; @@ -387,7 +389,6 @@ internal string GetContainerRegistryAccessToken(out ErrorRecord errRecord) _cmdletPassedIn.WriteVerbose("Access token retrieved."); tenantID = repositoryCredentialInfo.SecretName; - _cmdletPassedIn.WriteVerbose($"Tenant ID: {tenantID}"); } else { @@ -437,6 +438,7 @@ internal string GetContainerRegistryAccessToken(out ErrorRecord errRecord) /// internal bool IsContainerRegistryUnauthenticated(string containerRegistyUrl, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::IsContainerRegistryUnauthenticated()"); errRecord = null; string endpoint = $"{containerRegistyUrl}/v2/"; HttpResponseMessage response; @@ -463,6 +465,7 @@ internal bool IsContainerRegistryUnauthenticated(string containerRegistyUrl, out /// internal string GetContainerRegistryRefreshToken(string tenant, string accessToken, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetContainerRegistryRefreshToken()"); string content = string.Format(containerRegistryRefreshTokenTemplate, Registry, tenant, accessToken); var contentHeaders = new Collection> { new KeyValuePair("Content-Type", "application/x-www-form-urlencoded") }; string exchangeUrl = string.Format(containerRegistryOAuthExchangeUrlTemplate, Registry); @@ -480,6 +483,7 @@ internal string GetContainerRegistryRefreshToken(string tenant, string accessTok /// internal string GetContainerRegistryAccessTokenByRefreshToken(string refreshToken, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetContainerRegistryAccessTokenByRefreshToken()"); string content = string.Format(containerRegistryAccessTokenTemplate, Registry, refreshToken); var contentHeaders = new Collection> { new KeyValuePair("Content-Type", "application/x-www-form-urlencoded") }; string tokenUrl = string.Format(containerRegistryOAuthTokenUrlTemplate, Registry); @@ -501,6 +505,7 @@ internal string GetContainerRegistryAccessTokenByRefreshToken(string refreshToke /// private string GetDigestFromManifest(JObject manifest, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetDigestFromManifest()"); errRecord = null; string digest = String.Empty; @@ -544,6 +549,7 @@ private string GetDigestFromManifest(JObject manifest, out ErrorRecord errRecord /// internal JObject GetContainerRegistryRepositoryManifest(string packageName, string version, string containerRegistryAccessToken, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetContainerRegistryRepositoryManifest()"); // example of manifestUrl: https://psgetregistry.azurecr.io/hello-world:3.0.0 string manifestUrl = string.Format(containerRegistryManifestUrlTemplate, Registry, packageName, version); var defaultHeaders = GetDefaultHeaders(containerRegistryAccessToken); @@ -556,6 +562,7 @@ internal JObject GetContainerRegistryRepositoryManifest(string packageName, stri /// internal async Task GetContainerRegistryBlobAsync(string packageName, string digest, string containerRegistryAccessToken) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetContainerRegistryBlobAsync()"); string blobUrl = string.Format(containerRegistryBlobDownloadUrlTemplate, Registry, packageName, digest); var defaultHeaders = GetDefaultHeaders(containerRegistryAccessToken); return await GetHttpContentResponseJObject(blobUrl, defaultHeaders); @@ -585,6 +592,7 @@ internal JObject FindContainerRegistryImageTags(string packageName, string versi * } * }] */ + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::FindContainerRegistryImageTags()"); string resolvedVersion = string.Equals(version, "*", StringComparison.OrdinalIgnoreCase) ? null : $"/{version}"; string findImageUrl = string.Format(containerRegistryFindImageVersionUrlTemplate, Registry, packageName, resolvedVersion); var defaultHeaders = GetDefaultHeaders(containerRegistryAccessToken); @@ -596,6 +604,7 @@ internal JObject FindContainerRegistryImageTags(string packageName, string versi /// internal Hashtable GetContainerRegistryMetadata(string packageName, string exactTagVersion, string containerRegistryAccessToken, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetContainerRegistryMetadata()"); Hashtable requiredVersionResponse = new Hashtable(); var foundTags = FindContainerRegistryManifest(packageName, exactTagVersion, containerRegistryAccessToken, out errRecord); @@ -712,6 +721,7 @@ internal Hashtable GetContainerRegistryMetadata(string packageName, string exact /// internal JObject FindContainerRegistryManifest(string packageName, string version, string containerRegistryAccessToken, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::FindContainerRegistryManifest()"); var createManifestUrl = string.Format(containerRegistryManifestUrlTemplate, Registry, packageName, version); _cmdletPassedIn.WriteDebug($"GET manifest url: {createManifestUrl}"); @@ -724,6 +734,7 @@ internal JObject FindContainerRegistryManifest(string packageName, string versio /// internal ContainerRegistryInfo GetMetadataProperty(JObject foundTags, string packageName, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetMetadataProperty()"); errRecord = null; ContainerRegistryInfo serverPkgInfo = null; @@ -803,6 +814,7 @@ internal ContainerRegistryInfo GetMetadataProperty(JObject foundTags, string pac /// internal async Task UploadManifest(string packageName, string packageVersion, string configPath, bool isManifest, string containerRegistryAccessToken) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::UploadManifest()"); try { var createManifestUrl = string.Format(containerRegistryManifestUrlTemplate, Registry, packageName, packageVersion); @@ -817,6 +829,7 @@ internal async Task UploadManifest(string packageName, stri internal async Task GetHttpContentResponseJObject(string url, Collection> defaultHeaders) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetHttpContentResponseJObject()"); try { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); @@ -834,6 +847,7 @@ internal async Task GetHttpContentResponseJObject(string url, Colle /// internal JObject GetHttpResponseJObjectUsingDefaultHeaders(string url, HttpMethod method, Collection> defaultHeaders, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetHttpResponseJObjectUsingDefaultHeaders()"); try { errRecord = null; @@ -883,6 +897,7 @@ internal JObject GetHttpResponseJObjectUsingDefaultHeaders(string url, HttpMetho /// internal JObject GetHttpResponseJObjectUsingContentHeaders(string url, HttpMethod method, string content, Collection> contentHeaders, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetHttpResponseJObjectUsingContentHeaders()"); errRecord = null; try { @@ -1122,10 +1137,12 @@ internal bool PushNupkgContainerRegistry(string psd1OrPs1File, Hashtable dependencies, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::PushNupkgContainerRegistry()"); string fullNupkgFile = System.IO.Path.Combine(outputNupkgDir, packageName + "." + packageVersion.ToNormalizedString() + ".nupkg"); string packageNameLowercase = packageName.ToLower(); // Get access token (includes refresh tokens) + _cmdletPassedIn.WriteVerbose($"Get access token for container registry server."); var containerRegistryAccessToken = GetContainerRegistryAccessToken(out errRecord); if (errRecord != null) { @@ -1133,6 +1150,7 @@ internal bool PushNupkgContainerRegistry(string psd1OrPs1File, } // Upload .nupkg + _cmdletPassedIn.WriteVerbose($"Upload .nupkg file: {fullNupkgFile}"); string nupkgDigest = UploadNupkgFile(packageNameLowercase, containerRegistryAccessToken, fullNupkgFile, out errRecord); if (errRecord != null) { @@ -1148,6 +1166,7 @@ internal bool PushNupkgContainerRegistry(string psd1OrPs1File, // Create config.json file var configFilePath = System.IO.Path.Combine(outputNupkgDir, "config.json"); + _cmdletPassedIn.WriteVerbose($"Create config.json file at path: {configFilePath}"); string configDigest = CreateConfigFile(configFilePath, out errRecord); if (errRecord != null) { @@ -1178,6 +1197,7 @@ internal bool PushNupkgContainerRegistry(string psd1OrPs1File, /// private string UploadNupkgFile(string packageNameLowercase, string containerRegistryAccessToken, string fullNupkgFile, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::UploadNupkgFile()"); _cmdletPassedIn.WriteVerbose("Start uploading blob"); string nupkgDigest = string.Empty; errRecord = null; @@ -1240,6 +1260,7 @@ private string UploadNupkgFile(string packageNameLowercase, string containerRegi /// private void CreateAndUploadEmptyFile(string outputNupkgDir, string pkgNameLower, string containerRegistryAccessToken, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::CreateAndUploadEmptyFile()"); _cmdletPassedIn.WriteVerbose("Create an empty file"); string emptyFileName = "empty" + Guid.NewGuid().ToString() + ".txt"; var emptyFilePath = System.IO.Path.Combine(outputNupkgDir, emptyFileName); @@ -1290,6 +1311,7 @@ private void CreateAndUploadEmptyFile(string outputNupkgDir, string pkgNameLower /// private string CreateConfigFile(string configFilePath, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::CreateConfigFile()"); string configFileDigest = string.Empty; _cmdletPassedIn.WriteVerbose("Create the config file"); while (File.Exists(configFilePath)) @@ -1336,6 +1358,7 @@ private bool TryCreateAndUploadManifest(string fullNupkgFile, string containerRegistryAccessToken, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::TryCreateAndUploadManifest()"); errRecord = null; string packageNameLowercase = packageName.ToLower(); FileInfo nupkgFile = new FileInfo(fullNupkgFile); @@ -1377,6 +1400,7 @@ private string CreateManifestContent( ResourceType resourceType, string metadata) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::CreateManifestContent()"); StringBuilder stringBuilder = new StringBuilder(); StringWriter stringWriter = new StringWriter(stringBuilder); JsonTextWriter jsonWriter = new JsonTextWriter(stringWriter); @@ -1436,6 +1460,7 @@ private string CreateManifestContent( /// private string CreateDigest(string fileName, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::CreateDigest()"); errRecord = null; string digest = string.Empty; FileInfo fileInfo = new FileInfo(fileName); @@ -1457,8 +1482,6 @@ private string CreateDigest(string fileName, out ErrorRecord errRecord) } digest = stringBuilder.ToString(); - // Write the name and hash value of the file to the console. - _cmdletPassedIn.WriteVerbose($"{fileInfo.Name}: {digest}"); } catch (IOException ex) { @@ -1490,6 +1513,7 @@ private string CreateDigest(string fileName, out ErrorRecord errRecord) /// private string CreateMetadataContent(ResourceType resourceType, Hashtable parsedMetadata, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::CreateMetadataContent()"); errRecord = null; string jsonString = string.Empty; @@ -1533,6 +1557,7 @@ private string CreateMetadataContent(ResourceType resourceType, Hashtable parsed /// internal async Task GetStartUploadBlobLocation(string packageName, string containerRegistryAccessToken) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetStartUploadBlobLocation()"); try { var defaultHeaders = GetDefaultHeaders(containerRegistryAccessToken); @@ -1550,6 +1575,7 @@ internal async Task GetStartUploadBlobLocation(string packageName, strin /// internal async Task EndUploadBlob(string location, string filePath, string digest, bool isManifest, string containerRegistryAccessToken) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::EndUploadBlob()"); try { var endUploadUrl = string.Format(containerRegistryEndUploadTemplate, Registry, location, digest); @@ -1571,6 +1597,7 @@ internal async Task EndUploadBlob(string location, string f /// private Hashtable[] FindPackagesWithVersionHelper(string packageName, VersionType versionType, VersionRange versionRange, NuGetVersion requiredVersion, bool includePrerelease, bool getOnlyLatest, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::FindPackagesWithVersionHelper()"); string accessToken = string.Empty; string tenantID = string.Empty; string registryUrl = Repository.Uri.ToString(); @@ -1624,6 +1651,7 @@ private Hashtable[] FindPackagesWithVersionHelper(string packageName, VersionTyp /// private SortedDictionary GetPackagesWithRequiredVersion(List allPkgVersions, VersionType versionType, VersionRange versionRange, NuGetVersion specificVersion, string packageName, bool includePrerelease, out ErrorRecord errRecord) { + _cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetPackagesWithRequiredVersion()"); errRecord = null; // we need NuGetVersion to sort versions by order, and string pkgVersionString (which is the exact tag from the server) to call GetContainerRegistryMetadata() later with exact version tag. SortedDictionary sortedPkgs = new SortedDictionary(VersionComparer.Default); diff --git a/src/code/Microsoft.PowerShell.PSResourceGet.csproj b/src/code/Microsoft.PowerShell.PSResourceGet.csproj index cdf0feb9d..ac57a35e5 100644 --- a/src/code/Microsoft.PowerShell.PSResourceGet.csproj +++ b/src/code/Microsoft.PowerShell.PSResourceGet.csproj @@ -5,9 +5,9 @@ Library Microsoft.PowerShell.PSResourceGet Microsoft.PowerShell.PSResourceGet - 1.0.0.0 - 1.0.0 - 1.0.0 + 1.0.2.0 + 1.0.2 + 1.0.2 net472;netstandard2.0 9.0 true diff --git a/src/code/PSResourceInfo.cs b/src/code/PSResourceInfo.cs index 610e36046..9e2fc6bc2 100644 --- a/src/code/PSResourceInfo.cs +++ b/src/code/PSResourceInfo.cs @@ -969,6 +969,16 @@ public static bool TryConvertFromContainerRegistryJson( { metadata["Dependencies"] = ParseContainerRegistryDependencies(requiredModulesElement, out errorMsg).ToArray(); } + if (string.Equals(packageName, "Az", StringComparison.OrdinalIgnoreCase) || packageName.StartsWith("Az.", StringComparison.OrdinalIgnoreCase)) + { + if (rootDom.TryGetProperty("PrivateData", out JsonElement privateDataElement) && privateDataElement.TryGetProperty("PSData", out JsonElement psDataElement)) + { + if (psDataElement.TryGetProperty("ModuleList", out JsonElement moduleListDepsElement)) + { + metadata["Dependencies"] = ParseContainerRegistryDependencies(moduleListDepsElement, out errorMsg).ToArray(); + } + } + } var additionalMetadataHashtable = new Dictionary { diff --git a/src/code/UpdateModuleManifest.cs b/src/code/UpdateModuleManifest.cs index 8ba86d87a..566e12bbf 100644 --- a/src/code/UpdateModuleManifest.cs +++ b/src/code/UpdateModuleManifest.cs @@ -978,7 +978,6 @@ private void CreateModuleManifestForWinPSHelper(Hashtable parsedMetadata, string "ErrorCreatingTempDir", ErrorCategory.InvalidData, this); - return; } @@ -1059,7 +1058,6 @@ private string GetPrivateDataString(string[] tags, Uri licenseUri, Uri projectUr { /** Example PrivateData - PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. @@ -1093,7 +1091,6 @@ Example PrivateData string tagsString = string.Join(", ", tags.Select(item => "'" + item + "'")); string tagLine = tags.Length != 0 ? $"Tags = @({tagsString})" : "# Tags = @()"; - string licenseUriLine = licenseUri == null ? "# LicenseUri = ''" : $"LicenseUri = '{licenseUri.ToString()}'"; string projectUriLine = projectUri == null ? "# ProjectUri = ''" : $"ProjectUri = '{projectUri.ToString()}'"; string iconUriLine = iconUri == null ? "# IconUri = ''" : $"IconUri = '{iconUri.ToString()}'"; @@ -1105,7 +1102,6 @@ Example PrivateData string externalModuleDependenciesString = string.Join(", ", externalModuleDependencies.Select(item => "'" + item + "'")); string externalModuleDependenciesLine = externalModuleDependencies.Length == 0 ? "# ExternalModuleDependencies = @()" : $"ExternalModuleDependencies = @({externalModuleDependenciesString})"; - string initialPrivateDataString = "PrivateData = @{" + System.Environment.NewLine + "PSData = @{" + System.Environment.NewLine; string privateDataString = $@" @@ -1173,7 +1169,6 @@ private bool TryCreateNewPsd1WithUpdatedPrivateData(string privateDataString, st { leftBracket--; } - if (leftBracket == 0) { privateDataEndLine = i; @@ -1189,7 +1184,6 @@ private bool TryCreateNewPsd1WithUpdatedPrivateData(string privateDataString, st "PrivateDataEntryParsingError", ErrorCategory.InvalidOperation, this); - return false; } diff --git a/test/PublishPSResourceTests/PublishPSResource.Tests.ps1 b/test/PublishPSResourceTests/PublishPSResource.Tests.ps1 index 35ba14881..0decd5011 100644 --- a/test/PublishPSResourceTests/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResourceTests/PublishPSResource.Tests.ps1 @@ -433,8 +433,8 @@ Describe "Test Publish-PSResource" -tags 'CI' { It "Publish a module to PSGallery using incorrect API key, should throw" { $version = "1.0.0" New-ModuleManifest -Path (Join-Path -Path $script:PublishModuleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version -Description "$script:PublishModuleName module" - - Publish-PSResource -Path $script:PublishModuleBase -Repository PSGallery -APIKey "123456789" -ErrorAction SilentlyContinue + $APIKey = New-Guid + Publish-PSResource -Path $script:PublishModuleBase -Repository PSGallery -APIKey $APIKey -ErrorAction SilentlyContinue $Error[0].FullyQualifiedErrorId | Should -be "403Error,Microsoft.PowerShell.PSResourceGet.Cmdlets.PublishPSResource" }