diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs
index a4994df8a..b53f6d656 100644
--- a/src/code/RegisterPSResourceRepository.cs
+++ b/src/code/RegisterPSResourceRepository.cs
@@ -29,14 +29,19 @@ class RegisterPSResourceRepository : PSCmdlet, IDynamicParameters
#region Members
private readonly string PSGalleryRepoName = "PSGallery";
+ private readonly string MicrosoftArtifactRegistryRepoName = "MicrosoftArtifactRegistry";
+ private readonly string MicrosoftArtifactRegistryRepoUri = "https://mcr.microsoft.com";
private readonly string PSGalleryRepoUri = "https://www.powershellgallery.com/api/v2";
private const int DefaultPriority = 50;
private const bool DefaultTrusted = false;
+ private const bool MARDefaultTrusted = true;
+ private const int MARDefaultPriority = 40;
private const string NameParameterSet = "NameParameterSet";
private const string PSGalleryParameterSet = "PSGalleryParameterSet";
private const string RepositoriesParameterSet = "RepositoriesParameterSet";
private Uri _uri;
private CredentialProviderDynamicParameters _credentialProvider;
+ private const string MARParameterSet = "MARParameterSet";
#endregion
@@ -62,6 +67,13 @@ class RegisterPSResourceRepository : PSCmdlet, IDynamicParameters
[Parameter(Mandatory = true, ParameterSetName = PSGalleryParameterSet, HelpMessage = "PSGallery switch used to indicate registering PSGallery repository.")]
public SwitchParameter PSGallery { get; set; }
+ ///
+ /// When specified, registers Microsoft Artifact Registry repository.
+ ///
+ [Parameter(Mandatory = true, ParameterSetName = MARParameterSet, HelpMessage = "Switch used to indicate registering Microsoft Artifact Registry repository.")]
+ [Alias("MAR")]
+ public SwitchParameter MicrosoftArtifactRegistry { get; set; }
+
///
/// Specifies a hashtable of repositories and is used to register multiple repositories at once.
///
@@ -74,6 +86,7 @@ class RegisterPSResourceRepository : PSCmdlet, IDynamicParameters
///
[Parameter(ParameterSetName = NameParameterSet)]
[Parameter(ParameterSetName = PSGalleryParameterSet)]
+ [Parameter(ParameterSetName = MARParameterSet)]
public SwitchParameter Trusted { get; set; }
///
@@ -84,8 +97,9 @@ class RegisterPSResourceRepository : PSCmdlet, IDynamicParameters
///
[Parameter(ParameterSetName = NameParameterSet)]
[Parameter(ParameterSetName = PSGalleryParameterSet)]
+ [Parameter(ParameterSetName = MARParameterSet)]
[ValidateRange(0, 100)]
- public int Priority { get; set; } = DefaultPriority;
+ public int Priority { get; set; }
///
/// Specifies the Api version of the repository to be set.
@@ -122,6 +136,7 @@ public object GetDynamicParameters()
// It should also not appear when using the 'Repositories' parameter set.
if (ParameterSetName.Equals(PSGalleryParameterSet) ||
ParameterSetName.Equals(RepositoriesParameterSet) ||
+ ParameterSetName.Equals(MARParameterSet) ||
PSRepositoryInfo.IsValidContainerRegistryURL(Uri))
{
return null;
@@ -149,6 +164,18 @@ protected override void ProcessRecord()
repoApiVersion = ApiVersion;
}
+ if (!MyInvocation.BoundParameters.ContainsKey(nameof(Priority)))
+ {
+ if (ParameterSetName.Equals(MARParameterSet, StringComparison.OrdinalIgnoreCase))
+ {
+ Priority = MARDefaultPriority;
+ }
+ else
+ {
+ Priority = DefaultPriority;
+ }
+ }
+
PSRepositoryInfo.CredentialProviderType? credentialProvider = _credentialProvider?.CredentialProvider;
switch (ParameterSetName)
@@ -218,6 +245,25 @@ protected override void ProcessRecord()
}
break;
+ case MARParameterSet:
+ if (MicrosoftArtifactRegistry)
+ {
+ try
+ {
+ bool trustedValue = Trusted.IsPresent ? Trusted : MARDefaultTrusted;
+ items.Add(MicrosoftArtifactRegistryParameterSetHelper(Priority, trustedValue));
+ }
+ catch (Exception e)
+ {
+ ThrowTerminatingError(new ErrorRecord(
+ new PSInvalidOperationException(e.Message),
+ "ErrorInMARParameterSet",
+ ErrorCategory.InvalidArgument,
+ this));
+ }
+ }
+ break;
+
default:
Dbg.Assert(false, "Invalid parameter set");
break;
@@ -262,6 +308,34 @@ private PSRepositoryInfo PSGalleryParameterSetHelper(int repoPriority, bool repo
return addedRepo;
}
+ private PSRepositoryInfo MicrosoftArtifactRegistryParameterSetHelper(int repoPriority, bool repoTrusted)
+ {
+ WriteDebug("In RegisterPSResourceRepository::MicrosoftArtifactRegistryParameterSetHelper()");
+ Uri marUri = new Uri(MicrosoftArtifactRegistryRepoUri);
+ WriteDebug("Internal name and uri values for Microsoft Artifact Registry are hardcoded and validated. Priority and trusted values, if passed in, also validated");
+ var addedRepo = RepositorySettings.AddToRepositoryStore(MicrosoftArtifactRegistryRepoName,
+ marUri,
+ repoPriority,
+ repoTrusted,
+ apiVersion: null,
+ repoCredentialInfo: null,
+ credentialProvider: null,
+ Force,
+ this,
+ out string errorMsg);
+
+ if (!string.IsNullOrEmpty(errorMsg))
+ {
+ ThrowTerminatingError(new ErrorRecord(
+ new PSInvalidOperationException(errorMsg),
+ "RepositoryCredentialSecretManagementUnavailableModule",
+ ErrorCategory.ResourceUnavailable,
+ this));
+ }
+
+ return addedRepo;
+ }
+
private List RepositoriesParameterSetHelper()
{
WriteDebug("In RegisterPSResourceRepository::RepositoriesParameterSetHelper()");
@@ -323,7 +397,7 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo)
return null;
}
- if (repo["Name"].ToString().Equals("PSGallery"))
+ if (repo["Name"].ToString().Equals("PSGallery", StringComparison.OrdinalIgnoreCase))
{
WriteError(new ErrorRecord(
new PSInvalidOperationException("Cannot register PSGallery with -Name parameter. Try: Register-PSResourceRepository -PSGallery"),
@@ -334,6 +408,17 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo)
return null;
}
+ if (repo["Name"].ToString().Equals("MAR", StringComparison.OrdinalIgnoreCase))
+ {
+ WriteError(new ErrorRecord(
+ new PSInvalidOperationException("Cannot register MAR with -Name parameter. The MAR repository is automatically registered. Try: Reset-PSResourceRepository to restore default repositories."),
+ "MARProvidedAsNameRepoPSet",
+ ErrorCategory.InvalidArgument,
+ this));
+
+ return null;
+ }
+
if (!repo.ContainsKey("Uri") || repo["Uri"] == null || String.IsNullOrEmpty(repo["Uri"].ToString()))
{
WriteError(new ErrorRecord(
diff --git a/src/code/RepositorySettings.cs b/src/code/RepositorySettings.cs
index 7cf4f9261..40893d529 100644
--- a/src/code/RepositorySettings.cs
+++ b/src/code/RepositorySettings.cs
@@ -26,8 +26,12 @@ internal static class RepositorySettings
// The repository store file's location is currently only at '%LOCALAPPDATA%\PSResourceGet' for the user account.
private const string PSGalleryRepoName = "PSGallery";
private const string PSGalleryRepoUri = "https://www.powershellgallery.com/api/v2";
+ private const string MARRepoName = "MicrosoftArtifactRegistry";
+ private const string MARRepoUri = "https://mcr.microsoft.com";
private const int DefaultPriority = 50;
+ private const int MARDefaultPriority = 40;
private const bool DefaultTrusted = false;
+ private const bool MARDefaultTrusted = true;
private const string RepositoryFileName = "PSResourceRepository.xml";
private static readonly string RepositoryPath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData), "PSResourceGet");
@@ -63,6 +67,10 @@ public static void CheckRepositoryStore()
// Add PSGallery to the newly created store
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);
+
+ // Add MAR to the newly created store
+ Uri marUri = new Uri(MARRepoUri);
+ Add(MARRepoName, marUri, MARDefaultPriority, MARDefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.ContainerRegistry, force: false);
}
// Open file (which should exist now), if cannot/is corrupted then throw error
@@ -85,6 +93,12 @@ public static PSRepositoryInfo AddRepository(string repoName, Uri repoUri, int r
return null;
}
+ if (repoName.Equals("MicrosoftArtifactRegistry", StringComparison.OrdinalIgnoreCase))
+ {
+ errorMsg = "Cannot register MAR with -Name parameter. Try: Register-PSResourceRepository -MicrosoftArtifactRegistry.";
+ return null;
+ }
+
return AddToRepositoryStore(repoName, repoUri, repoPriority, repoTrusted, apiVersion, repoCredentialInfo, repoCredentialProvider, force, cmdletPassedIn, out errorMsg);
}
@@ -187,6 +201,20 @@ public static PSRepositoryInfo UpdateRepositoryStore(string repoName, Uri repoUr
return null;
}
+ // check MAR Uri is not trying to be set
+ if (repoName.Equals("MicrosoftArtifactRegistry", StringComparison.OrdinalIgnoreCase) && repoUri != null)
+ {
+ errorMsg = "The MAR repository has a predefined Uri. Setting the -Uri parameter for this repository is not allowed. Please run 'Reset-PSResourceRepository' to restore default repositories.";
+ return null;
+ }
+
+ // check MAR CredentialInfo is not trying to be set
+ if (repoName.Equals("MicrosoftArtifactRegistry", StringComparison.OrdinalIgnoreCase) && repoCredentialInfo != null)
+ {
+ errorMsg = "Setting the -CredentialInfo parameter for MAR is not allowed. Run 'Reset-PSResourceRepository' to restore default repositories.";
+ return null;
+ }
+
// determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable)
bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?();
@@ -908,6 +936,10 @@ public static PSRepositoryInfo Reset(out string errorMsg)
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
PSRepositoryInfo psGalleryRepo = Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);
+ // Add MAR to the newly created store
+ Uri marUri = new Uri(MARRepoUri);
+ Add(MARRepoName, marUri, MARDefaultPriority, MARDefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.ContainerRegistry, force: false);
+
// Clean up backup file on success
if (!string.IsNullOrEmpty(backupFilePath) && File.Exists(backupFilePath))
{
diff --git a/src/code/ResetPSResourceRepository.cs b/src/code/ResetPSResourceRepository.cs
index 2c217aab3..bc1d4848f 100644
--- a/src/code/ResetPSResourceRepository.cs
+++ b/src/code/ResetPSResourceRepository.cs
@@ -10,7 +10,7 @@ namespace Microsoft.PowerShell.PSResourceGet.Cmdlets
///
/// The Reset-PSResourceRepository cmdlet resets the repository store by creating a new PSRepositories.xml file.
/// This is useful when the repository store becomes corrupted.
- /// It will create a new repository store with only the PSGallery repository registered.
+ /// It will create a new repository store with PSGallery and MAR repositories registered.
///
[Cmdlet(VerbsCommon.Reset,
"PSResourceRepository",
@@ -39,8 +39,8 @@ protected override void ProcessRecord()
"PSResourceRepository.xml");
WriteVerbose($"Resetting repository store at: {repositoryStorePath}");
-
- if (!ShouldProcess(repositoryStorePath, "Reset repository store and create new PSRepositories.xml file with PSGallery registered"))
+
+ if (!ShouldProcess(repositoryStorePath, "Reset repository store and create new PSRepositories.xml file with PSGallery and MAR registered"))
{
return;
}
@@ -57,7 +57,7 @@ protected override void ProcessRecord()
return;
}
- WriteVerbose("Repository store reset successfully. PSGallery has been registered.");
+ WriteVerbose("Repository store reset successfully. PSGallery and MAR have been registered.");
if (PassThru)
{
diff --git a/src/code/SetPSResourceRepository.cs b/src/code/SetPSResourceRepository.cs
index 8dc17fe21..293db33e9 100644
--- a/src/code/SetPSResourceRepository.cs
+++ b/src/code/SetPSResourceRepository.cs
@@ -110,10 +110,11 @@ public SwitchParameter Trusted
public object GetDynamicParameters()
{
PSRepositoryInfo repository = RepositorySettings.Read(new[] { Name }, out string[] _).FirstOrDefault();
- // Dynamic parameter '-CredentialProvider' should not appear for PSGallery, or any container registry repository.
+ // Dynamic parameter '-CredentialProvider' should not appear for PSGallery, MAR, or any container registry repository.
// It should also not appear when using the 'Repositories' parameter set.
if (repository is not null &&
(repository.Name.Equals("PSGallery", StringComparison.OrdinalIgnoreCase) ||
+ repository.Name.Equals("MicrosoftArtifactRegistry", StringComparison.OrdinalIgnoreCase) ||
ParameterSetName.Equals(RepositoriesParameterSet) ||
repository.IsContainerRegistry()))
{
diff --git a/test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1 b/test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1
index e372720b0..c676fedcb 100644
--- a/test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1
+++ b/test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1
@@ -7,6 +7,7 @@ Import-Module $modPath -Force -Verbose
Describe 'Test HTTP Find-PSResource for ACR Server Protocol' -tags 'CI' {
BeforeAll {
+ $MARName = Get-MarName
$testModuleName = "test-module"
$testModuleWith2DigitVersion = "test-2DigitPkg"
$testModuleParentName = "test_parent_mod"
@@ -273,50 +274,51 @@ Describe 'Test HTTP Find-PSResource for ACR Server Protocol' -tags 'CI' {
}
Describe 'Test Find-PSResource for MAR Repository' -tags 'CI' {
+
BeforeAll {
- Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry"
+ Get-NewPSResourceRepositoryFile
}
AfterAll {
- Unregister-PSResourceRepository -Name "MAR"
+ Get-RevertPSResourceRepositoryFile
}
It "Should find resource given specific Name, Version null" {
- $res = Find-PSResource -Name "Az.Accounts" -Repository "MAR"
+ $res = Find-PSResource -Name "Az.Accounts" -Repository $MarName
$res.Name | Should -Be "Az.Accounts"
$res.Version | Should -BeGreaterThan ([Version]"4.0.0")
}
It "Should find resource and its dependency given specific Name and Version" {
- $res = Find-PSResource -Name "Az.Storage" -Version "8.0.0" -Repository "MAR"
+ $res = Find-PSResource -Name "Az.Storage" -Version "8.0.0" -Repository $MarName
$res.Dependencies.Length | Should -Be 1
$res.Dependencies[0].Name | Should -Be "Az.Accounts"
}
It "Should find Azpreview resource and it's dependency given specific Name and Version" {
- $res = Find-PSResource -Name "Azpreview" -Version "13.2.0" -Repository "MAR"
+ $res = Find-PSResource -Name "Azpreview" -Version "13.2.0" -Repository $MarName
$res.Dependencies.Length | Should -Not -Be 0
}
It "Should find resource with wildcard in Name" {
- $res = Find-PSResource -Name "Az.App*" -Repository "MAR"
+ $res = Find-PSResource -Name "Az.App*" -Repository $MarName
$res | Should -Not -BeNullOrEmpty
$res.Count | Should -BeGreaterThan 1
}
It "Should find all resource with wildcard in Name" {
- $res = Find-PSResource -Name "*" -Repository "MAR"
+ $res = Find-PSResource -Name "*" -Repository $MarName
$res | Should -Not -BeNullOrEmpty
$res.Count | Should -BeGreaterThan 1
}
It "Should find version range for Az dependencies" {
# Target known version to know the output from the API won't change
- $res = Find-PSResource -Repository 'MAR' -Name 'Az' -Version '14.4.0'
-
+ $res = Find-PSResource -Repository $MarName -Name 'Az' -Version '14.4.0'
+
# Version defined by "ModuleVersion"
$res.Dependencies.Where{$_.'Name' -eq 'Az.Accounts'}.'VersionRange'.ToString() | Should -Be '[5.3.0, )'
-
+
# Version defined by "RequiredVersion"
$res.Dependencies.Where{$_.'Name' -eq 'Az.Resources'}.'VersionRange'.ToString() | Should -Be '[8.1.0, 8.1.0]'
}
diff --git a/test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1
index afe81d2c3..0c66038b7 100644
--- a/test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1
+++ b/test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1
@@ -351,25 +351,24 @@ Describe 'Test Install-PSResource for Container Registry scenarios - Manual Vali
}
Describe 'Test Install-PSResource for MAR Repository' -tags 'CI' {
+
BeforeAll {
- [Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", "azure-powershell/");
- Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry"
+ Get-NewPSResourceRepositoryFile
}
-
AfterAll {
- [Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", $null);
- Unregister-PSResourceRepository -Name "MAR"
+ Get-RevertPSResourceRepositoryFile
}
- It "Should find resource given specific Name, Version null" {
+ It "Should install resource given specific Name, Version null" {
try {
- $pkg = Install-PSResource -Name "Az.Accounts" -Repository "MAR" -PassThru -TrustRepository -Reinstall
+ $pkg = Install-PSResource -Name "Az.Accounts" -Repository 'MicrosoftArtifactRegistry' -PassThru -TrustRepository -Reinstall
$pkg.Name | Should -Be "Az.Accounts"
- $pkg.Version | Should -Be "3.0.4"
+ $pkg.Version.Major | Should -BeGreaterOrEqual 5
+
}
finally {
if ($pkg) {
- Uninstall-PSResource -Name "Az.Accounts" -Version "3.0.4"
+ Uninstall-PSResource -Name "Az.Accounts" -Version $pkg.Version
}
}
}
diff --git a/test/InstallPSResourceTests/InstallPSResourceRepositorySearching.Tests.ps1 b/test/InstallPSResourceTests/InstallPSResourceRepositorySearching.Tests.ps1
index eea4dae94..86b981a7c 100644
--- a/test/InstallPSResourceTests/InstallPSResourceRepositorySearching.Tests.ps1
+++ b/test/InstallPSResourceTests/InstallPSResourceRepositorySearching.Tests.ps1
@@ -36,8 +36,9 @@ Describe 'Test Install-PSResource for searching and looping through repositories
It "install resources from highest priority repository where it exists (without -Repository specified)" {
# Package "test_module" exists in the following repositories (in this order): localRepo, PSGallery, NuGetGallery
+ # Package does not exist on MAR to we need to skip when validating that the highest priority repository is used
$res = Install-PSResource -Name $testModuleName -TrustRepository -SkipDependencyCheck -ErrorVariable err -ErrorAction SilentlyContinue -PassThru
- $err | Should -HaveCount 0
+ $err | Should -HaveCount 1 ## This comes from MAR
$res | Should -Not -BeNullOrEmpty
$res.Repository | Should -Be $localRepoName
@@ -45,8 +46,9 @@ Describe 'Test Install-PSResource for searching and looping through repositories
It "install resources from hightest priority repository where it exists and not write errors for repositories where it does not exist (without -Repository specified)" {
# Package "test_script" exists in the following repositories: PSGallery, NuGetGallery
+ # Package does not exist on MAR to we need to skip when validating that the highest priority repository is used
Install-PSResource -Name $testScriptName -TrustRepository -SkipDependencyCheck -ErrorVariable err -ErrorAction SilentlyContinue
- $err | Should -HaveCount 0
+ $err | Should -HaveCount 1 ## This comes from MAR
$res = Get-InstalledPSResource $testScriptName
$res | Should -Not -BeNullOrEmpty
@@ -55,7 +57,7 @@ Describe 'Test Install-PSResource for searching and looping through repositories
It "should install resources that exist and not install ones that do not exist while reporting error (without -Repository specified)" {
Install-PSResource -Name $testScriptName,"NonExistentModule" -TrustRepository -SkipDependencyCheck -ErrorVariable err -ErrorAction SilentlyContinue
- $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource"
+ "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" | Should -BeIn $err.FullyQualifiedErrorId
$res = Get-InstalledPSResource $testScriptName
$res | Should -Not -BeNullOrEmpty
@@ -64,13 +66,13 @@ Describe 'Test Install-PSResource for searching and looping through repositories
It "should not install resource given nonexistent Name (without -Repository specified)" {
Install-PSResource -Name "NonExistentModule" -TrustRepository -SkipDependencyCheck -ErrorVariable err -ErrorAction SilentlyContinue
- $err | Should -HaveCount 1
- $err[0].FullyQualifiedErrorId | Should -BeExactly "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource"
+ $err | Should -HaveCount 2 ## One error comes from MAR
+ "InstallPackageFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.InstallPSResource" | Should -BeIn $err.FullyQualifiedErrorId
}
It "install multiple resources from highest priority repository where it exists (without -Repository specified)" {
$res = Install-PSResource -Name "test_module","test_module2" -TrustRepository -SkipDependencyCheck -ErrorVariable err -ErrorAction SilentlyContinue -PassThru
- $err | Should -HaveCount 0
+ $err | Should -HaveCount 2 ## This comes from MAR
$res | Should -Not -BeNullOrEmpty
$pkg1 = $res[0]
diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1
index 6a384c17c..44202a58d 100644
--- a/test/PSGetTestUtils.psm1
+++ b/test/PSGetTestUtils.psm1
@@ -19,6 +19,9 @@ $script:IsCoreCLR = $PSVersionTable.ContainsKey('PSEdition') -and $PSVersionTabl
$script:PSGalleryName = 'PSGallery'
$script:PSGalleryLocation = 'https://www.powershellgallery.com/api/v2'
+$script:MARName = 'MicrosoftArtifactRegistry'
+$script:MARLocation = 'https://mcr.microsoft.com/'
+
$script:NuGetGalleryName = 'NuGetGallery'
$script:NuGetGalleryLocation = 'https://api.nuget.org/v3/index.json'
@@ -153,6 +156,16 @@ function Get-PSGalleryName
function Get-PSGalleryLocation {
return $script:PSGalleryLocation
}
+
+function Get-MARName
+{
+ return $script:MARName
+}
+
+function Get-MARLocation {
+ return $script:MARLocation
+}
+
function Get-NewTestDirs {
Param(
[string[]]
diff --git a/test/PublishPSResourceTests/PublishPSResource.Tests.ps1 b/test/PublishPSResourceTests/PublishPSResource.Tests.ps1
index ed0adf1b8..bb58ddeed 100644
--- a/test/PublishPSResourceTests/PublishPSResource.Tests.ps1
+++ b/test/PublishPSResourceTests/PublishPSResource.Tests.ps1
@@ -101,6 +101,7 @@ Describe "Test Publish-PSResource" -tags 'CI' {
CreateTestModule -Path $TestDrive -ModuleName 'ModuleWithMissingRequiredModule'
$script:PSGalleryName = 'PSGallery'
+ $script:MARName = 'MicrosoftArtifactRegistry'
}
AfterAll {
Get-RevertPSResourceRepositoryFile
@@ -802,8 +803,9 @@ Describe "Test Publish-PSResource with Module Prefix" -tags 'CI' {
$err[0].FullyQualifiedErrorId | Should -Be "RepositoryNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.PublishPSResource"
$registeredRepos = Get-PSResourceRepository
- $registeredRepos.Count | Should -Be 1
- $registeredRepos[0].Name | Should -Be $script:PSGalleryName
+ $registeredRepos.Count | Should -Be 2
+ $registeredRepos[0].Name | Should -Be $script:MARName
+ $registeredRepos[1].Name | Should -Be $script:PSGalleryName
}
finally {
# Cleanup
diff --git a/test/PublishPSResourceTests/PublishPSResourceContainerRegistryServer.Tests.ps1 b/test/PublishPSResourceTests/PublishPSResourceContainerRegistryServer.Tests.ps1
index bd59d69d0..abde6338e 100644
--- a/test/PublishPSResourceTests/PublishPSResourceContainerRegistryServer.Tests.ps1
+++ b/test/PublishPSResourceTests/PublishPSResourceContainerRegistryServer.Tests.ps1
@@ -590,14 +590,12 @@ Describe "Test Publish-PSResource" -tags 'CI' {
}
Describe 'Test Publish-PSResource for MAR Repository' -tags 'CI' {
+
BeforeAll {
- [Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", "azure-powershell/");
- Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry"
+ Get-NewPSResourceRepositoryFile
}
-
AfterAll {
- [Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", $null);
- Unregister-PSResourceRepository -Name "MAR"
+ Get-RevertPSResourceRepositoryFile
}
It "Should find resource given specific Name, Version null" {
@@ -606,6 +604,6 @@ Describe 'Test Publish-PSResource for MAR Repository' -tags 'CI' {
$psd1Path = Join-Path -Path $modulePath -ChildPath $fileName
New-ModuleManifest -Path $psd1Path -ModuleVersion "1.0.0" -Description "NonExistent module"
- { Publish-PSResource -Path $modulePath -Repository "MAR" -ErrorAction Stop } | Should -Throw -ErrorId "MARRepositoryPublishError,Microsoft.PowerShell.PSResourceGet.Cmdlets.PublishPSResource"
+ { Publish-PSResource -Path $modulePath -Repository "MicrosoftArtifactRegistry" -ErrorAction Stop } | Should -Throw -ErrorId "MARRepositoryPublishError,Microsoft.PowerShell.PSResourceGet.Cmdlets.PublishPSResource"
}
}
diff --git a/test/ResourceRepositoryTests/MARRepository.Tests.ps1 b/test/ResourceRepositoryTests/MARRepository.Tests.ps1
new file mode 100644
index 000000000..06f49931c
--- /dev/null
+++ b/test/ResourceRepositoryTests/MARRepository.Tests.ps1
@@ -0,0 +1,142 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+$modPath = "$psscriptroot/../PSGetTestUtils.psm1"
+Write-Verbose -Verbose -Message "PSGetTestUtils path: $modPath"
+Import-Module $modPath -Force -Verbose
+
+Describe "Test MAR Repository Registration" -tags 'CI' {
+ BeforeEach {
+ $MARName = Get-MARName
+ $MARUri = Get-MARLocation
+ $PSGalleryName = Get-PSGalleryName
+ $PSGalleryUri = Get-PSGalleryLocation
+ Get-NewPSResourceRepositoryFile
+ }
+ AfterEach {
+ Get-RevertPSResourceRepositoryFile
+ }
+
+ Context "MAR is auto-registered with expected values" {
+ It "MAR repository should be present with expected default values" {
+ $res = Get-PSResourceRepository -Name $MARName
+ $res | Should -Not -BeNullOrEmpty
+ $res.Name | Should -Be $MARName
+ $res.Uri | Should -Be "$MARUri"
+ $res.Trusted | Should -Be True
+ $res.Priority | Should -Be 40
+ $res.ApiVersion | Should -Be 'ContainerRegistry'
+ }
+
+ It "MAR repository should have lower priority number (higher priority) than PSGallery" {
+ $mar = Get-PSResourceRepository -Name $MARName
+ $psGallery = Get-PSResourceRepository -Name $PSGalleryName
+ $mar.Priority | Should -BeLessThan $psGallery.Priority
+ }
+ }
+
+ Context "MAR name protection" {
+ It "should not allow registering MAR with -Name parameter" {
+ { Register-PSResourceRepository -Name $MARName -Uri "https://mcr.microsoft.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
+ }
+
+ It "should not allow registering MAR (case insensitive) with -Name parameter" {
+ { Register-PSResourceRepository -Name $MARName -Uri "https://mcr.microsoft.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
+ }
+
+ It "should not allow registering MAR with -Name parameter in hashtable" {
+ Unregister-PSResourceRepository -Name $MARName
+ $hashtable = @{Name = "MAR"; Uri = "https://mcr.microsoft.com"}
+ Register-PSResourceRepository -Repository $hashtable -ErrorVariable err -ErrorAction SilentlyContinue
+ $err.Count | Should -BeGreaterThan 0
+ $err[0].FullyQualifiedErrorId | Should -BeExactly "MARProvidedAsNameRepoPSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
+ }
+
+ It "should not allow setting Uri for MAR repository" {
+ { Set-PSResourceRepository -Name $MARName -Uri "https://example.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.SetPSResourceRepository"
+ }
+
+ It "should not allow setting CredentialInfo for MAR repository" {
+ $randomSecret = [System.IO.Path]::GetRandomFileName()
+ $credentialInfo = New-Object Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo ("testvault", $randomSecret)
+ { Set-PSResourceRepository -Name $MARName -CredentialInfo $credentialInfo -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.SetPSResourceRepository"
+ }
+
+ It "should allow setting Trusted for MAR repository" {
+ Set-PSResourceRepository -Name $MARName -Trusted:$false
+ $res = Get-PSResourceRepository -Name $MARName
+ $res.Trusted | Should -Be False
+ }
+
+ It "should allow setting Priority for MAR repository" {
+ Set-PSResourceRepository -Name $MARName -Priority 10
+ $res = Get-PSResourceRepository -Name $MARName
+ $res.Priority | Should -Be 10
+ }
+ }
+
+ Context "Reset repository store includes MAR" {
+ It "Reset-PSResourceRepository should register MAR alongside PSGallery" {
+ Reset-PSResourceRepository -Confirm:$false
+ $res = Get-PSResourceRepository -Name $MARName
+ $res | Should -Not -BeNullOrEmpty
+ $res.Name | Should -Be $MARName
+ $res.Uri | Should -Be "$MARUri"
+ $res.Trusted | Should -Be True
+ $res.Priority | Should -Be 40
+ $res.ApiVersion | Should -Be 'ContainerRegistry'
+
+ $psGallery = Get-PSResourceRepository -Name $PSGalleryName
+ $psGallery | Should -Not -BeNullOrEmpty
+ }
+
+ It "Reset-PSResourceRepository should restore MAR after unregistration" {
+ Unregister-PSResourceRepository -Name $MARName
+ $res = Get-PSResourceRepository -Name $MARName -ErrorAction SilentlyContinue
+ $res | Should -BeNullOrEmpty
+
+ Reset-PSResourceRepository -Confirm:$false
+ $res = Get-PSResourceRepository -Name $MARName
+ $res | Should -Not -BeNullOrEmpty
+ $res.Name | Should -Be $MARName
+ $res.Uri | Should -Be "$MARUri"
+ $res.Trusted | Should -Be True
+ $res.Priority | Should -Be 40
+ }
+
+ It "Reset-PSResourceRepository should restore both PSGallery and MAR" {
+ Unregister-PSResourceRepository -Name $MARName
+ Unregister-PSResourceRepository -Name $PSGalleryName
+ Reset-PSResourceRepository -Confirm:$false
+
+ $mar = Get-PSResourceRepository -Name $MARName
+ $mar | Should -Not -BeNullOrEmpty
+ $mar.Priority | Should -Be 40
+ $mar.Trusted | Should -Be True
+ $mar.ApiVersion | Should -Be 'ContainerRegistry'
+
+ $psGallery = Get-PSResourceRepository -Name $PSGalleryName
+ $psGallery | Should -Not -BeNullOrEmpty
+ $psGallery.Priority | Should -Be 50
+ }
+ }
+
+ Context "MAR first due to higher priority" {
+ It "Find-PSResource Az.Accounts module from MAR" {
+ $res = Find-PSResource -Name "Az.Accounts"
+ $res | Should -Not -BeNullOrEmpty
+ $res.Count | Should -BeGreaterThan 0
+ $firstRes = $res | Select-Object -First 1
+ $firstRes.Name | Should -Be "Az.Accounts"
+ $firstRes.Repository | Should -Be $MARName
+ }
+
+ It 'Find-PSResource fallback to PSGallery if module not in MAR' {
+ $res = Find-PSResource -Name "Pscx"
+ $res | Should -Not -BeNullOrEmpty
+ $firstRes = $res | Select-Object -First 1
+ $firstRes.Name | Should -Be "Pscx"
+ $firstRes.Repository | Should -Be $PSGalleryName
+ }
+ }
+}
diff --git a/test/ResourceRepositoryTests/RegisterPSResourceRepository.Tests.ps1 b/test/ResourceRepositoryTests/RegisterPSResourceRepository.Tests.ps1
index f9c350356..332921c31 100644
--- a/test/ResourceRepositoryTests/RegisterPSResourceRepository.Tests.ps1
+++ b/test/ResourceRepositoryTests/RegisterPSResourceRepository.Tests.ps1
@@ -7,6 +7,8 @@ Import-Module $modPath -Force -Verbose
Describe "Test Register-PSResourceRepository" -tags 'CI' {
BeforeEach {
+ $MARName = Get-MARName
+ $MARUri = Get-MARLocation
$PSGalleryName = Get-PSGalleryName
$PSGalleryUri = Get-PSGalleryLocation
$TestRepoName1 = "testRepository"
@@ -85,6 +87,15 @@ Describe "Test Register-PSResourceRepository" -tags 'CI' {
$res.Priority | Should -Be 50
}
+ It "register repository with MicrosoftArtifactRegistry parameter (MicrosoftArtifactRegistryParameterSet)" {
+ Unregister-PSResourceRepository -Name $MARName
+ $res = Register-PSResourceRepository -MicrosoftArtifactRegistry -PassThru
+ $res.Name | Should -Be $MARName
+ $res.Uri | Should -Be $MARUri
+ $res.Trusted | Should -Be True
+ $res.Priority | Should -Be 40
+ }
+
It "register repository with PSGallery switch parameter value of false (PSGalleryParameterSet)" {
Unregister-PSResourceRepository -Name $PSGalleryName
$res = Register-PSResourceRepository -PSGallery:$false -PassThru
@@ -412,7 +423,7 @@ Describe "Test Register-PSResourceRepository" -tags 'CI' {
It "should throw error when trying to register repository with ApiVersion unknown" {
{Register-PSResourceRepository -Name $TestRepoName1 -Uri $tmpDir1Path -ApiVersion "unknown" -ErrorAction Stop} | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
-
+
# Verify the repository was not created
$repo = Get-PSResourceRepository $TestRepoName1 -ErrorAction SilentlyContinue
$repo | Should -BeNullOrEmpty
diff --git a/test/ResourceRepositoryTests/ResetPSResourceRepository.Tests.ps1 b/test/ResourceRepositoryTests/ResetPSResourceRepository.Tests.ps1
index f49570229..f658fac6b 100644
--- a/test/ResourceRepositoryTests/ResetPSResourceRepository.Tests.ps1
+++ b/test/ResourceRepositoryTests/ResetPSResourceRepository.Tests.ps1
@@ -7,6 +7,8 @@ Import-Module $modPath -Force -Verbose
Describe "Test Reset-PSResourceRepository" -tags 'CI' {
BeforeEach {
+ $MARName = Get-MARName
+ $MARUri = Get-MARLocation
$PSGalleryName = Get-PSGalleryName
$PSGalleryUri = Get-PSGalleryLocation
Get-NewPSResourceRepositoryFile
@@ -22,19 +24,19 @@ Describe "Test Reset-PSResourceRepository" -tags 'CI' {
$tmpDirPath = Join-Path -Path $TestDrive -ChildPath "tmpDir1"
New-Item -ItemType Directory -Path $tmpDirPath -Force | Out-Null
Register-PSResourceRepository -Name $TestRepoName -Uri $tmpDirPath
-
+
# Verify repository was added
$repos = Get-PSResourceRepository
$repos.Count | Should -BeGreaterThan 1
-
+
# Act: Reset repository store
Reset-PSResourceRepository -Confirm:$false
-
+
# Assert: Only PSGallery should exist
$repos = Get-PSResourceRepository
- $repos.Count | Should -Be 1
- $repos.Name | Should -Be $PSGalleryName
- $repos.Uri | Should -Be $PSGalleryUri
+ $repos.Count | Should -Be 2
+ $repos.Name | ForEach-Object { $_ | Should -BeIn @($PSGalleryName, $MARName) }
+ $repos.Uri | ForEach-Object { $_ | Should -BeIn @($PSGalleryUri, $MARUri) }
}
It "Reset repository store with PassThru parameter returns PSGallery" {
@@ -43,20 +45,22 @@ Describe "Test Reset-PSResourceRepository" -tags 'CI' {
$tmpDirPath = Join-Path -Path $TestDrive -ChildPath "tmpDir1"
New-Item -ItemType Directory -Path $tmpDirPath -Force | Out-Null
Register-PSResourceRepository -Name $TestRepoName -Uri $tmpDirPath
-
+
# Act: Reset repository store with PassThru
$result = Reset-PSResourceRepository -Confirm:$false -PassThru
-
+
# Assert: Result should be PSGallery repository info
$result | Should -Not -BeNullOrEmpty
$result.Name | Should -Be $PSGalleryName
$result.Uri | Should -Be $PSGalleryUri
$result.Trusted | Should -Be $false
$result.Priority | Should -Be 50
-
+
# Verify only PSGallery exists
$repos = Get-PSResourceRepository
- $repos.Count | Should -Be 1
+ $repos.Count | Should -Be 2
+ $repos.Name | ForEach-Object { $_ | Should -BeIn @($PSGalleryName, $MARName) }
+ $repos.Uri | ForEach-Object { $_ | Should -BeIn @($PSGalleryUri, $MARUri) }
}
It "Reset repository store should support -WhatIf" {
@@ -65,14 +69,14 @@ Describe "Test Reset-PSResourceRepository" -tags 'CI' {
$tmpDirPath = Join-Path -Path $TestDrive -ChildPath "tmpDir1"
New-Item -ItemType Directory -Path $tmpDirPath -Force | Out-Null
Register-PSResourceRepository -Name $TestRepoName -Uri $tmpDirPath
-
+
# Capture repository count before WhatIf
$reposBefore = Get-PSResourceRepository
$countBefore = $reposBefore.Count
-
+
# Act: Run with WhatIf
Reset-PSResourceRepository -WhatIf
-
+
# Assert: Repositories should not have changed
$reposAfter = Get-PSResourceRepository
$reposAfter.Count | Should -Be $countBefore
@@ -82,43 +86,43 @@ Describe "Test Reset-PSResourceRepository" -tags 'CI' {
# Arrange: Corrupt the repository file
$powerShellGetPath = Join-Path -Path ([Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData)) -ChildPath "PSResourceGet"
$repoFilePath = Join-Path -Path $powerShellGetPath -ChildPath "PSResourceRepository.xml"
-
+
# Write invalid XML to corrupt the file
"This is not valid XML" | Set-Content -Path $repoFilePath -Force
-
+
# Act: Reset the repository store
$result = Reset-PSResourceRepository -Confirm:$false -PassThru
-
+
# Assert: Should successfully reset and return PSGallery
$result | Should -Not -BeNullOrEmpty
$result.Name | Should -Be $PSGalleryName
-
+
# Verify we can now read repositories
$repos = Get-PSResourceRepository
- $repos.Count | Should -Be 1
- $repos.Name | Should -Be $PSGalleryName
+ $repos.Count | Should -Be 2
+ $repos.Name | ForEach-Object { $_ | Should -BeIn @($PSGalleryName, $MARName) }
}
It "Reset repository store when file doesn't exist should succeed" {
# Arrange: Delete the repository file
$powerShellGetPath = Join-Path -Path ([Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData)) -ChildPath "PSResourceGet"
$repoFilePath = Join-Path -Path $powerShellGetPath -ChildPath "PSResourceRepository.xml"
-
+
if (Test-Path -Path $repoFilePath) {
Remove-Item -Path $repoFilePath -Force
}
-
+
# Act: Reset the repository store
$result = Reset-PSResourceRepository -Confirm:$false -PassThru
-
+
# Assert: Should successfully reset and return PSGallery
$result | Should -Not -BeNullOrEmpty
$result.Name | Should -Be $PSGalleryName
-
+
# Verify PSGallery is registered
$repos = Get-PSResourceRepository
- $repos.Count | Should -Be 1
- $repos.Name | Should -Be $PSGalleryName
+ $repos.Count | Should -Be 2
+ $repos.Name | ForEach-Object { $_ | Should -BeIn @($PSGalleryName, $MARName) }
}
It "Reset repository store with multiple repositories should only keep PSGallery" {
@@ -129,21 +133,21 @@ Describe "Test Reset-PSResourceRepository" -tags 'CI' {
New-Item -ItemType Directory -Path $tmpDir1Path -Force | Out-Null
New-Item -ItemType Directory -Path $tmpDir2Path -Force | Out-Null
New-Item -ItemType Directory -Path $tmpDir3Path -Force | Out-Null
-
+
Register-PSResourceRepository -Name "testRepo1" -Uri $tmpDir1Path
Register-PSResourceRepository -Name "testRepo2" -Uri $tmpDir2Path
Register-PSResourceRepository -Name "testRepo3" -Uri $tmpDir3Path
-
+
# Verify multiple repositories exist
$reposBefore = Get-PSResourceRepository
$reposBefore.Count | Should -BeGreaterThan 1
-
+
# Act: Reset repository store
Reset-PSResourceRepository -Confirm:$false
-
+
# Assert: Only PSGallery should remain
$reposAfter = Get-PSResourceRepository
- $reposAfter.Count | Should -Be 1
- $reposAfter.Name | Should -Be $PSGalleryName
+ $reposAfter.Count | Should -Be 2
+ $reposAfter.Name | ForEach-Object { $_ | Should -BeIn @($PSGalleryName, $MARName) }
}
}
diff --git a/test/SavePSResourceTests/SavePSResourceLocal.Tests.ps1 b/test/SavePSResourceTests/SavePSResourceLocal.Tests.ps1
index b25a6c53b..3b22f8f9e 100644
--- a/test/SavePSResourceTests/SavePSResourceLocal.Tests.ps1
+++ b/test/SavePSResourceTests/SavePSResourceLocal.Tests.ps1
@@ -157,8 +157,8 @@ Describe 'Test Save-PSResource for local repositories' -tags 'CI' {
}
It "Save module, should search through all repositories and only install from the first repo containing the package" {
- Save-PSResource -Name $moduleName3 -Version "0.0.93" -Path $SaveDir -TrustRepository -ErrorVariable ev
- $ev | Should -BeNullOrEmpty
+ Save-PSResource -Name $moduleName3 -Version "0.0.93" -Path $SaveDir -TrustRepository -ErrorVariable ev -ErrorAction SilentlyContinue
+ $ev | Should -HaveCount 1 ## This comes from MAR not having the module so the error is thrown from that repository before falling back to the next repository
$pkgDir = Get-ChildItem -Path $SaveDir | Where-Object Name -eq "$moduleName3"
$pkgDir | Should -Not -BeNullOrEmpty
}
diff --git a/test/testRepositories.xml b/test/testRepositories.xml
index 1e330dda9..07d04d8e4 100644
--- a/test/testRepositories.xml
+++ b/test/testRepositories.xml
@@ -1,5 +1,6 @@
+