From c199e2a1b39d4b89f032af7ec2d7d85e75b15df7 Mon Sep 17 00:00:00 2001 From: "Stephen Weatherford (MSFT)" Date: Fri, 24 Jan 2025 14:20:15 -0800 Subject: [PATCH 1/2] work --- .../BuildCommandTests.cs | 4 +- .../BuildParamsCommandTests.cs | 2 +- .../LintCommandTests.cs | 9 +- .../PublishCommandTests.cs | 24 +- .../PublishProviderCommandTests.cs | 3 +- .../RestoreCommandTests.cs | 22 +- src/Bicep.Cli.IntegrationTests/TestBase.cs | 15 +- ...UseRecentModuleVersionsIntegrationTests.cs | 49 +- .../packages.lock.json | 7 +- src/Bicep.Cli.UnitTests/packages.lock.json | 7 +- .../Helpers/ServiceCollectionExtensions.cs | 4 +- src/Bicep.Cli/packages.lock.json | 7 +- .../AzTypesViaRegistryTests.cs | 19 +- .../Extensibility/RadiusCompatibilityTests.cs | 3 +- .../MsGraphTypesViaRegistryTests.cs | 11 +- .../SourceArchiveTests.cs | 12 +- .../ResourceTypeProviderFactoryTests.cs | 3 +- .../packages.lock.json | 148 ++--- src/Bicep.Core.Samples/DataSetsExtensions.cs | 18 +- src/Bicep.Core.Samples/MockRegistry.cs | 12 +- src/Bicep.Core.Samples/packages.lock.json | 148 ++--- .../ApiVersion/ApiVersionProviderTests.cs | 2 +- .../Assertions/OciModuleRegistryAssertions.cs | 2 +- .../Bicep.Core.UnitTests.csproj | 1 + .../BicepTestConstants.cs | 2 +- .../DecompilerCleanupRuleTests.cs | 2 +- ...xplicitValuesForLocationParamsRuleTests.cs | 2 +- .../UseRecentModuleVersionsRuleTests.cs | 49 +- .../IServiceCollectionExtensions.cs | 9 +- .../PublicRegistryModuleIndexClientMock.cs | 33 - .../PublicModuleIndexHttpClientMocks.cs | 30 + .../Mock/Registry/RegistryCatalogMocks.cs | 162 +++++ .../Registry/CachedModules.cs | 2 +- .../BaseModuleMetadataProviderTests.cs | 77 +++ .../PrivateAcrModuleMetadataProviderTests.cs | 409 ++++++++++++ .../PublicModuleMetadataProviderTests.cs} | 146 ++-- .../Registry/Catalog/RegistryCatalogTests.cs | 109 +++ .../Registry/FakeContainerRegistryClient.cs | 71 ++ .../Registry/MockRegistryBlobClient.cs | 2 +- ...ontainerRegistryClientFactoryExtensions.cs | 9 +- .../Utils/ExtensionTestHelper.cs | 2 +- .../Utils/OciRegistryHelper.cs | 10 +- .../Utils/RegistryHelper.cs | 210 ++++-- .../Utils/ServiceBuilderExtensions.cs | 2 +- ...stContainerRegistryClientFactoryBuilder.cs | 75 ++- src/Bicep.Core.UnitTests/packages.lock.json | 148 ++--- .../Analyzers/Linter/LinterAnalyzer.cs | 2 +- .../Rules/UseRecentModuleVersionsRule.cs | 42 +- src/Bicep.Core/Bicep.Core.csproj | 1 + .../AnalyzersConfigurationExtensions.cs | 14 + src/Bicep.Core/LanguageConstants.cs | 8 +- .../Registry/AzureContainerRegistryManager.cs | 127 +++- .../BaseModuleMetadataProvider.cs} | 125 ++-- .../IPublicModuleIndexHttpClient.cs | 14 + .../PublicModuleIndexHttpClient.cs | 63 ++ .../PublicModuleMetadataHttpClient.cs} | 17 +- ...PrivateAcrModuleMetadataProviderFactory.cs | 16 + .../Catalog/IPublicModuleMetadataProvider.cs | 12 + .../Catalog/IRegistryModuleCatalog.cs | 18 + .../IRegistryModuleMetadataProvider.cs | 45 ++ ...egistryModuleMetadataProviderExtensions.cs | 13 + .../Registry/Catalog/MightBeLazyAsync.cs | 56 ++ .../PrivateAcrModuleMetadataProvider.cs | 118 ++++ ...PrivateAcrModuleMetadataProviderFactory.cs | 20 + .../Catalog/PublicModuleMetadataProvider.cs | 57 ++ ...yCatalogExtensionsForIServiceCollection.cs | 30 + .../Catalog/RegistryMetadataDetails.cs | 14 + .../Registry/Catalog/RegistryModuleCatalog.cs | 60 ++ .../Catalog/RegistryModuleMetadata.cs | 77 +++ .../Catalog/RegistryModuleVersionMetadata.cs | 10 + .../ContainerRegistryClientFactory.cs | 30 +- .../DefaultArtifactRegistryProvider.cs | 4 +- .../IContainerRegistryClientFactory.cs | 11 +- src/Bicep.Core/Registry/ModuleDispatcher.cs | 2 +- .../Registry/Oci/OciArtifactResult.cs | 2 +- .../Registry/OciArtifactRegistry.cs | 30 +- .../IPublicRegistryModuleIndexClient.cs | 68 -- .../IPublicRegistryModuleMetadataProvider.cs | 31 - src/Bicep.Core/packages.lock.json | 15 + .../packages.lock.json | 148 ++--- .../packages.lock.json | 148 ++--- src/Bicep.Decompiler/packages.lock.json | 15 + .../CompletionTests.cs | 288 ++++++-- .../Helpers/ServerRequestHelper.cs | 11 +- .../LangServerScenarioTests.cs | 8 +- .../packages.lock.json | 1 + .../BicepCompletionProviderTests.cs | 10 +- .../ModuleReferenceCompletionProviderTests.cs | 320 +++++---- ...pExternalSourceDocumentLinkHandlerTests.cs | 41 +- .../packages.lock.json | 1 + .../Completions/BicepCompletionProvider.cs | 9 +- .../Completions/CompletionItemBuilder.cs | 12 + .../Completions/CompletionItemExtensions.cs | 29 + .../Completions/ICompletionProvider.cs | 5 + .../IModuleReferenceCompletionProvider.cs | 2 + .../ModuleReferenceCompletionProvider.cs | 628 ++++++++++++------ .../Handlers/BicepCompletionHandler.cs | 6 +- .../IServiceCollectionExtensions.cs | 4 +- .../AzureContainerRegistriesProvider.cs | 17 +- .../IAzureContainerRegistriesProvider.cs | 4 +- src/Bicep.LangServer/Server.cs | 6 +- src/Bicep.LangServer/packages.lock.json | 148 ++--- .../packages.lock.json | 148 ++--- src/Bicep.Local.Deploy/packages.lock.json | 15 + .../packages.lock.json | 15 + .../packages.lock.json | 148 ++--- .../packages.lock.json | 148 ++--- .../packages.lock.json | 148 ++--- .../IServiceCollectionExtensions.cs | 4 +- .../packages.lock.json | 15 + src/Bicep.Tools.Benchmark/packages.lock.json | 1 + .../IServiceCollectionExtensions.cs | 4 +- src/Bicep.Wasm/packages.lock.json | 15 + src/vscode-bicep/.vscode/launch.json | 2 +- src/vscode-bicep/package.json | 4 +- 115 files changed, 3950 insertions(+), 1823 deletions(-) delete mode 100644 src/Bicep.Core.UnitTests/Mock/PublicRegistryModuleIndexClientMock.cs create mode 100644 src/Bicep.Core.UnitTests/Mock/Registry/PublicModuleIndexHttpClientMocks.cs create mode 100644 src/Bicep.Core.UnitTests/Mock/Registry/RegistryCatalogMocks.cs create mode 100644 src/Bicep.Core.UnitTests/Registry/Catalog/BaseModuleMetadataProviderTests.cs create mode 100644 src/Bicep.Core.UnitTests/Registry/Catalog/PrivateAcrModuleMetadataProviderTests.cs rename src/Bicep.Core.UnitTests/Registry/{PublicRegistry/PublicRegistryModuleMetadataProviderTests.cs => Catalog/PublicModuleMetadataProviderTests.cs} (89%) create mode 100644 src/Bicep.Core.UnitTests/Registry/Catalog/RegistryCatalogTests.cs create mode 100644 src/Bicep.Core.UnitTests/Registry/FakeContainerRegistryClient.cs rename src/Bicep.Core/Registry/{PublicRegistry/PublicRegistryModuleMetadataProvider.cs => Catalog/BaseModuleMetadataProvider.cs} (53%) create mode 100644 src/Bicep.Core/Registry/Catalog/HttpClients/IPublicModuleIndexHttpClient.cs create mode 100644 src/Bicep.Core/Registry/Catalog/HttpClients/PublicModuleIndexHttpClient.cs rename src/Bicep.Core/Registry/{PublicRegistry/PublicRegistryModuleMetadataClient.cs => Catalog/HttpClients/PublicModuleMetadataHttpClient.cs} (68%) create mode 100644 src/Bicep.Core/Registry/Catalog/IPrivateAcrModuleMetadataProviderFactory.cs create mode 100644 src/Bicep.Core/Registry/Catalog/IPublicModuleMetadataProvider.cs create mode 100644 src/Bicep.Core/Registry/Catalog/IRegistryModuleCatalog.cs create mode 100644 src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProvider.cs create mode 100644 src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProviderExtensions.cs create mode 100644 src/Bicep.Core/Registry/Catalog/MightBeLazyAsync.cs create mode 100644 src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProvider.cs create mode 100644 src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProviderFactory.cs create mode 100644 src/Bicep.Core/Registry/Catalog/PublicModuleMetadataProvider.cs create mode 100644 src/Bicep.Core/Registry/Catalog/RegistryCatalogExtensionsForIServiceCollection.cs create mode 100644 src/Bicep.Core/Registry/Catalog/RegistryMetadataDetails.cs create mode 100644 src/Bicep.Core/Registry/Catalog/RegistryModuleCatalog.cs create mode 100644 src/Bicep.Core/Registry/Catalog/RegistryModuleMetadata.cs create mode 100644 src/Bicep.Core/Registry/Catalog/RegistryModuleVersionMetadata.cs delete mode 100644 src/Bicep.Core/Registry/PublicRegistry/IPublicRegistryModuleIndexClient.cs delete mode 100644 src/Bicep.Core/Registry/PublicRegistry/IPublicRegistryModuleMetadataProvider.cs create mode 100644 src/Bicep.LangServer/Completions/CompletionItemExtensions.cs diff --git a/src/Bicep.Cli.IntegrationTests/BuildCommandTests.cs b/src/Bicep.Cli.IntegrationTests/BuildCommandTests.cs index 4000475f485..603477246b6 100644 --- a/src/Bicep.Cli.IntegrationTests/BuildCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/BuildCommandTests.cs @@ -126,7 +126,7 @@ public async Task Build_Valid_SingleFile_WithTemplateSpecReference_ToStdOut_Shou if (dataSet.HasExternalModules) { - CachedModules.GetCachedRegistryModules(BicepTestConstants.FileSystem, settings.FeatureOverrides!.CacheRootDirectory!).Should().HaveCountGreaterThan(0) + CachedModules.GetCachedModules(BicepTestConstants.FileSystem, settings.FeatureOverrides!.CacheRootDirectory!).Should().HaveCountGreaterThan(0) .And.AllSatisfy(m => m.Should().HaveSource()); } @@ -194,7 +194,7 @@ public async Task Build_Valid_SingleFile_WithDigestReference_ShouldSucceed() var client = new MockRegistryBlobClient(); var clientFactory = StrictMock.Of(); - clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); + clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); var templateSpecRepositoryFactory = BicepTestConstants.TemplateSpecRepositoryFactory; diff --git a/src/Bicep.Cli.IntegrationTests/BuildParamsCommandTests.cs b/src/Bicep.Cli.IntegrationTests/BuildParamsCommandTests.cs index 25e34542c5e..763ef422434 100644 --- a/src/Bicep.Cli.IntegrationTests/BuildParamsCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/BuildParamsCommandTests.cs @@ -492,7 +492,7 @@ public async Task Build_bicepparam_should_fail_with_error_diagnostics_for_regist var clientFactory = StrictMock.Of(); clientFactory - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://mockregistry.io"), "parameters/basic")) + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://mockregistry.io"), "parameters/basic")) .Returns(client.Object); var templateSpecRepositoryFactory = StrictMock.Of(); diff --git a/src/Bicep.Cli.IntegrationTests/LintCommandTests.cs b/src/Bicep.Cli.IntegrationTests/LintCommandTests.cs index 4570dfb0baf..2b5c0f73276 100644 --- a/src/Bicep.Cli.IntegrationTests/LintCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/LintCommandTests.cs @@ -6,10 +6,12 @@ using Bicep.Cli.UnitTests; using Bicep.Core.Configuration; using Bicep.Core.Registry; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Samples; using Bicep.Core.UnitTests; using Bicep.Core.UnitTests.Mock; +using Bicep.Core.UnitTests.Mock.Registry; +using Bicep.Core.UnitTests.Mock.Registry.Catalog; using Bicep.Core.UnitTests.Registry; using Bicep.Core.UnitTests.Utils; using FluentAssertions; @@ -112,7 +114,7 @@ public async Task Lint_Valid_SingleFile_WithDigestReference_ShouldSucceed() var client = new MockRegistryBlobClient(); var clientFactory = StrictMock.Of(); - clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); + clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); var settings = new InvocationSettings(new(TestContext, RegistryEnabled: true), clientFactory.Object, BicepTestConstants.TemplateSpecRepositoryFactory); @@ -178,11 +180,10 @@ public async Task Lint_WithEmptyBicepConfig_ShouldProduceConfigurationError() string testOutputPath = FileHelper.GetUniqueTestOutputPath(TestContext); var inputFile = FileHelper.SaveResultFile(TestContext, "main.bicep", DataSets.Empty.Bicep, testOutputPath); var configurationPath = FileHelper.SaveResultFile(TestContext, "bicepconfig.json", string.Empty, testOutputPath); - var settings = new InvocationSettings() { ModuleMetadataClient = PublicRegistryModuleIndexClientMock.CreateToThrow(new Exception("unit test failed: shouldn't call this")).Object }; + var settings = new InvocationSettings() { ModuleMetadataClient = PublicModuleIndexHttpClientMocks.Create([]).Object }; var (output, error, result) = await Bicep(settings, "lint", inputFile); - result.Should().Be(1); output.Should().BeEmpty(); error.Should().StartWith($"{inputFile}(1,1) : Error BCP271: Failed to parse the contents of the Bicep configuration file \"{configurationPath}\" as valid JSON: The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. LineNumber: 0 | BytePositionInLine: 0."); diff --git a/src/Bicep.Cli.IntegrationTests/PublishCommandTests.cs b/src/Bicep.Cli.IntegrationTests/PublishCommandTests.cs index b355fd68b2a..383339b264e 100644 --- a/src/Bicep.Cli.IntegrationTests/PublishCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/PublishCommandTests.cs @@ -19,6 +19,7 @@ using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; using DataSet = Bicep.Core.Samples.DataSet; namespace Bicep.Cli.IntegrationTests @@ -155,14 +156,14 @@ public async Task Publish_AllValidDataSets_ShouldSucceed(string testName, DataSe var registryUri = new Uri($"https://{registryStr}"); var repository = $"test/{dataSet.Name}".ToLowerInvariant(); - var clientFactory = dataSet.CreateMockRegistryClients((registryStr, repository)); + var clientFactory = dataSet.CreateMockRegistryClients(new RepoDescriptor(registryStr, repository, ["tag"])); var templateSpecRepositoryFactory = dataSet.CreateMockTemplateSpecRepositoryFactory(TestContext); await dataSet.PublishModulesToRegistryAsync(clientFactory); var bicepFilePath = Path.Combine(outputDirectory, DataSet.TestFileMain); var compiledFilePath = Path.Combine(outputDirectory, DataSet.TestFileMainCompiled); // mock client factory caches the clients - var testClient = (MockRegistryBlobClient)clientFactory.CreateAuthenticatedBlobClient(BicepTestConstants.BuiltInConfiguration, registryUri, repository); + var testClient = (MockRegistryBlobClient)clientFactory.CreateAuthenticatedBlobClient(BicepTestConstants.BuiltInConfiguration.Cloud, registryUri, repository); var settings = new InvocationSettings(new(TestContext, RegistryEnabled: true), clientFactory, templateSpecRepositoryFactory); @@ -255,13 +256,13 @@ public async Task Publish_ValidArmTemplateFile_AllValidDataSets_ShouldSucceed(Da var registryUri = new Uri($"https://{registryStr}"); var repository = $"test/{dataSet.Name}".ToLowerInvariant(); - var clientFactory = dataSet.CreateMockRegistryClients((registryStr, repository)); + var clientFactory = dataSet.CreateMockRegistryClients(new RepoDescriptor(registryStr, repository, ["tag"])); var templateSpecRepositoryFactory = dataSet.CreateMockTemplateSpecRepositoryFactory(TestContext); await dataSet.PublishModulesToRegistryAsync(clientFactory); var compiledFilePath = Path.Combine(outputDirectory, DataSet.TestFileMainCompiled); // mock client factory caches the clients - var testClient = (MockRegistryBlobClient)clientFactory.CreateAuthenticatedBlobClient(BicepTestConstants.BuiltInConfiguration, registryUri, repository); + var testClient = (MockRegistryBlobClient)clientFactory.CreateAuthenticatedBlobClient(BicepTestConstants.BuiltInConfiguration.Cloud, registryUri, repository); var settings = new InvocationSettings(new(TestContext, RegistryEnabled: true), clientFactory, templateSpecRepositoryFactory); @@ -309,13 +310,13 @@ public async Task Publish_ValidArmTemplateFile_WithSource_ShouldFail() var registryUri = new Uri($"https://{registryStr}"); var repository = $"test/{dataSet.Name}".ToLowerInvariant(); - var clientFactory = dataSet.CreateMockRegistryClients((registryStr, repository)); + var clientFactory = dataSet.CreateMockRegistryClients(new RepoDescriptor(registryStr, repository, ["tag"])); var templateSpecRepositoryFactory = dataSet.CreateMockTemplateSpecRepositoryFactory(TestContext); await dataSet.PublishModulesToRegistryAsync(clientFactory); var compiledFilePath = Path.Combine(outputDirectory, DataSet.TestFileMainCompiled); // mock client factory caches the clients - var testClient = (MockRegistryBlobClient)clientFactory.CreateAuthenticatedBlobClient(BicepTestConstants.BuiltInConfiguration, registryUri, repository); + var testClient = (MockRegistryBlobClient)clientFactory.CreateAuthenticatedBlobClient(BicepTestConstants.BuiltInConfiguration.Cloud, registryUri, repository); var settings = new InvocationSettings(new(TestContext, RegistryEnabled: true), clientFactory, templateSpecRepositoryFactory); @@ -343,7 +344,7 @@ public async Task Publish_RequestFailedException_ShouldFail() var clientFactory = StrictMock.Of(); clientFactory - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://fake"), "fake")) + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://fake"), "fake")) .Returns(client.Object); var templateSpecRepositoryFactory = StrictMock.Of(); @@ -375,7 +376,7 @@ public async Task Publish_AggregateExceptionWithInnerRequestFailedExceptions_Sho var clientFactory = StrictMock.Of(); clientFactory - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://fake"), "fake")) + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://fake"), "fake")) .Returns(client.Object); var templateSpecRepositoryFactory = StrictMock.Of(); @@ -450,11 +451,14 @@ public async Task Publish_BicepModule_WithDescriptionAndDocUri_ShouldPlaceDescri var registryUri = new Uri($"https://{registryStr}"); var repository = $"test/{moduleName}".ToLowerInvariant(); - var (clientFactory, blobClients) = RegistryHelper.CreateMockRegistryClients((registryStr, repository)); + var (clientFactory, blobClients, _) = RegistryHelper.CreateMockRegistryClients(new RepoDescriptor(registryStr, repository, ["v1"])); var blobClient = blobClients[(registryUri, repository)]; - await RegistryHelper.PublishModuleToRegistryAsync(clientFactory, BicepTestConstants.FileSystem, "modulename", $"br:example.com/test/{moduleName}:v1", bicepModuleContents, publishSource: false, documentationUri); + await RegistryHelper.PublishModuleToRegistryAsync( + clientFactory, + BicepTestConstants.FileSystem, + new($"br:example.com/test/{moduleName}:v1", bicepModuleContents, WithSource: false, documentationUri)); var manifest = blobClient.Manifests.Single().Value.ToObjectFromJson(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); diff --git a/src/Bicep.Cli.IntegrationTests/PublishProviderCommandTests.cs b/src/Bicep.Cli.IntegrationTests/PublishProviderCommandTests.cs index af73c710a81..47d4f17e709 100644 --- a/src/Bicep.Cli.IntegrationTests/PublishProviderCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/PublishProviderCommandTests.cs @@ -12,6 +12,7 @@ using Bicep.Core.UnitTests.Utils; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.Cli.IntegrationTests; @@ -31,7 +32,7 @@ public async Task Publish_extension_should_succeed() var repository = $"test/extension"; var version = "0.0.1"; - var (clientFactory, blobClientMocks) = RegistryHelper.CreateMockRegistryClients((registryStr, repository)); + var (clientFactory, blobClientMocks, _) = RegistryHelper.CreateMockRegistryClients(new RepoDescriptor(registryStr, repository, ["tag"])); var mockBlobClient = blobClientMocks[(registryUri, repository)]; var indexPath = Path.Combine(outputDirectory, "index.json"); diff --git a/src/Bicep.Cli.IntegrationTests/RestoreCommandTests.cs b/src/Bicep.Cli.IntegrationTests/RestoreCommandTests.cs index 59aed218de1..acca369d019 100644 --- a/src/Bicep.Cli.IntegrationTests/RestoreCommandTests.cs +++ b/src/Bicep.Cli.IntegrationTests/RestoreCommandTests.cs @@ -71,7 +71,7 @@ public async Task Restore_ShouldSucceed(string testName, DataSet dataSet, bool p if (dataSet.HasExternalModules) { // ensure something got restored - CachedModules.GetCachedRegistryModules(BicepTestConstants.FileSystem, settings.FeatureOverrides.CacheRootDirectory!).Should().HaveCountGreaterThan(0) + CachedModules.GetCachedModules(BicepTestConstants.FileSystem, settings.FeatureOverrides.CacheRootDirectory!).Should().HaveCountGreaterThan(0) .And.AllSatisfy(m => m.Should().HaveSource(publishSource)); } } @@ -90,7 +90,7 @@ public async Task Restore_should_succeed_for_bicepparam_file_with_registry_refer result.Should().Succeed().And.NotHaveStdout().And.NotHaveStderr(); // ensure something got restored - CachedModules.GetCachedRegistryModules(BicepTestConstants.FileSystem, settings.FeatureOverrides!.CacheRootDirectory!).Should().HaveCountGreaterThan(0) + CachedModules.GetCachedModules(BicepTestConstants.FileSystem, settings.FeatureOverrides!.CacheRootDirectory!).Should().HaveCountGreaterThan(0) .And.AllSatisfy(m => m.Should().NotHaveSource()); } @@ -117,13 +117,13 @@ public async Task Restore_ShouldSucceedWithAnonymousClient(string testName, Data // this will force fallback to the anonymous client var clientFactoryForRestore = StrictMock.Of(); clientFactoryForRestore - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(clientWithCredentialUnavailable.Object); // anonymous client creation will redirect to the working client factory containing mock published modules clientFactoryForRestore - .Setup(m => m.CreateAnonymousBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(clientFactory.CreateAnonymousBlobClient); + .Setup(m => m.CreateAnonymousBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(clientFactory.CreateAnonymousBlobClient); var settings = new InvocationSettings(new(TestContext, RegistryEnabled: dataSet.HasExternalModules), clientFactoryForRestore.Object, templateSpecRepositoryFactory); TestContext.WriteLine($"Cache root = {settings.FeatureOverrides!.CacheRootDirectory}"); @@ -139,7 +139,7 @@ public async Task Restore_ShouldSucceedWithAnonymousClient(string testName, Data if (dataSet.HasExternalModules) { // ensure something got restored - CachedModules.GetCachedRegistryModules(BicepTestConstants.FileSystem, settings.FeatureOverrides.CacheRootDirectory!).Should().HaveCountGreaterThan(0) + CachedModules.GetCachedModules(BicepTestConstants.FileSystem, settings.FeatureOverrides.CacheRootDirectory!).Should().HaveCountGreaterThan(0) .And.AllSatisfy(m => m.Should().HaveSource(publishSource)); } } @@ -309,7 +309,7 @@ public async Task Restore_With_Force_Should_Overwrite_Existing_Cache(bool publis var client = new MockRegistryBlobClient(); var clientFactory = StrictMock.Of(); - clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); + clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); var templateSpecRepositoryFactory = BicepTestConstants.TemplateSpecRepositoryFactory; @@ -430,7 +430,7 @@ public async Task Restore_ByDigest_ShouldSucceed(bool publishSource) var client = new MockRegistryBlobClient(); var clientFactory = StrictMock.Of(); - clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); + clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); var templateSpecRepositoryFactory = BicepTestConstants.TemplateSpecRepositoryFactory; @@ -517,7 +517,7 @@ public async Task Restore_AggregateExceptionWithInnerRequestFailedExceptions_Sho var clientFactory = StrictMock.Of(); clientFactory - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://fake"), "fake")) + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://fake"), "fake")) .Returns(client.Object); var templateSpecRepositoryFactory = StrictMock.Of(); @@ -549,7 +549,7 @@ public async Task Restore_RequestFailedException_ShouldFail() var clientFactory = StrictMock.Of(); clientFactory - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://fake"), "fake")) + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://fake"), "fake")) .Returns(client.Object); var templateSpecRepositoryFactory = StrictMock.Of(); @@ -578,7 +578,7 @@ public async Task Restore_bicepparam_should_fail_with_error_diagnostics_for_regi var clientFactory = StrictMock.Of(); clientFactory - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://mockregistry.io"), "parameters/basic")) + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), new Uri("https://mockregistry.io"), "parameters/basic")) .Returns(client.Object); var templateSpecRepositoryFactory = StrictMock.Of(); diff --git a/src/Bicep.Cli.IntegrationTests/TestBase.cs b/src/Bicep.Cli.IntegrationTests/TestBase.cs index 196bb2bbf90..11b572760f9 100644 --- a/src/Bicep.Cli.IntegrationTests/TestBase.cs +++ b/src/Bicep.Cli.IntegrationTests/TestBase.cs @@ -7,7 +7,8 @@ using Bicep.Core.Extensions; using Bicep.Core.FileSystem; using Bicep.Core.Registry; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; +using Bicep.Core.Registry.Catalog.HttpClients; using Bicep.Core.Text; using Bicep.Core.UnitTests; using Bicep.Core.UnitTests.Features; @@ -23,14 +24,14 @@ namespace Bicep.Cli.IntegrationTests { public abstract class TestBase : Bicep.Core.UnitTests.TestBase { - private static BicepCompiler CreateCompiler(IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory, IPublicRegistryModuleIndexClient? moduleMetadataClient) + private static BicepCompiler CreateCompiler(IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory, IPublicModuleIndexHttpClient? moduleMetadataClient) => ServiceBuilder.Create( services => { services .AddSingleton(clientFactory) .AddSingleton(templateSpecRepositoryFactory) - .AddSingleton(); + .AddRegistryCatalogServices(); IServiceCollectionExtensions.AddMockHttpClientIfNotNull(services, moduleMetadataClient); } @@ -49,21 +50,21 @@ protected record InvocationSettings public IContainerRegistryClientFactory ClientFactory { get; init; } public ITemplateSpecRepositoryFactory TemplateSpecRepositoryFactory { get; init; } public IEnvironment? Environment { get; init; } - public IPublicRegistryModuleIndexClient ModuleMetadataClient { get; init; } + public IPublicModuleIndexHttpClient ModuleMetadataClient { get; init; } public InvocationSettings( FeatureProviderOverrides? FeatureOverrides = null, IContainerRegistryClientFactory? ClientFactory = null, ITemplateSpecRepositoryFactory? TemplateSpecRepositoryFactory = null, IEnvironment? Environment = null, - IPublicRegistryModuleIndexClient? ModuleMetadataClient = null) + IPublicModuleIndexHttpClient? ModuleMetadataClient = null) { this.FeatureOverrides = FeatureOverrides; this.ClientFactory = ClientFactory ?? Repository.Create().Object; this.TemplateSpecRepositoryFactory = TemplateSpecRepositoryFactory ?? Repository.Create().Object; this.Environment = Environment; - this.ModuleMetadataClient = ModuleMetadataClient ?? StrictMock.Of().Object; + this.ModuleMetadataClient = ModuleMetadataClient ?? StrictMock.Of().Object; } } @@ -109,7 +110,7 @@ protected static void AssertNoErrors(string error) } } - protected static async Task> GetAllDiagnostics(string bicepFilePath, IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory, IPublicRegistryModuleIndexClient? moduleMetadataClient = null) + protected static async Task> GetAllDiagnostics(string bicepFilePath, IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory, IPublicModuleIndexHttpClient? moduleMetadataClient = null) { var compilation = await CreateCompiler(clientFactory, templateSpecRepositoryFactory, moduleMetadataClient).CreateCompilation(PathHelper.FilePathToFileUrl(bicepFilePath)); diff --git a/src/Bicep.Cli.IntegrationTests/UseRecentModuleVersionsIntegrationTests.cs b/src/Bicep.Cli.IntegrationTests/UseRecentModuleVersionsIntegrationTests.cs index b0e7d13ed9b..c31c78310e8 100644 --- a/src/Bicep.Cli.IntegrationTests/UseRecentModuleVersionsIntegrationTests.cs +++ b/src/Bicep.Cli.IntegrationTests/UseRecentModuleVersionsIntegrationTests.cs @@ -14,18 +14,21 @@ using Bicep.Core.Modules; using Bicep.Core.Registry; using Bicep.Core.Registry.Oci; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; +using Bicep.Core.Registry.Catalog.HttpClients; using Bicep.Core.Samples; using Bicep.Core.UnitTests; using Bicep.Core.UnitTests.Assertions; using Bicep.Core.UnitTests.Baselines; -using Bicep.Core.UnitTests.Mock; +using Bicep.Core.UnitTests.Mock.Registry; using Bicep.Core.UnitTests.Registry; using Bicep.Core.UnitTests.Utils; using FluentAssertions; using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using Bicep.Core.UnitTests.Mock.Registry.Catalog; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.Cli.IntegrationTests; @@ -40,7 +43,7 @@ public class UseRecentModuleVersionsIntegrationTests : TestBase private class Options(string CacheRoot) { - private IPublicRegistryModuleIndexClient? _metadataClient = null; + private IPublicModuleIndexHttpClient? _metadataClient = null; private string? _config = null; public string Bicep { get; init; } = "/* bicep contents */"; @@ -73,18 +76,18 @@ public string? BicepConfig } } - // Automatically created from ModulesMetadata by default - public IPublicRegistryModuleIndexClient MetadataClient + // Automatically created from ModulesMetadata by default (set manually for testing) + internal IPublicModuleIndexHttpClient MetadataClient { set { _metadataClient = value; } - get => _metadataClient is { } ? _metadataClient : PublicRegistryModuleIndexClientMock.Create( - ModulesMetadata.Select(mm => new PublicRegistryModuleIndexEntry( + get => _metadataClient is { } ? _metadataClient : PublicModuleIndexHttpClientMocks.Create( + ModulesMetadata.Select(mm => new PublicModuleIndexEntry( mm.module, [.. mm.versions], - new Dictionary().ToImmutableDictionary()))).Object; + new Dictionary().ToImmutableDictionary()))).Object; } } @@ -96,7 +99,7 @@ private async Task Test(Options options) // compile and publish modules using throwaway file system var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( new MockFileSystem(), - [.. options.PublishedModules.Select(x => (x, "", true))]); + [.. options.PublishedModules.Select(x => new ModuleToPublish(x, BicepSource: "", WithSource: true))]); // create files var mainFile = FileHelper.SaveResultFile(TestContext, "main.bicep", options.Bicep, testOutputPath); @@ -111,30 +114,11 @@ private async Task Test(Options options) return await Bicep(settings, "lint", mainFile, options.NoRestore ? "--no-restore" : null); } - [TestMethod] - public async Task IfLevelIsOff_ShouldNotDownloadModuleMetadata() - { - var result = await Test(new Options(CacheRoot) - { - Bicep = """ - module m1 '{PREFIX}/fake/avm/res/app/container-app:0.2.0' = { - name: 'm1' - } - """.Replace("{PREFIX}", PREFIX), - DiagnosticLevel = "off", - PublishedModules = [$"{PREFIX}/fake/avm/res/app/container-app:0.2.0"], - MetadataClient = PublicRegistryModuleIndexClientMock.CreateToThrow(new Exception("unit test failed: shouldn't try to download in this scenario")).Object, - }); - - result.Should().NotHaveStderr(); - result.Should().HaveStdout(""); - result.Should().Succeed(); - } - [TestMethod] // We don't currently cache to disk, but rather on every check to restore modules. - public async Task IfNoRestoreSpecified_ThenShouldFailBecauseNoCache() + public async Task IfNoRestoreSpecified_ThenShouldNotDownloadMetadata_AndShouldFailBecauseNoCache() { + var moduleIndexClientMock = PublicModuleIndexHttpClientMocks.CreateToThrow(new Exception("shouldn't try to download metadata --no-restore is set")); var result = await Test(new Options(CacheRoot) { Bicep = """ @@ -144,6 +128,7 @@ public async Task IfNoRestoreSpecified_ThenShouldFailBecauseNoCache() """.Replace("{PREFIX}", PREFIX), PublishedModules = [$"{PREFIX}/fake/avm/res/app/container-app:0.2.0"], ModulesMetadata = [("fake/avm/res/app/container-app", ["0.2.0"])], + MetadataClient = moduleIndexClientMock.Object, NoRestore = true, }); @@ -151,6 +136,8 @@ public async Task IfNoRestoreSpecified_ThenShouldFailBecauseNoCache() result.Should().HaveStderrMatch("*Warning use-recent-module-versions: Available module versions have not yet been downloaded. If running from the command line, be sure --no-restore is not specified.*"); result.Should().HaveStdout(""); result.Should().Fail(); + + moduleIndexClientMock.Verify(client => client.GetModuleIndexAsync(), Times.Never, "shouldn't try to download metadata --no-restore is set"); } [TestMethod] @@ -165,7 +152,7 @@ public async Task IfMetadataDownloadFails_ThenShouldFail() } """.Replace("{PREFIX}", PREFIX), PublishedModules = [$"{PREFIX}/fake/avm/res/app/container-app:0.2.0"], - MetadataClient = PublicRegistryModuleIndexClientMock.CreateToThrow(new Exception("Download failed.")).Object, + MetadataClient = PublicModuleIndexHttpClientMocks.CreateToThrow(new Exception("Download failed.")).Object, }); result.Should().HaveStderrMatch($"*Warning use-recent-module-versions: Could not download available module versions: Download failed.*"); diff --git a/src/Bicep.Cli.IntegrationTests/packages.lock.json b/src/Bicep.Cli.IntegrationTests/packages.lock.json index e77c6d3776a..5e32bce5ca7 100644 --- a/src/Bicep.Cli.IntegrationTests/packages.lock.json +++ b/src/Bicep.Cli.IntegrationTests/packages.lock.json @@ -734,10 +734,10 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.10.48", - "contentHash": "7onkbbE0AOAhxKe+ZAa2NMzo4R5G4qypZmNIE0GhBohT/tl6e5aLnLx4Gg6trf6SUn3DfLRowMtNe5Q+PmhKgQ==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.VisualStudio.Threading.Analyzers": "17.10.48", + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", "Microsoft.VisualStudio.Validation": "17.8.8" } }, @@ -1594,6 +1594,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.Cli.UnitTests/packages.lock.json b/src/Bicep.Cli.UnitTests/packages.lock.json index fbea1f3e837..68cd74d6325 100644 --- a/src/Bicep.Cli.UnitTests/packages.lock.json +++ b/src/Bicep.Cli.UnitTests/packages.lock.json @@ -706,10 +706,10 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.10.48", - "contentHash": "7onkbbE0AOAhxKe+ZAa2NMzo4R5G4qypZmNIE0GhBohT/tl6e5aLnLx4Gg6trf6SUn3DfLRowMtNe5Q+PmhKgQ==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.VisualStudio.Threading.Analyzers": "17.10.48", + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", "Microsoft.VisualStudio.Validation": "17.8.8" } }, @@ -1466,6 +1466,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs b/src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs index b2bf0eb6c39..78e00f568e6 100644 --- a/src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs +++ b/src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs @@ -11,7 +11,7 @@ using Bicep.Core.FileSystem; using Bicep.Core.Registry; using Bicep.Core.Registry.Auth; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem.Providers; using Bicep.Core.Utils; @@ -75,7 +75,7 @@ public static IServiceCollection AddBicepCore(this IServiceCollection services) .AddSingleton() .AddSingleton() .AddSingleton() - .AddPublicRegistryModuleMetadataProviderServices() + .AddRegistryCatalogServices() .AddSingleton(); public static IServiceCollection AddBicepDecompiler(this IServiceCollection services) => services diff --git a/src/Bicep.Cli/packages.lock.json b/src/Bicep.Cli/packages.lock.json index 31f60fbe9bb..73612ff58b1 100644 --- a/src/Bicep.Cli/packages.lock.json +++ b/src/Bicep.Cli/packages.lock.json @@ -637,10 +637,10 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.10.48", - "contentHash": "7onkbbE0AOAhxKe+ZAa2NMzo4R5G4qypZmNIE0GhBohT/tl6e5aLnLx4Gg6trf6SUn3DfLRowMtNe5Q+PmhKgQ==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.VisualStudio.Threading.Analyzers": "17.10.48", + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", "Microsoft.VisualStudio.Validation": "17.8.8" } }, @@ -1363,6 +1363,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.Core.IntegrationTests/AzTypesViaRegistryTests.cs b/src/Bicep.Core.IntegrationTests/AzTypesViaRegistryTests.cs index 21cf7585828..f804b29177d 100644 --- a/src/Bicep.Core.IntegrationTests/AzTypesViaRegistryTests.cs +++ b/src/Bicep.Core.IntegrationTests/AzTypesViaRegistryTests.cs @@ -15,6 +15,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; using RegistryUtils = Bicep.Core.UnitTests.Utils.ContainerRegistryClientFactoryExtensions; namespace Bicep.Core.IntegrationTests @@ -52,7 +53,7 @@ private async Task GetServices() private async Task ServicesWithTestExtensionArtifact(ArtifactRegistryAddress artifactRegistryAddress, BinaryData artifactPayload) { - (var clientFactory, var blobClients) = RegistryUtils.CreateMockRegistryClients(artifactRegistryAddress.ClientDescriptor()); + (var clientFactory, var blobClients, _) = RegistryUtils.CreateMockRegistryClients(artifactRegistryAddress.ClientDescriptor()); (_, var client) = blobClients.First(); var configResult = await client.UploadBlobAsync(BinaryData.FromString("{}")); @@ -73,7 +74,7 @@ public async Task Bicep_module_artifact_specified_in_extension_declaration_synta // ARRANGE var fsMock = new MockFileSystem(); var testArtifact = new ArtifactRegistryAddress(LanguageConstants.BicepPublicMcrRegistry, "bicep/extensions/az", "0.2.661"); - var clientFactory = RegistryHelper.CreateMockRegistryClients((testArtifact.RegistryAddress, testArtifact.RepositoryPath)).factoryMock; + var clientFactory = RegistryHelper.CreateMockRegistryClients(new RepoDescriptor(testArtifact.RegistryAddress, testArtifact.RepositoryPath, ["v1"])).factoryMock; var services = new ServiceBuilder() .WithFileSystem(fsMock) .WithFeatureOverrides(new(ExtensibilityEnabled: true)) @@ -82,11 +83,7 @@ public async Task Bicep_module_artifact_specified_in_extension_declaration_synta await RegistryHelper.PublishModuleToRegistryAsync( clientFactory, fsMock, - moduleName: "az", - target: testArtifact.ToSpecificationString(':'), - moduleSource: "", - publishSource: false, - documentationUri: "mydocs.org/abc"); + new(testArtifact.ToSpecificationString(':'), BicepSource: "", WithSource: false, DocumentationUri: "mydocs.org/abc")); // ACT var result = await CompilationHelper.RestoreAndCompile(services, @$" @@ -128,7 +125,7 @@ public record ArtifactRegistryAddress(string RegistryAddress, string RepositoryP { public string ToSpecificationString(char delim) => $"br:{RegistryAddress}/{RepositoryPath}{delim}{ExtensionVersion}"; - public (string, string) ClientDescriptor() => (RegistryAddress, RepositoryPath); + public RepoDescriptor ClientDescriptor() => new(RegistryAddress, RepositoryPath, [ExtensionVersion]); } [TestMethod] @@ -145,9 +142,9 @@ public async Task Repository_not_found_in_registry( // mock the registry client to return the mock blob client var containerRegistryFactoryBuilder = new TestContainerRegistryClientFactoryBuilder(); - containerRegistryFactoryBuilder.RegisterMockRepositoryBlobClient( - artifactRegistryAddress.RegistryAddress, - artifactRegistryAddress.RepositoryPath, + containerRegistryFactoryBuilder.WithRepository( + new RepoDescriptor( + artifactRegistryAddress.RegistryAddress, artifactRegistryAddress.RepositoryPath, ["tag"]), mockBlobClient.Object); var services = new ServiceBuilder() diff --git a/src/Bicep.Core.IntegrationTests/Extensibility/RadiusCompatibilityTests.cs b/src/Bicep.Core.IntegrationTests/Extensibility/RadiusCompatibilityTests.cs index 143dd3588d7..7179d44eb15 100644 --- a/src/Bicep.Core.IntegrationTests/Extensibility/RadiusCompatibilityTests.cs +++ b/src/Bicep.Core.IntegrationTests/Extensibility/RadiusCompatibilityTests.cs @@ -7,6 +7,7 @@ using Bicep.Core.UnitTests.Assertions; using Bicep.Core.UnitTests.Utils; using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.Core.IntegrationTests.Extensibility; @@ -15,7 +16,7 @@ public class RadiusCompatibilityTests { private static ServiceBuilder GetServiceBuilder(IFileSystem fileSystem, string registryHost, string repositoryPath) { - var clientFactory = RegistryHelper.CreateMockRegistryClient(registryHost, repositoryPath); + var clientFactory = RegistryHelper.CreateMockRegistryClient(new RepoDescriptor(registryHost, repositoryPath, ["tag"])); return new ServiceBuilder() .WithFeatureOverrides(new(ExtensibilityEnabled: true)) diff --git a/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs b/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs index bf905061878..f9e7f5e3449 100644 --- a/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs +++ b/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs @@ -16,6 +16,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; using RegistryUtils = Bicep.Core.UnitTests.Utils.ContainerRegistryClientFactoryExtensions; namespace Bicep.Core.IntegrationTests @@ -68,7 +69,7 @@ private async Task GetServices() private async Task ServicesWithTestExtensionArtifact(ArtifactRegistryAddress artifactRegistryAddress, BinaryData artifactPayload) { - (var clientFactory, var blobClients) = RegistryUtils.CreateMockRegistryClients(artifactRegistryAddress.ClientDescriptor()); + (var clientFactory, var blobClients, _) = RegistryUtils.CreateMockRegistryClients(artifactRegistryAddress.ClientDescriptor()); (_, var client) = blobClients.First(); var configResult = await client.UploadBlobAsync(BinaryData.FromString("{}")); @@ -110,7 +111,7 @@ public record ArtifactRegistryAddress(string RegistryAddress, string RepositoryP { public string ToSpecificationString(char delim) => $"br:{RegistryAddress}/{RepositoryPath}{delim}{ExtensionVersion}"; - public (string, string) ClientDescriptor() => (RegistryAddress, RepositoryPath); + public RepoDescriptor/*asdfg?*/ ClientDescriptor() => new(RegistryAddress, RepositoryPath, [ExtensionVersion]); } [TestMethod] @@ -127,10 +128,8 @@ public async Task Repository_not_found_in_registry( // mock the registry client to return the mock blob client var containerRegistryFactoryBuilder = new TestContainerRegistryClientFactoryBuilder(); - containerRegistryFactoryBuilder.RegisterMockRepositoryBlobClient( - artifactRegistryAddress.RegistryAddress, - artifactRegistryAddress.RepositoryPath, - mockBlobClient.Object); + containerRegistryFactoryBuilder.WithRepository( + new RepoDescriptor(artifactRegistryAddress.RegistryAddress, artifactRegistryAddress.RepositoryPath, [artifactRegistryAddress.ExtensionVersion]), mockBlobClient.Object); var services = new ServiceBuilder() .WithFeatureOverrides(new(ExtensibilityEnabled: true)) diff --git a/src/Bicep.Core.IntegrationTests/SourceArchiveTests.cs b/src/Bicep.Core.IntegrationTests/SourceArchiveTests.cs index e8dcb91e0be..06ec7591ba3 100644 --- a/src/Bicep.Core.IntegrationTests/SourceArchiveTests.cs +++ b/src/Bicep.Core.IntegrationTests/SourceArchiveTests.cs @@ -120,7 +120,7 @@ public async Task SourceArtifactId_ForExternalModulesWithoutSource_ShouldBeNull( var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "param p1 bool", withSource: false), + new("br:mockregistry.io/test/module1:v1", "param p1 bool", WithSource: false), ]); var moduleDispatcher = CreateModuleDispatcher(clientFactory); var result = await CompilationHelper.RestoreAndCompile( @@ -148,7 +148,7 @@ public async Task SourceArtifactId_ForExternalModulesWithSource_ShouldBeTheArtif var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "param p1 bool", withSource: true), + new("br:mockregistry.io/test/module1:v1", "param p1 bool", WithSource: true), ]); var moduleDispatcher = CreateModuleDispatcher(clientFactory); var result = await CompilationHelper.RestoreAndCompile( @@ -177,9 +177,9 @@ public async Task SourceArtifactId_ShouldHandleMultipleRefsToSameModule() var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "param p1 bool", withSource: true), - ("br:mockregistry.io/test/module2:v1", "param p2 string", withSource: true), - ("br:mockregistry.io/test/module1:v2", "param p12 string", withSource: true), + new("br:mockregistry.io/test/module1:v1", "param p1 bool", WithSource: true), + new("br:mockregistry.io/test/module2:v1", "param p2 string", WithSource: true), + new("br:mockregistry.io/test/module1:v2", "param p12 string", WithSource: true), ]); var moduleDispatcher = CreateModuleDispatcher(clientFactory); var result = await CompilationHelper.RestoreAndCompile( @@ -255,7 +255,7 @@ public async Task SourceArtifactId_ShouldIgnoreModuleRefsWithErrors() var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "param p1 bool", withSource: true), + new("br:mockregistry.io/test/module1:v1", "param p1 bool", WithSource: true), ]); var moduleDispatcher = CreateModuleDispatcher(clientFactory); var result = await CompilationHelper.RestoreAndCompile( diff --git a/src/Bicep.Core.IntegrationTests/TypeSystem/Providers/ResourceTypeProviderFactoryTests.cs b/src/Bicep.Core.IntegrationTests/TypeSystem/Providers/ResourceTypeProviderFactoryTests.cs index f11c327f0fc..5656b2f30c3 100644 --- a/src/Bicep.Core.IntegrationTests/TypeSystem/Providers/ResourceTypeProviderFactoryTests.cs +++ b/src/Bicep.Core.IntegrationTests/TypeSystem/Providers/ResourceTypeProviderFactoryTests.cs @@ -7,6 +7,7 @@ using Bicep.Core.UnitTests.Utils; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.Core.IntegrationTests; @@ -28,7 +29,7 @@ public async Task ExtensionNameAndVersionAreUsedAsCacheKeys() var repositoryPath = $"test/extension"; var repositoryNames = new[] { "foo", "bar" }; - var (clientFactory, _) = RegistryHelper.CreateMockRegistryClients(repositoryNames.Select(name => (registry, $"{repositoryPath}/{name}")).ToArray()); + var (clientFactory, _, _) = RegistryHelper.CreateMockRegistryClients([.. repositoryNames.Select(name => new RepoDescriptor(registry, $"{repositoryPath}/{name}", ["tag"]))]); var services = new ServiceBuilder() .WithFeatureOverrides(new(TestContext, ExtensibilityEnabled: true)) diff --git a/src/Bicep.Core.IntegrationTests/packages.lock.json b/src/Bicep.Core.IntegrationTests/packages.lock.json index 99f4223eeb3..2f5c0691094 100644 --- a/src/Bicep.Core.IntegrationTests/packages.lock.json +++ b/src/Bicep.Core.IntegrationTests/packages.lock.json @@ -618,8 +618,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -701,28 +701,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1304,11 +1301,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1518,6 +1515,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1601,11 +1599,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2008,11 +2006,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2084,11 +2082,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2491,11 +2489,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2567,11 +2565,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2974,11 +2972,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3050,11 +3048,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3457,11 +3455,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3533,11 +3531,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3940,11 +3938,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4016,11 +4014,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4324,11 +4322,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4400,11 +4398,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4708,11 +4706,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.Core.Samples/DataSetsExtensions.cs b/src/Bicep.Core.Samples/DataSetsExtensions.cs index f32a117905a..7a3a6860a0f 100644 --- a/src/Bicep.Core.Samples/DataSetsExtensions.cs +++ b/src/Bicep.Core.Samples/DataSetsExtensions.cs @@ -15,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.Core.Samples { @@ -46,17 +47,21 @@ public static string SaveFilesToTestDirectory(this DataSet dataSet, TestContext return (compilation, outputDirectory, fileUri); } - public static IContainerRegistryClientFactory CreateMockRegistryClients(this DataSet dataSet, params (string registryUri, string repository)[] additionalClients) + public static IContainerRegistryClientFactory CreateMockRegistryClients( + this DataSet dataSet, + params RepoDescriptor[] additionalClients) => CreateMockRegistryClients(dataSet.RegistryModules, additionalClients); - public static IContainerRegistryClientFactory CreateMockRegistryClients(ImmutableDictionary registryModules, params (string registryUri, string repository)[] additionalClients) + public static IContainerRegistryClientFactory CreateMockRegistryClients( + ImmutableDictionary registryModules, + params RepoDescriptor[] additionalClients) { var dispatcher = ServiceBuilder.Create(s => s.WithDisabledAnalyzersConfiguration() .AddSingleton(BicepTestConstants.ClientFactory) .AddSingleton(BicepTestConstants.TemplateSpecRepositoryFactory) ).Construct(); - var clients = new List<(string, string)>(); + var clients = new List(); foreach (var (moduleName, publishInfo) in registryModules) { @@ -67,7 +72,7 @@ public static IContainerRegistryClientFactory CreateMockRegistryClients(Immutabl throw new InvalidOperationException($"Module '{moduleName}' has an invalid target reference '{target}'. Specify a reference to an OCI artifact."); } - clients.Add((targetReference.Registry, targetReference.Repository)); + clients.Add(new(targetReference.Registry, targetReference.Repository, [targetReference.Tag ?? "tagasdfg"])); } return RegistryHelper.CreateMockRegistryClients([.. clients, .. additionalClients]).factoryMock; @@ -118,7 +123,10 @@ public static async Task PublishModulesToRegistryAsync(ImmutableDictionary modules); + private record MockRegistryCatalog( + ImmutableDictionary modules + ); public record ClientFactories( IContainerRegistryClientFactory ContainerRegistry, - ITemplateSpecRepositoryFactory TemplateSpec); + ITemplateSpecRepositoryFactory TemplateSpec + ); public static async Task Build(bool publishSource = false) => new( @@ -29,7 +31,7 @@ await CreateMockBicepRegistry(publishSource), private static async Task CreateMockBicepRegistry(bool publishSource) { var registryFiles = EmbeddedFile.LoadAll(typeof(Bicep.Core.Samples.AssemblyInitializer).Assembly, "mockregistry", _ => true).ToArray(); - var index = registryFiles.First(x => x.StreamPath == "Files/mockregistry/index.json").Contents.FromJson(); + var index = registryFiles.First(x => x.StreamPath == "Files/mockregistry/index.json").Contents.FromJson(); var modules = new Dictionary(); foreach (var (registryPath, filePath) in index.modules.Where(x => x.Key.StartsWith(OciArtifactReferenceFacts.SchemeWithColon))) @@ -48,7 +50,7 @@ private static async Task CreateMockBicepRegist private static ITemplateSpecRepositoryFactory CreateMockTemplateSpecRegistry() { var registryFiles = EmbeddedFile.LoadAll(typeof(Bicep.Core.Samples.AssemblyInitializer).Assembly, "mockregistry", _ => true).ToArray(); - var index = registryFiles.First(x => x.StreamPath == "Files/mockregistry/index.json").Contents.FromJson(); + var index = registryFiles.First(x => x.StreamPath == "Files/mockregistry/index.json").Contents.FromJson(); var modules = new Dictionary(); foreach (var (registryPath, filePath) in index.modules.Where(x => x.Key.StartsWith("ts:"))) diff --git a/src/Bicep.Core.Samples/packages.lock.json b/src/Bicep.Core.Samples/packages.lock.json index 145f054ed49..c30c08cfc31 100644 --- a/src/Bicep.Core.Samples/packages.lock.json +++ b/src/Bicep.Core.Samples/packages.lock.json @@ -618,8 +618,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -701,28 +701,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1304,11 +1301,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1518,6 +1515,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1590,11 +1588,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1997,11 +1995,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2073,11 +2071,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2480,11 +2478,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2556,11 +2554,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2963,11 +2961,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3039,11 +3037,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3446,11 +3444,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3522,11 +3520,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3929,11 +3927,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4005,11 +4003,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4313,11 +4311,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4389,11 +4387,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4697,11 +4695,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs b/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs index d17ab7ee646..87e267f8f2a 100644 --- a/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs +++ b/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs @@ -58,7 +58,7 @@ public void GetResourceTypeNames_ResourceGroup() } [DataTestMethod] - public void GetResourceTypeNames_SeparateScopes() + public void GetResourceTypeNames_SeparateScopes() //asdfg very slow, why? { var apiVersionProvider = CreateDefaultApiVersionProvider(); apiVersionProvider.InjectTypeReferences(ResourceScope.ResourceGroup, FakeResourceTypes.GetFakeResourceTypeReferences(FakeResourceTypes.ResourceScopeTypes)); diff --git a/src/Bicep.Core.UnitTests/Assertions/OciModuleRegistryAssertions.cs b/src/Bicep.Core.UnitTests/Assertions/OciModuleRegistryAssertions.cs index fc1f460595b..de7777edefd 100644 --- a/src/Bicep.Core.UnitTests/Assertions/OciModuleRegistryAssertions.cs +++ b/src/Bicep.Core.UnitTests/Assertions/OciModuleRegistryAssertions.cs @@ -32,7 +32,7 @@ public AndConstraint HaveValidCachedModules(IFile private static void ShouldHaveValidCachedModules(IFileSystem fileSystem, IDirectoryHandle cacheRootDirectory, bool? withSource = null) { - var modules = CachedModules.GetCachedRegistryModules(fileSystem, cacheRootDirectory); + var modules = CachedModules.GetCachedModules(fileSystem, cacheRootDirectory); modules.Should().HaveCountGreaterThan(0); if (withSource.HasValue) { diff --git a/src/Bicep.Core.UnitTests/Bicep.Core.UnitTests.csproj b/src/Bicep.Core.UnitTests/Bicep.Core.UnitTests.csproj index 35d93d47c68..9b1f95535f7 100644 --- a/src/Bicep.Core.UnitTests/Bicep.Core.UnitTests.csproj +++ b/src/Bicep.Core.UnitTests/Bicep.Core.UnitTests.csproj @@ -35,6 +35,7 @@ + diff --git a/src/Bicep.Core.UnitTests/BicepTestConstants.cs b/src/Bicep.Core.UnitTests/BicepTestConstants.cs index f1f8a3f6c48..0217656f489 100644 --- a/src/Bicep.Core.UnitTests/BicepTestConstants.cs +++ b/src/Bicep.Core.UnitTests/BicepTestConstants.cs @@ -14,7 +14,7 @@ using Bicep.Core.Json; using Bicep.Core.Registry; using Bicep.Core.Registry.Oci; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem; using Bicep.Core.TypeSystem.Providers; diff --git a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/DecompilerCleanupRuleTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/DecompilerCleanupRuleTests.cs index 7030595662a..3fb06bd3b11 100644 --- a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/DecompilerCleanupRuleTests.cs +++ b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/DecompilerCleanupRuleTests.cs @@ -218,7 +218,7 @@ public void ResourceNameFluff(string bicep, string[] expectedFailingResourceName // pass }, DisplayName = "_var passes for non-variable names")] - public void VariableNameFluff(string bicep, string[] expectedFailingResourceNames) + public void VariableNameFluff(string bicep, string[] expectedFailingResourceNames) //asdfg null ref? { AssertLinterRuleDiagnostics( DecompilerCleanupRule.Code, diff --git a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/ExplicitValuesForLocationParamsRuleTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/ExplicitValuesForLocationParamsRuleTests.cs index a94e65c2872..488494ac1e1 100644 --- a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/ExplicitValuesForLocationParamsRuleTests.cs +++ b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/ExplicitValuesForLocationParamsRuleTests.cs @@ -331,7 +331,7 @@ param location string }); } - [TestMethod] + [TestMethod] //asdfg null ref? public void If_Module_HasErrors_LocationParam_WithDefault_AndValuePassedIn_CaseInsensitive_ShouldPass() { var result = CompilationHelper.Compile( diff --git a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseRecentModuleVersionsRuleTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseRecentModuleVersionsRuleTests.cs index e4a384c9fe8..20bcc261f47 100644 --- a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseRecentModuleVersionsRuleTests.cs +++ b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/UseRecentModuleVersionsRuleTests.cs @@ -12,7 +12,7 @@ using Bicep.Core.Diagnostics; using Bicep.Core.Json; using Bicep.Core.Parsing; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Resources; using Bicep.Core.TypeSystem; using Bicep.Core.UnitTests.Assertions; @@ -43,22 +43,25 @@ private static CompilationResult Compile( string[] availableVersions, // for simplicity, mock returns these same versions for all available modules string? downloadError = null) { - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()) - .Returns(availableModules.Select(m => new PublicRegistryModuleMetadata(m, null, null)).ToImmutableArray()); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata(It.IsAny())) - .Returns((string module) => - { - return availableModules.Contains(module) ? - availableVersions.Select(v => new PublicRegistryModuleVersionMetadata(v, null, null)).ToImmutableArray() : - []; - }); - publicRegistryModuleMetadataProvider.Setup(x => x.IsCached) + var publicModuleMetadataProvider = StrictMock.Of(); + publicModuleMetadataProvider.Setup(x => x.GetCachedModules()) + .Returns([.. availableModules + .Select(m => new RegistryModuleMetadata( + "mcr.microsoft.com", + m, + new RegistryModuleMetadata.ComputedData( + new RegistryMetadataDetails(null, null), + [.. availableVersions + .Select(v => new RegistryModuleVersionMetadata(v, new("det", "doc.html")))] + ))) + ]); + + publicModuleMetadataProvider.Setup(x => x.IsCached) .Returns(availableModules.Length > 0); - publicRegistryModuleMetadataProvider.Setup(x => x.DownloadError) + publicModuleMetadataProvider.Setup(x => x.DownloadError) .Returns(downloadError); - var services = Services.WithRegistration(x => x.AddSingleton(publicRegistryModuleMetadataProvider.Object)); + var services = Services.WithRegistration(x => x.AddSingleton(publicModuleMetadataProvider.Object)); var result = CompilationHelper.Compile(services, [("main.bicep", bicep)]); return result; } @@ -70,7 +73,7 @@ public void IfUsingOldVersionThatWeDontKnowAbout_Fail() module m1 'br/public:avm/res/network/public-ip-address:0.4.0' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["1.0.0"] ); result.Diagnostics.Where(d => d.Code == UseRecentModuleVersionsRule.Code) @@ -90,7 +93,7 @@ public void IfUsingNewVersionThatWeDontKnowAbout_Passes() module m1 'br/public:avm/res/network/public-ip-address:1.4.0' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["1.0.0"] ); result.Diagnostics.Where(d => d.Code == UseRecentModuleVersionsRule.Code) @@ -105,7 +108,7 @@ public void UsingNewestVersion_NoFailures() module m1 'br/public:avm/res/network/public-ip-address:0.4.0' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["0.3.0", "0.3.1", "0.4.0"] ); result.Diagnostics.Where(d => d.Code == UseRecentModuleVersionsRule.Code) @@ -180,7 +183,7 @@ public void HasDownloadError() module m1 'br/public:avm/res/network/public-ip-address:0.4.0' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["1.0.0"], "My download error" ); @@ -198,7 +201,7 @@ public void SingleMinorVersionBehind() module m1 'br/public:avm/res/network/public-ip-address:0.4.0' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["0.3.0", "0.4.0", "0.5.0", "1.0.1"] ); result.Diagnostics.Where(d => d.Code == UseRecentModuleVersionsRule.Code) @@ -215,7 +218,7 @@ public void SingleMajorVersionBehind() module m1 'br/public:avm/res/network/public-ip-address:0.4.0' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["0.3.0", "0.4.0", "1.0.0"] ); result.Diagnostics.Where(d => d.Code == UseRecentModuleVersionsRule.Code) @@ -232,7 +235,7 @@ public void SinglePatchVersionBehind() module m1 'br/public:avm/res/network/public-ip-address:0.4.1' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["0.3.0", "0.4.1", "0.4.2", "0.5.0"] ); result.Diagnostics.Where(d => d.Code == UseRecentModuleVersionsRule.Code) @@ -249,7 +252,7 @@ public void MultiplePatchVersionsBehind() module m1 'br/public:avm/res/network/public-ip-address:0.4.1' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["0.3.0", "0.4.1", "0.4.2", "0.4.5"] ); result.Diagnostics.Where(d => d.Code == UseRecentModuleVersionsRule.Code) @@ -266,7 +269,7 @@ public void HasFix() module m1 'br/public:avm/res/network/public-ip-address:0.4.0' = { } """, - ["avm/res/network/public-ip-address"], + ["bicep/avm/res/network/public-ip-address"], ["0.3.0", "0.4.0", "0.5.0", "1.0.1"] ); diff --git a/src/Bicep.Core.UnitTests/IServiceCollectionExtensions.cs b/src/Bicep.Core.UnitTests/IServiceCollectionExtensions.cs index 694bdfbda6e..186d12d02c5 100644 --- a/src/Bicep.Core.UnitTests/IServiceCollectionExtensions.cs +++ b/src/Bicep.Core.UnitTests/IServiceCollectionExtensions.cs @@ -8,7 +8,7 @@ using Bicep.Core.FileSystem; using Bicep.Core.Registry; using Bicep.Core.Registry.Auth; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem.Providers; using Bicep.Core.TypeSystem.Providers.Az; @@ -16,7 +16,8 @@ using Bicep.Core.TypeSystem.Types; using Bicep.Core.UnitTests.Configuration; using Bicep.Core.UnitTests.Features; -using Bicep.Core.UnitTests.Mock; +using Bicep.Core.UnitTests.Mock.Registry; +using Bicep.Core.UnitTests.Mock.Registry.Catalog; using Bicep.Core.UnitTests.Utils; using Bicep.Core.Utils; using Bicep.Core.Workspaces; @@ -51,10 +52,10 @@ public static IServiceCollection AddBicepCore(this IServiceCollection services) .AddSingleton() .AddSingleton() .AddSingleton() - .AddPublicRegistryModuleMetadataProviderServices() + .AddRegistryCatalogServices() .AddSingleton(); - AddMockHttpClient(services, PublicRegistryModuleIndexClientMock.Create([]).Object); + AddMockHttpClient(services, PublicModuleIndexHttpClientMocks.Create([]).Object); return services; } diff --git a/src/Bicep.Core.UnitTests/Mock/PublicRegistryModuleIndexClientMock.cs b/src/Bicep.Core.UnitTests/Mock/PublicRegistryModuleIndexClientMock.cs deleted file mode 100644 index f56a44ea59b..00000000000 --- a/src/Bicep.Core.UnitTests/Mock/PublicRegistryModuleIndexClientMock.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Immutable; -using Bicep.Core.Registry.PublicRegistry; -using Bicep.Core.UnitTests.Mock; -using Moq; - -namespace Bicep.Core.UnitTests.Mock; - -public static class PublicRegistryModuleIndexClientMock -{ - // CONSIDER: Mock HttpClient rather than the typed client - - public static Mock Create(IEnumerable metadata) - { - var mock = StrictMock.Of(); - mock - .Setup(client => client.GetModuleIndexAsync()) - .ReturnsAsync(() => metadata.ToImmutableArray()); - return mock; - } - - public static Mock CreateToThrow(Exception exception) - { - var mock = StrictMock.Of(); - mock - .Setup(client => client.GetModuleIndexAsync()) - .ThrowsAsync(exception); - return mock; - } -} diff --git a/src/Bicep.Core.UnitTests/Mock/Registry/PublicModuleIndexHttpClientMocks.cs b/src/Bicep.Core.UnitTests/Mock/Registry/PublicModuleIndexHttpClientMocks.cs new file mode 100644 index 00000000000..a83c51163e5 --- /dev/null +++ b/src/Bicep.Core.UnitTests/Mock/Registry/PublicModuleIndexHttpClientMocks.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Immutable; +using Bicep.Core.Registry.Catalog.HttpClients; +using Moq; + +namespace Bicep.Core.UnitTests.Mock.Registry.Catalog; + +public static class PublicModuleIndexHttpClientMocks +{ + public static Mock Create(IEnumerable metadata) + { + var mock = StrictMock.Of(); + mock + .Setup(client => client.GetModuleIndexAsync()) + .ReturnsAsync(() => [.. metadata]); + return mock; + } + + public static Mock CreateToThrow(Exception exception) + { + var mock = StrictMock.Of(); + mock + .Setup(client => client.GetModuleIndexAsync()) + .ThrowsAsync(exception); + return mock; + } +} diff --git a/src/Bicep.Core.UnitTests/Mock/Registry/RegistryCatalogMocks.cs b/src/Bicep.Core.UnitTests/Mock/Registry/RegistryCatalogMocks.cs new file mode 100644 index 00000000000..50041f6af45 --- /dev/null +++ b/src/Bicep.Core.UnitTests/Mock/Registry/RegistryCatalogMocks.cs @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Security.Principal; +using System.Text; +using System.Threading.Tasks; +using Bicep.Core.Configuration; +using Bicep.Core.Json; +using Bicep.Core.Registry; +using Bicep.Core.Registry.Catalog; +using FluentAssertions; +using Microsoft.WindowsAzure.ResourceStack.Common.Extensions; +using Moq; + +namespace Bicep.Core.UnitTests.Mock.Registry +{ + //asdfg refactor? + public static class RegistryCatalogMocks + { + private const string PublicRegistry = "mcr.microsoft.com"; + + public static Mock MockPublicMetadataProvider( + IEnumerable<(string moduleName, string? description, string? documentationUri, IEnumerable<(string version, string? description, string? documentUri)> versions)> modules) + { + if (modules.Any()) + { + modules.Should().AllSatisfy( + m => m.moduleName.Should().StartWith("bicep/", "All public modules should start with '/bicep'") + ); + } + + var publicProvider = StrictMock.Of(); + + publicProvider.Setup(x => x.Registry).Returns(PublicRegistry); + + publicProvider.Setup(x => x.TryGetModulesAsync()) + .ReturnsAsync( + [.. modules + .Select(m => new RegistryModuleMetadata( + PublicRegistry, + m.moduleName, + new RegistryModuleMetadata.ComputedData( + new RegistryMetadataDetails(m.description, m.documentationUri), + [.. modules.Single(m2 => m2.moduleName.EqualsOrdinally(m.moduleName)) + .versions + .Select(v => new RegistryModuleVersionMetadata(v.version,new(v.description,v.documentUri))) + ] + ) + ))] + ); + + return publicProvider; + } + + public static Mock MockPrivateMetadataProvider( + string registry, + IEnumerable<(string moduleName, string? description, string? documentationUri, IEnumerable<(string version, string? description, string? documentUri)> versions)> modules + ) + { + var privateProvider = StrictMock.Of(); + + privateProvider.Setup(x => x.Registry).Returns(registry); + + privateProvider.Setup(x => x.TryGetModulesAsync()) + .ReturnsAsync([.. + modules.Select(m => new RegistryModuleMetadata( + registry, + m.moduleName, + getDataAsyncFunc: async () => + new RegistryModuleMetadata.ComputedData( + await DelayedValue(new RegistryMetadataDetails( m.description, m.documentationUri)), + [.. modules.Single(m => m.moduleName.EqualsOrdinally(m.moduleName)) + .versions + .Select(v => new RegistryModuleVersionMetadata(v.version, new(v.description, v.documentUri)) + )] + ) + )) + ]); + + return privateProvider; + } + + private static async Task DelayedValue(T value) + { + await Task.Delay(1); + return value; + } + + public static Mock MockFailingPrivateMetadataProvider( + string registry, + Exception? exception = null + ) + { + exception ??= new Exception($"Loading metadata for registry {registry} (intentionally) failed"); + + var privateProvider = StrictMock.Of(); + privateProvider.Setup(x => x.Registry).Returns(registry); + privateProvider.Setup(x => x.DownloadError).Returns(exception.Message); + privateProvider.Setup(x => x.TryGetModulesAsync()).ReturnsAsync([]); + + return privateProvider; + } + + public static IRegistryModuleCatalog CreateCatalogWithMocks( + Mock? publicProvider = null, + params Mock[] privateProviders + ) + { + if (publicProvider is null) + { + publicProvider = MockPublicMetadataProvider([]); + } + + var privateFactory = StrictMock.Of(); + + // Default - when an unrecognized registry is requested, return a provider that fails to load (similar to real behavior) + privateFactory.Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((CloudConfiguration _, string registry, IContainerRegistryClientFactory _) => MockFailingPrivateMetadataProvider(registry).Object); + + foreach (var privateProvider in privateProviders) + { + privateProvider.Object.Registry.Should().NotBe(PublicRegistry); + privateFactory.Setup(x => x.Create(It.IsAny(), privateProvider.Object.Registry, It.IsAny())) + .Returns(privateProvider.Object); + } + + var indexer = new RegistryModuleCatalog( + publicProvider.Object, + privateFactory.Object, + StrictMock.Of().Object, + BicepTestConstants.BuiltInOnlyConfigurationManager); + + return indexer; + } + + public static ModuleAliasesConfiguration ModuleAliases( //asdfg extension method? + string moduleAliasesJson + ) + { + return ModuleAliasesConfiguration.Bind(JsonElementFactory.CreateElement(moduleAliasesJson), null); + } + + //public static Mock MockConfigurationManager(RootConfiguration rootConfiguration) + //{ + // var configurationManager = StrictMock.Of(); + // configurationManager.Setup(x => x.GetConfiguration(It.IsAny())).Returns(rootConfiguration); + // return configurationManager; + //} + + //asdfg needed? + //public static ModuleAliasesConfiguration ModuleAliasesConfig2( //asdfg extension method? + // string moduleAliasesJson + //) + //{ + // return ModuleAliasesConfiguration.Bind(JsonElementFactory.CreateElement(moduleAliasesJson), null); + //} + } +} diff --git a/src/Bicep.Core.UnitTests/Registry/CachedModules.cs b/src/Bicep.Core.UnitTests/Registry/CachedModules.cs index d5fc6493737..2847a0da6cb 100644 --- a/src/Bicep.Core.UnitTests/Registry/CachedModules.cs +++ b/src/Bicep.Core.UnitTests/Registry/CachedModules.cs @@ -19,7 +19,7 @@ namespace Bicep.Core.UnitTests.Registry; public static class CachedModules { // Get all cached modules from the local on-disk registry cache - public static ImmutableArray GetCachedRegistryModules(IFileSystem fileSystem, IDirectoryHandle cacheRootDirectory) + public static ImmutableArray GetCachedModules(IFileSystem fileSystem, IDirectoryHandle cacheRootDirectory) { var cacheDir = fileSystem.DirectoryInfo.New(cacheRootDirectory.Uri.GetLocalFilePath()); if (!cacheDir.Exists) diff --git a/src/Bicep.Core.UnitTests/Registry/Catalog/BaseModuleMetadataProviderTests.cs b/src/Bicep.Core.UnitTests/Registry/Catalog/BaseModuleMetadataProviderTests.cs new file mode 100644 index 00000000000..45c2269d209 --- /dev/null +++ b/src/Bicep.Core.UnitTests/Registry/Catalog/BaseModuleMetadataProviderTests.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Net.Http; +using System.Text; +using System.Text.Json; +using Bicep.Core.Registry.Catalog; +using Bicep.Core.Registry.Catalog.HttpClients; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using RichardSzalay.MockHttp; + +namespace Bicep.Core.UnitTests.Registry.Catalog +{ + [TestClass] + public class BaseModuleMetadataProviderTests + { + [TestMethod] + public void GetExponentialDelay_ZeroCount_ShouldGiveInitialDelay() + { + TimeSpan initial = TimeSpan.FromDays(2.5); + TimeSpan max = TimeSpan.FromDays(10); + var delay = BaseModuleMetadataProvider.GetExponentialDelay(initial, 0, max); + + delay.Should().Be(initial); + } + + [TestMethod] + public void GetExponentialDelay_1Count_ShouldGiveDoubleInitialDelay() + { + TimeSpan initial = TimeSpan.FromDays(2.5); + TimeSpan max = TimeSpan.FromDays(10); + var delay = BaseModuleMetadataProvider.GetExponentialDelay(initial, 1, max); + + delay.Should().Be(initial * 2); + } + + [TestMethod] + public void GetExponentialDelay_2Count_ShouldGiveQuadrupleInitialDelay() + { + TimeSpan initial = TimeSpan.FromDays(2.5); + TimeSpan max = TimeSpan.FromDays(10); + var delay = BaseModuleMetadataProvider.GetExponentialDelay(initial, 2, max); + + delay.Should().Be(initial * 4); + } + + [TestMethod] + public void GetExponentialDelay_AboveMaxCount_ShouldGiveMaxDelay() + { + TimeSpan initial = TimeSpan.FromSeconds(1); + TimeSpan max = TimeSpan.FromDays(365); + + TimeSpan exponentiallyGrowingDelay = initial; + int count = 0; + while (exponentiallyGrowingDelay < max * 1000) + { + var delay = BaseModuleMetadataProvider.GetExponentialDelay(initial, count, max); + + if (exponentiallyGrowingDelay < max) + { + delay.Should().Be(exponentiallyGrowingDelay); + } + else + { + delay.Should().Be(max); + } + + delay.Should().BeLessThanOrEqualTo(max); + + ++count; + exponentiallyGrowingDelay *= 2; + } + } + } +} diff --git a/src/Bicep.Core.UnitTests/Registry/Catalog/PrivateAcrModuleMetadataProviderTests.cs b/src/Bicep.Core.UnitTests/Registry/Catalog/PrivateAcrModuleMetadataProviderTests.cs new file mode 100644 index 00000000000..ca578a72437 --- /dev/null +++ b/src/Bicep.Core.UnitTests/Registry/Catalog/PrivateAcrModuleMetadataProviderTests.cs @@ -0,0 +1,409 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO.Abstractions.TestingHelpers; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using Bicep.Core.Configuration; +using Bicep.Core.Registry; +using Bicep.Core.Registry.Catalog; +using Bicep.Core.Registry.Catalog.HttpClients; +using Bicep.Core.Registry.Oci; +using Bicep.Core.UnitTests.Mock; +using Bicep.Core.UnitTests.Mock.Registry; +using Bicep.Core.UnitTests.Utils; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using RichardSzalay.MockHttp; + +namespace Bicep.Core.UnitTests.Registry.Catalog +{ + [TestClass] + public class PrivateAcrModuleMetadataProviderTests //asdfg2 + //asdfg: test after calling to get details, calling to get versions shouldn't require another call to the server + + + { + //asdfg + //private IConfigurationManager ConfigManagerWithModuleAliases(string moduleAliasesJson) + //{ + // var configuration = BicepTestConstants.BuiltInConfiguration.With( + // moduleAliases: RegistryIndexerMocks.ModuleAliases(moduleAliasesJson)); + // return RegistryIndexerMocks.MockConfigurationManager(configuration).Object; + //} + + //private PublicModuleMetadataHttpClient CreateTypedClient() { //asdfg + // var httpClient = MockHttpMessageHandler.ToHttpClient(); + // return new PublicModuleMetadataHttpClient(httpClient); + //} + + + //private static readonly MockHttpMessageHandler MockHttpMessageHandler = new(); + + //[ClassInitialize] + //public static void ClassInitialize(TestContext _) + //{ + // MockHttpMessageHandler + // .When(HttpMethod.Get, "*") + // .Respond("application/json", "asdfg ModuleIndexJson"); + //} + + //asdfg: test after calling to get details, calling to get versions shouldn't require another call to the server + + //[TestMethod] + //public async Task Asdfg() + //{ + // //var moduleName = "module1"; + // //var registryStr = "example.com"; + // //var registryUri = new Uri($"https://{registryStr}"); + // //var repository = $"test/{moduleName}".ToLowerInvariant(); + + // //asdfg + // //var bicepModuleContents = "// hello"; + // //var documentationUri = "https://contoso.com/hep"; + + // //var (clientFactory, blobClients) = RegistryHelper.CreateMockRegistryClients((registryStr, repository)); + + // //var client = new MockContainerRegistryClient(); + + // //var blobClient = blobClients[(registryUri, repository)]; + + // //await RegistryHelper.PublishModuleToRegistryAsync(clientFactory, BicepTestConstants.FileSystem, "modulename", $"br:example.com/test/{moduleName}:v1", bicepModuleContents, publishSource: false, documentationUri); + + // //var manifest = blobClient.Manifests.Single().Value.ToObjectFromJson(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + + + // //// compile and publish modules using throwaway file system + // //var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync(asdfg + // // new MockFileSystem(), + + // // [.. options.PublishedModules.Select(x => (x, "", true))]); + + // var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + // new MockFileSystem(), + // [ + // ("br:registry.contoso.io/test/module1:v1", "param p1 bool", withSource: true), + // ("br:registry.contoso.io/test/module2:v1", "param p2 string", withSource: true), + // ("br:registry.contoso.io/test/module1:v2", "param p12 string", withSource: false), + // ]); + + + // //var clientFactory2 = StrictMock.Of(); + // //clientFactory2.Setup(m => m.CreateAuthenticatedRegistryClient(It.IsAny(), It.IsAny())).Returns(client); + + // //clientFactory2.Setup(m => m.) + + // //var acrManager = new AzureContainerRegistryManager(clientFactory); + + // //var asdfg = await acrManager.GetCatalogAsync(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); + // //client.MockRepositoryNames = ["abc", "def", "bicep/abc", "bicep/def"]; + // //var asdfg1 = acrManager.GetCatalogAsync(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); + + // //var indexer = RegistryIndexerMocks.CreateRegistryIndexer(null, + // // RegistryIndexerMocks.MockPrivateMetadataProvider( + // // "registry.contoso.io", + // // [ + // // ("bicep/abc", "description", "https://contoso.com/hep", [ ("1.0.0", "abc 1.0.0 description", "https://contoso.com/help/abc") ]), + // // ("bicep/def", "description", "https://contoso.com/hep", [ ("1.0.0", "def 1.0.0 description", "https://contoso.com/help/def") ]), + // // ])); + + // //var configuration = BicepTestConstants.BuiltInConfiguration.With( + // // moduleAliases: RegistryIndexerMocks.ModuleAliases( + // // """ + // // { + // // "br": { + // // "contoso": { + // // "registry": "private.contoso.io" + // // } + // // } + // // } + // // """)); + // //var configurationManager = StrictMock.Of(); //asdfg extract + // //configurationManager.Setup(x => x.GetConfiguration(It.IsAny())).Returns(configuration); + + // //var registry = indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); + // //registry.Should().NotBeNull(); + // //indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io").Should().BeSameAs(registry); //asdfg separate test + + // //var modules = await registry.TryGetModulesAsync(); + // //modules.Should().HaveCount(2); + + // //modules.Should().SatisfyRespectively( + // // x => + // // { + // // x.ModuleName.Should().Be("bicep/abc"); + // // (await x.TryGetVersionsAsync()).Should().HaveCount(1); + // // x.TryGetVersionsAsync().Result[0].Should().BeEquivalentTo( + // // new RegistryModuleVersionMetadata("1.0.0", new RegistryMetadataDetails("abc 1.0.0 description", "https://contoso.com/help/abc"))); + // // }, + // // x => x.ModuleName.Should().Be("bicep/def") + // //); + + // var provider = new PrivateAcrModuleMetadataProvider( + // BicepTestConstants.BuiltInConfiguration.Cloud, + // "registry.contoso.io", + // clientFactory); + // var modules = await provider.TryGetModulesAsync(); + // modules.Should().HaveCount(2); + //} + + //[TestMethod] + //public void Asdfg2() + //{ + // var provider = new PrivateAcrModuleMetadataProvider( + // BicepTestConstants.BuiltInConfiguration.Cloud, + // "registry.contoso.io", + // StrictMock.Of().Object); + // //var containerRegistryClientFactory = StrictMock.Of(); + + // //var provider = RegistryIndexerMocks.MockPrivateMetadataProvider(asdfg + // // "registry.contoso.io", + // // [ + // // ("bicep/abc", "description", "https://contoso.com/hep", [ ("1.0.0", "abc 1.0.0 description", "https://contoso.com/help/abc") ]), + // // ("bicep/def", "description", "https://contoso.com/hep", [ ("1.0.0", "def 1.0.0 description", "https://contoso.com/help/def") ]), + // // ]); + + // provider.Registry.Should().Be("registry.contoso.io"); + // ////var configManager = ConfigManagerWithModuleAliases(asdfg + // //// """ + // //// { + // //// "br": { + // //// "contoso": { + // //// "registry": "private.contoso.io" + // //// } + // //// } + // //// } + // //// """); + + // //var registry = indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); + // //registry.Should().NotBeNull(); + // //indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io").Should().BeSameAs(registry); //asdfg separate test + + // //var modules = await registry.TryGetModulesAsync(); + // //modules.Should().HaveCount(2); + //} + + //[TestMethod] + //public async Task Asdfg3() + //{ + // var provider = new PrivateAcrModuleMetadataProvider( + // BicepTestConstants.BuiltInConfiguration.Cloud, + // "registry.contoso.io", + // StrictMock.Of().Object); + // var containerRegistryClientFactory = StrictMock.Of(); + // containerRegistryClientFactory.Setup(x => x.CreateAuthenticatedContainerClient(It.IsAny(), It.IsAny())).Returns(new FakeContainerRegistryClient()); + + // //var provider = RegistryIndexerMocks.MockPrivateMetadataProvider(asdfg + // // "registry.contoso.io", + // // [ + // // ("bicep/abc", "description", "https://contoso.com/hep", [ ("1.0.0", "abc 1.0.0 description", "https://contoso.com/help/abc") ]), + // // ("bicep/def", "description", "https://contoso.com/hep", [ ("1.0.0", "def 1.0.0 description", "https://contoso.com/help/def") ]), + // // ]); + + // var modules = await provider.TryGetModulesAsync(); + // modules.Should().HaveCount(2); + // provider.GetCachedModules().Should().BeEmpty(); + + // ////var configManager = ConfigManagerWithModuleAliases(asdfg + // //// """ + // //// { + // //// "br": { + // //// "contoso": { + // //// "registry": "private.contoso.io" + // //// } + // //// } + // //// } + // //// """); + + // //var registry = indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); + // //registry.Should().NotBeNull(); + // //indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io").Should().BeSameAs(registry); //asdfg separate test + + // //var modules = await registry.TryGetModulesAsync(); + // //modules.Should().HaveCount(2); + //} + + [TestMethod] + public async Task TryGetModulesAsync() + { + FakeContainerRegistryClient containerRegistryClient = new(); + var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + new MockFileSystem(), + containerRegistryClient, + [ + new("br:registry.contoso.io/test/module1:v1", "param p1 bool", WithSource: true), + new("br:registry.contoso.io/test/module2:v1", "param p2 string", WithSource: true), + new("br:registry.contoso.io/test/module1:v2", "param p12 string", WithSource: false), + ]); + + var provider = new PrivateAcrModuleMetadataProvider( + BicepTestConstants.BuiltInConfiguration.Cloud, + "registry.contoso.io", + clientFactory); + var modules = await provider.TryGetModulesAsync(); + + modules.Should().HaveCount(2); + } + + [TestMethod] + public async Task GetCachedModules() + { + var containerClient = new FakeContainerRegistryClient(); + var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + new MockFileSystem(), + containerClient, + [ + new("br:registry.contoso.io/test/module1:v1", "param p1 bool", WithSource: true), + new("br:registry.contoso.io/test/module2:v1", "param p2 string", WithSource : true), + new("br:registry.contoso.io/test/module1:v2", "param p12 string", WithSource: false), + ]); + + var provider = new PrivateAcrModuleMetadataProvider( + BicepTestConstants.BuiltInConfiguration.Cloud, + "registry.contoso.io", + clientFactory); + + provider.GetCachedModules().Should().HaveCount(0); + + var modules = await provider.TryGetModulesAsync(); + modules.Should().HaveCount(2); + + provider.GetCachedModules().Should().HaveCount(2); + provider.GetCachedModules().Should().HaveCount(2); + } + + [TestMethod] + public async Task TryGetModulesAsync_ShouldCacheResult() + { + var containerClient = new FakeContainerRegistryClient(); + var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + new MockFileSystem(), + containerClient, + [ + new("br:registry.contoso.io/test/module1:v1", "param p1 bool", WithSource: true), + new("br:registry.contoso.io/test/module2:v1", "param p2 string", WithSource: true), + new("br:registry.contoso.io/test/module1:v2", "param p12 string", WithSource : false), + ]); + + var provider = new PrivateAcrModuleMetadataProvider( + BicepTestConstants.BuiltInConfiguration.Cloud, + "registry.contoso.io", + clientFactory); + + var modules = await provider.TryGetModulesAsync(); + modules.Should().HaveCount(2); + containerClient.CallsToGetRepositoryNamesAsync.Should().Be(1); + + modules = await provider.TryGetModulesAsync(); + containerClient.CallsToGetRepositoryNamesAsync.Should().Be(1); + } + + [TestMethod] + public async Task GetDetails_ShouldBeCached() + { + var containerClient = new FakeContainerRegistryClient(); + var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + new MockFileSystem(), + containerClient, + [ + new("br:registry.contoso.io/test/module1:v1", "metadata description = 'this is module 1 version 1'\nparam p1 bool", WithSource: true, DocumentationUri: "http://contoso.com/help11"), + new("br:registry.contoso.io/test/module2:v1", "metadata description = 'this is module 2 version 1'\nparam p2 string", WithSource: true, DocumentationUri: "http://contoso.com/help21"), + new("br:registry.contoso.io/test/module1:v2", "metadata description = 'this is module 1 version 2'\nparam p12 string", WithSource: false, DocumentationUri: "http://contoso.com/help12"), + ]); + + var provider = new PrivateAcrModuleMetadataProvider( + BicepTestConstants.BuiltInConfiguration.Cloud, + "registry.contoso.io", + clientFactory); + provider.GetCachedModules().Should().BeEmpty(); + + var module = await provider.TryGetModuleAsync("test/module1"); + module.Should().NotBeNull(); + provider.GetCachedModules().Should().NotBeEmpty(); + + var details = await module!.TryGetDetailsAsync(); + details.Description.Should().Be("this is module 1 version 2"); + details.DocumentationUri.Should().Be("http://contoso.com/help12"); + containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(1); + + // Verify it's cached + var details2 = await module!.TryGetDetailsAsync(); + details.Should().BeSameAs(details2); + containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(1); + + var cached = provider.GetCachedModules(); + provider.GetCachedModules().Should().NotBeEmpty(); + containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(1); + } + + [TestMethod] + public async Task GetVersions_ShouldBeCached() + { + var containerClient = new FakeContainerRegistryClient(); + var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + new MockFileSystem(), + containerClient, + [ + new("br:registry.contoso.io/test/module1:v1", "metadata description = 'this is module 1 version 1'\nparam p1 bool", WithSource: true, DocumentationUri: "http://contoso.com/help11"), + new("br:registry.contoso.io/test/module2:v1", "metadata description = 'this is module 2 version 1'\nparam p2 string", WithSource: true, DocumentationUri: "http://contoso.com/help21"), + new("br:registry.contoso.io/test/module1:v2", "metadata description = 'this is module 1 version 2'\nparam p12 string", WithSource: false, DocumentationUri: "http://contoso.com/help12"), + ]); + + var provider = new PrivateAcrModuleMetadataProvider( + BicepTestConstants.BuiltInConfiguration.Cloud, + "registry.contoso.io", + clientFactory); + + var module = await provider.TryGetModuleAsync("test/module1"); + module.Should().NotBeNull(); + module!.GetCachedVersions().Should().BeEmpty(); + containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(0); + module.GetCachedVersions().Should().BeEmpty(); + + var versions = await module!.TryGetVersionsAsync(); + containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(1); + versions[0].Version.Should().Be("v1"); + module.GetCachedVersions()[0].Version.Should().Be("v1"); + + var versions2 = await module!.TryGetVersionsAsync(); + containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(1); + versions[0].Version.Should().Be("v1"); + module.GetCachedVersions()[0].Version.Should().Be("v1"); + versions.Should().BeEquivalentTo(versions2); + versions[0].Version.Should().BeSameAs(versions2[0].Version); + } + + [TestMethod] + public async Task GetDetails_ShouldAlsoCacheVersions_BecauseGettingModuleDetailsRequiresGettingVersionDetails() + { + var containerClient = new FakeContainerRegistryClient(); + var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + new MockFileSystem(), + containerClient, + [ + new("br:registry.contoso.io/test/module1:v1", "metadata description = 'this is module 1 version 1'\nparam p1 bool", WithSource: true, DocumentationUri: "http://contoso.com/help11"), + new("br:registry.contoso.io/test/module2:v1", "metadata description = 'this is module 2 version 1'\nparam p2 string", WithSource: true, DocumentationUri: "http://contoso.com/help21"), + new("br:registry.contoso.io/test/module1:v2", "metadata description = 'this is module 1 version 2'\nparam p12 string", WithSource: false, DocumentationUri: "http://contoso.com/help12"), + ]); + + var provider = new PrivateAcrModuleMetadataProvider( + BicepTestConstants.BuiltInConfiguration.Cloud, + "registry.contoso.io", + clientFactory); + + var module = await provider.TryGetModuleAsync("test/module1"); + module.Should().NotBeNull(); + module!.GetCachedVersions().Should().BeEmpty(); + + var details = await module!.TryGetDetailsAsync(); + details.Description.Should().Be("this is module 1 version 2"); + containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(1); + + var versions = await module!.TryGetVersionsAsync(); + containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(1); + } + } +} diff --git a/src/Bicep.Core.UnitTests/Registry/PublicRegistry/PublicRegistryModuleMetadataProviderTests.cs b/src/Bicep.Core.UnitTests/Registry/Catalog/PublicModuleMetadataProviderTests.cs similarity index 89% rename from src/Bicep.Core.UnitTests/Registry/PublicRegistry/PublicRegistryModuleMetadataProviderTests.cs rename to src/Bicep.Core.UnitTests/Registry/Catalog/PublicModuleMetadataProviderTests.cs index 5bfcaa5948b..4c04cdbaecd 100644 --- a/src/Bicep.Core.UnitTests/Registry/PublicRegistry/PublicRegistryModuleMetadataProviderTests.cs +++ b/src/Bicep.Core.UnitTests/Registry/Catalog/PublicModuleMetadataProviderTests.cs @@ -1,29 +1,26 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Net.Http; using System.Text; using System.Text.Json; -using Bicep.Core.Extensions; -using Bicep.Core.Registry.PublicRegistry; -using Bicep.Core.UnitTests; -using Bicep.LanguageServer.Providers; +using Bicep.Core.Registry.Catalog; +using Bicep.Core.Registry.Catalog.HttpClients; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; using RichardSzalay.MockHttp; -namespace Bicep.Core.UnitTests.Registry.PublicRegistry +namespace Bicep.Core.UnitTests.Registry.Catalog { [TestClass] - public class PublicRegistryModuleMetadataProviderTests + public class PublicModuleMetadataProviderTests { - private IServiceProvider GetServiceProvider() - { + private PublicModuleMetadataHttpClient CreateTypedClient() { var httpClient = MockHttpMessageHandler.ToHttpClient(); - return new ServiceBuilder().WithRegistration(x => - x.AddSingleton(new PublicRegistryModuleMetadataClient(httpClient)) - ).Build().Construct(); + return new PublicModuleMetadataHttpClient(httpClient); } + private const string ModuleIndexJson = """ [ { @@ -568,31 +565,31 @@ private IServiceProvider GetServiceProvider() ], "properties": { "1.1.1": { - "description": "These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", + "description": "v1.1.1: These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.1.1/modules/lz/sub-vending/README.md" }, "1.1.2": { - "description": "These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", + "description": "v1.1.2: These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.1.2/modules/lz/sub-vending/README.md" }, "1.2.1": { - "description": "These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", + "description": "v1.2.1: These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.2.1/modules/lz/sub-vending/README.md" }, "1.2.2": { - "description": "These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", + "description": "v1.2.2: These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.2.2/modules/lz/sub-vending/README.md" }, "1.3.1": { - "description": "These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", + "description": "v1.3.1: These are the input parameters for the Bicep module: [`main.bicep`](./main.bicep)\n\nThis is the orchestration module that is used and called by a consumer of the module to deploy a Landing Zone Subscription and its associated resources, based on the parameter input values that are provided to it at deployment time.\n\n> For more information and examples please see the [wiki](https://github.com/Azure/bicep-lz-vending/wiki)", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.3.1/modules/lz/sub-vending/README.md" }, "1.4.1": { - "description": "This module is designed to accelerate deployment of landing zones (aka Subscriptions) within an Azure AD Tenant.", + "description": "v1.4.1: This module is designed to accelerate deployment of landing zones (aka Subscriptions) within an Azure AD Tenant.", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.4.1/modules/lz/sub-vending/README.md" }, "1.4.2": { - "description": "This module is designed to accelerate deployment of landing zones (aka Subscriptions) within an Azure AD Tenant.", + "description": "v1.4.2: This module is designed to accelerate deployment of landing zones (aka Subscriptions) within an Azure AD Tenant.", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.4.2/modules/lz/sub-vending/README.md" } } @@ -762,6 +759,7 @@ private IServiceProvider GetServiceProvider() }, { "moduleName": "samples/array-loop", + "$comment": "Tags intentionally out of order", "tags": [ "1.0.1", "1.10.1", @@ -774,11 +772,11 @@ private IServiceProvider GetServiceProvider() "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/samples/array-loop/1.0.1/modules/samples/array-loop/README.md" }, "1.0.2": { - "description": "A sample Bicep registry module demonstrating array iterations.", + "description": "v1.0.1: A sample Bicep registry module demonstrating array iterations.", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/samples/array-loop/1.0.2/modules/samples/array-loop/README.md" }, "1.0.3": { - "description": "A sample Bicep registry module demonstrating array iterations.", + "description": "v1.0.3: A sample Bicep registry module demonstrating array iterations.", "documentationUri": "https://github.com/Azure/bicep-registry-modules/tree/samples/array-loop/1.0.3/modules/samples/array-loop/README.md" } } @@ -1098,64 +1096,6 @@ public static void ClassInitialize(TestContext _) .Respond("application/json", ModuleIndexJson); } - [TestMethod] - public void GetExponentialDelay_ZeroCount_ShouldGiveInitialDelay() - { - TimeSpan initial = TimeSpan.FromDays(2.5); - TimeSpan max = TimeSpan.FromDays(10); - var delay = PublicRegistryModuleMetadataProvider.GetExponentialDelay(initial, 0, max); - - delay.Should().Be(initial); - } - - [TestMethod] - public void GetExponentialDelay_1Count_ShouldGiveDoubleInitialDelay() - { - TimeSpan initial = TimeSpan.FromDays(2.5); - TimeSpan max = TimeSpan.FromDays(10); - var delay = PublicRegistryModuleMetadataProvider.GetExponentialDelay(initial, 1, max); - - delay.Should().Be(initial * 2); - } - - [TestMethod] - public void GetExponentialDelay_2Count_ShouldGiveQuadrupleInitialDelay() - { - TimeSpan initial = TimeSpan.FromDays(2.5); - TimeSpan max = TimeSpan.FromDays(10); - var delay = PublicRegistryModuleMetadataProvider.GetExponentialDelay(initial, 2, max); - - delay.Should().Be(initial * 4); - } - - [TestMethod] - public void GetExponentialDelay_AboveMaxCount_ShouldGiveMaxDelay() - { - TimeSpan initial = TimeSpan.FromSeconds(1); - TimeSpan max = TimeSpan.FromDays(365); - - TimeSpan exponentiallyGrowingDelay = initial; - int count = 0; - while (exponentiallyGrowingDelay < max * 1000) - { - var delay = PublicRegistryModuleMetadataProvider.GetExponentialDelay(initial, count, max); - - if (exponentiallyGrowingDelay < max) - { - delay.Should().Be(exponentiallyGrowingDelay); - } - else - { - delay.Should().Be(max); - } - - delay.Should().BeLessThanOrEqualTo(max); - - ++count; - exponentiallyGrowingDelay *= 2; - } - } - private record ModuleMetadata_Original(string moduleName, List tags); [TestMethod] @@ -1174,50 +1114,50 @@ public void GetModules_ForwardsCompatibleWithOriginalVersion() [TestMethod] public async Task GetModules_Count_SanityCheck() { - PublicRegistryModuleMetadataProvider provider = new(GetServiceProvider()); + PublicModuleMetadataProvider provider = new(CreateTypedClient()); (await provider.TryUpdateCacheAsync()).Should().BeTrue(); - var modules = provider.GetModulesMetadata(); + var modules = await provider.TryGetModulesAsync(); modules.Should().HaveCount(50); } [TestMethod] - public async Task GetModules_OnlyLastTagHasDescription() + public async Task GetModules_IfOnlyLastTagHasDescription() { - PublicRegistryModuleMetadataProvider provider = new(GetServiceProvider()); + PublicModuleMetadataProvider provider = new(CreateTypedClient()); (await provider.TryUpdateCacheAsync()).Should().BeTrue(); - var modules = provider.GetModulesMetadata(); - var m = modules.Should().Contain(m => m.Name == "samples/hello-world") + var modules = await provider.TryGetModulesAsync(); + var m = modules.Should().Contain(m => m.ModuleName == "bicep/samples/hello-world") .Which; - m.Description.Should().Be("A \"שָׁלוֹם עוֹלָם\" sample Bicep registry module"); - m.DocumentationUri.Should().Be("https://github.com/Azure/bicep-registry-modules/tree/samples/hello-world/1.0.4/modules/samples/hello-world/README.md"); + var details = await m.TryGetDetailsAsync(); + details.Description.Should().Be("A \"שָׁלוֹם עוֹלָם\" sample Bicep registry module"); + details.DocumentationUri.Should().Be("https://github.com/Azure/bicep-registry-modules/tree/samples/hello-world/1.0.4/modules/samples/hello-world/README.md"); } [TestMethod] - public async Task GetModules_MultipleTagsHaveDescriptions() + public async Task GetModules_IfMultipleTagsHaveDescriptions() { - PublicRegistryModuleMetadataProvider provider = new(GetServiceProvider()); + PublicModuleMetadataProvider provider = new(CreateTypedClient()); (await provider.TryUpdateCacheAsync()).Should().BeTrue(); - var modules = provider.GetModulesMetadata(); - var m = modules.Should().Contain(m => m.Name == "lz/sub-vending") + var modules = await provider.TryGetModulesAsync(); + var m = modules.Should().Contain(m => m.ModuleName == "bicep/lz/sub-vending") .Which; - m.Description.Should().Be("This module is designed to accelerate deployment of landing zones (aka Subscriptions) within an Azure AD Tenant."); - m.DocumentationUri.Should().Be("https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.4.2/modules/lz/sub-vending/README.md"); + var details = await m.TryGetDetailsAsync(); + details.Description.Should().Be("v1.4.2: This module is designed to accelerate deployment of landing zones (aka Subscriptions) within an Azure AD Tenant."); + details.DocumentationUri.Should().Be("https://github.com/Azure/bicep-registry-modules/tree/lz/sub-vending/1.4.2/modules/lz/sub-vending/README.md"); } [TestMethod] - public async Task GetModuleVerionsMetadata_ByDefault_ReturnsMetadataSortedByVersion() + public async Task GetModuleVersions_SortsBySemver() { - PublicRegistryModuleMetadataProvider provider = new(GetServiceProvider()); - (await provider.TryUpdateCacheAsync()).Should().BeTrue(); - - var versions = provider.GetModuleVersionsMetadata("samples/array-loop").Select(x => x.Version); + PublicModuleMetadataProvider provider = new(CreateTypedClient()); + var versions = await (await provider.TryGetModuleAsync("bicep/samples/array-loop"))!.TryGetVersionsAsync(); - versions.Should().Equal( - "1.10.1", - "1.0.3", - "1.0.2", - "1.0.2-preview", - "1.0.1"); + versions.Select(v => v.Version).Should().Equal( + "1.0.1", + "1.0.2-preview", + "1.0.2", + "1.0.3", + "1.10.1"); } } } diff --git a/src/Bicep.Core.UnitTests/Registry/Catalog/RegistryCatalogTests.cs b/src/Bicep.Core.UnitTests/Registry/Catalog/RegistryCatalogTests.cs new file mode 100644 index 00000000000..0821bc59b0f --- /dev/null +++ b/src/Bicep.Core.UnitTests/Registry/Catalog/RegistryCatalogTests.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO.Abstractions.TestingHelpers; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using Bicep.Core.Configuration; +using Bicep.Core.Registry; +using Bicep.Core.Registry.Catalog; +using Bicep.Core.Registry.Catalog.HttpClients; +using Bicep.Core.Registry.Oci; +using Bicep.Core.UnitTests.Mock; +using Bicep.Core.UnitTests.Mock.Registry; +using Bicep.Core.UnitTests.Utils; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Win32; +using Moq; +using RichardSzalay.MockHttp; + +namespace Bicep.Core.UnitTests.Registry.Catalog +{ + [TestClass] + public class RegistryCatalogTests + { + [TestMethod] + public void GetRegistry_ShouldReturnSameObjectEachTime() + { + var indexer = RegistryCatalogMocks.CreateCatalogWithMocks(null, + RegistryCatalogMocks.MockPrivateMetadataProvider( + "private.contoso.io", + [ + ("bicep/abc", "description", "https://contoso.com/hep", [ ("1.0.0", "abc 1.0.0 description", "https://contoso.com/help/abc") ]), + ("bicep/def", "description", "https://contoso.com/hep", [ ("1.0.0", "def 1.0.0 description", "https://contoso.com/help/def") ]), + ])); + + var registry = indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "private.contoso.io"); + registry.Should().NotBeNull(); + registry.Registry.Should().Be("private.contoso.io"); + indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "private.contoso.io").Should().BeSameAs(registry); + indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "private.contoso.io").Should().BeSameAs(registry); + + var registry2 = indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "mcr.microsoft.com"); + registry2.Should().NotBeNull(); + registry2.Registry.Should().Be("mcr.microsoft.com"); + indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "mcr.microsoft.com").Should().BeSameAs(registry2); + indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "mcr.microsoft.com").Should().BeSameAs(registry2); + + var registry3 = indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "unknown:and:invalid"); + registry3.Should().NotBeNull(); + registry3.Registry.Should().Be("unknown:and:invalid"); + indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "unknown:and:invalid").Should().BeSameAs(registry3); + indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "unknown:and:invalid").Should().BeSameAs(registry3); + + var registry4 = indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "unknown 2"); + registry4.Should().NotBeNull(); + registry4.Registry.Should().Be("unknown 2"); + indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "unknown 2").Should().BeSameAs(registry4); + indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "unknown 2").Should().BeSameAs(registry4); + + registry.Should().NotBeSameAs(registry2); + registry.Should().NotBeSameAs(registry3); + registry.Should().NotBeSameAs(registry4); + registry2.Should().NotBeSameAs(registry3); + registry2.Should().NotBeSameAs(registry4); + registry3.Should().NotBeSameAs(registry4); + } + + [TestMethod] + public void GetRegistry_ForMcrMicrosoftCom_ShouldReturnPublicRegistry() + { + var publicProvider = RegistryCatalogMocks.MockPublicMetadataProvider([]); + var indexer = RegistryCatalogMocks.CreateCatalogWithMocks( + publicProvider, + RegistryCatalogMocks.MockPrivateMetadataProvider( + "private.contoso.io", + [ + ("bicep/abc", "description", "https://contoso.com/hep", [ ("1.0.0", "abc 1.0.0 description", "https://contoso.com/help/abc") ]), + ("bicep/def", "description", "https://contoso.com/hep", [ ("1.0.0", "def 1.0.0 description", "https://contoso.com/help/def") ]), + ])); + + var registry = indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "mcr.microsoft.com"); + registry.Registry.Should().Be("mcr.microsoft.com"); + registry.Should().Be(publicProvider.Object); + + registry.Should().BeAssignableTo(); + } + + [TestMethod] + public void TryGetCachedRegistry() + { + var publicProvider = RegistryCatalogMocks.MockPublicMetadataProvider([]); + var indexer = RegistryCatalogMocks.CreateCatalogWithMocks(publicProvider); + + indexer.TryGetCachedRegistry("mcr.microsoft.com").Should().NotBeNull(); + indexer.TryGetCachedRegistry("private.contoso.io").Should().BeNull(); + + var registry = indexer.GetProviderForRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "private.contoso.io"); + + indexer.TryGetCachedRegistry("mcr.microsoft.com").Should().NotBeNull(); + indexer.TryGetCachedRegistry("private.contoso.io").Should().NotBeNull(); + + registry.Should().BeAssignableTo(); + registry.Should().NotBeAssignableTo(); + } + } +} diff --git a/src/Bicep.Core.UnitTests/Registry/FakeContainerRegistryClient.cs b/src/Bicep.Core.UnitTests/Registry/FakeContainerRegistryClient.cs new file mode 100644 index 00000000000..9ab331e56e9 --- /dev/null +++ b/src/Bicep.Core.UnitTests/Registry/FakeContainerRegistryClient.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Concurrent; +using System.Collections.Immutable; +using System.Reflection; +using Azure; +using Azure.Containers.ContainerRegistry; +using Bicep.Core.Modules; +using Bicep.Core.Registry.Oci; +using Bicep.Core.UnitTests.Mock; +using Microsoft.WindowsAzure.ResourceStack.Common.Extensions; +using Microsoft.WindowsAzure.ResourceStack.Common.Legacy.Table; +using Moq; + +namespace Bicep.Core.UnitTests.Registry +{ + /// + /// Mock OCI registry blob client. This client is intended to represent a single repository within a specific registry Uri. + /// + public class FakeContainerRegistryClient : ContainerRegistryClient + { + public record FakeRepository(string Registry, string Repository, List Tags); + + public FakeContainerRegistryClient() : base() + { + // ensure we call the base parameterless constructor to prevent outgoing calls + } + + public int CallsToGetRepositoryNamesAsync { get; private set; } + public int CallsToGetAllManifestPropertiesAsync { get; private set; } + + public SortedList FakeRepositories { get; } = new(); + + public override AsyncPageable GetRepositoryNamesAsync(CancellationToken cancellationToken = default) //asdfg test with lots and lots + { + CallsToGetRepositoryNamesAsync++; + + var page = Page.FromValues(FakeRepositories.Keys.ToArray(), continuationToken: null, StrictMock.Of().Object); + return AsyncPageable.FromPages([page]); + } + + public override ContainerRepository GetRepository(string repositoryName) + { + var repository = StrictMock.Of(); + repository.Setup(x => x.GetAllManifestPropertiesAsync(It.IsAny(), It.IsAny())) + .Returns((ArtifactManifestOrder order, CancellationToken token) => + { + CallsToGetAllManifestPropertiesAsync++; + + var constructor = typeof(ArtifactManifestProperties).GetConstructor( + BindingFlags.NonPublic | BindingFlags.Instance, + null, + [typeof(string), typeof(DateTimeOffset), typeof(DateTimeOffset)], + null); + + var properties = (ArtifactManifestProperties)constructor!.Invoke(["digest", DateTimeOffset.Now, DateTimeOffset.Now]); + + var tagsField = typeof(ArtifactManifestProperties).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance); + tagsField!.SetValue(properties, FakeRepositories[repositoryName].Tags.ToImmutableArray()); + + return AsyncPageable.FromPages( + new[] { Page.FromValues( + new[] { properties }, null, StrictMock.Of().Object) } + ); + } + ); + return repository.Object; + } + } +} diff --git a/src/Bicep.Core.UnitTests/Registry/MockRegistryBlobClient.cs b/src/Bicep.Core.UnitTests/Registry/MockRegistryBlobClient.cs index 1e61d6981ba..00354eccb1a 100644 --- a/src/Bicep.Core.UnitTests/Registry/MockRegistryBlobClient.cs +++ b/src/Bicep.Core.UnitTests/Registry/MockRegistryBlobClient.cs @@ -14,7 +14,7 @@ namespace Bicep.Core.UnitTests.Registry /// /// Mock OCI registry blob client. This client is intended to represent a single repository within a specific registry Uri. /// - public class MockRegistryBlobClient : ContainerRegistryContentClient + public class MockRegistryBlobClient : ContainerRegistryContentClient //adsfg rename to match base class { public MockRegistryBlobClient() : base() { diff --git a/src/Bicep.Core.UnitTests/Utils/ContainerRegistryClientFactoryExtensions.cs b/src/Bicep.Core.UnitTests/Utils/ContainerRegistryClientFactoryExtensions.cs index 48e81d4265c..178c996db7e 100644 --- a/src/Bicep.Core.UnitTests/Utils/ContainerRegistryClientFactoryExtensions.cs +++ b/src/Bicep.Core.UnitTests/Utils/ContainerRegistryClientFactoryExtensions.cs @@ -4,19 +4,20 @@ using System.Collections.Immutable; using Bicep.Core.Registry; using Bicep.Core.UnitTests.Registry; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.Core.UnitTests.Utils; public class ContainerRegistryClientFactoryExtensions { - public static (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient> blobClientMocks) CreateMockRegistryClients(params (string, string)[] clients) + public static (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient> blobClientMocks, FakeContainerRegistryClient containerRegistryClient) + CreateMockRegistryClients(params RepoDescriptor[] repos) { var containerRegistryFactoryBuilder = new TestContainerRegistryClientFactoryBuilder(); - foreach (var (registryHost, repository) in clients) + foreach (var repo in repos) { - containerRegistryFactoryBuilder.RegisterMockRepositoryBlobClient(registryHost, repository); - + containerRegistryFactoryBuilder.WithRepository(repo); } return containerRegistryFactoryBuilder.Build(); diff --git a/src/Bicep.Core.UnitTests/Utils/ExtensionTestHelper.cs b/src/Bicep.Core.UnitTests/Utils/ExtensionTestHelper.cs index a5e47f44568..2c2b544ebb3 100644 --- a/src/Bicep.Core.UnitTests/Utils/ExtensionTestHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/ExtensionTestHelper.cs @@ -18,7 +18,7 @@ public static ServiceBuilder GetServiceBuilder( string repositoryPath, FeatureProviderOverrides featureOverrides) { - var clientFactory = RegistryHelper.CreateMockRegistryClient(registryHost, repositoryPath); + var clientFactory = RegistryHelper.CreateMockRegistryClient(new(registryHost, repositoryPath, ["tag"])); return new ServiceBuilder() .WithFeatureOverrides(featureOverrides) diff --git a/src/Bicep.Core.UnitTests/Utils/OciRegistryHelper.cs b/src/Bicep.Core.UnitTests/Utils/OciRegistryHelper.cs index fa9e71949b9..45e6c1cbe9b 100644 --- a/src/Bicep.Core.UnitTests/Utils/OciRegistryHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/OciRegistryHelper.cs @@ -7,7 +7,7 @@ using Bicep.Core.Modules; using Bicep.Core.Registry; using Bicep.Core.Registry.Oci; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.UnitTests.Mock; using Bicep.Core.UnitTests.Registry; using Bicep.IO.Abstraction; @@ -83,7 +83,7 @@ public static (OciArtifactRegistry, MockRegistryBlobClient) CreateModuleRegistry var blobClient = new MockRegistryBlobClient(); var clientFactory = StrictMock.Of(); clientFactory - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(blobClient); var registry = new OciArtifactRegistry( @@ -91,7 +91,7 @@ public static (OciArtifactRegistry, MockRegistryBlobClient) CreateModuleRegistry clientFactory.Object, featureProvider, BicepTestConstants.BuiltInConfiguration, - StrictMock.Of().Object, + StrictMock.Of().Object, parentModuleUri); return (registry, blobClient); @@ -102,7 +102,7 @@ public static (OciArtifactRegistry, MockRegistryBlobClient) CreateModuleRegistry var client = new MockRegistryBlobClient(); var clientFactory = StrictMock.Of(); - clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); + clientFactory.Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), registryUri, repository)).Returns(client); var containerRegistryManager = new AzureContainerRegistryManager(clientFactory.Object); var configurationManager = new ConfigurationManager(BicepTestConstants.FileExplorer); @@ -114,7 +114,7 @@ public static (OciArtifactRegistry, MockRegistryBlobClient) CreateModuleRegistry var moduleReference = CreateModuleReference(registry, repository, "v1", null); await containerRegistryManager.PushArtifactAsync( - configuration: configuration, + cloud: configuration.Cloud, artifactReference: moduleReference, mediaType: mediaType, artifactType: artifactType, diff --git a/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs b/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs index 89424cbed31..4dc39bcf25f 100644 --- a/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs @@ -3,7 +3,9 @@ using System.Collections.Immutable; using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; using System.Linq; +using Azure.Containers.ContainerRegistry; using Bicep.Core.Configuration; using Bicep.Core.Diagnostics; using Bicep.Core.Extensions; @@ -11,45 +13,114 @@ using Bicep.Core.FileSystem; using Bicep.Core.Registry; using Bicep.Core.Registry.Extensions; +using Bicep.Core.Registry.Oci; using Bicep.Core.SourceCode; using Bicep.Core.UnitTests.Extensions; using Bicep.Core.UnitTests.Features; using Bicep.Core.UnitTests.Registry; using Bicep.IO.FileSystem; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; +using Microsoft.WindowsAzure.ResourceStack.Common.Extensions; +using static Bicep.Core.UnitTests.Registry.FakeContainerRegistryClient; namespace Bicep.Core.UnitTests.Utils; public static class RegistryHelper { - public static IContainerRegistryClientFactory CreateMockRegistryClient(string registry, string repository) + public record class RepoDescriptor( + string Registry, // e.g. "registry.contoso.io" + string Repository, // e.g. "test/module1" + List Tags) + { + public RepoDescriptor( + string Registry, // e.g. "registry.contoso.io" + string Repository, // e.g. "test/module1" + IEnumerable Tags) : this(Registry, Repository, ToTagDescriptors(Tags)) { } + } + + public record RepoTagDescriptor( + string Tag, + string? Description = null, + string? DocumentationUri = null + ); + + public record class ModuleToPublish( + string PublishTarget, // e.g. "br:registry.contoso.io/test/module1:v1" + string BicepSource, + bool WithSource = false, // whether to publish the source with the module + string? DocumentationUri = null) + { + public static string ToTarget(string registry, string repo, string tag) => $"br:{registry}/{repo}:{tag}"; + + private string TargetWithoutScheme + { + get + { + PublishTarget.Should().StartWith("br:"); + return PublishTarget.Substring("br:".Length); + } + } + + private IArtifactAddressComponents ParsedTarget + { + get + { + if (OciArtifactReference.TryParseFullyQualifiedComponents(TargetWithoutScheme).IsSuccess(out var parsedTarget, out var errorBuilder)) + { + return parsedTarget; + } + else + { + throw new InvalidOperationException($"Failed to parse target '{errorBuilder(DiagnosticBuilder.ForPosition(new(0, 0))).Message}'."); + } + } + } + + public string Registry => ParsedTarget.Registry; + public string Repository => ParsedTarget.Repository; + public string Tag => ParsedTarget.Tag!; + public string ModuleName => Repository.Split('/').Last(); + } + + public static IContainerRegistryClientFactory CreateMockRegistryClient(RepoDescriptor repo) { return new TestContainerRegistryClientFactoryBuilder() - .RegisterMockRepositoryBlobClient(registry, repository) + .WithRepository(repo) .Build().clientFactory; } - public static (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient> blobClientMocks) CreateMockRegistryClients(params (string, string)[] clients) + public static + (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient>, FakeContainerRegistryClient containerRegistryClient/*asdfg don't return?*/) + CreateMockRegistryClients(params RepoDescriptor[] clients) + { + return CreateMockRegistryClients(new FakeContainerRegistryClient(), clients); + } + + public static + /* create type */ (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient>, FakeContainerRegistryClient containerRegistryClient/*asdfg don't return?*/) + CreateMockRegistryClients( + FakeContainerRegistryClient containerRegistryClient, + params RepoDescriptor[] repos) { var containerRegistryFactoryBuilder = new TestContainerRegistryClientFactoryBuilder(); - foreach (var (registryHost, repository) in clients) + containerRegistryFactoryBuilder.WithFakeContainerRegistryClient(containerRegistryClient); + + var modules = DescriptorsToModulesToPublish(repos); + + foreach (var repo in repos) { - containerRegistryFactoryBuilder.RegisterMockRepositoryBlobClient(registryHost, repository); + containerRegistryFactoryBuilder.WithRepository(repo); } return containerRegistryFactoryBuilder.Build(); } - // Example target: br:mockregistry.io/test/module1:v1 public static async Task PublishModuleToRegistryAsync( IContainerRegistryClientFactory clientFactory, IFileSystem fileSystem, - string moduleName, - string target, - string moduleSource, - bool publishSource, - string? documentationUri = null) + ModuleToPublish module) { var fileExplorer = new FileSystemFileExplorer(fileSystem); var configurationManager = new ConfigurationManager(fileExplorer); @@ -64,73 +135,58 @@ public static async Task PublishModuleToRegistryAsync( var dispatcher = services.Build().Construct(); - var targetReference = dispatcher.TryGetArtifactReference(ArtifactType.Module, target, RandomFileUri()).IsSuccess(out var @ref) ? @ref - : throw new InvalidOperationException($"Module '{moduleName}' has an invalid target reference '{target}'. Specify a reference to an OCI artifact."); + var targetReference = dispatcher.TryGetArtifactReference(ArtifactType.Module, module.PublishTarget, RandomFileUri()).IsSuccess(out var @ref) ? @ref + : throw new InvalidOperationException($"Module '{module.ModuleName}' has an invalid target reference '{module.PublishTarget}'. Specify a reference to an OCI artifact."); - var result = await CompilationHelper.RestoreAndCompile(services, moduleSource); + var result = await CompilationHelper.RestoreAndCompile(services, module.BicepSource); if (result.Template is null) { - throw new InvalidOperationException($"Module {moduleName} failed to produce a template."); + throw new InvalidOperationException($"Module {module.ModuleName} failed to produce a template."); } var features = featureProviderFactory.GetFeatureProvider(result.BicepFile.Uri); - BinaryData? sourcesStream = publishSource ? BinaryData.FromStream(SourceArchive.PackSourcesIntoStream(dispatcher, result.Compilation.SourceFileGrouping, features.CacheRootDirectory)) : null; - await dispatcher.PublishModule(targetReference, BinaryData.FromString(result.Template.ToString()), sourcesStream, documentationUri); - } - - // Example target: br:mockregistry.io/test/module1:v1 - // Module name is automatically extracted from target (in this case, "module1") - public static async Task PublishModuleToRegistryAsync(IContainerRegistryClientFactory clientFactory, IFileSystem fileSystem, string target, string source, bool withSource) - { - await PublishModuleToRegistryAsync( - clientFactory, - fileSystem, - target.Substring(target.LastIndexOf('/')), - target, - source, - publishSource: withSource); + BinaryData? sourcesStream = module.WithSource ? BinaryData.FromStream(SourceArchive.PackSourcesIntoStream(dispatcher, result.Compilation.SourceFileGrouping, features.CacheRootDirectory)) : null; + await dispatcher.PublishModule(targetReference, BinaryData.FromString(result.Template.ToString()), sourcesStream, module.DocumentationUri); } // Creates a new registry client factory and publishes the specified modules to the registry. // Example usage: - // var clientFactory = await PublishModules([ - // ("br:mockregistry.io/test/module1:v1", "param p1 bool", withSource: true), - // ("br:mockregistry.io/test/module2:v1", "param p2 string", withSource: true), - // ("br:mockregistry.io/test/module1:v2", "param p12 string", withSource: false), - // ]); + // var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + // new MockFileSystem(), + // [ + // new("br:mockregistry.io/test/module1:v1", "param p1 bool", WithSource: true), + // new("br:mockregistry.io/test/module2:v1", "param p2 string", WithSource: true), + // new("br:mockregistry.io/test/module1:v2", "param p12 string"), + // ]); public static async Task CreateMockRegistryClientWithPublishedModulesAsync( IFileSystem fileSystem, - params (string target, string source, bool withSource)[] modules) + FakeContainerRegistryClient containerRegistryClient, + params ModuleToPublish[] modules + ) { - var repos = new List<(string registry, string repo)>(); - - foreach (var module in modules) - { - var (registry, repo) = module.target.ExtractRegexGroups( - "^br:(?.+?)/(?.+?)[:@](?.+?)$", - ["registry", "repo"]); + var repos = ModulesToPublishToDescriptors(modules); - if (!repos.Contains((registry, repo))) - { - repos.Add((registry, repo)); - } - } - - var clientFactory = CreateMockRegistryClients([.. repos]).factoryMock; + var clientFactory = CreateMockRegistryClients(containerRegistryClient, repos).factoryMock; foreach (var module in modules) { await PublishModuleToRegistryAsync( clientFactory, fileSystem, - module.target, - module.source, - module.withSource); + module); } return clientFactory; } + public static async Task CreateMockRegistryClientWithPublishedModulesAsync( + IFileSystem fileSystem, + params ModuleToPublish[] modules + ) + { + return await CreateMockRegistryClientWithPublishedModulesAsync(fileSystem, new FakeContainerRegistryClient(), modules); + } + public static async Task PublishExtensionToRegistryAsync(IDependencyHelper services, string pathToIndexJson, string target) { var fileSystem = services.Construct(); @@ -160,6 +216,44 @@ public static async Task PublishExtensionToRegistryAsync(IDependencyHelper servi await dispatcher.PublishExtension(targetReference, new(tgzData, false, [])); } + private static List ToTagDescriptors(IEnumerable tags) + { + return [.. tags.Select(tag => new RepoTagDescriptor(tag))]; + } + + private static ModuleToPublish[] DescriptorsToModulesToPublish(IEnumerable descriptors, bool withSource = false) + { + return [.. descriptors.SelectMany( + descriptor => descriptor.Tags.Select( + tag => new ModuleToPublish( + ModuleToPublish.ToTarget(descriptor.Registry, descriptor.Repository, tag.Tag), + BicepSource: "// bicep source", + WithSource: withSource, + DocumentationUri: tag.DocumentationUri) + ) + )]; + } + + private static RepoDescriptor[] ModulesToPublishToDescriptors(IEnumerable modules) + { + var descriptors = new List(); + + foreach (var module in modules) + { + var found = descriptors.SingleOrDefault(d => d.Registry == module.Registry && d.Repository == module.Repository); + if (found is { }) + { + found.Tags.Add(new RepoTagDescriptor(module.Tag)); + } + else + { + descriptors.Add(new(module.Registry, module.Repository, [.. ToTagDescriptors([module.Tag])])); + } + } + + return [.. descriptors]; + } + private static Uri RandomFileUri() => PathHelper.FilePathToFileUrl(Path.GetTempFileName()); public static async Task PublishAzExtension(IDependencyHelper services, string pathToIndexJson) @@ -176,11 +270,15 @@ public static async Task PublishMsGraphExtension(IDependencyHelper services, str } public static IContainerRegistryClientFactory CreateOciClientForAzExtension() - => CreateMockRegistryClients((LanguageConstants.BicepPublicMcrRegistry, $"bicep/extensions/az")).factoryMock; + => CreateMockRegistryClients(new RepoDescriptor( + LanguageConstants.BicepPublicMcrRegistry, + "bicep/extensions/az", + new List { new("tag") } + )).factoryMock; public static IContainerRegistryClientFactory CreateOciClientForMsGraphExtension() => CreateMockRegistryClients( - (LanguageConstants.BicepPublicMcrRegistry, $"bicep/extensions/microsoftgraph/beta"), - (LanguageConstants.BicepPublicMcrRegistry, $"bicep/extensions/microsoftgraph/v1") + new RepoDescriptor(LanguageConstants.BicepPublicMcrRegistry, $"bicep/extensions/microsoftgraph/beta", ["tag"]), + new RepoDescriptor(LanguageConstants.BicepPublicMcrRegistry, $"bicep/extensions/microsoftgraph/v1", ["tag"]) ).factoryMock; } diff --git a/src/Bicep.Core.UnitTests/Utils/ServiceBuilderExtensions.cs b/src/Bicep.Core.UnitTests/Utils/ServiceBuilderExtensions.cs index 5d46e8869bd..243a08833fe 100644 --- a/src/Bicep.Core.UnitTests/Utils/ServiceBuilderExtensions.cs +++ b/src/Bicep.Core.UnitTests/Utils/ServiceBuilderExtensions.cs @@ -7,7 +7,7 @@ using Bicep.Core.Features; using Bicep.Core.FileSystem; using Bicep.Core.Registry; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem.Providers; diff --git a/src/Bicep.Core.UnitTests/Utils/TestContainerRegistryClientFactoryBuilder.cs b/src/Bicep.Core.UnitTests/Utils/TestContainerRegistryClientFactoryBuilder.cs index dae71d9377a..259c77238ba 100644 --- a/src/Bicep.Core.UnitTests/Utils/TestContainerRegistryClientFactoryBuilder.cs +++ b/src/Bicep.Core.UnitTests/Utils/TestContainerRegistryClientFactoryBuilder.cs @@ -2,60 +2,83 @@ // Licensed under the MIT License. using System.Collections.Immutable; +using Azure.Containers.ContainerRegistry; using Bicep.Core.Configuration; using Bicep.Core.Registry; using Bicep.Core.UnitTests.Mock; using Bicep.Core.UnitTests.Registry; +using FluentAssertions; using Moq; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.Core.UnitTests.Utils { public class TestContainerRegistryClientFactoryBuilder { - private readonly ImmutableDictionary<(Uri registryUri, string repository), MockRegistryBlobClient>.Builder clientsBuilder = ImmutableDictionary.CreateBuilder<(Uri registryUri, string repository), MockRegistryBlobClient>(); + private readonly ImmutableDictionary<(Uri registryUri, string repository), MockRegistryBlobClient>.Builder blobClientsBuilder = ImmutableDictionary.CreateBuilder<(Uri registryUri, string repository), MockRegistryBlobClient>(); + private FakeContainerRegistryClient containerClient = new(); - public TestContainerRegistryClientFactoryBuilder RegisterMockRepositoryBlobClient(string registryHost, string repository) + public TestContainerRegistryClientFactoryBuilder WithRepository(RepoDescriptor repo) { - clientsBuilder.TryAdd((new Uri($"https://{registryHost}"), repository), new MockRegistryBlobClient()); + blobClientsBuilder.TryAdd((new Uri($"https://{repo.Registry}"), repo.Repository), new MockRegistryBlobClient()); + + if (!containerClient.FakeRepositories.ContainsKey(repo.Repository)) + { + containerClient.FakeRepositories.Add(repo.Repository, new(repo.Registry, repo.Repository, [.. repo.Tags.Select(t => t.Tag)])); + } return this; } + public TestContainerRegistryClientFactoryBuilder WithRepository(RepoDescriptor repo, MockRegistryBlobClient client) //asdfg combine with above + { + blobClientsBuilder.TryAdd((new Uri($"https://{repo.Registry}"), repo.Repository), client); - public void RegisterMockRepositoryBlobClient(string registryHost, string repository, MockRegistryBlobClient client) + if (!containerClient.FakeRepositories.ContainsKey(repo.Repository)) + { + containerClient.FakeRepositories.Add(repo.Repository, new(repo.Registry, repo.Repository, [.. repo.Tags.Select(t => t.Tag)])); + } + + return this; + } + + public TestContainerRegistryClientFactoryBuilder WithFakeContainerRegistryClient(FakeContainerRegistryClient containerRegistryClient) { - clientsBuilder.TryAdd((new Uri($"https://{registryHost}"), repository), client); + this.containerClient.FakeRepositories.Should().BeEmpty("Must set up ContainerRegistryClient before adding repos"); + this.containerClient = containerRegistryClient; + return this; } - public (IContainerRegistryClientFactory clientFactory, ImmutableDictionary<(Uri, string), MockRegistryBlobClient> blobClientMocks) Build() + public (IContainerRegistryClientFactory clientFactory, ImmutableDictionary<(Uri, string), MockRegistryBlobClient> blobClientMocks, FakeContainerRegistryClient containerRegistryClient) Build() { - var repoToClient = clientsBuilder.ToImmutable(); + var blobClientsByRepository = blobClientsBuilder.ToImmutable(); + var clientFactory = StrictMock.Of(); clientFactory - .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((_, registryUri, repository) => + .Setup(m => m.CreateAuthenticatedBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((_, registryUri, repository) => GetBlobClient(registryUri, repository)); + clientFactory + .Setup(m => m.CreateAnonymousBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((_, registryUri, repository) => GetBlobClient(registryUri, repository)); + + clientFactory + .Setup(m => m.CreateAuthenticatedContainerClient(It.IsAny(), It.IsAny())) + .Returns((_, registryUri) => containerClient); + clientFactory + .Setup(m => m.CreateAuthenticatedContainerClient(It.IsAny(), It.IsAny())) + .Returns((_, registryUri) => containerClient); + + return (clientFactory.Object, blobClientsByRepository, containerClient); + + MockRegistryBlobClient GetBlobClient(Uri registryUri, string repository) { - if (repoToClient.TryGetValue((registryUri, repository), out var client)) + if (blobClientsByRepository.TryGetValue((registryUri, repository), out var client)) { return client; } - throw new InvalidOperationException($"No mock authenticated client was registered for Uri '{registryUri}' and repository '{repository}'."); - }); - - clientFactory - .Setup(m => m.CreateAnonymousBlobClient(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((_, registryUri, repository) => - { - if (repoToClient.TryGetValue((registryUri, repository), out var client)) - { - return client; - } - - throw new InvalidOperationException($"No mock anonymous client was registered for Uri '{registryUri}' and repository '{repository}'."); - }); - - return (clientFactory.Object, repoToClient); + throw new InvalidOperationException($"No mock blob client was registered for Uri '{registryUri}' and repository '{repository}'."); + } } } } diff --git a/src/Bicep.Core.UnitTests/packages.lock.json b/src/Bicep.Core.UnitTests/packages.lock.json index 466b555d000..27f644ff4d9 100644 --- a/src/Bicep.Core.UnitTests/packages.lock.json +++ b/src/Bicep.Core.UnitTests/packages.lock.json @@ -653,8 +653,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -736,28 +736,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1310,11 +1307,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1524,6 +1521,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1579,11 +1577,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1986,11 +1984,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2062,11 +2060,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2469,11 +2467,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2545,11 +2543,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2952,11 +2950,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3028,11 +3026,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3435,11 +3433,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3511,11 +3509,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3918,11 +3916,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3994,11 +3992,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4302,11 +4300,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4378,11 +4376,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4686,11 +4684,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.Core/Analyzers/Linter/LinterAnalyzer.cs b/src/Bicep.Core/Analyzers/Linter/LinterAnalyzer.cs index e783514742e..72e87e7e8df 100644 --- a/src/Bicep.Core/Analyzers/Linter/LinterAnalyzer.cs +++ b/src/Bicep.Core/Analyzers/Linter/LinterAnalyzer.cs @@ -7,7 +7,7 @@ using Bicep.Core.Configuration; using Bicep.Core.Diagnostics; using Bicep.Core.Parsing; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics; using Microsoft.Extensions.DependencyInjection; diff --git a/src/Bicep.Core/Analyzers/Linter/Rules/UseRecentModuleVersionsRule.cs b/src/Bicep.Core/Analyzers/Linter/Rules/UseRecentModuleVersionsRule.cs index def3a083fc1..f9b62279e15 100644 --- a/src/Bicep.Core/Analyzers/Linter/Rules/UseRecentModuleVersionsRule.cs +++ b/src/Bicep.Core/Analyzers/Linter/Rules/UseRecentModuleVersionsRule.cs @@ -10,7 +10,7 @@ using Bicep.Core.Navigation; using Bicep.Core.Parsing; using Bicep.Core.Registry.Oci; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Resources; using Bicep.Core.Semantics; using Bicep.Core.Syntax; @@ -22,6 +22,7 @@ using Microsoft.WindowsAzure.ResourceStack.Common.Extensions; using Semver; using Semver.Comparers; +using System.Collections.Immutable; namespace Bicep.Core.Analyzers.Linter.Rules { @@ -67,7 +68,7 @@ override public IEnumerable AnalyzeInternal(SemanticModel model, IS private static IEnumerable GetFailures(SemanticModel model, IServiceProvider serviceProvider, DiagnosticLevel diagnosticLevel) { - var publicRegistryModuleMetadataProvider = serviceProvider.GetRequiredService(); + var publicModuleMetadataProvider = serviceProvider.GetRequiredService(); var hasShownDownloadWarning = false; foreach (var (syntax, artifactResolutionInfo) in model.SourceFileGrouping.ArtifactLookup @@ -82,12 +83,12 @@ private static IEnumerable GetFailures(SemanticModel model, IServicePro && ociReference.Registry.Equals(LanguageConstants.BicepPublicMcrRegistry, StringComparison.Ordinal) && ociReference.Tag is string tag) { - if (TryGetBicepModuleName(ociReference) is not string publicModulePath) + if (TryRemoveBicepModuleNamePrefix(ociReference) is not string publicModulePath) { continue; } - if (publicRegistryModuleMetadataProvider.DownloadError is string downloadError) + if (publicModuleMetadataProvider.DownloadError is string downloadError) { if (!hasShownDownloadWarning) { @@ -96,7 +97,7 @@ private static IEnumerable GetFailures(SemanticModel model, IServicePro } continue; } - else if (!publicRegistryModuleMetadataProvider.IsCached) + else if (!publicModuleMetadataProvider.IsCached) { if (!hasShownDownloadWarning) { @@ -106,7 +107,7 @@ private static IEnumerable GetFailures(SemanticModel model, IServicePro continue; } - foreach (var failure in AnalyzeBicepModule(publicRegistryModuleMetadataProvider, moduleSyntax, errorSpan, tag, publicModulePath)) + foreach (var failure in AnalyzeBicepModule(publicModuleMetadataProvider, moduleSyntax, errorSpan, tag, publicModulePath)) { yield return failure; } @@ -116,11 +117,19 @@ private static IEnumerable GetFailures(SemanticModel model, IServicePro yield break; } - private static IEnumerable AnalyzeBicepModule(IPublicRegistryModuleMetadataProvider publicRegistryModuleMetadataProvider, ModuleDeclarationSyntax moduleSyntax, TextSpan errorSpan, string tag, string publicModulePath) + private static IEnumerable AnalyzeBicepModule(IPublicModuleMetadataProvider publicModuleMetadataProvider, ModuleDeclarationSyntax moduleSyntax, TextSpan errorSpan, string tag, string publicModulePath) { - var availableVersions = publicRegistryModuleMetadataProvider.GetModuleVersionsMetadata(publicModulePath) + // NOTE: We don't want linter tests to download anything during analysis. So metadata is loaded + // and cached during module restore. So don't use the Get*Async methods of IPublicModuleMetadataProvider, + // just the GetCached* methods + var fullModuleName = $"{LanguageConstants.BicepPublicMcrPathPrefix}{publicModulePath}"; + var availableVersions = publicModuleMetadataProvider.GetCachedModules() + .FirstOrDefault(m => m.ModuleName.EqualsOrdinally(fullModuleName)) + ?.GetCachedVersions() .Select(v => v.Version) - .ToArray(); + .ToArray() + ?? []; + if (availableVersions.Length == 0) { // If the module doesn't exist, we assume the compiler will flag as an error, no need for us to show anything in the linter. Or else @@ -144,19 +153,15 @@ private static IEnumerable AnalyzeBicepModule(IPublicRegistryModuleMeta } } - private static string? TryGetBicepModuleName(IOciArtifactReference ociReference) + private static string? TryRemoveBicepModuleNamePrefix(IOciArtifactReference ociReference) { - // IPublicRegistryModuleMetadataProvider does not return the "bicep/" that prefixes all - // public registry module paths (it's embedded in the default "bicep" alias path), - // so we need to remove it here. - const string bicepPrefix = "bicep/"; var repoPath = ociReference.Repository; - if (!repoPath.StartsWith("bicep/", StringComparison.Ordinal)) + if (!repoPath.StartsWith(LanguageConstants.BicepPublicMcrPathPrefix, StringComparison.Ordinal)) { return null; } - return repoPath.Substring(bicepPrefix.Length); + return repoPath.Substring(LanguageConstants.BicepPublicMcrPathPrefix.Length); } public static string[] GetMoreRecentModuleVersions(string[] availableVersions, string modulePath, string referencedVersion) @@ -176,10 +181,9 @@ public static string[] GetMoreRecentModuleVersions(string[] availableVersions, s var availableParsedVersions = availableVersions .Select(v => (version: v, semVersion: SemVersion.Parse(v, SemVersionStyles.Strict))); - return availableParsedVersions.Where(v => v.semVersion.ComparePrecedenceTo(requestedSemver) > 0) + return [.. availableParsedVersions.Where(v => v.semVersion.ComparePrecedenceTo(requestedSemver) > 0) .OrderByDescending(v => v.semVersion, SemVersion.PrecedenceComparer) - .Select(v => v.version) - .ToArray(); + .Select(v => v.version)]; } // Find the portion of the module/path:version string that corresponds to the module version, diff --git a/src/Bicep.Core/Bicep.Core.csproj b/src/Bicep.Core/Bicep.Core.csproj index cb6c177d286..7cbfea98474 100644 --- a/src/Bicep.Core/Bicep.Core.csproj +++ b/src/Bicep.Core/Bicep.Core.csproj @@ -50,6 +50,7 @@ + diff --git a/src/Bicep.Core/Configuration/AnalyzersConfigurationExtensions.cs b/src/Bicep.Core/Configuration/AnalyzersConfigurationExtensions.cs index 6cbedf11b35..0ff140eb0f0 100644 --- a/src/Bicep.Core/Configuration/AnalyzersConfigurationExtensions.cs +++ b/src/Bicep.Core/Configuration/AnalyzersConfigurationExtensions.cs @@ -57,5 +57,19 @@ public static RootConfiguration WithAnalyzersDisabled(this RootConfiguration cur public static RootConfiguration WithAllAnalyzers(this RootConfiguration current) => current.WithAnalyzersConfiguration(current.Analyzers.WithAllAnalyzers()); + + public static RootConfiguration WithCloudConfiguration(this RootConfiguration current, CloudConfiguration cloudConfiguration) => + new( + cloudConfiguration, + current.ModuleAliases, + current.Extensions, + current.ImplicitExtensions, + current.Analyzers, + current.CacheRootDirectory, + current.ExperimentalFeaturesEnabled, + current.Formatting, + current.ConfigFileUri, + current.Diagnostics); + } } diff --git a/src/Bicep.Core/LanguageConstants.cs b/src/Bicep.Core/LanguageConstants.cs index ce97d096ee1..34ec7ccf7ec 100644 --- a/src/Bicep.Core/LanguageConstants.cs +++ b/src/Bicep.Core/LanguageConstants.cs @@ -36,6 +36,7 @@ public static class LanguageConstants public const string ArmTemplateFileExtension = ".arm"; public const string BicepPublicMcrRegistry = "mcr.microsoft.com"; + public const string BicepPublicMcrPathPrefix = "bicep/"; // All modules in the public bicep registry start with this prefix public const int MaxParameterCount = 256; public const int MaxIdentifierLength = 255; @@ -102,8 +103,7 @@ public static class LanguageConstants public static readonly ImmutableSortedSet DeclarationKeywords = ImmutableSortedSet.Create( StringComparer.Ordinal, - new[] - { + [ AssertKeyword, ImportKeyword, MetadataKeyword, @@ -113,7 +113,7 @@ public static class LanguageConstants OutputKeyword, ModuleKeyword, TypeKeyword - }); + ]); public static readonly ImmutableSortedSet ContextualKeywords = DeclarationKeywords .Add(TargetScopeKeyword) @@ -132,8 +132,6 @@ public static class LanguageConstants public const string ListFunctionPrefix = "list"; - public const string McrRepositoryPrefix = "bicep/"; - public static readonly ImmutableDictionary NonContextualKeywords = new Dictionary(StringComparer.Ordinal) { [TrueKeyword] = TokenType.TrueKeyword, diff --git a/src/Bicep.Core/Registry/AzureContainerRegistryManager.cs b/src/Bicep.Core/Registry/AzureContainerRegistryManager.cs index 08c81520855..17d2aa9b638 100644 --- a/src/Bicep.Core/Registry/AzureContainerRegistryManager.cs +++ b/src/Bicep.Core/Registry/AzureContainerRegistryManager.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Net; using Azure; using Azure.Containers.ContainerRegistry; using Azure.Identity; @@ -34,27 +35,122 @@ public AzureContainerRegistryManager(IContainerRegistryClientFactory clientFacto this.clientFactory = clientFactory; } + public async Task GetRepositoryNamesAsync( + CloudConfiguration cloud, + string registry, + int maxResults) + { + var registryUri = GetRegistryUri(registry); + + // Note: This won't work for MCR + static async Task GetCatalogAsync(ContainerRegistryClient client, int maxResults) + { + List catalog = []; + + await foreach (var repository in client.GetRepositoryNamesAsync()) + { + if (catalog.Count >= maxResults) + { + Trace.WriteLine($"Stopping catalog enumeration after reaching {maxResults} repositories."); + break; + } + + catalog.Add(repository); + } + + return [.. catalog]; + } + + try + { + // Try authenticated client first. + Trace.WriteLine($"Attempt to list catalog for registry {registryUri} using authentication."); + return await GetCatalogInternalAsync(anonymousAccess: false); + } + catch (RequestFailedException exception) when (exception.Status == 401 || exception.Status == 403) + { + // Fall back to anonymous client. + Trace.WriteLine($"Authenticated attempt to list catalog for registry {registryUri} failed, received code {exception.Status}. Falling back to anonymous."); + return await GetCatalogInternalAsync(anonymousAccess: true); + } + catch (CredentialUnavailableException) + { + // Fall back to anonymous client. + Trace.WriteLine($"Authenticated attempt to pull catalog for registry {registryUri} failed due to missing login step. Falling back to anonymous."); + return await GetCatalogInternalAsync(anonymousAccess: true); + } + + async Task GetCatalogInternalAsync(bool anonymousAccess) + { + var client = CreateContainerClient(cloud, registryUri, anonymousAccess); + return await GetCatalogAsync(client, maxResults); + } + } + + public async Task GetRepositoryTagsAsync( + CloudConfiguration cloud, + string registry, + string repository) + { + var registryUri = GetRegistryUri(registry); + + try + { + // Try authenticated client first. + Trace.WriteLine($"Attempting to list repository tags for module {registryUri}/{repository} using authentication."); + return await GetTagsInternalAsync(anonymousAccess: false); + } + catch (RequestFailedException exception) when (exception.Status == 401 || exception.Status == 403) + { + // Fall back to anonymous client. + Trace.WriteLine($"Authenticated attempt to list repository tags for module {registryUri}/{repository} failed, received code {exception.Status}. Falling back to anonymous."); + return await GetTagsInternalAsync(anonymousAccess: true); + } + catch (CredentialUnavailableException) + { + // Fall back to anonymous client. + Trace.WriteLine($"Authenticated attempt to list repository tags for module {registryUri}/{repository} failed due to missing login step. Falling back to anonymous."); + return await GetTagsInternalAsync(anonymousAccess: true); + } + + async Task GetTagsInternalAsync(bool anonymousAccess) + { + var client = CreateContainerClient(cloud, registryUri, anonymousAccess); + + var tags = new List(); + await foreach (var manifestProps in client.GetRepository(repository).GetAllManifestPropertiesAsync()) + { + foreach (var tag in manifestProps.Tags) //asdfg? - don't list if not a bicep module + { + tags.Add(tag); + } + } + + return [.. tags]; + } + } + public async Task PullArtifactAsync( - RootConfiguration configuration, + CloudConfiguration cloud, IOciArtifactReference artifactReference) { async Task DownloadManifestInternalAsync(bool anonymousAccess) { - var client = CreateBlobClient(configuration, artifactReference, anonymousAccess); + var client = CreateBlobClient(cloud, artifactReference, anonymousAccess); return await DownloadManifestAndLayersAsync(artifactReference, client); } try { // Try anonymous auth first. - Trace.WriteLine($"Attempt to pull artifact for module {artifactReference.FullyQualifiedReference} with anonymous authentication."); + Trace.WriteLine($"Attempting to pull artifact for module {artifactReference.FullyQualifiedReference} with anonymous authentication."); return await DownloadManifestInternalAsync(anonymousAccess: true); } catch (RequestFailedException requestedFailedException) when (requestedFailedException.Status is 401 or 403) { - Trace.WriteLine($"Anonymous authetncation failed with status code {requestedFailedException.Status}. Retrying with authenticated client."); + Trace.WriteLine($"Anonymous authentication failed with status code {requestedFailedException.Status}. Retrying with authenticated client."); } - catch (Exception exception) + catch (Exception exception) //asdfg shouldn't fall back if InvalidArtifactException { Trace.WriteLine($"Anonymous authentication failed with unexpected exception {exception.Message}. Retrying with authenticated client."); } @@ -64,7 +160,7 @@ async Task DownloadManifestInternalAsync(bool anonymousAccess } public async Task PushArtifactAsync( - RootConfiguration configuration, + CloudConfiguration cloud, IOciArtifactReference artifactReference, string? mediaType, string? artifactType, @@ -72,9 +168,8 @@ public async Task PushArtifactAsync( IEnumerable layers, OciManifestAnnotationsBuilder annotations) { - // push is not supported anonymously - var blobClient = this.CreateBlobClient(configuration, artifactReference, anonymousAccess: false); + var blobClient = this.CreateBlobClient(cloud, artifactReference, anonymousAccess: false); _ = await blobClient.UploadBlobAsync(config.Data); @@ -116,14 +211,22 @@ public async Task PushArtifactAsync( _ = await blobClient.SetManifestAsync(manifestBinaryData, artifactReference.Tag, mediaType: ManifestMediaType.OciImageManifest); } - private static Uri GetRegistryUri(IOciArtifactReference artifactReference) => new($"https://{artifactReference.Registry}"); + private static Uri GetRegistryUri(IOciArtifactReference artifactReference) => GetRegistryUri(artifactReference.Registry); + private static Uri GetRegistryUri(string loginServer) => new($"https://{loginServer}"); private ContainerRegistryContentClient CreateBlobClient( - RootConfiguration configuration, + CloudConfiguration cloud, IOciArtifactReference artifactReference, bool anonymousAccess) => anonymousAccess - ? this.clientFactory.CreateAnonymousBlobClient(configuration, GetRegistryUri(artifactReference), artifactReference.Repository) - : this.clientFactory.CreateAuthenticatedBlobClient(configuration, GetRegistryUri(artifactReference), artifactReference.Repository); + ? this.clientFactory.CreateAnonymousBlobClient(cloud, GetRegistryUri(artifactReference), artifactReference.Repository) + : this.clientFactory.CreateAuthenticatedBlobClient(cloud, GetRegistryUri(artifactReference), artifactReference.Repository); + + private ContainerRegistryClient CreateContainerClient( + CloudConfiguration cloud, + Uri registryUri, + bool anonymousAccess) => anonymousAccess + ? this.clientFactory.CreateAnonymousContainerClient(cloud, registryUri) + : this.clientFactory.CreateAuthenticatedContainerClient(cloud, registryUri); private static async Task DownloadManifestAndLayersAsync(IOciArtifactReference artifactReference, ContainerRegistryContentClient client) { diff --git a/src/Bicep.Core/Registry/PublicRegistry/PublicRegistryModuleMetadataProvider.cs b/src/Bicep.Core/Registry/Catalog/BaseModuleMetadataProvider.cs similarity index 53% rename from src/Bicep.Core/Registry/PublicRegistry/PublicRegistryModuleMetadataProvider.cs rename to src/Bicep.Core/Registry/Catalog/BaseModuleMetadataProvider.cs index 550e0a82164..aaed2d952b4 100644 --- a/src/Bicep.Core/Registry/PublicRegistry/PublicRegistryModuleMetadataProvider.cs +++ b/src/Bicep.Core/Registry/Catalog/BaseModuleMetadataProvider.cs @@ -5,39 +5,26 @@ using System.Diagnostics; using System.Net; using System.Net.Http.Json; +using System.Linq; using System.Text.Json; using Bicep.Core.Extensions; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Win32; +using System.Threading.Tasks; +using Bicep.Core.Registry.Oci; +using System.Security.Cryptography.Xml; +using Bicep.Core.Registry.Catalog; -namespace Bicep.Core.Registry.PublicRegistry; - -public static class PublicRegistryModuleMetadataProviderExtensions -{ - public static IServiceCollection AddPublicRegistryModuleMetadataProviderServices(this IServiceCollection services) - { - services.AddSingleton(); - - // using type based registration for Http clients so dependencies can be injected automatically - // without manually constructing up the graph, see https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory#typed-clients - services - .AddHttpClient() - .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler - { - AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate - }); - - return services; - } -} +namespace Bicep.Core.Registry.Catalog; //asdfg split into PublicRegistry/PrivateRegistry /// -/// Provider to get modules metadata that we store at a public endpoint. +/// Provider to get metadata for modules stored in a public or private registry. /// -public class PublicRegistryModuleMetadataProvider : IPublicRegistryModuleMetadataProvider +public abstract class BaseModuleMetadataProvider( + string registry +) : IRegistryModuleMetadataProvider { - private readonly IServiceProvider serviceProvider; - private readonly TimeSpan CacheValidFor = TimeSpan.FromHours(1); private readonly TimeSpan InitialThrottleDelay = TimeSpan.FromSeconds(5); private readonly TimeSpan MaxThrottleDelay = TimeSpan.FromMinutes(2); @@ -47,32 +34,35 @@ public class PublicRegistryModuleMetadataProvider : IPublicRegistryModuleMetadat private DateTime? lastSuccessfulQuery; private int consecutiveFailures = 0; - private ImmutableArray cachedIndex = []; + public string Registry => registry; + + // Using array instead of dictionary to preserve sort order + private ImmutableArray cachedModules = []; private string? lastDownloadError = null; - public bool IsCached => cachedIndex.Length > 0; + public bool IsCached => cachedModules.Length > 0; public string? DownloadError => IsCached ? null : lastDownloadError; - public PublicRegistryModuleMetadataProvider(IServiceProvider serviceProvider) - { - this.serviceProvider = serviceProvider; - } + protected abstract Task> GetLiveDataCoreAsync(); + + protected abstract Task> GetLiveModuleVersionsAsync(string modulePath); public Task TryAwaitCache(bool forceUpdate) { - return UpdateCacheIfNeeded(forceUpdate: forceUpdate, initialDelay: false); + return UpdateCacheIfNeededAsync(forceUpdate: forceUpdate, initialDelay: false); } - public void StartUpdateCache(bool forceUpdate) + public void StartCache() { - _ = TryAwaitCache(forceUpdate); + _ = TryAwaitCache(false); } - public async Task TryUpdateCacheAsync() + + public async Task TryUpdateCacheAsync()//asdfg implement/test threading { - if (await TryGetLiveIndexAsync() is { } modules) + if (await TryGetLiveDataAsync() is { } modules) { - this.cachedIndex = modules; + this.cachedModules = modules; this.lastSuccessfulQuery = DateTime.Now; return true; } @@ -82,46 +72,50 @@ public async Task TryUpdateCacheAsync() } } - // If cache has not yet successfully been updated, returns empty - public ImmutableArray GetModulesMetadata() + protected IRegistryModuleMetadata? GetCachedModule(string modulePath, bool throwIfNotFound) { - StartCacheUpdateInBackgroundIfNeeded(); + var found = this.cachedModules.FirstOrDefault(x => + x.Registry.Equals(Registry, StringComparison.Ordinal) + && x.ModuleName.Equals(modulePath, StringComparison.Ordinal)); + if (found != null && throwIfNotFound) + { + throw new InvalidOperationException($"Module '{modulePath}' not found in cache."); + } - return [.. this.cachedIndex.Select(x => new PublicRegistryModuleMetadata(x.ModulePath, x.GetDescription(), x.GetDocumentationUri()))]; + return found; } - public ImmutableArray GetModuleVersionsMetadata(string modulePath) + public async Task> TryGetModulesAsync() { - StartCacheUpdateInBackgroundIfNeeded(); - - PublicRegistryModuleIndexEntry? entry = this.cachedIndex.FirstOrDefault(x => x.ModulePath.Equals(modulePath, StringComparison.Ordinal)); - - if (entry == null) - { - return []; - } - - return [.. entry.Versions.Select(version => new PublicRegistryModuleVersionMetadata(version, entry.GetDescription(version), entry.GetDocumentationUri(version)))]; + await TryAwaitCache(forceUpdate: false); + return GetCachedModules(); } - private void StartCacheUpdateInBackgroundIfNeeded(bool initialDelay = false) + // If cache has not yet successfully been updated, returns empty + public ImmutableArray GetCachedModules() { - _ = UpdateCacheIfNeeded(forceUpdate: false, initialDelay); + return [.. cachedModules + .Where(x => x.Registry.Equals(Registry, StringComparison.Ordinal)) + ]; } - private Task UpdateCacheIfNeeded(bool forceUpdate, bool initialDelay) + private Task UpdateCacheIfNeededAsync(bool forceUpdate, bool initialDelay) { - if (!this.cachedIndex.Any()) + if (this.DownloadError is not null) { - Trace.WriteLineIf(IsCacheExpired(), $"{nameof(PublicRegistryModuleMetadataProvider)}: First data retrieval..."); + Trace.WriteLine($"{nameof(PublicModuleMetadataProvider)}: [{Registry}] Last cache load failed, trying again..."); + } + else if (this.lastSuccessfulQuery is null) + { + Trace.WriteLineIf(IsCacheExpired(), $"{nameof(PublicModuleMetadataProvider)}: [{Registry}] First data retrieval..."); } else if (forceUpdate) { - Trace.WriteLine($"{nameof(PublicRegistryModuleMetadataProvider)}: Force updating cache..."); + Trace.WriteLine($"{nameof(PublicModuleMetadataProvider)}: [{Registry}] Force updating cache..."); } else if (IsCacheExpired()) { - Trace.WriteLineIf(IsCacheExpired(), $"{nameof(PublicRegistryModuleMetadataProvider)}: Cache expired, updating..."); + Trace.WriteLineIf(IsCacheExpired(), $"{nameof(PublicModuleMetadataProvider)}: [{Registry}] Cache expired, updating..."); } else { @@ -158,7 +152,7 @@ Task QueryData(bool initialDelay) if (delay > 0) { - Trace.WriteLine($"{nameof(PublicRegistryModuleMetadataProvider)}: Delaying {delay} before retry..."); + Trace.WriteLine($"{nameof(PublicModuleMetadataProvider)}: [{Registry}] Delaying {delay} before retry..."); await Task.Delay(delay); } @@ -175,7 +169,7 @@ Task QueryData(bool initialDelay) { lock (this.queryingLiveSyncObject) { - Trace.Assert(this.queryLiveDataTask is { }, $"{nameof(PublicRegistryModuleMetadataProvider)}: should be querying live data"); + Trace.Assert(this.queryLiveDataTask is { }, $"{nameof(PublicModuleMetadataProvider)}: [{Registry}] Should be querying live data"); this.queryLiveDataTask = null; } } @@ -183,25 +177,22 @@ Task QueryData(bool initialDelay) } } - private bool IsCacheExpired() + private bool IsCacheExpired() //asdfg test { var expired = this.lastSuccessfulQuery.HasValue && this.lastSuccessfulQuery.Value + this.CacheValidFor < DateTime.Now; if (expired) { - Trace.TraceInformation($"{nameof(PublicRegistryModuleMetadataProvider)}: Public modules cache is expired."); + Trace.TraceInformation($"{nameof(PublicModuleMetadataProvider)}: [{Registry}] Cache has expired."); } return expired; } - private async Task?> TryGetLiveIndexAsync() + private async Task?> TryGetLiveDataAsync() { try { - var client = serviceProvider.GetRequiredService(); - var modules = await client.GetModuleIndexAsync(); - - return modules; + return await GetLiveDataCoreAsync(); } catch (Exception ex) { diff --git a/src/Bicep.Core/Registry/Catalog/HttpClients/IPublicModuleIndexHttpClient.cs b/src/Bicep.Core/Registry/Catalog/HttpClients/IPublicModuleIndexHttpClient.cs new file mode 100644 index 00000000000..ba45b7c231e --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/HttpClients/IPublicModuleIndexHttpClient.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; + +namespace Bicep.Core.Registry.Catalog.HttpClients; + +public interface IPublicModuleIndexHttpClient +{ + Task> GetModuleIndexAsync(); +} + +public readonly record struct PublicModuleIndexProperties(string Description, string DocumentationUri); + diff --git a/src/Bicep.Core/Registry/Catalog/HttpClients/PublicModuleIndexHttpClient.cs b/src/Bicep.Core/Registry/Catalog/HttpClients/PublicModuleIndexHttpClient.cs new file mode 100644 index 00000000000..4d6b18f84db --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/HttpClients/PublicModuleIndexHttpClient.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; +using System.Text.Json.Serialization; +using Bicep.Core.Extensions; +using Bicep.Core.Registry.Oci; +using Microsoft.WindowsAzure.ResourceStack.Common.Extensions; +using Semver; +using Semver.Comparers; + +namespace Bicep.Core.Registry.Catalog.HttpClients; + +/// +/// This is the DTO for modules listed in the public bicep registry +/// +public record PublicModuleIndexEntry( + [property: JsonPropertyName("moduleName")] string ModulePath, // e.g. "avm/app/dapr-containerapp" - the "bicep/" prefix is assumed and not included in the index + ImmutableArray Tags, // e.g. "1.0.0" (not guaranteed to be in semver format, although it currently is for all our public modules) + [property: JsonPropertyName("properties")] ImmutableDictionary PropertiesByTag // Module properties per tag +) +{ + private static readonly SemVersion DefaultVersion = new(0); + + public ImmutableArray Versions + { + get + { + { + var parsedVersions = Tags.Select(x => + (@string: x, version: SemVersion.TryParse(x, SemVersionStyles.AllowV, out var version) ? version : DefaultVersion)) + .ToArray(); + // Sort by ascending version number here, the completion provider will reverse it to show the most recent version first + return [.. parsedVersions.OrderByAscending(x => x.version, SemVersion.SortOrderComparer).Select(x => x.@string)]; + } + } + } + + public string? GetDescription(string? version = null) => GetProperty(version, x => x.Description); + + public string? GetDocumentationUri(string? version = null) => GetProperty(version, x => x.DocumentationUri); + + private string? GetProperty(string? version, Func propertySelector) + { + if (version is null) + { + // Get description for most recent version with a description + foreach (var tag in Versions.Reverse()) + { + if (PropertiesByTag.TryGetValue(tag, out var propertiesEntry)) + { + return propertySelector(propertiesEntry); + } + } + + return null; + } + else + { + return PropertiesByTag.TryGetValue(version, out var propertiesEntry) ? propertySelector(propertiesEntry) : null; + } + } +} diff --git a/src/Bicep.Core/Registry/PublicRegistry/PublicRegistryModuleMetadataClient.cs b/src/Bicep.Core/Registry/Catalog/HttpClients/PublicModuleMetadataHttpClient.cs similarity index 68% rename from src/Bicep.Core/Registry/PublicRegistry/PublicRegistryModuleMetadataClient.cs rename to src/Bicep.Core/Registry/Catalog/HttpClients/PublicModuleMetadataHttpClient.cs index d01c3b0dee9..e6592cb64ba 100644 --- a/src/Bicep.Core/Registry/PublicRegistry/PublicRegistryModuleMetadataClient.cs +++ b/src/Bicep.Core/Registry/Catalog/HttpClients/PublicModuleMetadataHttpClient.cs @@ -10,32 +10,29 @@ using Bicep.Core.Extensions; using Microsoft.Extensions.DependencyInjection; -namespace Bicep.Core.Registry.PublicRegistry; +namespace Bicep.Core.Registry.Catalog.HttpClients; /// /// Typed http client to get modules metadata that we store at a public endpoint (currently https://github.com/Azure/bicep-registry-modules) /// -public class PublicRegistryModuleMetadataClient(HttpClient httpClient) : IPublicRegistryModuleIndexClient +public class PublicModuleMetadataHttpClient(HttpClient httpClient) : IPublicModuleIndexHttpClient { private const string LiveDataEndpoint = "https://aka.ms/br-module-index-data"; - private static readonly JsonSerializerOptions JsonSerializerOptions = new() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }; + private static readonly JsonSerializerOptions JsonSerializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; [SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Relying on references to required properties of the generic type elsewhere in the codebase.")] - public async Task> GetModuleIndexAsync() + public async Task> GetModuleIndexAsync() { - Trace.WriteLine($"{nameof(PublicRegistryModuleMetadataClient)}: Retrieving list of public registry modules..."); + Trace.WriteLine($"{nameof(PublicModuleMetadataHttpClient)}: Retrieving list of public registry modules..."); try { - var metadata = await httpClient.GetFromJsonAsync(LiveDataEndpoint, JsonSerializerOptions); + var metadata = await httpClient.GetFromJsonAsync(LiveDataEndpoint, JsonSerializerOptions); if (metadata is not null) { - Trace.WriteLine($"{nameof(PublicRegistryModuleMetadataClient)}: Retrieved info on {metadata.Length} public registry modules."); + Trace.WriteLine($"{nameof(PublicModuleMetadataHttpClient)}: Retrieved info on {metadata.Length} public registry modules."); return [.. metadata]; } else diff --git a/src/Bicep.Core/Registry/Catalog/IPrivateAcrModuleMetadataProviderFactory.cs b/src/Bicep.Core/Registry/Catalog/IPrivateAcrModuleMetadataProviderFactory.cs new file mode 100644 index 00000000000..f76d97d8211 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/IPrivateAcrModuleMetadataProviderFactory.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; +using Bicep.Core.Configuration; +using Microsoft.Win32; + +namespace Bicep.Core.Registry.Catalog; + +/// +/// Creates IRegistryModuleMetadataProvider instances +/// +public interface IPrivateAcrModuleMetadataProviderFactory +{ + public IRegistryModuleMetadataProvider Create(CloudConfiguration cloud, string registry, IContainerRegistryClientFactory containerRegistryClientFactory); +} diff --git a/src/Bicep.Core/Registry/Catalog/IPublicModuleMetadataProvider.cs b/src/Bicep.Core/Registry/Catalog/IPublicModuleMetadataProvider.cs new file mode 100644 index 00000000000..9fa5a4fe9b8 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/IPublicModuleMetadataProvider.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; + +namespace Bicep.Core.Registry.Catalog; + +/// +/// An IRegistryModuleMetadataProvider that will be registered as a singleton to handle +/// the public bicep registry only. +/// +public interface IPublicModuleMetadataProvider : IRegistryModuleMetadataProvider { } diff --git a/src/Bicep.Core/Registry/Catalog/IRegistryModuleCatalog.cs b/src/Bicep.Core/Registry/Catalog/IRegistryModuleCatalog.cs new file mode 100644 index 00000000000..9d071fcb183 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/IRegistryModuleCatalog.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Bicep.Core.Configuration; + +namespace Bicep.Core.Registry.Catalog; + +/// +/// Retrieves module metadata from OCI registries (public or private) +/// +public interface IRegistryModuleCatalog +{ + // Aways returns a provider instance, either from cache or newly created, from which loading will fail if the registry + // is not accessible or doesn't exist + IRegistryModuleMetadataProvider GetProviderForRegistry(CloudConfiguration cloud, string registry); + + IRegistryModuleMetadataProvider? TryGetCachedRegistry(string registry); +} diff --git a/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProvider.cs b/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProvider.cs new file mode 100644 index 00000000000..b6b1827240d --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProvider.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; +using Bicep.Core.Configuration; +using Bicep.Core.Registry.Oci; +using Microsoft.Win32; +using static Bicep.Core.Registry.Catalog.IRegistryModuleMetadata; + +namespace Bicep.Core.Registry.Catalog; + +public interface IRegistryModuleMetadata +{ //asdfg better name? asdfg combine asdfg interface? + public string Registry { get; init; } // e.g. "mcr.microsoft.com" + public string ModuleName { get; init; } // e.g. "bicep/avm/app/dapr-containerapp" + + public Task TryGetDetailsAsync(); + + // In order returned by the registry, generally oldest first + public Task> TryGetVersionsAsync(); + + public ImmutableArray GetCachedVersions(); +} + +/// +/// Retrieves metadata about modules from a specific OCI registry (public or private). +/// Use IRegistryCatalog to retrieve an instance for a specific registry. +/// +public interface IRegistryModuleMetadataProvider +{ + public string Registry { get; } + + bool IsCached { get; } + + string? DownloadError { get; } + + void StartCache(); + + Task TryAwaitCache(bool forceUpdate = false); + + Task> TryGetModulesAsync(); + + ImmutableArray GetCachedModules(); +} + diff --git a/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProviderExtensions.cs b/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProviderExtensions.cs new file mode 100644 index 00000000000..f89d107bdb8 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProviderExtensions.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Bicep.Core.Registry.Catalog; + +public static class IRegistryModuleMetadataProviderExtensions +{ + public static async Task TryGetModuleAsync(this IRegistryModuleMetadataProvider provider, string modulePath) + { + return (await provider.TryGetModulesAsync()) + .FirstOrDefault(x => x.ModuleName.Equals(modulePath, StringComparison.Ordinal)); + } +} diff --git a/src/Bicep.Core/Registry/Catalog/MightBeLazyAsync.cs b/src/Bicep.Core/Registry/Catalog/MightBeLazyAsync.cs new file mode 100644 index 00000000000..dda4ea8f76d --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/MightBeLazyAsync.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.VisualStudio.Threading; + +namespace Bicep.Core.Registry.Catalog; + +public class MightBeLazyAsync where T : class +{ + private readonly AsyncLazy? lazy; + private readonly T? value; + + public MightBeLazyAsync(T value) + { + this.lazy = null; + this.value = value; + } + + public MightBeLazyAsync(Func> initializer) + { + this.lazy = new AsyncLazy(initializer, new(new JoinableTaskContext())); + this.value = default; + } + + private bool IsLazy => lazy is not null; + + //asdfg public bool HasValue => lazy is null || lazy.IsValueFactoryCompleted; + + public bool TryGetValue([NotNullWhen(true)] out T? value) + { + if (IsLazy) + { + if (lazy!.IsValueFactoryCompleted) + { + value = lazy.GetValue(); //asdfg can this throw?//asdfg testpoint + return true; + } + else + { + value = default; + return false; + } + } + else + { + value = this.value!; + return true; + } + } + + public async Task GetValueAsync() + { + return IsLazy ? await lazy!.GetValueAsync() : value!; + } +} diff --git a/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProvider.cs b/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProvider.cs new file mode 100644 index 00000000000..c83dbbf3ac4 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProvider.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO.Packaging; +using System.Net; +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.RegularExpressions; +using Bicep.Core.Configuration; +using Bicep.Core.Extensions; +using Bicep.Core.Registry.Oci; +using Microsoft.Extensions.DependencyInjection; +using static Bicep.Core.Registry.Catalog.RegistryModuleMetadata; + +namespace Bicep.Core.Registry.Catalog; + +/// +/// Provider to get modules metadata from a private ACR registry +/// +public class PrivateAcrModuleMetadataProvider : BaseModuleMetadataProvider, IRegistryModuleMetadataProvider +{ + private const int MaxReturnedModules = 10; //asdfg? + + private readonly CloudConfiguration cloud; + private readonly IContainerRegistryClientFactory containerRegistryClientFactory; + + public PrivateAcrModuleMetadataProvider( + CloudConfiguration cloud, + string registry, + IContainerRegistryClientFactory containerRegistryClientFactory + ) : base(registry) + { + this.cloud = cloud; + this.containerRegistryClientFactory = containerRegistryClientFactory; + } + + protected override async Task> GetLiveModuleVersionsAsync(string modulePath) + { + var registry = Registry; + var repository = modulePath; + + AzureContainerRegistryManager acrManager = new(containerRegistryClientFactory); + + // For this part we want to throw on errors + var tags = await acrManager.GetRepositoryTagsAsync(cloud, registry, repository); + + // For the rest, we'll be resilient + return [.. await Task.WhenAll( + tags.Select(async tag => await TryGetLiveModuleVersionMetadataAsync(modulePath, tag)) + )]; + } + + private async Task TryGetLiveModuleVersionMetadataAsync(string modulePath, string version) + { + try + { + AzureContainerRegistryManager acrManager = new(containerRegistryClientFactory); + var artifactResult = await acrManager.PullArtifactAsync(cloud, new OciArtifactReference(ArtifactType.Module, Registry, modulePath, version, null, new Uri("file://asdfg"))); + var manifest = artifactResult.Manifest; + string? description = null; + string? documentationUri = null; + string? title = null; + + manifest.Annotations?.TryGetValue(OciAnnotationKeys.OciOpenContainerImageDescriptionAnnotation, out description); + manifest.Annotations?.TryGetValue(OciAnnotationKeys.OciOpenContainerImageDocumentationAnnotation, out documentationUri); + manifest.Annotations?.TryGetValue(OciAnnotationKeys.OciOpenContainerImageTitleAnnotation, out title); + + return new RegistryModuleVersionMetadata(version, + new RegistryMetadataDetails(description ?? title, documentationUri)); + } + catch (Exception ex) + { + Trace.WriteLine($"Failed to get version details for module {modulePath} version {version}: {ex.Message}"); + return new RegistryModuleVersionMetadata(version, new RegistryMetadataDetails(null, null)); + } + } + + protected override async Task> GetLiveDataCoreAsync() + { + Trace.WriteLine($"Retrieving catalog for registry {Registry}..."); + + AzureContainerRegistryManager acrManager = new(containerRegistryClientFactory); + var catalog = await acrManager.GetRepositoryNamesAsync(cloud, Registry, MaxReturnedModules); //asdfg limit? + + Trace.WriteLine($"Found {catalog.Length} repositories"); + + var modules = catalog + .Reverse() // Reverse to inspect the latest modules first + .Select(m => + { + return new RegistryModuleMetadata( + Registry, + m, + async () => + { + var versions = await GetLiveModuleVersionsAsync(m); + var moduleDetails = GetModuleDetails(versions); + return new ComputedData(moduleDetails, versions); + }); + } + ).ToImmutableArray(); + + return [.. modules.Cast()]; + } + + private RegistryMetadataDetails GetModuleDetails(ImmutableArray versions) + { + // OCI modules don't have a description or documentation URI, we just use the most recent version's metadata + if (versions.LastOrDefault() is { } lastVersion) + { + return lastVersion.Details; + } + + return new RegistryMetadataDetails(null, null);//asdfg testpoint + } +} diff --git a/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProviderFactory.cs b/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProviderFactory.cs new file mode 100644 index 00000000000..8257cceb213 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProviderFactory.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; +using Bicep.Core.Configuration; +using Microsoft.Win32; +using Microsoft.WindowsAzure.ResourceStack.Common.Extensions; + +namespace Bicep.Core.Registry.Catalog; + +/// +/// Creates IRegistryModuleMetadataProvider instances for private registries +/// +public class PrivateAcrModuleMetadataProviderFactory : IPrivateAcrModuleMetadataProviderFactory +{ + public IRegistryModuleMetadataProvider Create(CloudConfiguration cloud, string registry, IContainerRegistryClientFactory containerRegistryClientFactory) + { + return new PrivateAcrModuleMetadataProvider(cloud, registry, containerRegistryClientFactory); + } +} diff --git a/src/Bicep.Core/Registry/Catalog/PublicModuleMetadataProvider.cs b/src/Bicep.Core/Registry/Catalog/PublicModuleMetadataProvider.cs new file mode 100644 index 00000000000..67a53545b83 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/PublicModuleMetadataProvider.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Http.Json; +using System.Reflection; +using System.Text.Json; +using Bicep.Core.Extensions; +using Bicep.Core.Registry.Catalog.HttpClients; +using Microsoft.Extensions.DependencyInjection; +using static Bicep.Core.Registry.Catalog.RegistryModuleMetadata; + +namespace Bicep.Core.Registry.Catalog; + +/// +/// Provider to get modules metadata that we store at the public mcr.microsoft.com/bicep registry. +/// +public class PublicModuleMetadataProvider : BaseModuleMetadataProvider, IPublicModuleMetadataProvider +{ + private readonly IPublicModuleIndexHttpClient client; + + public PublicModuleMetadataProvider(IPublicModuleIndexHttpClient publicModuleIndexClient) + : base(LanguageConstants.BicepPublicMcrRegistry) + { + this.client = publicModuleIndexClient; //asdfg lifetime + } + + protected override async Task> GetLiveDataCoreAsync() + { + var modules = await client.GetModuleIndexAsync(); + + return [.. modules.Select(m => + { + var moduleDetails = new RegistryMetadataDetails(m.GetDescription(), m.GetDocumentationUri()); + var versions = ImmutableArray.Create([.. m.Versions.Select( + t => new RegistryModuleVersionMetadata( + t, + new RegistryMetadataDetails( + m.PropertiesByTag.ContainsKey(t) ? m.PropertiesByTag[t].Description:null, + m.PropertiesByTag.ContainsKey(t) ? m.PropertiesByTag[t].DocumentationUri:null) + ) + )]); + return new RegistryModuleMetadata( + Registry, + $"{LanguageConstants.BicepPublicMcrPathPrefix}{m.ModulePath}", //asdfg remove prefix and get tests to fail + new ComputedData(moduleDetails, versions)); + })]; + } + + protected override Task> GetLiveModuleVersionsAsync(string modulePath) + { + throw new NotImplementedException("This method should never get called because versions are pre-filled with a resolved task"); throw new NotImplementedException(); + } +} diff --git a/src/Bicep.Core/Registry/Catalog/RegistryCatalogExtensionsForIServiceCollection.cs b/src/Bicep.Core/Registry/Catalog/RegistryCatalogExtensionsForIServiceCollection.cs new file mode 100644 index 00000000000..73dd169bf18 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/RegistryCatalogExtensionsForIServiceCollection.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Net; +using Bicep.Core.Configuration; +using Bicep.Core.Registry.Catalog.HttpClients; +using Microsoft.Extensions.DependencyInjection; + +namespace Bicep.Core.Registry.Catalog; + +public static class RegistryCatalogExtensionsForIServiceCollection +{ + public static IServiceCollection AddRegistryCatalogServices(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // using type based registration for Http clients so dependencies can be injected automatically + // without manually constructing up the graph, see https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory#typed-clients + services + .AddHttpClient() + .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler + { + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate + }); + + return services; + } +} diff --git a/src/Bicep.Core/Registry/Catalog/RegistryMetadataDetails.cs b/src/Bicep.Core/Registry/Catalog/RegistryMetadataDetails.cs new file mode 100644 index 00000000000..4a76ab649db --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/RegistryMetadataDetails.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Bicep.Core.Registry.Catalog; + +/// +/// Metadata about a module or a version. +/// +/// +/// +public record RegistryMetadataDetails( + string? Description, + string? DocumentationUri); + diff --git a/src/Bicep.Core/Registry/Catalog/RegistryModuleCatalog.cs b/src/Bicep.Core/Registry/Catalog/RegistryModuleCatalog.cs new file mode 100644 index 00000000000..f45e17c5f83 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/RegistryModuleCatalog.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Bicep.Core.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Bicep.Core.Registry.Catalog; + +/// +/// Provides a list of modules from a registry (public or private) +/// +public class RegistryModuleCatalog : IRegistryModuleCatalog +{ + private readonly IPrivateAcrModuleMetadataProviderFactory providerFactory; + private readonly IContainerRegistryClientFactory containerRegistryClientFactory; + private readonly IConfigurationManager configurationManager; + private readonly object lockObject = new(); + + private readonly Dictionary registryProviders = new(); + + public RegistryModuleCatalog( + IPublicModuleMetadataProvider publicModuleMetadataProvider, + IPrivateAcrModuleMetadataProviderFactory privateProviderFactory, + IContainerRegistryClientFactory containerRegistryClientFactory, + IConfigurationManager configurationManager + ) + { + this.providerFactory = privateProviderFactory; + this.containerRegistryClientFactory = containerRegistryClientFactory; + this.configurationManager = configurationManager; + + registryProviders["mcr.microsoft.com"] = publicModuleMetadataProvider; + } + + public IRegistryModuleMetadataProvider GetProviderForRegistry(CloudConfiguration cloud, string registry) + { + lock (lockObject) + { + if (registryProviders.TryGetValue(registry, out var provider)) + { + return provider; + } + + provider = providerFactory.Create(cloud, registry, containerRegistryClientFactory); + registryProviders[registry] = provider; //asdfg threading + + return provider; + } + } + + public IRegistryModuleMetadataProvider? TryGetCachedRegistry(string registry) + { + if (registryProviders.TryGetValue(registry, out var provider)) + { + return provider; + } + + return null; + } +} diff --git a/src/Bicep.Core/Registry/Catalog/RegistryModuleMetadata.cs b/src/Bicep.Core/Registry/Catalog/RegistryModuleMetadata.cs new file mode 100644 index 00000000000..e6e1c8d1409 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/RegistryModuleMetadata.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; + +namespace Bicep.Core.Registry.Catalog; //asdfg split into PublicRegistry/PrivateRegistry + +public class RegistryModuleMetadata : IRegistryModuleMetadata +{ + public string Registry { get; init; } + public string ModuleName { get; init; } + + public record ComputedData( + RegistryMetadataDetails Details, + ImmutableArray Versions + ); + + public readonly MightBeLazyAsync data; + + public RegistryModuleMetadata( + string registry, + string moduleName, + ComputedData computedData) + { + this.Registry = registry; + this.ModuleName = moduleName; + + this.data = new(computedData); + } + + public RegistryModuleMetadata( + string registry, + string moduleName, + Func> getDataAsyncFunc) + { + this.Registry = registry; + this.ModuleName = moduleName; + + this.data = new(getDataAsyncFunc); + } + + public async Task TryGetDetailsAsync() + { + try + { + return (await data.GetValueAsync()).Details; + } + catch + { + return new RegistryMetadataDetails(null, null); + } + } + + public async Task> TryGetVersionsAsync() + { + try + { + return (await data.GetValueAsync()).Versions; + } + catch + { + return []; + } + } + + public ImmutableArray GetCachedVersions() + { + if (this.data.TryGetValue(out var data)) + { + return data.Versions; + } + else + { + return []; + } + } +} diff --git a/src/Bicep.Core/Registry/Catalog/RegistryModuleVersionMetadata.cs b/src/Bicep.Core/Registry/Catalog/RegistryModuleVersionMetadata.cs new file mode 100644 index 00000000000..715fa2a06e2 --- /dev/null +++ b/src/Bicep.Core/Registry/Catalog/RegistryModuleVersionMetadata.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Bicep.Core.Registry.Catalog; + +public record RegistryModuleVersionMetadata( + string Version, + RegistryMetadataDetails Details +); + diff --git a/src/Bicep.Core/Registry/ContainerRegistryClientFactory.cs b/src/Bicep.Core/Registry/ContainerRegistryClientFactory.cs index 4570ec29bf1..2d0dadb51a7 100644 --- a/src/Bicep.Core/Registry/ContainerRegistryClientFactory.cs +++ b/src/Bicep.Core/Registry/ContainerRegistryClientFactory.cs @@ -17,24 +17,44 @@ public ContainerRegistryClientFactory(ITokenCredentialFactory credentialFactory) this.credentialFactory = credentialFactory; } - public ContainerRegistryContentClient CreateAuthenticatedBlobClient(RootConfiguration configuration, Uri registryUri, string repository) + public ContainerRegistryContentClient CreateAuthenticatedBlobClient(CloudConfiguration cloud, Uri registryUri, string repository) { var options = new ContainerRegistryClientOptions(); options.Diagnostics.ApplySharedContainerRegistrySettings(); - options.Audience = new ContainerRegistryAudience(configuration.Cloud.ResourceManagerAudience); + options.Audience = new ContainerRegistryAudience(cloud.ResourceManagerAudience); - var credential = this.credentialFactory.CreateChain(configuration.Cloud.CredentialPrecedence, configuration.Cloud.CredentialOptions, configuration.Cloud.ActiveDirectoryAuthorityUri); + var credential = this.credentialFactory.CreateChain(cloud.CredentialPrecedence, cloud.CredentialOptions, cloud.ActiveDirectoryAuthorityUri); return new(registryUri, repository, credential, options); } - public ContainerRegistryContentClient CreateAnonymousBlobClient(RootConfiguration configuration, Uri registryUri, string repository) + public ContainerRegistryContentClient CreateAnonymousBlobClient(CloudConfiguration cloud, Uri registryUri, string repository) { var options = new ContainerRegistryClientOptions(); options.Diagnostics.ApplySharedContainerRegistrySettings(); - options.Audience = new ContainerRegistryAudience(configuration.Cloud.ResourceManagerAudience); + options.Audience = new ContainerRegistryAudience(cloud.ResourceManagerAudience); return new(registryUri, repository, options); } + + public ContainerRegistryClient CreateAuthenticatedContainerClient(CloudConfiguration cloud, Uri registryUri) + { + var options = new ContainerRegistryClientOptions(); + options.Diagnostics.ApplySharedContainerRegistrySettings(); + options.Audience = new ContainerRegistryAudience(cloud.ResourceManagerAudience); + + var credential = this.credentialFactory.CreateChain(cloud.CredentialPrecedence, cloud.CredentialOptions, cloud.ActiveDirectoryAuthorityUri); + + return new(registryUri, credential, options); + } + + public ContainerRegistryClient CreateAnonymousContainerClient(CloudConfiguration cloud, Uri registryUri) + { + var options = new ContainerRegistryClientOptions(); + options.Diagnostics.ApplySharedContainerRegistrySettings(); + options.Audience = new ContainerRegistryAudience(cloud.ResourceManagerAudience); + + return new(registryUri, options); + } } } diff --git a/src/Bicep.Core/Registry/DefaultArtifactRegistryProvider.cs b/src/Bicep.Core/Registry/DefaultArtifactRegistryProvider.cs index f5128ff1f8e..808f5c8ca8a 100644 --- a/src/Bicep.Core/Registry/DefaultArtifactRegistryProvider.cs +++ b/src/Bicep.Core/Registry/DefaultArtifactRegistryProvider.cs @@ -6,7 +6,7 @@ using Bicep.Core.Configuration; using Bicep.Core.Features; using Bicep.Core.FileSystem; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Microsoft.Extensions.DependencyInjection; namespace Bicep.Core.Registry @@ -44,7 +44,7 @@ public ImmutableArray Registries(Uri templateUri) // Using IServiceProvider instead of constructor injection due to a dependency cycle builder.Add(new LocalModuleRegistry(fileResolver, features, templateUri)); - builder.Add(new OciArtifactRegistry(this.fileResolver, this.clientFactory, features, configuration, serviceProvider.GetRequiredService(), templateUri)); + builder.Add(new OciArtifactRegistry(this.fileResolver, this.clientFactory, features, configuration, serviceProvider.GetRequiredService(), templateUri)); builder.Add(new TemplateSpecModuleRegistry(this.fileResolver, this.templateSpecRepositoryFactory, features, configuration, templateUri)); return builder.ToImmutableArray(); diff --git a/src/Bicep.Core/Registry/IContainerRegistryClientFactory.cs b/src/Bicep.Core/Registry/IContainerRegistryClientFactory.cs index d1633ca8280..b2562466155 100644 --- a/src/Bicep.Core/Registry/IContainerRegistryClientFactory.cs +++ b/src/Bicep.Core/Registry/IContainerRegistryClientFactory.cs @@ -12,8 +12,15 @@ namespace Bicep.Core.Registry /// This exists because we need to inject mock clients in integration tests and because the real client constructor requires parameters. public interface IContainerRegistryClientFactory { - ContainerRegistryContentClient CreateAuthenticatedBlobClient(RootConfiguration configuration, Uri registryUri, string repository); + ContainerRegistryContentClient CreateAuthenticatedBlobClient(CloudConfiguration cloud, Uri registryUri, string repository); + ContainerRegistryContentClient CreateAnonymousBlobClient(CloudConfiguration cloud, Uri registryUri, string repository); + + public ContainerRegistryClient CreateAuthenticatedContainerClient(CloudConfiguration cloud, Uri registryUri); + public ContainerRegistryClient CreateAnonymousContainerClient(CloudConfiguration cloud, Uri registryUri); + } + + public interface IContainerRegistryClient + { - ContainerRegistryContentClient CreateAnonymousBlobClient(RootConfiguration configuration, Uri registryUri, string repository); } } diff --git a/src/Bicep.Core/Registry/ModuleDispatcher.cs b/src/Bicep.Core/Registry/ModuleDispatcher.cs index 378b47d99aa..c1ce53a180c 100644 --- a/src/Bicep.Core/Registry/ModuleDispatcher.cs +++ b/src/Bicep.Core/Registry/ModuleDispatcher.cs @@ -10,7 +10,7 @@ using Bicep.Core.Diagnostics; using Bicep.Core.Modules; using Bicep.Core.Navigation; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics; using Bicep.Core.SourceCode; using Bicep.Core.Syntax; diff --git a/src/Bicep.Core/Registry/Oci/OciArtifactResult.cs b/src/Bicep.Core/Registry/Oci/OciArtifactResult.cs index f3ba9c4133c..68b2dc48741 100644 --- a/src/Bicep.Core/Registry/Oci/OciArtifactResult.cs +++ b/src/Bicep.Core/Registry/Oci/OciArtifactResult.cs @@ -15,7 +15,7 @@ public OciArtifactResult(BinaryData manifestData, string manifestDigest, IEnumer this.ManifestData = manifestData; this.Manifest = OciManifest.FromBinaryData(manifestData) ?? throw new InvalidArtifactException("Unable to deserialize OCI manifest"); this.ManifestDigest = manifestDigest; - this.Layers = layers.ToImmutableArray(); + this.Layers = [.. layers]; } public BinaryData ManifestData { get; } diff --git a/src/Bicep.Core/Registry/OciArtifactRegistry.cs b/src/Bicep.Core/Registry/OciArtifactRegistry.cs index 6e8b88c3a07..f81beac4b7e 100644 --- a/src/Bicep.Core/Registry/OciArtifactRegistry.cs +++ b/src/Bicep.Core/Registry/OciArtifactRegistry.cs @@ -13,7 +13,7 @@ using Bicep.Core.FileSystem; using Bicep.Core.Modules; using Bicep.Core.Registry.Oci; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics; using Bicep.Core.SourceCode; using Bicep.Core.Tracing; @@ -35,14 +35,14 @@ public sealed class OciArtifactRegistry : ExternalArtifactRegistry ArtifactReferenceSchemes.Oci; @@ -105,7 +105,7 @@ public override async Task CheckArtifactExists(ArtifactType artifactType, try { // Get module - await this.client.PullArtifactAsync(configuration, reference); + await this.client.PullArtifactAsync(configuration.Cloud, reference); } catch (RequestFailedException exception) when (exception.Status == 404) { @@ -158,9 +158,9 @@ public override ResultWithDiagnosticBuilder TryGetLocalArtifactEntryPointUr || string.IsNullOrWhiteSpace(documentationUri)) { // Automatically generate a help URI for public MCR modules - if (ociArtifactModuleReference.Registry == LanguageConstants.BicepPublicMcrRegistry && ociArtifactModuleReference.Repository.StartsWith(LanguageConstants.McrRepositoryPrefix, StringComparison.Ordinal)) + if (ociArtifactModuleReference.Registry == LanguageConstants.BicepPublicMcrRegistry && ociArtifactModuleReference.Repository.StartsWith(LanguageConstants.BicepPublicMcrPathPrefix, StringComparison.Ordinal)) { - var moduleName = ociArtifactModuleReference.Repository.Substring(LanguageConstants.McrRepositoryPrefix.Length); + var moduleName = ociArtifactModuleReference.Repository.Substring(LanguageConstants.BicepPublicMcrPathPrefix.Length); return ociArtifactModuleReference.Tag is null ? null : GetPublicBicepModuleDocumentationUri(moduleName, ociArtifactModuleReference.Tag); } @@ -218,7 +218,15 @@ private OciManifest GetCachedManifest(OciArtifactReference ociArtifactModuleRefe public override async Task OnRestoreArtifacts(bool forceRestore) { - await publicRegistryModuleMetadataProvider.TryAwaitCache(forceRestore); + // We don't want linter tests to download anything during analysis. So we are downloading + // metadata here to avoid downloading during analysis, and tests can use cached data if it + // exists (e.g. IRegistryModuleMetadataProvider.GetCached* methods). + // If --no-restore has been specified on the command ine, we don't want to download anything at all. + // Therefore we do the cache download here so that lint rules can have access to the cached metadata. + // CONSIDER: Revisit if it's okay to download metadata during analysis? This will be more of a problem + // when we extend the linter rules to include private registry modules. + + await publicModuleMetadataProvider.TryAwaitCache(forceRestore); } public override async Task> RestoreArtifacts(IEnumerable references) @@ -284,7 +292,7 @@ public override async Task PublishModule(OciArtifactReference reference, BinaryD try { await this.client.PushArtifactAsync( - configuration, + configuration.Cloud, reference, // Technically null should be fine for mediaType, but ACR guys recommend OciImageManifest for safer compatibility ManifestMediaType.OciImageManifest.ToString(), @@ -339,7 +347,7 @@ public override async Task PublishExtension(OciArtifactReference reference, Exte try { await this.client.PushArtifactAsync( - configuration, + configuration.Cloud, reference, // Technically null should be fine for mediaType, but ACR guys recommend OciImageManifest for safer compatibility ManifestMediaType.OciImageManifest.ToString(), @@ -492,7 +500,7 @@ protected override IDirectoryHandle GetArtifactDirectory(OciArtifactReference re { try { - var result = await client.PullArtifactAsync(configuration, reference); + var result = await client.PullArtifactAsync(configuration.Cloud, reference); await WriteArtifactContentToCacheAsync(reference, result); diff --git a/src/Bicep.Core/Registry/PublicRegistry/IPublicRegistryModuleIndexClient.cs b/src/Bicep.Core/Registry/PublicRegistry/IPublicRegistryModuleIndexClient.cs deleted file mode 100644 index 27bc792282d..00000000000 --- a/src/Bicep.Core/Registry/PublicRegistry/IPublicRegistryModuleIndexClient.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Collections.Immutable; -using System.Text.Json.Serialization; -using Bicep.Core.Extensions; -using Bicep.Core.Registry.Oci; -using Semver; -using Semver.Comparers; - -namespace Bicep.Core.Registry.PublicRegistry; - -public readonly record struct PublicRegistryModuleProperties(string Description, string DocumentationUri); - -public record PublicRegistryModuleIndexEntry( - [property: JsonPropertyName("moduleName")] string ModulePath, // e.g. "avm/app/dapr-containerapp" - ImmutableArray Tags, // e.g. "1.0.0" (not guaranteed to be in that format, although it currently is for public modules) - [property: JsonPropertyName("properties")] ImmutableDictionary PropertiesByTag // Module properties per tag -) -{ - private static readonly SemVersion DefaultVersion = new(0); - - // Sort tags by version numbers in descending order. - public ImmutableArray Versions - { - get - { - { - var parsedVersions = Tags.Select(x => - (@string: x, version: SemVersion.TryParse(x, SemVersionStyles.AllowV, out var version) ? version : DefaultVersion)) - .ToArray(); - return parsedVersions.OrderByDescending(x => x.version, SemVersion.SortOrderComparer) - .Select(x => x.@string) - .ToImmutableArray(); - } - } - } - - public string? GetDescription(string? version = null) => this.GetProperty(version, x => x.Description); - - public string? GetDocumentationUri(string? version = null) => this.GetProperty(version, x => x.DocumentationUri); - - private string? GetProperty(string? version, Func propertySelector) - { - if (version is null) - { - // Get description for most recent version with a description - foreach (var tag in this.Versions) - { - if (this.PropertiesByTag.TryGetValue(tag, out var propertiesEntry)) - { - return propertySelector(propertiesEntry); - } - } - - return null; - } - else - { - return this.PropertiesByTag.TryGetValue(version, out var propertiesEntry) ? propertySelector(propertiesEntry) : null; - } - } -} - -public interface IPublicRegistryModuleIndexClient -{ - Task> GetModuleIndexAsync(); -} diff --git a/src/Bicep.Core/Registry/PublicRegistry/IPublicRegistryModuleMetadataProvider.cs b/src/Bicep.Core/Registry/PublicRegistry/IPublicRegistryModuleMetadataProvider.cs deleted file mode 100644 index 4c6b7b2582e..00000000000 --- a/src/Bicep.Core/Registry/PublicRegistry/IPublicRegistryModuleMetadataProvider.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Collections.Immutable; - -namespace Bicep.Core.Registry.PublicRegistry; - -public readonly record struct PublicRegistryModuleMetadata( - string Name, // e.g. "avm/app/dapr-containerapp" (note: the actual repo name has "bicep/" at the beginning, but the "public" alias takes care of that) - string? Description, - string? DocumentationUri); - -public readonly record struct PublicRegistryModuleVersionMetadata( - string Version, - string? Description, - string? DocumentationUri); - -public interface IPublicRegistryModuleMetadataProvider -{ - public bool IsCached { get; } - - public string? DownloadError { get; } - - public Task TryAwaitCache(bool forceUpdate = false); - - public void StartUpdateCache(bool forceUpdate = false); - - ImmutableArray GetModulesMetadata(); - - ImmutableArray GetModuleVersionsMetadata(string modulePath); -} diff --git a/src/Bicep.Core/packages.lock.json b/src/Bicep.Core/packages.lock.json index e24dda30af7..c5e5bf25700 100644 --- a/src/Bicep.Core/packages.lock.json +++ b/src/Bicep.Core/packages.lock.json @@ -210,6 +210,16 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, + "Microsoft.VisualStudio.Threading": { + "type": "Direct", + "requested": "[17.12.19, )", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, "Microsoft.VisualStudio.Threading.Analyzers": { "type": "Direct", "requested": "[17.12.19, )", @@ -509,6 +519,11 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "4.7.0", diff --git a/src/Bicep.Decompiler.IntegrationTests/packages.lock.json b/src/Bicep.Decompiler.IntegrationTests/packages.lock.json index 145f054ed49..c30c08cfc31 100644 --- a/src/Bicep.Decompiler.IntegrationTests/packages.lock.json +++ b/src/Bicep.Decompiler.IntegrationTests/packages.lock.json @@ -618,8 +618,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -701,28 +701,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1304,11 +1301,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1518,6 +1515,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1590,11 +1588,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1997,11 +1995,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2073,11 +2071,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2480,11 +2478,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2556,11 +2554,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2963,11 +2961,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3039,11 +3037,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3446,11 +3444,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3522,11 +3520,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3929,11 +3927,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4005,11 +4003,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4313,11 +4311,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4389,11 +4387,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4697,11 +4695,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.Decompiler.UnitTests/packages.lock.json b/src/Bicep.Decompiler.UnitTests/packages.lock.json index 145f054ed49..c30c08cfc31 100644 --- a/src/Bicep.Decompiler.UnitTests/packages.lock.json +++ b/src/Bicep.Decompiler.UnitTests/packages.lock.json @@ -618,8 +618,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -701,28 +701,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1304,11 +1301,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1518,6 +1515,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1590,11 +1588,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1997,11 +1995,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2073,11 +2071,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2480,11 +2478,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2556,11 +2554,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2963,11 +2961,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3039,11 +3037,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3446,11 +3444,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3522,11 +3520,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3929,11 +3927,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4005,11 +4003,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4313,11 +4311,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4389,11 +4387,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4697,11 +4695,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.Decompiler/packages.lock.json b/src/Bicep.Decompiler/packages.lock.json index b05a0726c21..4d2f4ec4415 100644 --- a/src/Bicep.Decompiler/packages.lock.json +++ b/src/Bicep.Decompiler/packages.lock.json @@ -440,6 +440,20 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "4.7.0", @@ -970,6 +984,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs index eb6d6e35561..0b100e44362 100644 --- a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs @@ -1,15 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +//asdfg make sure public versions still sorted correctly + using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions.TestingHelpers; using System.Reflection; using System.Text; +using System.Text.Json; using Bicep.Core; +using Bicep.Core.Configuration; using Bicep.Core.Extensions; using Bicep.Core.FileSystem; +using Bicep.Core.Json; using Bicep.Core.Parsing; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Oci; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Samples; using Bicep.Core.Text; using Bicep.Core.UnitTests; @@ -29,6 +35,7 @@ using Bicep.LanguageServer.Utils; using FluentAssertions; using FluentAssertions.Execution; +using Microsoft.Extensions.Azure; using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -41,6 +48,7 @@ using CompilationHelper = Bicep.Core.UnitTests.Utils.CompilationHelper; using LocalFileSystem = System.IO.Abstractions.FileSystem; using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; +using Bicep.Core.UnitTests.Mock.Registry; namespace Bicep.LangServer.IntegrationTests.Completions { @@ -226,33 +234,34 @@ private async Task RequestSnippetCompletion(string bicepFileName, Comple return completion.TextEdit.TextEdit.NewText; } - [DataTestMethod] - [DynamicData(nameof(GetData), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(GetDisplayName))] - [TestCategory(BaselineHelper.BaselineTestCategory)] - public async Task CompletionRequestShouldProduceExpectedCompletions(DataSet dataSet, string setName, IList positions) - { - // ensure all files are present locally - string basePath = dataSet.SaveFilesToTestDirectory(this.TestContext); + //asdfg slow, add back + //[DataTestMethod] + //[DynamicData(nameof(GetData), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(GetDisplayName))] + //[TestCategory(BaselineHelper.BaselineTestCategory)] + //public async Task CompletionRequestShouldProduceExpectedCompletions(DataSet dataSet, string setName, IList positions) + //{ + // // ensure all files are present locally + // string basePath = dataSet.SaveFilesToTestDirectory(this.TestContext); - var entryPoint = Path.Combine(basePath, "main.bicep"); + // var entryPoint = Path.Combine(basePath, "main.bicep"); - var uri = DocumentUri.FromFileSystemPath(entryPoint); + // var uri = DocumentUri.FromFileSystemPath(entryPoint); - var helper = await ServerWithNamespaceAndTestResolver.GetAsync(); + // var helper = await ServerWithNamespaceAndTestResolver.GetAsync(); - await helper.OpenFileOnceAsync(this.TestContext, dataSet.Bicep, uri); + // await helper.OpenFileOnceAsync(this.TestContext, dataSet.Bicep, uri); - var intermediate = new List<(Position position, JToken actual)>(); + // var intermediate = new List<(Position position, JToken actual)>(); - foreach (var position in positions) - { - var actual = await GetActualCompletions(helper.Client, uri, position); + // foreach (var position in positions) + // { + // var actual = await GetActualCompletions(helper.Client, uri, position); - intermediate.Add((position, actual)); - } + // intermediate.Add((position, actual)); + // } - ValidateCompletions(dataSet, setName, intermediate); - } + // ValidateCompletions(dataSet, setName, intermediate); + //} [TestMethod] public async Task String_segments_do_not_return_completions() @@ -4030,7 +4039,7 @@ public async Task LoadFunctionsPathArgument_returnsSymbolsAndFilePathsInCompleti [DataRow("import {} from |", "other.bicep", "import {} from 'other.bicep'|")] [DataRow("import {} from 'oth|'", "other.bicep", "import {} from 'other.bicep'|")] [DataRow("import {} from oth|", "other.bicep", "import {} from 'other.bicep'|")] - public async Task Module_path_completions_are_offered(string fileWithCursors, string expectedLabel, string expectedResult) + public async Task Module_local_path_completions_are_offered(string fileWithCursors, string expectedLabel, string expectedResult) { var fileUri = InMemoryFileResolver.GetFileUri("/path/to/main.bicep"); var fileResolver = new InMemoryFileResolver(new Dictionary @@ -4119,15 +4128,15 @@ public async Task ModuleRegistryReferenceCompletions_GetCompletionsForFolderInsi } [DataTestMethod] - [DataRow("module test 'br:mcr.microsoft.com/bicep/|'", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br:mcr.microsoft.com/bicep/|", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br/public:|'", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br/public:|", BicepSourceFileKind.BicepFile)] - [DataRow("using 'br:mcr.microsoft.com/bicep/|'", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br:mcr.microsoft.com/bicep/|", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br/public:|'", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br/public:|", BicepSourceFileKind.ParamsFile)] - public async Task ModuleRegistryReferenceCompletions_GetPathCompletions(string inputWithCursors, BicepSourceFileKind kind) + [DataRow("module test 'br/public:app/dapr-containerapp:|'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/public:app/dapr-containerapp:|", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|", BicepSourceFileKind.BicepFile)] + [DataRow("using 'br/public:app/dapr-containerapp:|'", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br/public:app/dapr-containerapp:|", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|'", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|", BicepSourceFileKind.ParamsFile)] + public async Task Public_module_version_completions(string inputWithCursors, BicepSourceFileKind kind) { var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep"; var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|'); @@ -4136,33 +4145,52 @@ public async Task ModuleRegistryReferenceCompletions_GetPathCompletions(string i var settingsProvider = StrictMock.Of(); settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false); - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-containerapp", "d1", "contoso.com/help1"), new("app/dapr-containerapp-env", "d2", "contoso.com/help2")]); + var publicModuleMetadataProvider = RegistryCatalogMocks.MockPublicMetadataProvider( + [("bicep/app/dapr-containerapp", "d1", "contoso.com/help1", [ + new("1.0.1", null, null), + new("1.0.2", "d1", "contoso.com/help1") + ])]); using var helper = await MultiFileLanguageServerHelper.StartLanguageServer( TestContext, services => services - .AddSingleton(publicRegistryModuleMetadataProvider.Object) + .AddSingleton(publicModuleMetadataProvider.Object) .AddSingleton(settingsProvider.Object)); var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText); var completions = await file.RequestCompletion(cursor); completions.Count().Should().Be(2); - completions.Should().Contain(x => x.Label == "app/dapr-containerapp" && x.Kind == CompletionItemKind.Snippet && x.Detail == "d1" && x.Documentation!.MarkupContent!.Value == "[View Documentation](contoso.com/help1)"); - completions.Should().Contain(x => x.Label == "app/dapr-containerapp-env" && x.Kind == CompletionItemKind.Snippet && x.Detail == "d2" && x.Documentation!.MarkupContent!.Value == "[View Documentation](contoso.com/help2)"); + completions.Should().SatisfyRespectively( + first => + { + first.Label.Should().Be("1.0.2"); + first.SortText.Should().Be("0000"); + first.Kind.Should().Be(CompletionItemKind.Snippet); + first.Detail.Should().Be("d1"); + first.Documentation!.MarkupContent!.Value.Should().Be("[View Documentation](contoso.com/help1)"); + }, + second => + { + second.Label.Should().Be("1.0.1"); + second.SortText.Should().Be("0001"); + second.Kind.Should().Be(CompletionItemKind.Snippet); + second.Detail.Should().BeNull(); + second.Documentation.Should().BeNull(); + } + ); } [DataTestMethod] - [DataRow("module test 'br/public:app/dapr-containerapp:|'", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br/public:app/dapr-containerapp:|", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|'", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|", BicepSourceFileKind.BicepFile)] - [DataRow("using 'br/public:app/dapr-containerapp:|'", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br/public:app/dapr-containerapp:|", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|'", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:|", BicepSourceFileKind.ParamsFile)] - public async Task ModuleRegistryReferenceCompletions_GetVersionCompletions(string inputWithCursors, BicepSourceFileKind kind) + [DataRow("module test 'br/contoso:app/private-app:|'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/contoso:app/private-app:|", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br:private.contoso.com/app/private-app:|'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br:private.contoso.com/app/private-app:|", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/contoso:app/private-app:|'", BicepSourceFileKind.ParamsFile)] + [DataRow("module test 'br/contoso:app/private-app:|", BicepSourceFileKind.ParamsFile)] + [DataRow("module test 'br:private.contoso.com/app/private-app:|'", BicepSourceFileKind.ParamsFile)] + [DataRow("module test 'br:private.contoso.com/app/private-app:|", BicepSourceFileKind.ParamsFile)] + public async Task Private_module_version_completions(string inputWithCursors, BicepSourceFileKind kind) { var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep"; var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(inputWithCursors, '|'); @@ -4171,35 +4199,72 @@ public async Task ModuleRegistryReferenceCompletions_GetVersionCompletions(strin var settingsProvider = StrictMock.Of(); settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false); - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-containerapp", "d1", "contoso.com/help1")]); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.2", "d1", "contoso.com/help1"), new("1.0.1", null, null)]); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.2", "d1", "contoso.com/help1"), new("1.0.1", null, null)]); + var privateModuleMetadataProvider = RegistryCatalogMocks.MockPrivateMetadataProvider( + "private.contoso.com", + [("app/private-app", "d1", "contoso.com/help1", [ + new("v100", "d100", "contoso.com/help/d100.html"), + new("v101", "d101", "contoso.com/help/d101.html")]) + ]); + var indexer = RegistryCatalogMocks.CreateCatalogWithMocks( + null, + privateModuleMetadataProvider); + + var configurationManager = StrictMock.Of(); //asdfg extract + var moduleAliasesConfiguration = BicepTestConstants.BuiltInConfiguration.With( + moduleAliases: RegistryCatalogMocks.ModuleAliases( + """ + { + "br": { + "contoso": { + "registry": "private.contoso.com" + } + } + } + """)); + configurationManager.Setup(x => x.GetConfiguration(fileUri)).Returns(moduleAliasesConfiguration); using var helper = await MultiFileLanguageServerHelper.StartLanguageServer( TestContext, services => services - .AddSingleton(publicRegistryModuleMetadataProvider.Object) - .AddSingleton(settingsProvider.Object)); + .AddSingleton(settingsProvider.Object) + .AddSingleton(configurationManager.Object) + .AddSingleton(indexer)); var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText); var completions = await file.RequestCompletion(cursor); completions.Count().Should().Be(2); - completions.Should().Contain(x => x.Label == "1.0.1" && x.SortText == "0001" && x.Kind == CompletionItemKind.Snippet && x.Detail == null && x.Documentation == null); - completions.Should().Contain(x => x.Label == "1.0.2" && x.SortText == "0000" && x.Kind == CompletionItemKind.Snippet && x.Detail == "d1" && x.Documentation!.MarkupContent!.Value == "[View Documentation](contoso.com/help1)"); + completions.Should().SatisfyRespectively( + first => + { + first.Label.Should().Be("v101"); + first.SortText.Should().Be("0000"); + first.Kind.Should().Be(CompletionItemKind.Snippet); + first.Detail.Should().Be("d101"); + first.Documentation!.MarkupContent!.Value.Should().Be("[View Documentation](contoso.com/help/d101.html)"); + }, + second => + { + second.Label.Should().Be("v100"); + second.SortText.Should().Be("0001"); + second.Kind.Should().Be(CompletionItemKind.Snippet); + second.Detail.Should().Be("d100"); + second.Documentation!.MarkupContent!.Value.Should().Be("[View Documentation](contoso.com/help/d100.html)"); + } + ); } + //asdfg2 [TestMethod] - [DataRow("module test 'br:mcr.microsoft.com/bicep/foo|'", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br:mcr.microsoft.com/bicep/foo|", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br/public:foo|'", BicepSourceFileKind.BicepFile)] - [DataRow("module test 'br/public:foo|", BicepSourceFileKind.BicepFile)] - [DataRow("using 'br:mcr.microsoft.com/bicep/foo|'", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br:mcr.microsoft.com/bicep/foo|", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br/public:foo|'", BicepSourceFileKind.ParamsFile)] - [DataRow("using 'br/public:foo|", BicepSourceFileKind.ParamsFile)] - public async Task Public_registry_completions_support_prefix_matching(string text, BicepSourceFileKind kind) + [DataRow("module test 'br:mcr.microsoft.com/bicep/abc/foo|'", "bicep/abc/foo/bar", "'br:mcr.microsoft.com/bicep/abc/foo/bar:$0'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br:mcr.microsoft.com/bicep/abc/foo|", "bicep/abc/foo/bar", "'br:mcr.microsoft.com/bicep/abc/foo/bar:$0'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/public:abc/foo|'", "abc/foo/bar", "'br/public:abc/foo/bar:$0'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/public:abc/foo|", "abc/foo/bar", "'br/public:abc/foo/bar:$0'", BicepSourceFileKind.BicepFile)] + [DataRow("using 'br:mcr.microsoft.com/bicep/abc/foo|'", "bicep/abc/foo/bar", "'br:mcr.microsoft.com/bicep/abc/foo/bar:$0'", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br:mcr.microsoft.com/bicep/abc/foo|", "bicep/abc/foo/bar", "'br:mcr.microsoft.com/bicep/abc/foo/bar:$0'", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br/public:abc/foo|'", "abc/foo/bar", "'br/public:abc/foo/bar:$0'", BicepSourceFileKind.ParamsFile)] + [DataRow("using 'br/public:abc/foo|", "abc/foo/bar", "'br/public:abc/foo/bar:$0'", BicepSourceFileKind.ParamsFile)] + public async Task Public_registry_module_completions_support_prefix_matching(string text, string expectedLabelForFoo, string expectedInsertTextForFoo, BicepSourceFileKind kind) { var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep"; var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(text, '|'); @@ -4208,21 +4273,110 @@ public async Task Public_registry_completions_support_prefix_matching(string tex var settingsProvider = StrictMock.Of(); settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false); - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("foo/bar", "d1", "contoso.com/help1"), new("food/bar", "d2", "contoso.com/help2"), new("bar/bar", "d2", "contoso.com/help2")]); + var publicModuleMetadataProvider = RegistryCatalogMocks.MockPublicMetadataProvider([ + ("bicep/abc/foo/bar", "d1", "contoso.com/help1", []), + ("bicep/abc/food/bar", "d2", "contoso.com/help2", []), + ("bicep/abc/bar/bar", "d3", "contoso.com/help3", []), + ]); using var helper = await MultiFileLanguageServerHelper.StartLanguageServer( TestContext, services => services - .AddSingleton(publicRegistryModuleMetadataProvider.Object) - .AddSingleton(settingsProvider.Object)); + .AddSingleton(publicModuleMetadataProvider.Object) + .AddSingleton(settingsProvider.Object)); var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText); var completions = await file.RequestCompletion(cursor); completions.Count().Should().Be(2); - completions.Should().Contain(x => x.Label == "foo/bar"); - completions.Should().Contain(x => x.Label == "food/bar"); + completions.Select(x => (Label: x.Label, InsertText: x.TextEdit!.TextEdit!.NewText)).Should().SatisfyRespectively( + c => + { + c.Label.Should().Be(expectedLabelForFoo); + c.InsertText.Should().Be(expectedInsertTextForFoo); + }, + c => + { + c.Label.Should().Be(expectedLabelForFoo.Replace("foo/", "food/")); + c.InsertText.Should().Be(expectedInsertTextForFoo.Replace("foo/", "food/")); + } + ); + } + + //asdfg if a different base path is specified for an alias in bicepconfig.json, should we add it automaticaly to filter? + + [TestMethod] + [DataRow("module test 'br:registry.contoso.io/bicep/whatever/abc/foo|'", "bicep/whatever/abc/foo/bar", "'br:registry.contoso.io/bicep/whatever/abc/foo/bar:$0'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br:registry.contoso.io/bicep/whatever/abc/foo|", "bicep/whatever/abc/foo/bar", "'br:registry.contoso.io/bicep/whatever/abc/foo/bar:$0'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/myRegistry:abc/foo|'", "abc/foo/bar", "'br/myRegistry:abc/foo/bar:$0'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br/myRegistry_noPath:bicep/whatever/abc/foo|", "bicep/whatever/abc/foo/bar", "'br/myRegistry_noPath:bicep/whatever/abc/foo/bar:$0'", BicepSourceFileKind.BicepFile)] + [DataRow("module test 'br:registry.contoso.io/bicep/whatever/abc/foo|'", "bicep/whatever/abc/foo/bar", "'br:registry.contoso.io/bicep/whatever/abc/foo/bar:$0'", BicepSourceFileKind.ParamsFile)] + [DataRow("module test 'br/myRegistry_noPath:bicep/whatever/abc/foo|", "bicep/whatever/abc/foo/bar", "'br/myRegistry_noPath:bicep/whatever/abc/foo/bar:$0'", BicepSourceFileKind.ParamsFile)] + public async Task Private_registry_completions_support_prefix_matching(string text, string expectedLabelForFoo, string expectedInsertTextForFoo, BicepSourceFileKind kind) + { + var extension = kind == BicepSourceFileKind.ParamsFile ? "bicepparam" : "bicep"; + var (fileText, cursor) = ParserHelper.GetFileWithSingleCursor(text, '|'); + var baseFolder = $"{Guid.NewGuid():D}"; + var fileUri = new Uri($"file:///{baseFolder}/{TestContext.TestName}/main.{extension}"); + + var configurationManager = StrictMock.Of(); + var moduleAliasesConfiguration = BicepTestConstants.BuiltInConfiguration.With( + moduleAliases: ModuleAliasesConfiguration.Bind(JsonElementFactory.CreateElement( + """ + { + "br": { + "myRegistry": { + "registry": "registry.contoso.io", + "modulePath": "bicep/whatever" + }, + "myRegistry_noPath": { + "registry": "registry.contoso.io" + } + } + } + """), + null)); + configurationManager.Setup(x => x.GetConfiguration(fileUri)).Returns(moduleAliasesConfiguration); + + var settingsProvider = StrictMock.Of(); + settingsProvider.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(false); + + var indexer = RegistryCatalogMocks.CreateCatalogWithMocks( + null, + RegistryCatalogMocks.MockPrivateMetadataProvider( + "registry.contoso.io", + [ + ("bicep/whatever/abc/foo/bar", "d1", "contoso.com/help1", []), + ("bicep/whatever/abc/food/bar", "d2", "contoso.com/help2", []), + ("bicep/whatever/abc/bar/bar", "d3", "contoso.com/help3", []), + + ]) + ); + + using var helper = await MultiFileLanguageServerHelper.StartLanguageServer( + TestContext, + services => services + .AddSingleton(settingsProvider.Object) + .AddSingleton(indexer) + .AddSingleton(configurationManager.Object) + ); + + var file = await new ServerRequestHelper(TestContext, helper).OpenFile(fileUri, fileText); + var completions = await file.RequestCompletion(cursor); + + completions.Count().Should().Be(2); + completions.Select(x => (Label: x.Label, InsertText: x.TextEdit!.TextEdit!.NewText)).Should().SatisfyRespectively( + c => + { + c.Label.Should().Be(expectedLabelForFoo); + c.InsertText.Should().Be(expectedInsertTextForFoo); + }, + c => + { + c.Label.Should().Be(expectedLabelForFoo.Replace("foo/", "food/")); + c.InsertText.Should().Be(expectedInsertTextForFoo.Replace("foo/", "food/")); + } + ); } [DataTestMethod] diff --git a/src/Bicep.LangServer.IntegrationTests/Helpers/ServerRequestHelper.cs b/src/Bicep.LangServer.IntegrationTests/Helpers/ServerRequestHelper.cs index 97d3d2dfbfa..0b5835f4b98 100644 --- a/src/Bicep.LangServer.IntegrationTests/Helpers/ServerRequestHelper.cs +++ b/src/Bicep.LangServer.IntegrationTests/Helpers/ServerRequestHelper.cs @@ -44,11 +44,20 @@ public async Task> RequestCompletions(IEnumerable public async Task RequestCompletion(int cursor) { - return await client.RequestCompletion(new CompletionParams + CompletionList completions = await client.RequestCompletion(new CompletionParams { TextDocument = new TextDocumentIdentifier(bicepFile.Uri), Position = TextCoordinateConverter.GetPosition(bicepFile.LineStarts, cursor) }); + + var resolved = new List(); + foreach (var completion in completions.Items) + { + var resolvedCompletion = completion.Data is null ? completion : await client.ResolveCompletion(completion); + resolved.Add(resolvedCompletion); + } + + return new CompletionList(resolved, completions.IsIncomplete); } public async Task RequestReferences(int cursor, bool includeDeclaration) diff --git a/src/Bicep.LangServer.IntegrationTests/LangServerScenarioTests.cs b/src/Bicep.LangServer.IntegrationTests/LangServerScenarioTests.cs index c4ea4b8610c..277a476e0ec 100644 --- a/src/Bicep.LangServer.IntegrationTests/LangServerScenarioTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/LangServerScenarioTests.cs @@ -15,6 +15,7 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.LangServer.IntegrationTests; @@ -66,15 +67,12 @@ public async Task Test_Issue13254() // https://github.com/Azure/bicep/issues/132 // * The module is re-published with different contents. The module cache (on disk) is not aware of this change // * The user forces a module restore to fetch the latest contents - var clientFactory = RegistryHelper.CreateMockRegistryClient("mockregistry.io", "test/foo"); + var clientFactory = RegistryHelper.CreateMockRegistryClient(new RepoDescriptor("mockregistry.io", "test/foo", ["v1"])); async Task publish(string source) => await RegistryHelper.PublishModuleToRegistryAsync( clientFactory, BicepTestConstants.FileSystem, - "modulename", - "br:mockregistry.io/test/foo:1.1", - source, - publishSource: false); + new("br:mockregistry.io/test/foo:1.1", source, WithSource: false)); var cacheRoot = FileHelper.GetCacheRootDirectory(TestContext); var helper = await MultiFileLanguageServerHelper.StartLanguageServer( diff --git a/src/Bicep.LangServer.IntegrationTests/packages.lock.json b/src/Bicep.LangServer.IntegrationTests/packages.lock.json index 96851b2aae8..31997c2f421 100644 --- a/src/Bicep.LangServer.IntegrationTests/packages.lock.json +++ b/src/Bicep.LangServer.IntegrationTests/packages.lock.json @@ -1529,6 +1529,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs index 0f8b6efea2e..1a784e1cef9 100644 --- a/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs +++ b/src/Bicep.LangServer.UnitTests/BicepCompletionProviderTests.cs @@ -4,7 +4,7 @@ using Bicep.Core; using Bicep.Core.Extensions; using Bicep.Core.Features; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Syntax; using Bicep.Core.TypeSystem; using Bicep.Core.UnitTests; @@ -27,7 +27,7 @@ namespace Bicep.LangServer.UnitTests { - // See also Bicep.LangServer.IntegrationTests/CompletionTests.cs4 + // See also Bicep.LangServer.IntegrationTests/CompletionTests.cs [TestClass] public class BicepCompletionProviderTests @@ -39,7 +39,7 @@ private static BicepCompletionProvider CreateProvider() var mockHttpMessageHandler = new MockHttpMessageHandler(); mockHttpMessageHandler.When("*").Respond("application/json", "{}"); - var publicRegistryModuleMetadataProvider = StrictMock.Of(); + var publicModuleMetadataProvider = StrictMock.Of(); var helper = ServiceBuilder.Create(services => services .AddSingleton(server) @@ -47,11 +47,11 @@ private static BicepCompletionProvider CreateProvider() .AddSingleton() .AddSingleton() .AddSingleton() - .AddHttpClient() + .AddHttpClient() .ConfigurePrimaryHttpMessageHandler(() => mockHttpMessageHandler).Services .AddSingleton() .AddSingleton() - .AddSingleton(publicRegistryModuleMetadataProvider.Object) + .AddSingleton(publicModuleMetadataProvider.Object) ); return helper.Construct(); diff --git a/src/Bicep.LangServer.UnitTests/Completions/ModuleReferenceCompletionProviderTests.cs b/src/Bicep.LangServer.UnitTests/Completions/ModuleReferenceCompletionProviderTests.cs index f1e5c443c95..7d13c4174ee 100644 --- a/src/Bicep.LangServer.UnitTests/Completions/ModuleReferenceCompletionProviderTests.cs +++ b/src/Bicep.LangServer.UnitTests/Completions/ModuleReferenceCompletionProviderTests.cs @@ -1,13 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions.TestingHelpers; using System.Runtime.CompilerServices; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Configuration; +using Bicep.Core.Registry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.UnitTests; using Bicep.Core.UnitTests.FileSystem; using Bicep.Core.UnitTests.Mock; +using Bicep.Core.UnitTests.Mock.Registry; +using Bicep.Core.UnitTests.Mock.Registry.Catalog; using Bicep.Core.UnitTests.Utils; using Bicep.IO.FileSystem; using Bicep.LanguageServer; @@ -32,10 +37,20 @@ public class ModuleReferenceCompletionProviderTests public TestContext? TestContext { get; set; } private IAzureContainerRegistriesProvider azureContainerRegistriesProvider = StrictMock.Of().Object; - private static IPublicRegistryModuleMetadataProvider publicRegistryModuleMetadataProvider = StrictMock.Of().Object; private ISettingsProvider settingsProvider = StrictMock.Of().Object; - // TODO: We need improved assertions for all the completion item tests + private static async Task> GetAndResolveCompletionItems(DocumentUri documentUri, BicepCompletionContext completionContext, ModuleReferenceCompletionProvider moduleReferenceCompletionProvider) + { + var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var resolved = new List(); + foreach (var completion in completions) + { + var c = await moduleReferenceCompletionProvider.ResolveCompletionItem(completion, CancellationToken.None); + resolved.Add(c); + } + + return resolved; + } [DataTestMethod] [DataRow("module test |''", 14)] @@ -50,10 +65,10 @@ public async Task GetFilteredCompletions_WithBicepRegistryAndTemplateSpecShemaCo var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Count().Should().Be(4); @@ -110,30 +125,30 @@ public async Task GetFilteredCompletions_WithBicepRegistryAndTemplateSpecShemaCo public async Task GetFilteredCompletions_WithBicepRegistryAndTemplateSpecShemaCompletionContext_AndTemplateSpecAliasInBicepConfigFile_ReturnsCompletionItems() { var bicepConfigFileContents = @"{ - ""moduleAliases"": { - ""br"": { - ""test"": { - ""registry"": ""testacr.azurecr.io"", - ""modulePath"": ""bicep/modules"" - } - }, - ""ts"": { - ""mySpecRG"": { - ""subscription"": ""00000000-0000-0000-0000-000000000000"", - ""resourceGroup"": ""test-rg"" - } - } - } -}"; + ""moduleAliases"": { + ""br"": { + ""test"": { + ""registry"": ""testacr.azurecr.io"", + ""modulePath"": ""bicep/modules"" + } + }, + ""ts"": { + ""mySpecRG"": { + ""subscription"": ""00000000-0000-0000-0000-000000000000"", + ""resourceGroup"": ""test-rg"" + } + } + } + }"; var (completionContext, configMgr, documentUri) = GetBicepCompletionContext("module test '|'", bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Count().Should().Be(5); @@ -198,20 +213,21 @@ public async Task GetFilteredCompletions_WithInvalidTextInCompletionContext_Retu var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().BeEmpty(); } [DataTestMethod] + // CONSIDER: This doesn't actually test anything useful because the current code takes the entire string + // into account, and ignores where the cursor is. [DataRow("module test 'br/public:app/dapr-containerapp:1.0.1|")] [DataRow("module test 'br/public:app/dapr-containerapp:1.0.1|'")] [DataRow("module test |'br/public:app/dapr-containerapp:1.0.1'")] [DataRow("module test 'br/public:app/dapr-containerapp:1.0.1'|")] - [DataRow("module test 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:1.0.1|")] [DataRow("module test 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:1.0.1|'")] [DataRow("module test |'br:mcr.microsoft.com/bicep/app/dapr-containerapp:1.0.1'")] [DataRow("module test 'br:mcr.microsoft.com/bicep/app/dapr-containerapp:1.0.1'|")] @@ -221,18 +237,20 @@ public async Task GetFilteredCompletions_WithInvalidTextInCompletionContext_Retu [DataRow("module test 'br:contoso.com/app/dapr-containerapp:1.0.1'|")] public async Task GetFilteredCompletions_WithInvalidCompletionContext_ReturnsEmptyList(string inputWithCursors) { - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([]); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.1", null, null), new("1.0.2", null, null)]); + var catalog = RegistryCatalogMocks.CreateCatalogWithMocks( + RegistryCatalogMocks.MockPublicMetadataProvider([ + ("bicep/app/dapr-containerapp", null, null, [ new("1.0.1", null, null), new ("1.0.2", null, null) ]) + ]) + ); var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider.Object, + catalog, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().BeEmpty(); } @@ -243,26 +261,26 @@ public async Task GetFilteredCompletions_WithInvalidCompletionContext_ReturnsEmp public async Task GetFilteredCompletions_WithAliasCompletionContext_ReturnsCompletionItems(string inputWithCursors, int expectedEnd) { var bicepConfigFileContents = @"{ - ""moduleAliases"": { - ""br"": { - ""test1"": { - ""registry"": ""testacr.azurecr.io"", - ""modulePath"": ""bicep/modules"" - }, - ""test2"": { - ""registry"": ""testacr2.azurecr.io"" - } - } - } -}"; + ""moduleAliases"": { + ""br"": { + ""test1"": { + ""registry"": ""testacr.azurecr.io"", + ""modulePath"": ""bicep/modules"" + }, + ""test2"": { + ""registry"": ""testacr2.azurecr.io"" + } + } + } + }"; var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().SatisfyRespectively( c => @@ -312,21 +330,21 @@ public async Task GetFilteredCompletions_WithAliasCompletionContext_ReturnsCompl public async Task GetFilteredCompletions_WithACRCompletionSettingSetToFalse_ReturnsACRCompletionItemsUsingBicepConfig(string inputWithCursors) { var bicepConfigFileContents = @"{ - ""moduleAliases"": { - ""br"": { - ""test1"": { - ""registry"": ""testacr1.azurecr.io"", - ""modulePath"": ""bicep/modules"" - }, - ""test2"": { - ""registry"": ""testacr2.azurecr.io"" - }, - ""test3"": { - ""registry"": ""testacr2.azurecr.io"" - } - } - } -}"; + ""moduleAliases"": { + ""br"": { + ""test1"": { + ""registry"": ""testacr1.azurecr.io"", + ""modulePath"": ""bicep/modules"" + }, + ""test2"": { + ""registry"": ""testacr2.azurecr.io"" + }, + ""test3"": { + ""registry"": ""testacr2.azurecr.io"" + } + } + } + }"; var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var settingsProviderMock = StrictMock.Of(); @@ -335,10 +353,10 @@ public async Task GetFilteredCompletions_WithACRCompletionSettingSetToFalse_Retu var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProviderMock.Object, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().SatisfyRespectively( c => @@ -376,33 +394,34 @@ public async Task GetFilteredCompletions_WithACRCompletionSettingSetToFalse_Retu public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_ReturnsACRCompletionItemsUsingResourceGraphClient(string inputWithCursors) { var bicepConfigFileContents = @"{ - ""moduleAliases"": { - ""br"": { - ""test1"": { - ""registry"": ""testacr1.azurecr.io"", - ""modulePath"": ""bicep/modules"" - }, - ""test2"": { - ""registry"": ""testacr2.azurecr.io"" - } - } - } -}"; + ""moduleAliases"": { + ""br"": { + ""test1"": { + ""registry"": ""testacr1.azurecr.io"", + ""modulePath"": ""bicep/modules"" + }, + ""test2"": { + ""registry"": ""testacr2.azurecr.io"" + } + } + } + }"; var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var settingsProviderMock = StrictMock.Of(); settingsProviderMock.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(true); var azureContainerRegistriesProvider = StrictMock.Of(); - azureContainerRegistriesProvider.Setup(x => x.GetRegistryUrisAccessibleFromAzure(documentUri.ToUriEncoded(), CancellationToken.None)).Returns(new List { "testacr3.azurecr.io", "testacr4.azurecr.io" }.ToAsyncEnumerable()); + var cloud = configMgr.GetConfiguration(documentUri.ToUriEncoded()).Cloud; + azureContainerRegistriesProvider.Setup(x => x.GetContainerRegistriesAccessibleFromAzure(cloud, CancellationToken.None)).Returns(new List { "testacr3.azurecr.io", "testacr4.azurecr.io" }.ToAsyncEnumerable()); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider.Object, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProviderMock.Object, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().SatisfyRespectively( c => @@ -446,15 +465,16 @@ public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_AndN settingsProviderMock.Setup(x => x.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)).Returns(true); var azureContainerRegistriesProvider = StrictMock.Of(); - azureContainerRegistriesProvider.Setup(x => x.GetRegistryUrisAccessibleFromAzure(documentUri.ToUriEncoded(), CancellationToken.None)).Returns(new List().ToAsyncEnumerable()); + var cloud = configMgr.GetConfiguration(documentUri.ToUriEncoded()).Cloud; + azureContainerRegistriesProvider.Setup(x => x.GetContainerRegistriesAccessibleFromAzure(cloud, CancellationToken.None)).Returns(new List().ToAsyncEnumerable()); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider.Object, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProviderMock.Object, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().SatisfyRespectively( c => @@ -469,8 +489,8 @@ public async Task GetFilteredCompletions_WithACRCompletionsSettingSetToTrue_AndN } [DataTestMethod] - [DataRow("module test 'br:mcr.microsoft.com/bicep/|'", "app/dapr-cntrapp1", "'br:mcr.microsoft.com/bicep/app/dapr-cntrapp1:$0'", "app/dapr-cntrapp2", "'br:mcr.microsoft.com/bicep/app/dapr-cntrapp2:$0'", 41)] - [DataRow("module test 'br:mcr.microsoft.com/bicep/|", "app/dapr-cntrapp1", "'br:mcr.microsoft.com/bicep/app/dapr-cntrapp1:$0'", "app/dapr-cntrapp2", "'br:mcr.microsoft.com/bicep/app/dapr-cntrapp2:$0'", 40)] + [DataRow("module test 'br:mcr.microsoft.com/bicep/|'", "bicep/app/dapr-cntrapp1", "'br:mcr.microsoft.com/bicep/app/dapr-cntrapp1:$0'", "bicep/app/dapr-cntrapp2", "'br:mcr.microsoft.com/bicep/app/dapr-cntrapp2:$0'", 41)] + [DataRow("module test 'br:mcr.microsoft.com/bicep/|", "bicep/app/dapr-cntrapp1", "'br:mcr.microsoft.com/bicep/app/dapr-cntrapp1:$0'", "bicep/app/dapr-cntrapp2", "'br:mcr.microsoft.com/bicep/app/dapr-cntrapp2:$0'", 40)] [DataRow("module test 'br/public:|'", "app/dapr-cntrapp1", "'br/public:app/dapr-cntrapp1:$0'", "app/dapr-cntrapp2", "'br/public:app/dapr-cntrapp2:$0'", 24)] [DataRow("module test 'br/public:|", "app/dapr-cntrapp1", "'br/public:app/dapr-cntrapp1:$0'", "app/dapr-cntrapp2", "'br/public:app/dapr-cntrapp2:$0'", 23)] public async Task GetFilteredCompletions_WithPublicMcrModuleRegistryCompletionContext_ReturnsCompletionItems( @@ -481,17 +501,21 @@ public async Task GetFilteredCompletions_WithPublicMcrModuleRegistryCompletionCo string expectedCompletionText2, int expectedEnd) { - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-cntrapp1", null, null), new("app/dapr-cntrapp2", "description2", "contoso.com/help2")]); + var catalog = RegistryCatalogMocks.CreateCatalogWithMocks( + RegistryCatalogMocks.MockPublicMetadataProvider([ + new("bicep/app/dapr-cntrapp1", null, null, []), + new("bicep/app/dapr-cntrapp2", "description2", "contoso.com/help2", []), + ]) + ); var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, BicepTestConstants.BuiltInOnlyConfigurationManager, - publicRegistryModuleMetadataProvider.Object, + catalog, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().SatisfyRespectively( c => @@ -527,7 +551,7 @@ public async Task GetFilteredCompletions_WithPublicMcrModuleRegistryCompletionCo [DataTestMethod] [DataRow("module test 'br:testacr1.azurecr.io/|'", "bicep/modules", "'br:testacr1.azurecr.io/bicep/modules:$0'", 0, 12, 0, 37)] [DataRow("module test 'br:testacr1.azurecr.io/|", "bicep/modules", "'br:testacr1.azurecr.io/bicep/modules:$0'", 0, 12, 0, 36)] - public async Task GetFilteredCompletions_WithPathCompletionContext_ReturnsCompletionItems( + public async Task GetFilteredCompletions_IfAliasesInBicepConfig_AndRegistriesNotAvailable_GetPartialCompletionsBasedOnConfigOnly( string inputWithCursors, string expectedLabel, string expectedCompletionText, @@ -537,26 +561,43 @@ public async Task GetFilteredCompletions_WithPathCompletionContext_ReturnsComple int endCharacter) { var bicepConfigFileContents = @"{ - ""moduleAliases"": { - ""br"": { - ""test1"": { - ""registry"": ""testacr1.azurecr.io"", - ""modulePath"": ""bicep/modules"" - }, - ""test2"": { - ""registry"": ""testacr2.azurecr.io"" - } - } - } -}"; + ""moduleAliases"": { + ""br"": { + ""test1"": { + ""registry"": ""testacr1.azurecr.io"", + ""modulePath"": ""bicep/modules"" + }, + ""test2"": { + ""registry"": ""testacr2.azurecr.io"" + } + } + } + }"; + + var catalog = RegistryCatalogMocks.CreateCatalogWithMocks( + null); + + //asdfg test if also have access to the registry metadata - duplicate completions? Or is that expected? + //var catalog = RegistryIndexerMocks.CreateRegistryIndexer( + // null, + // RegistryIndexerMocks.MockPrivateMetadataProvider( + // "testacr1.azurecr.io", [ + // ("bicep/modules/module", null, null, []) + // ]), + // RegistryIndexerMocks.MockPrivateMetadataProvider( + // "testacr2.azurecr.io", [ + // ("bicep/modules/module", null, null, []), + // ]) + //); + var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider, + catalog, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().Contain( x => x.Label == expectedLabel && @@ -599,18 +640,21 @@ public async Task GetFilteredCompletions_WithMcrVersionCompletionContext_Returns } } }"; - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([]); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerapp")).Returns([new("1.0.2", null, null), new("1.0.1", "d2", "contoso.com/help%20page.html")]); + var catalog = RegistryCatalogMocks.CreateCatalogWithMocks( + RegistryCatalogMocks.MockPublicMetadataProvider([ + new("bicep/app/dapr-containerapp", null, null, [new("1.0.1", "d2", "contoso.com/help%20page.html"), new("1.0.2", null, null)]), + new("bicep/app/dapr-containerappapp", null, null, [new("1.0.1", "d2", "contoso.com/help%20page.html"), new("1.0.2", null, null)]) + ]) + ); var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider.Object, + catalog, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().Contain(c => c.Label == expectedLabel1) .Which.Should().Match(x => @@ -642,18 +686,24 @@ public async Task GetFilteredCompletions_WithMcrVersionCompletionContext_Returns [TestMethod] public async Task GetFilteredCompletions_WithMcrVersionCompletionContext_AndNoMatchingModuleName_ReturnsEmptyListOfCompletionItems() { - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([]); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModuleVersionsMetadata("app/dapr-containerappapp")).Returns([]); + var catalog = new RegistryModuleCatalog( + RegistryCatalogMocks.MockPublicMetadataProvider([ + new("bicep/app/dapr-containerappapp", null, null, []), + new("bicep/app/app/dapr-cntrapp2", "description2", "contoso.com/help2", []), + ]).Object, + StrictMock.Of().Object, + StrictMock.Of().Object, + BicepTestConstants.BuiltInOnlyConfigurationManager + ); var (completionContext, configMgr, documentUri) = GetBicepCompletionContext("module test 'br/public:app/dapr-containerappapp:|'"); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider.Object, + catalog, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().BeEmpty(); } @@ -687,10 +737,10 @@ public async Task GetFilteredCompletions_WithPublicAliasOverriddenInBicepConfigA var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - var completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + var completions = await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); completions.Should().Contain( x => x.Label == expectedLabel && @@ -704,10 +754,10 @@ public async Task GetFilteredCompletions_WithPublicAliasOverriddenInBicepConfigA } [DataTestMethod] - [DataRow("module test 'br/test1:|'", "dapr-containerappapp", "'br/test1:dapr-containerappapp:$0'", 0, 12, 0, 23)] - [DataRow("module test 'br/test1:|", "dapr-containerappapp", "'br/test1:dapr-containerappapp:$0'", 0, 12, 0, 22)] - [DataRow("module test 'br/test2:|'", "bicep/app/dapr-containerappapp", "'br/test2:bicep/app/dapr-containerappapp:$0'", 0, 12, 0, 23)] - [DataRow("module test 'br/test2:|", "bicep/app/dapr-containerappapp", "'br/test2:bicep/app/dapr-containerappapp:$0'", 0, 12, 0, 22)] + [DataRow("module test 'br/test1:|'", "dapr-containerapp", "'br/test1:dapr-containerapp:$0'", 0, 12, 0, 23)] + [DataRow("module test 'br/test1:|", "dapr-containerapp", "'br/test1:dapr-containerapp:$0'", 0, 12, 0, 22)] + [DataRow("module test 'br/test2:|'", "bicep/app/dapr-containerapp", "'br/test2:bicep/app/dapr-containerapp:$0'", 0, 12, 0, 23)] + [DataRow("module test 'br/test2:|", "bicep/app/dapr-containerapp", "'br/test2:bicep/app/dapr-containerapp:$0'", 0, 12, 0, 22)] public async Task GetFilteredCompletions_WithAliasForMCRInBicepConfigAndModulePath_ReturnsCompletionItems( string inputWithCursors, string expectedLabel, @@ -730,17 +780,21 @@ public async Task GetFilteredCompletions_WithAliasForMCRInBicepConfigAndModulePa } } }"; - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-containerappapp", "dapr description", "contoso.com/help")]); + + var catalog = RegistryCatalogMocks.CreateCatalogWithMocks( + RegistryCatalogMocks.MockPublicMetadataProvider([ + new("bicep/app/dapr-containerapp", "dapr description", "contoso.com/help", []), + ]) + ); var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider.Object, + catalog, settingsProvider, BicepTestConstants.CreateMockTelemetryProvider().Object); - IEnumerable completions = await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + IEnumerable completions = await GetAndResolveCompletionItems(documentUri, completionContext, moduleReferenceCompletionProvider); CompletionItem actualCompletionItem = completions.First(x => x.Label == expectedLabel); actualCompletionItem.Kind.Should().Be(CompletionItemKind.Snippet); @@ -764,7 +818,7 @@ public async Task GetFilteredCompletions_WithAliasForMCRInBicepConfigAndModulePa [DataRow("module foo 'br/test1:|", ModuleRegistryType.ACR)] [DataRow("module foo 'br/test2:|", ModuleRegistryType.ACR)] [DataRow("module foo 'br/test3:|", ModuleRegistryType.MCR)] - [DataRow("module foo 'br/test4:|", ModuleRegistryType.MCR)] + //asdfg [DataRow("module foo 'br/test4:|", ModuleRegistryType.MCR)] public async Task VerifyTelemetryEventIsPostedOnModuleRegistryPathCompletion(string inputWithCursors, string moduleRegistryType) { var bicepConfigFileContents = @"{ @@ -789,8 +843,18 @@ public async Task VerifyTelemetryEventIsPostedOnModuleRegistryPathCompletion(str }"; var (completionContext, configMgr, documentUri) = GetBicepCompletionContext(inputWithCursors, bicepConfigFileContents); - var publicRegistryModuleMetadataProvider = StrictMock.Of(); - publicRegistryModuleMetadataProvider.Setup(x => x.GetModulesMetadata()).Returns([new("app/dapr-cntrapp1", "description1", null), new("app/dapr-cntrapp2", null, "contoso.com/help2")]); + var catalog = RegistryCatalogMocks.CreateCatalogWithMocks( + RegistryCatalogMocks.MockPublicMetadataProvider([ + new("bicep/app/dapr-cntrapp1", "description1", null, []), + new("bicep/app/dapr-cntrapp2", null, "contoso.com/help2", []) + ]), + RegistryCatalogMocks.MockPrivateMetadataProvider( + "myacr.azurecr.io", + [ + new("bicep/modules", null, null, []) + ] + ) + ); var telemetryProvider = StrictMock.Of(); telemetryProvider.Setup(x => x.PostEvent(It.IsAny())); @@ -798,10 +862,10 @@ public async Task VerifyTelemetryEventIsPostedOnModuleRegistryPathCompletion(str var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider, configMgr, - publicRegistryModuleMetadataProvider.Object, + catalog, settingsProvider, telemetryProvider.Object); - await moduleReferenceCompletionProvider.GetFilteredCompletions(documentUri.ToUriEncoded(), completionContext, CancellationToken.None); + await GetAndResolveCompletionItems(documentUri.ToUriEncoded(), completionContext, moduleReferenceCompletionProvider); telemetryProvider.Verify(m => m.PostEvent(It.Is( p => p.EventName == TelemetryConstants.EventNames.ModuleRegistryPathCompletion && @@ -836,13 +900,13 @@ async IAsyncEnumerable GetUris([EnumeratorCancellation] CancellationToke secondItemReturned = true; yield return "testacr4.azurecr.io"; } - azureContainerRegistriesProvider.Setup(x => x.GetRegistryUrisAccessibleFromAzure(documentUri.ToUriEncoded(), It.IsAny())) - .Returns((Uri uri, CancellationToken ct) => GetUris(ct)); + azureContainerRegistriesProvider.Setup(x => x.GetContainerRegistriesAccessibleFromAzure(It.IsAny(), It.IsAny())) + .Returns((CloudConfiguration _, CancellationToken ct) => GetUris(ct)); var moduleReferenceCompletionProvider = new ModuleReferenceCompletionProvider( azureContainerRegistriesProvider.Object, configMgr, - publicRegistryModuleMetadataProvider, + RegistryCatalogMocks.CreateCatalogWithMocks(), settingsProviderMock.Object, BicepTestConstants.CreateMockTelemetryProvider().Object); diff --git a/src/Bicep.LangServer.UnitTests/Handlers/BicepExternalSourceDocumentLinkHandlerTests.cs b/src/Bicep.LangServer.UnitTests/Handlers/BicepExternalSourceDocumentLinkHandlerTests.cs index b776c635f76..dbbb3f3fd43 100644 --- a/src/Bicep.LangServer.UnitTests/Handlers/BicepExternalSourceDocumentLinkHandlerTests.cs +++ b/src/Bicep.LangServer.UnitTests/Handlers/BicepExternalSourceDocumentLinkHandlerTests.cs @@ -33,6 +33,7 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Window; using static System.Net.Mime.MediaTypeNames; +using static Bicep.Core.UnitTests.Utils.RegistryHelper; namespace Bicep.LangServer.UnitTests.Handlers { @@ -134,8 +135,8 @@ private TextDocumentIdentifier GetDocumentIdForExternalModuleSource(string displ private (string registry, string repo, string tag)[] GetCachedModules() { - var cachedModules = CachedModules.GetCachedRegistryModules(MockFileSystem, CacheRootDirectory); - return cachedModules.Select(x => (x.Registry, x.Repository, x.Tag)).ToArray(); + var cachedModules = CachedModules.GetCachedModules(MockFileSystem, CacheRootDirectory); + return [.. cachedModules.Select(x => (x.Registry, x.Repository, x.Tag))]; } private async Task PublishThreeNestedModules(bool module1WithSource = true, bool module2WithSource = true, bool module3WithSource = true) @@ -146,8 +147,8 @@ private async Task PublishThreeNestedModules(bo // module3 references module1 and module2 and is published with source return await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "param p1 bool", module1WithSource), - ("br:mockregistry.io/test/module1:v2", """ + new("br:mockregistry.io/test/module1:v1", "param p1 bool", module1WithSource), + new("br:mockregistry.io/test/module1:v2", """ module m1 'br:mockregistry.io/test/module1:v1' = { name: 'm1' params: { @@ -155,7 +156,7 @@ private async Task PublishThreeNestedModules(bo } } """, module2WithSource), - ("br:mockregistry.io/test/module1:v3", """ + new("br:mockregistry.io/test/module1:v3", """ module m1v2 'br:mockregistry.io/test/module1:v2' = { name: 'm1v2' } @@ -181,7 +182,7 @@ public async Task IfNotShowingExternalModuleSourceCode_ThenReturnNoLinks() { var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "metadata m = ''", withSource: true) + new("br:mockregistry.io/test/module1:v1", "metadata m = ''", WithSource: true) ]); var moduleDispatcher = CreateModuleDispatcher(clientFactory); await RestoreModuleViaLocalCode(clientFactory, "module1", "v1"); @@ -200,15 +201,15 @@ public async Task NestedExternalSource_ShouldGetCorrectLink() // module2 references module1 and is published with source var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "param p1 bool", withSource: true), - ("br:mockregistry.io/test/module2:v2", """ + new("br:mockregistry.io/test/module1:v1", "param p1 bool", WithSource : true), + new("br:mockregistry.io/test/module2:v2", """ module m1 'br:mockregistry.io/test/module1:v1' = { name: 'm1' params: { p1: true } } - """, withSource: true) + """, WithSource: true) ]); // Compile some code to force restoration of module2 (which should always be the case if we're displaying its source) @@ -240,16 +241,16 @@ public async Task DoublyNestedExternalSource_ShouldGetCorrectLink() // module3 references module1 and module2 and is published with source var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "param p1 bool", withSource: true), - ("br:mockregistry.io/test/module1:v2", """ + new ("br:mockregistry.io/test/module1:v1", "param p1 bool", WithSource: true), + new ("br:mockregistry.io/test/module1:v2", """ module m1 'br:mockregistry.io/test/module1:v1' = { name: 'm1' params: { p1: true } } - """, withSource: true), - ("br:mockregistry.io/test/module1:v3", """ + """, WithSource: true), + new ("br:mockregistry.io/test/module1:v3", """ module m1 'br:mockregistry.io/test/module1:v1' = { name: 'm1' params: { @@ -259,7 +260,7 @@ public async Task DoublyNestedExternalSource_ShouldGetCorrectLink() module m1v2 'br:mockregistry.io/test/module1:v2' = { name: 'm1v2' } - """, withSource: true) + """, WithSource: true) ]); // Compile some code to force restoration of module1:v3 (which should always be the case if we're displaying its source) @@ -315,20 +316,20 @@ public async Task NestedExternalSource_ExternalModuleSourceBeingDisplayedNotYetR // module3 references module1 and module2 and is published with source var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( MockFileSystem, [ - ("br:mockregistry.io/test/module1:v1", "param p1 bool", withSource: true), - ("br:mockregistry.io/test/module1:v2", """ + new("br:mockregistry.io/test/module1:v1", "param p1 bool", WithSource: true), + new("br:mockregistry.io/test/module1:v2", """ module m1 'br:mockregistry.io/test/module1:v1' = { name: 'm1' params: { p1: true } } - """, withSource: true), - ("br:mockregistry.io/test/module1:v3", """ + """, WithSource: true), + new("br:mockregistry.io/test/module1:v3", """ module m1v2 'br:mockregistry.io/test/module1:v2' = { name: 'm1v2' } - """, withSource: true) + """, WithSource: true) ]); // Do *not* force restoration of module1:v3 before showing its source (shouldn't normally happen) @@ -399,7 +400,7 @@ public async Task NestedExternalSource_LinkToModuleNotYetRestored_AndRestoreFail var module3Uri = GetDocumentIdForExternalModuleSource("mockregistry.io/test/module1:v3"); // Unregister module1:v2 so that it can't be restored - clientFactory = RegistryHelper.CreateMockRegistryClients([("mockregistry.io", "test/module1")]).factoryMock; + clientFactory = RegistryHelper.CreateMockRegistryClients(new RepoDescriptor("mockregistry.io", "test/module1", ["tag"])).factoryMock; // Compile some code to force restoration of module1:v3 (which should always be the case if we're displaying its source) var moduleDispatcher = CreateModuleDispatcher(clientFactory); diff --git a/src/Bicep.LangServer.UnitTests/packages.lock.json b/src/Bicep.LangServer.UnitTests/packages.lock.json index 7fd5d406b8a..8d0e44c2a7d 100644 --- a/src/Bicep.LangServer.UnitTests/packages.lock.json +++ b/src/Bicep.LangServer.UnitTests/packages.lock.json @@ -1538,6 +1538,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index 3fbce71d0a8..9f1fb6ba524 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -96,6 +96,11 @@ public async Task> GetFilteredCompletions(Compilatio .Concat(await moduleReferenceCompletionProvider.GetFilteredCompletions(model.SourceFile.Uri, context, cancellationToken)); } + public Task Resolve(CompletionItem completionItem, CancellationToken cancellationToken) + { + return moduleReferenceCompletionProvider.ResolveCompletionItem(completionItem, cancellationToken); + } + private IEnumerable GetParamIdentifierCompletions(SemanticModel paramsSemanticModel, BicepCompletionContext paramsCompletionContext) { if (paramsCompletionContext.Kind.HasFlag(BicepCompletionContextKind.ParamIdentifier) && @@ -541,8 +546,8 @@ private record FileCompletionInfo( if (searchDirectory.Exists()) { - files = searchDirectory.EnumerateFiles().Select(x => x.Uri.ToUri()).ToList(); - dirs = searchDirectory.EnumerateDirectories().Select(x => x.Uri.ToUri()).ToList(); + files = [.. searchDirectory.EnumerateFiles().Select(x => x.Uri.ToUri())]; + dirs = [.. searchDirectory.EnumerateDirectories().Select(x => x.Uri.ToUri())]; // include the parent folder as a completion if we're not at the file system root if (searchDirectory.GetParent() is { } parentSearchDirectory) diff --git a/src/Bicep.LangServer/Completions/CompletionItemBuilder.cs b/src/Bicep.LangServer/Completions/CompletionItemBuilder.cs index 38b6719617c..2c4d71a6f34 100644 --- a/src/Bicep.LangServer/Completions/CompletionItemBuilder.cs +++ b/src/Bicep.LangServer/Completions/CompletionItemBuilder.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Bicep.Core.Json; +using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; @@ -20,6 +22,7 @@ public class CompletionItemBuilder private InsertTextFormat insertTextFormat; private TextEditOrInsertReplaceEdit? textEdit; private InsertTextMode insertTextMode; + private object? data; private string? sortText; private bool preselect; @@ -53,9 +56,18 @@ public CompletionItem Build() SortText = this.sortText, Preselect = this.preselect, Command = this.command, + Data = data is null ? null : JObject.FromObject(data), }; } + // Pass in any object here and the completion handler will be asked to resolve the completion item when it is selected + // (e.g. by filling in details or documentation). + public CompletionItemBuilder WithResolveData(string key, object? data) + { + this.data = data is null ? null : new Dictionary { { key, data } }; + return this; + } + public CompletionItemBuilder WithAdditionalEdits(TextEditContainer editContainer) { this.additionalTextEdits = editContainer; diff --git a/src/Bicep.LangServer/Completions/CompletionItemExtensions.cs b/src/Bicep.LangServer/Completions/CompletionItemExtensions.cs new file mode 100644 index 00000000000..f93e4932810 --- /dev/null +++ b/src/Bicep.LangServer/Completions/CompletionItemExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using Bicep.Core.Json; +using Newtonsoft.Json.Linq; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; + +namespace Bicep.LanguageServer.Completions +{ + public static class CompletionItemExtensions + { + public static CompletionItem WithDocumentation(this CompletionItem completionItem, string? markdown) + { + if (!string.IsNullOrEmpty(markdown)) + { + return completionItem with + { + Documentation = new StringOrMarkupContent(new MarkupContent + { + Kind = MarkupKind.Markdown, + Value = markdown + }) + }; + } + + return completionItem; + } + } +} diff --git a/src/Bicep.LangServer/Completions/ICompletionProvider.cs b/src/Bicep.LangServer/Completions/ICompletionProvider.cs index 966615bc77c..8f7d8669959 100644 --- a/src/Bicep.LangServer/Completions/ICompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/ICompletionProvider.cs @@ -9,5 +9,10 @@ namespace Bicep.LanguageServer.Completions public interface ICompletionProvider { Task> GetFilteredCompletions(Compilation compilation, BicepCompletionContext context, CancellationToken cancellationToken); + + Task Resolve(CompletionItem completionItem, CancellationToken cancellationToken) + { + return Task.FromResult(completionItem); + } } } diff --git a/src/Bicep.LangServer/Completions/IModuleReferenceCompletionProvider.cs b/src/Bicep.LangServer/Completions/IModuleReferenceCompletionProvider.cs index 576fc669850..ae78b20f192 100644 --- a/src/Bicep.LangServer/Completions/IModuleReferenceCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/IModuleReferenceCompletionProvider.cs @@ -8,5 +8,7 @@ namespace Bicep.LanguageServer.Completions public interface IModuleReferenceCompletionProvider { Task> GetFilteredCompletions(Uri templateUri, BicepCompletionContext context, CancellationToken cancellationToken); + + Task ResolveCompletionItem(CompletionItem completionItem, CancellationToken cancellationToken); } } diff --git a/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs b/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs index 2fd8cf7d1d5..5cf7dedffe2 100644 --- a/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +//asdfg testpoint using System.Collections.Immutable; +using System.Configuration; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -9,26 +11,35 @@ using Bicep.Core; using Bicep.Core.Configuration; using Bicep.Core.Parsing; +using Bicep.Core.Registry; using Bicep.Core.Registry.Oci; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Syntax; using Bicep.LanguageServer.Providers; using Bicep.LanguageServer.Settings; using Bicep.LanguageServer.Telemetry; using Bicep.LanguageServer.Utils; +using Microsoft.Win32; +using Microsoft.WindowsAzure.ResourceStack.Common.Extensions; +using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Microsoft.WindowsAzure.ResourceStack.Common.Json; + +//asdfg test documentationUri shows up +//asdfg registry should be case-insensitive +//asdfg bug: module m1 'br/demo:|' = { -> gets nothing +// should be module m1 'br/demo:avm/key-vault:' = { namespace Bicep.LanguageServer.Completions { /// /// Provides completions for OCI (public or private) module references, e.g. br/public:modulePath:version /// - public class ModuleReferenceCompletionProvider : IModuleReferenceCompletionProvider + public partial class ModuleReferenceCompletionProvider : IModuleReferenceCompletionProvider { private readonly IAzureContainerRegistriesProvider azureContainerRegistriesProvider; - private readonly IConfigurationManager configurationManager; - private readonly IPublicRegistryModuleMetadataProvider publicRegistryModuleMetadataProvider; + private readonly IRegistryModuleCatalog registryModuleCatalog; private readonly ISettingsProvider settingsProvider; private readonly ITelemetryProvider telemetryProvider; @@ -42,27 +53,114 @@ private enum ModuleCompletionPriority // Direct reference to a full registry login server URI via br: private static readonly Regex ModulePrefixWithFullPath = new(@"^br:(?(.*?))/", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase); - // Aliased reference to a registry via br/alias:path - private static readonly Regex ModuleWithAliasAndVersionSeparator = new(@"^br/(.*):(?(.*?)):", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase); + //asdfg bug: + // module automation_account 'br:sawbicep.azurecr.io/nginx|' = { + // suggests demo, if you select it, it becomes: + // module automation_account 'br:sawbicep.azurecr.io/nginxdemo:' = { + + //asdfg test? + [GeneratedRegex( + """ + (?x) # Extended mode (allow comments and whitespace) + ^ + ( # Prefix and registry or alias + + br/(?[a-zA-Z0-9-_]*): # see src\Bicep.Core\Configuration\ModuleAliasesConfiguration.cs::ModuleAliasNameRegex + | + br:(?[-0-9A-Za-z|.^]*)\/ + ) + + # Path + ( + (?[a-z0-9._\-/]*) # see src\Bicep.Core\Configuration\ModuleAliasesConfiguration.cs::OciNamespaceSegmentRegex + )? + + # Version + ( + (?:) + (?[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127})? # see src\Bicep.Core\Registry\Oci\OciArtifactReferenceFacts.cs::TagNameRegex + )? + + '? + $ + """, + RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant, + matchTimeoutMilliseconds: 10 + )] + private static partial Regex PartialModuleReferenceRegex(); + + // Examples: asdfg move? + // br:contoso.io/path1/path2/module:2.0.1 => + // SpecifiedRegistry: "contoso.io" + // ResolvedRegistry: "contoso.io" + // SpecifiedModulePath: "path1/path2/module" + // ModulePathPrefix: "contosoBasePath/" (from bicepconfig.json, may be empty string) + // ResolvedModulePath: "contosoBasePath/path1/path2/module" + // Version: "2.0.1" + // ToNotation: "br:contoso.io/path1/path2/module:2.0.1" + // br/public:path1/path2/module:2.0.1 => + // SpecifiedRegistry: null + // ResolvedRegistry: "mcr.microsoft.com" + // ModulePathPrefix: "bicep/" (from bicepconfig.json default values) + // SpecifiedModulePath: "path1/path2/module" + // ResolvedModulePath: "bicep/path1/path2/module" + // Version: "2.0.1" + // ToNotation: "br/public:path1/path2/module:2.0.1" + private record Parts( + string? SpecifiedRegistry, + string ResolvedRegistry, + string? SpecifiedAlias, + string? ModulePathPrefix, + string? SpecifiedModulePath, + string? Version, + bool HasVersionSeparator + ) + { + public string ToNotation() => + SpecifiedAlias is string + ? $"br/{SpecifiedAlias}:{SpecifiedModulePath}{VersionTextWithSeparator}" + : $"br:{SpecifiedRegistry}/{SpecifiedModulePath}{VersionTextWithSeparator}"; - // Direct reference to the MCR (public) registry via br:mcr.microsoft.com/bicep/path - private static readonly Regex PublicModuleWithFullPathAndVersionSeparator = new($"^br:{PublicMCRRegistry}/bicep/(?(.*?)):'?$", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase); + private string VersionTextWithSeparator => HasVersionSeparator ? $":{Version}" : string.Empty; - // Aliased reference to the MCR (public) registry via br/public: - private static readonly Regex PublicModuleWithAliasAndVersionSeparator = new(@"^br/public:(?(.*?)):'?$", RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase); + public string ModulePathPrefixWithSeparator => string.IsNullOrWhiteSpace(ModulePathPrefix) ? "" : $"{ModulePathPrefix}/"; - private const string PublicMCRRegistry = LanguageConstants.BicepPublicMcrRegistry; // "mcr.microsoft.com" + public string ResolvedModulePath => $"{ModulePathPrefixWithSeparator}{SpecifiedModulePath}"; + + public Parts WithModulePath(string newResolvedModulePath) //asdfg test? + { + if (!string.IsNullOrWhiteSpace(ModulePathPrefix) && newResolvedModulePath.StartsWith(ModulePathPrefixWithSeparator, StringComparison.Ordinal)) + { + return this with + { + SpecifiedModulePath = newResolvedModulePath.Substring(ModulePathPrefixWithSeparator.Length) + }; + } + else + { + return this with + { + SpecifiedModulePath = newResolvedModulePath + }; + } + } + } + + private const string PublicMcrRegistry = LanguageConstants.BicepPublicMcrRegistry; // "mcr.microsoft.com" + + private const string ModuleVersionResolutionKey = "ociVersion"; + private const string ModuleResolutionKey = "oci"; public ModuleReferenceCompletionProvider( IAzureContainerRegistriesProvider azureContainerRegistriesProvider, IConfigurationManager configurationManager, - IPublicRegistryModuleMetadataProvider publicRegistryModuleMetadataProvider, + IRegistryModuleCatalog registryModuleCatalog, ISettingsProvider settingsProvider, ITelemetryProvider telemetryProvider) { this.azureContainerRegistriesProvider = azureContainerRegistriesProvider; this.configurationManager = configurationManager; - this.publicRegistryModuleMetadataProvider = publicRegistryModuleMetadataProvider; + this.registryModuleCatalog = registryModuleCatalog; this.settingsProvider = settingsProvider; this.telemetryProvider = telemetryProvider; } @@ -76,7 +174,8 @@ public async Task> GetFilteredCompletions(Uri source replacementText = token.Text; } - var completions = GetTopLevelCompletions(context, replacementText, sourceFileUri); + var rootConfiguration = configurationManager.GetConfiguration(sourceFileUri); + var completions = GetTopLevelCompletions(context, replacementText, rootConfiguration); var startsWithSingleQuote = replacementText.StartsWith('\''); if (startsWithSingleQuote) @@ -84,9 +183,9 @@ public async Task> GetFilteredCompletions(Uri source var trimmedReplacementText = replacementText.Trim('\''); var replacementsRequiringStartingQuote = - GetOciModulePathCompletions(context, trimmedReplacementText, sourceFileUri) - .Concat(GetPublicModuleVersionCompletions(context, trimmedReplacementText, sourceFileUri)) - .Concat(await GetAllRegistryNameAndAliasCompletions(context, trimmedReplacementText, sourceFileUri, cancellationToken)); + (await GetOciModuleCompletions(context, trimmedReplacementText, rootConfiguration)) + .Concat(await GetVersionCompletions(context, trimmedReplacementText, rootConfiguration)) + .Concat(await GetAllRegistryNameAndAliasCompletions(context, trimmedReplacementText, rootConfiguration, cancellationToken)); completions = [ .. completions, @@ -97,12 +196,60 @@ public async Task> GetFilteredCompletions(Uri source return completions; } + private Parts? ParseParts(string text, RootConfiguration rootConfiguration) + { + var match = PartialModuleReferenceRegex().Match(text); + if (!match.Success) + { + return null; + } + + var path = NullIfEmpty(match.Groups["path"].Value); + var version = NullIfEmpty(match.Groups["version"].Value); + var hasVersionSeparator = match.Groups["versionSeparator"].Success; + + if (NullIfEmpty(match.Groups["registry"].Value) is string registry) + { + // Reference with fully-specified registry + return new Parts(//asdfg testpoint + SpecifiedRegistry: registry, + ResolvedRegistry: registry, + SpecifiedAlias: null, + SpecifiedModulePath: path, + ModulePathPrefix: null, + Version: version, + HasVersionSeparator: hasVersionSeparator + ); + } + else if (NullIfEmpty(match.Groups["alias"].Value) is string alias) + { + // Reference with alias + var aliases = GetModuleAliases(rootConfiguration); + if (aliases.TryGetValue(alias, out OciArtifactModuleAlias? aliasConfig) && !string.IsNullOrWhiteSpace(aliasConfig.Registry)) //asdfg extract + { + return new Parts(//asdfg testpoint + SpecifiedRegistry: null, + ResolvedRegistry: aliasConfig.Registry, + SpecifiedAlias: alias, + ModulePathPrefix: aliasConfig.ModulePath, + SpecifiedModulePath: path, + Version: version, + HasVersionSeparator: hasVersionSeparator + ); + } + } + + return null; + + string? NullIfEmpty(string? value) => string.IsNullOrWhiteSpace(value) ? null : value; + } + // Handles bicep registry and template spec top-level schema completions. // I.e. typing with an empty path: module m1 - private IEnumerable GetTopLevelCompletions(BicepCompletionContext context, string untrimmedReplacementText, Uri sourceFileUri) + private IEnumerable GetTopLevelCompletions(BicepCompletionContext context, string untrimmedReplacementText, RootConfiguration rootConfiguration) { if (!context.Kind.HasFlag(BicepCompletionContextKind.ModulePath) && - !context.Kind.HasFlag(BicepCompletionContextKind.UsingFilePath)) + !context.Kind.HasFlag(BicepCompletionContextKind.UsingFilePath)) //asdfg? { return []; } @@ -114,15 +261,14 @@ private IEnumerable GetTopLevelCompletions(BicepCompletionContex List completionItems = new(); - var rootConfiguration = configurationManager.GetConfiguration(sourceFileUri); var templateSpecModuleAliases = rootConfiguration.ModuleAliases.GetTemplateSpecModuleAliases(); - var bicepModuleAliases = GetModuleAliases(sourceFileUri); + var bicepModuleAliases = GetModuleAliases(rootConfiguration); // Top-level TemplateSpec completions AddCompletionItem("ts:", null, "Template spec", ModuleCompletionPriority.FullPath, "template spec completion"); if (templateSpecModuleAliases.Any()) { - // ts/ + // ts///asdfg testpoint foreach (var kvp in templateSpecModuleAliases) { AddCompletionItem("ts/", kvp.Key, "Template spec", ModuleCompletionPriority.Alias, "template spec alias completion"); @@ -144,7 +290,7 @@ private IEnumerable GetTopLevelCompletions(BicepCompletionContex var alias = kvp.Key; var registry = kvp.Value.Registry as string; var modulePath = kvp.Value.ModulePath as string; - var detail = (string.CompareOrdinal(registry, PublicMCRRegistry) == 0 && string.CompareOrdinal(modulePath, "bicep") == 0) + var detail = (string.CompareOrdinal(registry, PublicMcrRegistry) == 0 && string.CompareOrdinal(modulePath, "bicep") == 0) ? "Public Bicep registry" : $"Alias for br:{registry}/{(modulePath == null ? "" : (modulePath + "/"))}"; @@ -153,7 +299,7 @@ private IEnumerable GetTopLevelCompletions(BicepCompletionContex } else { - // "br/" + // "br/"//asdfg testpoint AddCompletionItem("br/", null, "Bicep registry (alias)", ModuleCompletionPriority.Alias, "module registry alias completion"); } @@ -190,124 +336,101 @@ private bool IsOciArtifactRegistryReference(string trimmedText) return false; } - // Handles version completions for Microsoft Container Registries (MCR): - // - // br/module/name: - // br:mcr.microsoft/bicep/module/name: - // - // etc - private IEnumerable GetPublicModuleVersionCompletions(BicepCompletionContext context, string trimmedText, Uri sourceFileUri) + private async Task> GetVersionCompletions(BicepCompletionContext context, string trimmedText, RootConfiguration rootConfiguration) { - if (!IsOciArtifactRegistryReference(trimmedText)) - { - return []; - } - - string? modulePath; - - if (PublicModuleWithAliasAndVersionSeparator.IsMatch(trimmedText)) - { - var matches = PublicModuleWithAliasAndVersionSeparator.Matches(trimmedText); - modulePath = matches[0].Groups["path"].Value; - } - else if (PublicModuleWithFullPathAndVersionSeparator.IsMatch(trimmedText)) - { - var matches = PublicModuleWithFullPathAndVersionSeparator.Matches(trimmedText); - modulePath = matches[0].Groups["path"].Value; - } - else - { - modulePath = GetAliasedMCRModulePath(trimmedText, sourceFileUri); - } - - if (modulePath is null) + if (ParseParts(trimmedText, rootConfiguration) is not Parts parts + || parts.ResolvedModulePath is null + || !parts.HasVersionSeparator + || !string.IsNullOrWhiteSpace(parts.Version) + ) { return []; } List completions = new(); - var versionsMetadata = publicRegistryModuleMetadataProvider.GetModuleVersionsMetadata(modulePath); - - for (int i = versionsMetadata.Length - 1; i >= 0; i--) + if (await registryModuleCatalog.GetProviderForRegistry(rootConfiguration.Cloud, parts.ResolvedRegistry) + .TryGetModuleAsync($"{parts.ResolvedModulePath}") is { } module) { - var (version, description, documentationUri) = versionsMetadata[i]; + var versions = await module.TryGetVersionsAsync(); - var insertText = $"'{trimmedText}{version}'$0"; + for (int i = 0; i < versions.Length; ++i) + { + var version = versions[versions.Length - 1 - i].Version; // Show versions from most recent to oldest + var insertText = $"'{trimmedText}{version}'$0"; - // Module version is last completion, no follow-up completions triggered - var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, version) - .WithSnippetEdit(context.ReplacementRange, insertText) - .WithFilterText(insertText) - .WithSortText(GetSortText(i)) - .WithDetail(description) - .WithDocumentation(MarkdownHelper.GetDocumentationLink(documentationUri)) - .Build(); + // Module version is last completion, no follow-up completions triggered + // Note: Description and documentation will be resolved later + var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, version) //asdfg + .WithSnippetEdit(context.ReplacementRange, insertText) + .WithFilterText(insertText) + .WithSortText(GetSortText(i)) + .WithResolveData(ModuleVersionResolutionKey, new { Registry = parts.ResolvedRegistry, Module = parts.ResolvedModulePath, Version = version }) //asdfg test + .Build(); - completions.Add(completionItem); + completions.Add(completionItem); + } } return completions; - // Handles scenario where the user has configured an alias for MCR in bicepconfig.json. - string? GetAliasedMCRModulePath(string trimmedText, Uri sourceFileUri) - { - foreach (var kvp in GetModuleAliases(sourceFileUri)) - { - if (kvp.Value.Registry is string registry && - registry.Equals(PublicMCRRegistry, StringComparison.Ordinal)) - { - var aliasFromBicepConfig = $"br/{kvp.Key}:"; - - if (trimmedText.StartsWith(aliasFromBicepConfig, StringComparison.Ordinal)) - { - var matches = ModuleWithAliasAndVersionSeparator.Matches(trimmedText); - - if (!matches.Any()) - { - continue; - } - - string subpath = matches[0].Groups["path"].Value; - - if (subpath is null) - { - continue; - } - - var modulePath = kvp.Value.ModulePath; - - if (modulePath is not null) - { - if (modulePath.StartsWith("bicep/")) - { - modulePath = modulePath.Substring("bicep/".Length); - return $"{modulePath}/{subpath}"; - } - } - else - { - if (subpath.StartsWith("bicep/")) - { - return subpath.Substring("bicep/".Length); - } - } - } - } - } - - return null; - } + //asdfg test + //// Handles scenario where the user has configured an alias for MCR in bicepconfig.json. + //string? GetAliasedMCRModulePath(string trimmedText, Uri sourceFileUri) + //{ + // foreach (var kvp in GetModuleAliases(sourceFileUri)) + // { + // if (kvp.Value.Registry is string registry && + // registry.Equals(PublicMcrRegistry, StringComparison.Ordinal)) + // { + // var aliasFromBicepConfig = $"br/{kvp.Key}:"; + + // if (trimmedText.StartsWith(aliasFromBicepConfig, StringComparison.Ordinal)) + // { + // var matches = ModuleWithAliasAndVersionSeparator.Matches(trimmedText); + // if (!matches.Any()) + // { + // continue; + // } + + // string subpath = matches[0].Groups["path"].Value; + // if (subpath is null) + // { + // continue; + // } + + // var modulePath = kvp.Value.ModulePath; + + // if (modulePath is not null) + // { + // if (modulePath.StartsWith(LanguageConstants.BicepPublicMcrPathPrefix)) //asdfg + // { + // modulePath = modulePath.Substring(LanguageConstants.BicepPublicMcrPathPrefix.Length); + // return $"{modulePath}/{subpath}"; + // } + // } + // else + // { + // if (subpath.StartsWith(LanguageConstants.BicepPublicMcrPathPrefix)) + // { + // return subpath.Substring(LanguageConstants.BicepPublicMcrPathPrefix.Length); //asdfg + // } + // } + // } + // } + // } + + // return null; + //} } - private ImmutableSortedDictionary GetModuleAliases(Uri sourceFileUri) + private ImmutableSortedDictionary GetModuleAliases(RootConfiguration configuration) { - var rootConfiguration = configurationManager.GetConfiguration(sourceFileUri); - return rootConfiguration.ModuleAliases.GetOciArtifactModuleAliases(); + return configuration.ModuleAliases.GetOciArtifactModuleAliases(); } - // Handles remote (OCI) path completions, e.g. br: and br/ - private IEnumerable GetOciModulePathCompletions(BicepCompletionContext context, string trimmedText, Uri sourceFileUri) + // Handles remote (OCI) module path completions, e.g. br: and br/ + private async Task> GetOciModuleCompletions(BicepCompletionContext context, string trimmedText, RootConfiguration rootConfiguration) //asdfg rename? { if (!IsOciArtifactRegistryReference(trimmedText)) { @@ -315,15 +438,15 @@ private IEnumerable GetOciModulePathCompletions(BicepCompletionC } return [ - .. GetPublicModuleCompletions(trimmedText, context), - .. GetPartialPrivatePathCompletionsFromAliases(trimmedText, context, sourceFileUri), - .. GetPublicPathCompletionFromAliases(trimmedText, context, sourceFileUri), + .. await GetModuleCompletions(trimmedText, context, rootConfiguration), + .. GetPartialPrivatePathCompletionsFromAliases(trimmedText, context, rootConfiguration), + .. await GetPublicPathCompletionFromAliases(trimmedText, context, rootConfiguration), ]; } - // Handles path completions for case where user has specified an alias in bicepconfig.json with registry set to "mcr.microsoft.com". - private IEnumerable GetPublicPathCompletionFromAliases(string trimmedText, BicepCompletionContext context, Uri sourceFileUri) + //asdfg combine + private async Task> GetPublicPathCompletionFromAliases(string trimmedText, BicepCompletionContext context, RootConfiguration rootConfiguration) //asdfg rewrite or remove { List completions = new(); @@ -332,23 +455,24 @@ private IEnumerable GetPublicPathCompletionFromAliases(string tr return completions; } - foreach (var kvp in GetModuleAliases(sourceFileUri)) + foreach (var kvp in GetModuleAliases(rootConfiguration)) //asdfg test { - if (kvp.Value.Registry is string registry) + if (kvp.Value.Registry is string inputRegistry) { - // We currently don't support path completion for private modules, but we'll go ahead and log telemetry to track usage. - if (!registry.Equals(PublicMCRRegistry, StringComparison.Ordinal) && + //asdfg ntoe: breaks VerifyTelemetryEventIsPostedOnModuleRegistryPathCompletion + // We currently don't support path completion for private modules, but we'll go ahead and log telemetry to track usage. asdfg + if (!inputRegistry.Equals(PublicMcrRegistry, StringComparison.Ordinal) && //asdfg? trimmedText.Equals($"br/{kvp.Key}:")) { - telemetryProvider.PostEvent(BicepTelemetryEvent.ModuleRegistryPathCompletion(ModuleRegistryType.ACR)); + telemetryProvider.PostEvent(BicepTelemetryEvent.ModuleRegistryPathCompletion(ModuleRegistryType.ACR));//asdfg testpoint break; } // br/[alias-that-points-to-mcr.microsoft.com]: - if (registry.Equals(PublicMCRRegistry, StringComparison.Ordinal) && + if (inputRegistry.Equals(PublicMcrRegistry, StringComparison.Ordinal) && trimmedText.Equals($"br/{kvp.Key}:")) { - var modulePath = kvp.Value.ModulePath; + var modulePath = kvp.Value.ModulePath;//asdfg testpoint if (modulePath is null) { @@ -363,19 +487,22 @@ private IEnumerable GetPublicPathCompletionFromAliases(string tr // } // } - if (trimmedText.Equals($"br/{kvp.Key}:", StringComparison.Ordinal)) + if (trimmedText.Equals($"br/{kvp.Key}:", StringComparison.Ordinal)) //asdfg? { - var modules = publicRegistryModuleMetadataProvider.GetModulesMetadata(); - foreach (var (moduleName, description, documentationUri) in modules) + var modules = await registryModuleCatalog.GetProviderForRegistry(rootConfiguration.Cloud, PublicMcrRegistry).TryGetModulesAsync();//asdfg testpoint + foreach (var module in modules) { - var label = $"bicep/{moduleName}"; - var insertText = $"'{trimmedText}bicep/{moduleName}:$0'"; + //asdfg make sure registry is inputRegistry? + + var label = $"bicep/{module.ModuleName}";//asdfg??//asdfg testpoint + var insertText = $"'{trimmedText}bicep/{module.ModuleName}:$0'"; + var details = await module.TryGetDetailsAsync(); var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, label) .WithSnippetEdit(context.ReplacementRange, insertText) .WithFilterText(insertText) .WithSortText(GetSortText(label, ModuleCompletionPriority.Alias)) - .WithDetail(description) - .WithDocumentation(MarkdownHelper.GetDocumentationLink(documentationUri)) + .WithDetail(details.Description) + .WithDocumentation(MarkdownHelper.GetDocumentationLink(details.DocumentationUri)) .WithFollowupCompletion("module version completion") .Build(); @@ -396,21 +523,21 @@ private IEnumerable GetPublicPathCompletionFromAliases(string tr // } // } // } - - if (modulePath.Equals("bicep", StringComparison.Ordinal) || !modulePath.StartsWith("bicep/", StringComparison.Ordinal)) + //asdfg testpoint + if (modulePath.Equals(LanguageConstants.BicepPublicMcrPathPrefix, StringComparison.Ordinal) || !modulePath.StartsWith(LanguageConstants.BicepPublicMcrPathPrefix, StringComparison.Ordinal)) { continue; } // Completions are e.g. br/[alias]/[module] - var modulePathWithoutBicepKeyword = TrimStart(modulePath, "bicep/"); - var modules = publicRegistryModuleMetadataProvider.GetModulesMetadata(); + var modulePathWithoutBicepKeyword = TrimStart(modulePath, LanguageConstants.BicepPublicMcrPathPrefix); + var modules = await registryModuleCatalog.GetProviderForRegistry(rootConfiguration.Cloud, PublicMcrRegistry).TryGetModulesAsync(); //asdfg testpoint - var matchingModules = modules.Where(x => x.Name.StartsWith($"{modulePathWithoutBicepKeyword}/")); + var matchingModules = modules.Where(x => x.ModuleName.StartsWith($"{modulePathWithoutBicepKeyword}/")); foreach (var module in matchingModules) - { - var label = module.Name.Substring($"{modulePathWithoutBicepKeyword}/".Length); + {//asdfg testpoint + var label = module.ModuleName.Substring($"{modulePathWithoutBicepKeyword}/".Length); StringBuilder sb = new($"'{trimmedText}"); if (!trimmedText.EndsWith(':')) @@ -419,13 +546,14 @@ private IEnumerable GetPublicPathCompletionFromAliases(string tr } sb.Append($"{label}:$0'"); var insertText = sb.ToString(); + var details = await module.TryGetDetailsAsync(); var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, label) .WithSnippetEdit(context.ReplacementRange, insertText) .WithFilterText(insertText) .WithSortText(GetSortText(label, ModuleCompletionPriority.Alias)) - .WithDetail(module.Description) - .WithDocumentation(MarkdownHelper.GetDocumentationLink(module.DocumentationUri)) + .WithDetail(details.Description) + .WithDocumentation(MarkdownHelper.GetDocumentationLink(details.DocumentationUri)) .WithFollowupCompletion("module version completion") .Build(); completions.Add(completionItem); @@ -436,7 +564,7 @@ private IEnumerable GetPublicPathCompletionFromAliases(string tr } if (completions.Any()) - { + {//asdfg testpoint telemetryProvider.PostEvent(BicepTelemetryEvent.ModuleRegistryPathCompletion(ModuleRegistryType.MCR)); } @@ -445,6 +573,23 @@ private IEnumerable GetPublicPathCompletionFromAliases(string tr private string TrimStart(string text, string prefixToTrim) => text.StartsWith(prefixToTrim) ? text.Substring(prefixToTrim.Length) : text; + private string? GetFirstMatch(Regex regex, string text, string group, bool allowEmpty) + { + var matches = regex.Matches(text); + if (!matches.Any()) + { + return null; + } + + string? value = matches[0].Groups[group].Value; + if (!allowEmpty && string.IsNullOrWhiteSpace(value)) + { + return null;//asdfg testpoint + } + + return value; + } + /// /// True if a direct reference to a private ACR registry (i.e. not pointing to the Microsoft public bicep registry) /// Example: @@ -455,17 +600,8 @@ private IEnumerable GetPublicPathCompletionFromAliases(string tr /// private bool IsPrivateRegistryReference(string text, [NotNullWhen(true)] out string? registry) { - registry = null; - - var matches = ModulePrefixWithFullPath.Matches(text); - if (!matches.Any()) - { - return false; - } - - registry = matches[0].Groups["registry"].Value; - - return !registry.Equals(PublicMCRRegistry, StringComparison.Ordinal); + registry = GetFirstMatch(ModulePrefixWithFullPath, text, "registry", allowEmpty: false); + return registry is not null && !registry.Equals(PublicMcrRegistry, StringComparison.Ordinal); } // We only support partial path completions for ACR using module paths listed in bicepconfig.json @@ -484,17 +620,25 @@ private bool IsPrivateRegistryReference(string text, [NotNullWhen(true)] out str // br:privateacr.azurecr.io/ // => // br:privateacr.azurecr.io/bicep/app: - private IEnumerable GetPartialPrivatePathCompletionsFromAliases(string trimmedText, BicepCompletionContext context, Uri sourceFileUri) + //asdfg make sure tested, then remove + private IEnumerable GetPartialPrivatePathCompletionsFromAliases(string trimmedText, BicepCompletionContext context, RootConfiguration rootConfiguration) //asdfg test { + if (ParseParts(trimmedText, rootConfiguration) is not Parts parts + || !string.IsNullOrWhiteSpace(parts.ResolvedModulePath) + || parts.HasVersionSeparator) + { + return []; + } + List completions = new(); if (!IsPrivateRegistryReference(trimmedText, out string? registry) || string.IsNullOrWhiteSpace(registry)) { - return completions; + return completions;//asdfg testpoint } telemetryProvider.PostEvent(BicepTelemetryEvent.ModuleRegistryPathCompletion(ModuleRegistryType.ACR)); - foreach (var kvp in GetModuleAliases(sourceFileUri)) + foreach (var kvp in GetModuleAliases(rootConfiguration)) { if (registry.Equals(kvp.Value.Registry, StringComparison.Ordinal)) { @@ -502,75 +646,83 @@ private IEnumerable GetPartialPrivatePathCompletionsFromAliases( if (modulePath is null) { - continue; + continue;//asdfg testpoint } var insertText = $"'{trimmedText}{modulePath}:$0'"; var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Reference, modulePath) - .WithSnippetEdit(context.ReplacementRange, insertText) - .WithFilterText(insertText) - .WithSortText(GetSortText(modulePath)) - .WithFollowupCompletion("module path completion") - .Build(); - completions.Add(completionItem); + .WithSnippetEdit(context.ReplacementRange, insertText) + .WithFilterText(insertText) + .WithSortText(GetSortText(modulePath)) + .WithResolveData(ModuleResolutionKey, new { Registry = registry, Module = modulePath }) //asdfg test + .WithFollowupCompletion("module path completion") + .Build(); + completions.Add(completionItem);//asdfg testpoint } } return completions; } + //asdfg make sure sorted by version + // Handles module path completions for MCR: // br/public: // or // br:mcr.microsoft.com/bicep/: - private IEnumerable GetPublicModuleCompletions(string trimmedText, BicepCompletionContext context) + private async Task> GetModuleCompletions(string trimmedText, BicepCompletionContext context, RootConfiguration rootConfiguration)//asdfgasdfgasdfg { - var (prefix, suffix) = trimmedText switch - { - { } x when x.StartsWith("br/public:", StringComparison.Ordinal) => ("br/public:", x["br/public:".Length..]), - { } x when x.StartsWith($"br:{PublicMCRRegistry}/bicep/", StringComparison.Ordinal) => ($"br:{PublicMCRRegistry}/bicep/", x[$"br:{PublicMCRRegistry}/bicep/".Length..]), - _ => (null, null), - }; - - if (prefix is null || suffix is null) + if (ParseParts(trimmedText, rootConfiguration) is not Parts parts + || parts.ResolvedModulePath is null + || parts.HasVersionSeparator) { return []; } List completions = new(); - var modules = publicRegistryModuleMetadataProvider.GetModulesMetadata(); - foreach (var (moduleName, description, documentationUri) in modules) + var modules = await registryModuleCatalog.GetProviderForRegistry(rootConfiguration.Cloud, parts.ResolvedRegistry) + .TryGetModulesAsync(); + foreach (var module in modules) { - if (!moduleName.StartsWith(suffix, StringComparison.Ordinal)) + var moduleName = module.ModuleName; + + if (!moduleName.StartsWith(parts.ResolvedModulePath, StringComparison.Ordinal)) { continue; } - var insertText = $"'{prefix}{moduleName}:$0'"; + string insertText = $"'{parts.WithModulePath(moduleName).ToNotation()}:$0'"; - var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, moduleName) - .WithSnippetEdit(context.ReplacementRange, insertText) - .WithFilterText(insertText) - .WithSortText(GetSortText(moduleName)) - .WithDetail(description) - .WithDocumentation(MarkdownHelper.GetDocumentationLink(documentationUri)) - .WithFollowupCompletion("module version completion") - .Build(); + // Remove the base path prefix from the label if we're dealing with a module alias, because the user doesn't need to see it + var label = !string.IsNullOrWhiteSpace(parts.SpecifiedAlias) && !string.IsNullOrWhiteSpace(parts.ModulePathPrefix) + ? moduleName.Substring(parts.ModulePathPrefixWithSeparator.Length) + : moduleName; + + var completionItem = CompletionItemBuilder.Create( + CompletionItemKind.Snippet, label) + .WithSnippetEdit(context.ReplacementRange, insertText) + .WithFilterText(insertText) + .WithSortText(GetSortText(moduleName)) + .WithResolveData( //asdfg test + ModuleResolutionKey, + new { Registry = module.Registry, Module = moduleName }) + .WithFollowupCompletion("module version completion") + .Build(); completions.Add(completionItem); } if (completions.Any()) { - telemetryProvider.PostEvent(BicepTelemetryEvent.ModuleRegistryPathCompletion(ModuleRegistryType.MCR)); + telemetryProvider.PostEvent(BicepTelemetryEvent.ModuleRegistryPathCompletion(ModuleRegistryType.MCR/*asdfg wrong*/)); } return completions; } // Handles top-level completions of registry names/aliases after br: and br/ - private async Task> GetAllRegistryNameAndAliasCompletions(BicepCompletionContext context, string trimmedText, Uri sourceFileUri, CancellationToken cancellationToken) + private async Task> GetAllRegistryNameAndAliasCompletions(BicepCompletionContext context, string trimmedText, RootConfiguration rootConfiguration, CancellationToken cancellationToken) { var completions = new List(); @@ -581,7 +733,7 @@ private async Task> GetAllRegistryNameAndAliasComple if (trimmedText == "br/") { - foreach (var kvp in GetModuleAliases(sourceFileUri)) + foreach (var kvp in GetModuleAliases(rootConfiguration)) { var alias = kvp.Key; var insertText = $"'{trimmedText}{alias}:$0'"; @@ -596,7 +748,7 @@ private async Task> GetAllRegistryNameAndAliasComple } else if (trimmedText == "br:") { - var label = $"{PublicMCRRegistry}/bicep"; + var label = $"{PublicMcrRegistry}/bicep";//asdfg testpoint var insertText = $"'{trimmedText}{label}/$0'"; var mcrCompletionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, label) .WithFilterText(insertText) @@ -607,23 +759,23 @@ private async Task> GetAllRegistryNameAndAliasComple completions.Add(mcrCompletionItem); - completions.AddRange(await GetPrivateModuleCompletions(trimmedText, context, sourceFileUri, cancellationToken)); + completions.AddRange(await GetPrivateModuleCompletionsAsdfg(trimmedText, context, rootConfiguration, cancellationToken)); } return completions; } // Handles registry name completions for private modules possibly available in ACR registries - private async Task> GetPrivateModuleCompletions(string trimmedText, BicepCompletionContext context, Uri sourceFileUri, CancellationToken cancellationToken) + private async Task> GetPrivateModuleCompletionsAsdfg(string trimmedText, BicepCompletionContext context, RootConfiguration rootConfiguration, CancellationToken cancellationToken) { if (settingsProvider.GetSetting(LangServerConstants.GetAllAzureContainerRegistriesForCompletionsSetting)) { - return await GetACRModuleRegistriesCompletionsFromAzure(trimmedText, context, sourceFileUri, cancellationToken); + return await GetACRModuleRegistriesCompletionsFromAzure(trimmedText, context, rootConfiguration, cancellationToken); //asdfg testpoint } else { // CONSIDER: Somehow indicate in the completion list that users can get more completions by setting GetAllAzureContainerRegistriesForCompletionsSetting - return GetACRModuleRegistriesCompletionsFromBicepConfig(trimmedText, context, sourceFileUri); + return GetPartialACRModuleRegistriesCompletionsFromBicepConfig(trimmedText, context, rootConfiguration); //asdfg testpoint } } @@ -631,13 +783,13 @@ private async Task> GetPrivateModuleCompletions(stri // This returns all registries that the user has access to via Azure (whether or not they contain bicep modules, and whether // or not they're registered in the bicepconfig.json file) // This is for completions after typing "br:" - private async Task> GetACRModuleRegistriesCompletionsFromAzure(string trimmedText, BicepCompletionContext context, Uri sourceFileUri, CancellationToken cancellationToken) + private async Task> GetACRModuleRegistriesCompletionsFromAzure(string trimmedText, BicepCompletionContext context, RootConfiguration rootConfiguration, CancellationToken cancellationToken) { List completions = new(); try { - await foreach (string registryName in azureContainerRegistriesProvider.GetRegistryUrisAccessibleFromAzure(sourceFileUri, cancellationToken) + await foreach (string registryName in azureContainerRegistriesProvider.GetContainerRegistriesAccessibleFromAzure(rootConfiguration.Cloud, cancellationToken) .WithCancellation(cancellationToken)) { var insertText = $"'{trimmedText}{registryName}/$0'"; @@ -648,7 +800,7 @@ private async Task> GetACRModuleRegistriesCompletion .WithSortText(GetSortText(registryName, ModuleCompletionPriority.FullPath)) .WithFollowupCompletion("module path completion") .Build(); - completions.Add(completionItem); + completions.Add(completionItem);//asdfg testpoint } return completions; @@ -660,21 +812,81 @@ private async Task> GetACRModuleRegistriesCompletion } } + public async Task ResolveCompletionItem(CompletionItem completionItem, CancellationToken _) + { + if (completionItem.Data != null && completionItem.Data[ModuleVersionResolutionKey] is JObject versionData) + { + var registry = versionData["Registry"]?.ToString(); + var modulePath = versionData["Module"]?.ToString(); + var version = versionData["Version"]?.ToString(); + + if (registry != null && modulePath != null && version != null) + { + return await ResolveVersionCompletionItem(completionItem, registry, modulePath, version); + } + } + else if (completionItem.Data != null && completionItem.Data[ModuleResolutionKey] is JObject moduleVersion) + { + var registry = moduleVersion["Registry"]?.ToString(); + var modulePath = moduleVersion["Module"]?.ToString(); + + if (registry != null && modulePath != null) + { + return await ResolveModuleCompletionItem(completionItem, registry, modulePath); + } + } + + return completionItem; + } + + private async Task ResolveVersionCompletionItem(CompletionItem completionItem, string registry, string modulePath, string version) + { + if (registryModuleCatalog.TryGetCachedRegistry(registry) is IRegistryModuleMetadataProvider cachedRegistry + && await cachedRegistry.TryGetModuleAsync(modulePath) is { } module + && await module.TryGetVersionsAsync() is { } versions + &&/*extract?*/ versions.FirstOrDefault(v => v.Version.Equals(version, StringComparison.Ordinal)) is RegistryModuleVersionMetadata metadata/*asdfg does this work if not found?*/) + { + return (completionItem with //asdfg extract + { + Detail = metadata.Details.Description, + }) + .WithDocumentation(MarkdownHelper.GetDocumentationLink(metadata.Details.DocumentationUri)); + } + + return completionItem; + } + + private async Task ResolveModuleCompletionItem(CompletionItem completionItem, string registry, string modulePath) + { + if (registryModuleCatalog.TryGetCachedRegistry(registry) is IRegistryModuleMetadataProvider cachedRegistry + && await cachedRegistry.TryGetModuleAsync(modulePath) is { } module + && await module.TryGetDetailsAsync() is { } details + ) + { + return (completionItem with + { + Detail = details.Description, + }).WithDocumentation(MarkdownHelper.GetDocumentationLink(details.DocumentationUri)); + } + + return completionItem; + } + // Handles private ACR registry name completions only for registries that are configured via aliases in the bicepconfig.json file - private IEnumerable GetACRModuleRegistriesCompletionsFromBicepConfig(string trimmedText, BicepCompletionContext context, Uri sourceFileUri) + private IEnumerable GetPartialACRModuleRegistriesCompletionsFromBicepConfig(string trimmedText, BicepCompletionContext context, RootConfiguration rootConfiguration) { List completions = new(); HashSet aliases = new(); - foreach (var kvp in GetModuleAliases(sourceFileUri)) + foreach (var kvp in GetModuleAliases(rootConfiguration)) { var label = kvp.Value.Registry; - if (label is not null && !label.Equals(PublicMCRRegistry, StringComparison.Ordinal)) + if (label is not null && !label.Equals(PublicMcrRegistry, StringComparison.Ordinal)) { if (!aliases.TryGetValue(label, out _)) { - var insertText = $"'{trimmedText}{label}/$0'"; + var insertText = $"'{trimmedText}{label}/$0'";//asdfg testpoint var completionItem = CompletionItemBuilder.Create(CompletionItemKind.Snippet, label) .WithFilterText(insertText) .WithSnippetEdit(context.ReplacementRange, insertText) diff --git a/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs b/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs index 033ef01853a..c466312acc4 100644 --- a/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs @@ -55,19 +55,19 @@ public override async Task Handle(CompletionParams request, Canc this.logger.LogError("Error with Completion in file {Uri} with {Context}. Underlying exception is: {Exception}", request.TextDocument.Uri, completionContext, e.ToString()); } - return new CompletionList(completions, isIncomplete: false); + return new CompletionList(completions, isIncomplete: false/*asdfg*/); } public override Task Handle(CompletionItem request, CancellationToken cancellationToken) { - return Task.FromResult(request); + return this.completionProvider.Resolve(request, cancellationToken); } protected override CompletionRegistrationOptions CreateRegistrationOptions(CompletionCapability capability, ClientCapabilities clientCapabilities) => new() { DocumentSelector = documentSelectorFactory.CreateForBicepAndParams(), AllCommitCharacters = new Container(), - ResolveProvider = false, + ResolveProvider = true, TriggerCharacters = new Container(":", " ", ".", "/", "'", "@", "{", "#", "?") }; } diff --git a/src/Bicep.LangServer/IServiceCollectionExtensions.cs b/src/Bicep.LangServer/IServiceCollectionExtensions.cs index 9ac0d158eb4..088d2ef7bda 100644 --- a/src/Bicep.LangServer/IServiceCollectionExtensions.cs +++ b/src/Bicep.LangServer/IServiceCollectionExtensions.cs @@ -9,7 +9,7 @@ using Bicep.Core.FileSystem; using Bicep.Core.Registry; using Bicep.Core.Registry.Auth; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem.Providers; using Bicep.Core.Utils; @@ -51,7 +51,7 @@ public static IServiceCollection AddBicepCore(this IServiceCollection services) .AddSingleton() .AddSingleton() .AddSingleton() - .AddPublicRegistryModuleMetadataProviderServices() + .AddRegistryCatalogServices() .AddSingleton(); public static IServiceCollection AddBicepDecompiler(this IServiceCollection services) => services diff --git a/src/Bicep.LangServer/Providers/AzureContainerRegistriesProvider.cs b/src/Bicep.LangServer/Providers/AzureContainerRegistriesProvider.cs index 52295ef2fd2..52dba5af98e 100644 --- a/src/Bicep.LangServer/Providers/AzureContainerRegistriesProvider.cs +++ b/src/Bicep.LangServer/Providers/AzureContainerRegistriesProvider.cs @@ -19,25 +19,23 @@ namespace Bicep.LanguageServer.Providers /// public class AzureContainerRegistriesProvider : IAzureContainerRegistriesProvider { - private readonly IConfigurationManager configurationManager; private readonly ITokenCredentialFactory tokenCredentialFactory; private const string queryToGetRegistryNames = @"Resources | where type == ""microsoft.containerregistry/registries"" | project properties[""loginServer""]"; - public AzureContainerRegistriesProvider(IConfigurationManager configurationManager, ITokenCredentialFactory tokenCredentialFactory) + public AzureContainerRegistriesProvider(ITokenCredentialFactory tokenCredentialFactory) { - this.configurationManager = configurationManager; this.tokenCredentialFactory = tokenCredentialFactory; } // Used for completions after typing "'br:" - public async IAsyncEnumerable GetRegistryUrisAccessibleFromAzure(Uri templateUri, [EnumeratorCancellation] CancellationToken cancellationToken) + public async IAsyncEnumerable GetContainerRegistriesAccessibleFromAzure(CloudConfiguration cloud, [EnumeratorCancellation] CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested();//asdfg testpoint - var armClient = GetArmClient(templateUri); + var armClient = GetArmClient(cloud); TenantCollection tenants = armClient.GetTenants(); await foreach (TenantResource tenant in tenants) @@ -67,14 +65,13 @@ jToken is not null && } } - private ArmClient GetArmClient(Uri templateUri) + private ArmClient GetArmClient(CloudConfiguration cloud) { - var rootConfiguration = configurationManager.GetConfiguration(templateUri); - var credential = tokenCredentialFactory.CreateChain(rootConfiguration.Cloud.CredentialPrecedence, rootConfiguration.Cloud.CredentialOptions, rootConfiguration.Cloud.ActiveDirectoryAuthorityUri); + var credential = tokenCredentialFactory.CreateChain(cloud.CredentialPrecedence, cloud.CredentialOptions, cloud.ActiveDirectoryAuthorityUri); var options = new ArmClientOptions(); options.Diagnostics.ApplySharedResourceManagerSettings(); - options.Environment = new ArmEnvironment(rootConfiguration.Cloud.ResourceManagerEndpointUri, rootConfiguration.Cloud.AuthenticationScope); + options.Environment = new ArmEnvironment(cloud.ResourceManagerEndpointUri, cloud.AuthenticationScope); return new ArmClient(credential); } diff --git a/src/Bicep.LangServer/Providers/IAzureContainerRegistriesProvider.cs b/src/Bicep.LangServer/Providers/IAzureContainerRegistriesProvider.cs index c348580d449..d6853420fa0 100644 --- a/src/Bicep.LangServer/Providers/IAzureContainerRegistriesProvider.cs +++ b/src/Bicep.LangServer/Providers/IAzureContainerRegistriesProvider.cs @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Bicep.Core.Configuration; + namespace Bicep.LanguageServer.Providers { public interface IAzureContainerRegistriesProvider { // Returns login server URIs, e.g. "contoso.azurecr.io" - IAsyncEnumerable GetRegistryUrisAccessibleFromAzure(Uri templateUri, CancellationToken cancellation); + IAsyncEnumerable GetContainerRegistriesAccessibleFromAzure(CloudConfiguration cloud, CancellationToken cancellation); } } diff --git a/src/Bicep.LangServer/Server.cs b/src/Bicep.LangServer/Server.cs index ac989eaeb79..b9e15d40573 100644 --- a/src/Bicep.LangServer/Server.cs +++ b/src/Bicep.LangServer/Server.cs @@ -5,7 +5,7 @@ using System.Net; using System.ServiceProcess; using Bicep.Core.Features; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Tracing; using Bicep.LanguageServer.Handlers; using Bicep.LanguageServer.Options; @@ -96,8 +96,8 @@ public async Task RunAsync(CancellationToken cancellationToken) #pragma warning restore VSTHRD003 // Avoid awaiting foreign Tasks } - var moduleMetadataProvider = server.GetRequiredService(); - moduleMetadataProvider.StartUpdateCache(); + var publicModuleMetadataProvider = server.GetRequiredService(); + publicModuleMetadataProvider.StartCache(); } public void Dispose() diff --git a/src/Bicep.LangServer/packages.lock.json b/src/Bicep.LangServer/packages.lock.json index 3ef21cf0a60..015e133c7b2 100644 --- a/src/Bicep.LangServer/packages.lock.json +++ b/src/Bicep.LangServer/packages.lock.json @@ -578,8 +578,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -602,28 +602,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1155,11 +1152,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1360,6 +1357,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1402,11 +1400,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1809,11 +1807,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1885,11 +1883,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2292,11 +2290,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2368,11 +2366,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2775,11 +2773,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2851,11 +2849,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3258,11 +3256,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3334,11 +3332,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3741,11 +3739,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3817,11 +3815,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4125,11 +4123,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4201,11 +4199,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4509,11 +4507,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.Local.Deploy.IntegrationTests/packages.lock.json b/src/Bicep.Local.Deploy.IntegrationTests/packages.lock.json index 67915bc7451..f8e0e79ade2 100644 --- a/src/Bicep.Local.Deploy.IntegrationTests/packages.lock.json +++ b/src/Bicep.Local.Deploy.IntegrationTests/packages.lock.json @@ -636,8 +636,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -719,28 +719,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1314,11 +1311,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1528,6 +1525,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1600,11 +1598,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2007,11 +2005,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2083,11 +2081,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2490,11 +2488,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2566,11 +2564,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2973,11 +2971,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3049,11 +3047,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3456,11 +3454,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3532,11 +3530,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3939,11 +3937,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4015,11 +4013,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4323,11 +4321,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4399,11 +4397,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4707,11 +4705,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.Local.Deploy/packages.lock.json b/src/Bicep.Local.Deploy/packages.lock.json index a30fe869dd9..7e71227f678 100644 --- a/src/Bicep.Local.Deploy/packages.lock.json +++ b/src/Bicep.Local.Deploy/packages.lock.json @@ -567,6 +567,20 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "4.7.0", @@ -1252,6 +1266,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.Local.Extension.Mock/packages.lock.json b/src/Bicep.Local.Extension.Mock/packages.lock.json index 9cb1f5a45f8..4e2d7d73e91 100644 --- a/src/Bicep.Local.Extension.Mock/packages.lock.json +++ b/src/Bicep.Local.Extension.Mock/packages.lock.json @@ -508,6 +508,20 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "4.7.0", @@ -1038,6 +1052,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.RegistryModuleTool.IntegrationTests/packages.lock.json b/src/Bicep.RegistryModuleTool.IntegrationTests/packages.lock.json index 241fa7493e4..032bb87789c 100644 --- a/src/Bicep.RegistryModuleTool.IntegrationTests/packages.lock.json +++ b/src/Bicep.RegistryModuleTool.IntegrationTests/packages.lock.json @@ -770,8 +770,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -853,28 +853,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1522,11 +1519,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1736,6 +1733,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1837,11 +1835,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2244,11 +2242,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2320,11 +2318,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2727,11 +2725,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2803,11 +2801,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3210,11 +3208,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3286,11 +3284,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3693,11 +3691,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3769,11 +3767,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4176,11 +4174,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4252,11 +4250,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4560,11 +4558,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4636,11 +4634,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4944,11 +4942,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.RegistryModuleTool.TestFixtures/packages.lock.json b/src/Bicep.RegistryModuleTool.TestFixtures/packages.lock.json index 48c74f01f85..c674201123d 100644 --- a/src/Bicep.RegistryModuleTool.TestFixtures/packages.lock.json +++ b/src/Bicep.RegistryModuleTool.TestFixtures/packages.lock.json @@ -758,8 +758,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -841,28 +841,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1516,11 +1513,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1730,6 +1727,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1820,11 +1818,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2227,11 +2225,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2303,11 +2301,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2710,11 +2708,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2786,11 +2784,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3193,11 +3191,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3269,11 +3267,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3676,11 +3674,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3752,11 +3750,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4159,11 +4157,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4235,11 +4233,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4543,11 +4541,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4619,11 +4617,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4927,11 +4925,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.RegistryModuleTool.UnitTests/packages.lock.json b/src/Bicep.RegistryModuleTool.UnitTests/packages.lock.json index 241fa7493e4..032bb87789c 100644 --- a/src/Bicep.RegistryModuleTool.UnitTests/packages.lock.json +++ b/src/Bicep.RegistryModuleTool.UnitTests/packages.lock.json @@ -770,8 +770,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -853,28 +853,25 @@ }, "Microsoft.VisualStudio.Threading": { "type": "Transitive", - "resolved": "17.6.40", - "contentHash": "hLa/0xargG7p3bF7aeq2/lRYn/bVnfZXurUWVHx+MNqxxAUjIDMKi4OIOWbYQ/DTkbn9gv8TLvgso+6EtHVQQg==", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "7.0.0", - "Microsoft.VisualStudio.Threading.Analyzers": "17.6.40", - "Microsoft.VisualStudio.Validation": "17.0.71", - "Microsoft.Win32.Registry": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" } }, "Microsoft.VisualStudio.Validation": { "type": "Transitive", - "resolved": "17.6.11", - "contentHash": "J+9L/iac6c8cwcgVSCMuoIYOlD1Jw4mbZ8XMe1IZVj8p8+3dJ46LnnkIkTRMjK7xs9UtU9MoUp1JGhWoN6fAEw==" + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -1522,11 +1519,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -1736,6 +1733,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", @@ -1837,11 +1835,11 @@ "net8.0/linux-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2244,11 +2242,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2320,11 +2318,11 @@ "net8.0/linux-musl-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -2727,11 +2725,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -2803,11 +2801,11 @@ "net8.0/linux-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3210,11 +3208,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3286,11 +3284,11 @@ "net8.0/osx-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -3693,11 +3691,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -3769,11 +3767,11 @@ "net8.0/osx-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4176,11 +4174,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4252,11 +4250,11 @@ "net8.0/win-arm64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4560,11 +4558,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { @@ -4636,11 +4634,11 @@ "net8.0/win-x64": { "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" } }, "Microsoft.Win32.Registry.AccessControl": { @@ -4944,11 +4942,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" } }, "System.Security.Cryptography.Pkcs": { diff --git a/src/Bicep.RegistryModuleTool/Extensions/IServiceCollectionExtensions.cs b/src/Bicep.RegistryModuleTool/Extensions/IServiceCollectionExtensions.cs index e5a4a58b1a7..5f01054e308 100644 --- a/src/Bicep.RegistryModuleTool/Extensions/IServiceCollectionExtensions.cs +++ b/src/Bicep.RegistryModuleTool/Extensions/IServiceCollectionExtensions.cs @@ -10,7 +10,7 @@ using Bicep.Core.FileSystem; using Bicep.Core.Registry; using Bicep.Core.Registry.Auth; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem.Providers; using Bicep.Core.Utils; @@ -39,7 +39,7 @@ public static IServiceCollection AddBicepCompiler(this IServiceCollection servic .AddSingleton() .AddSingleton() .AddSingleton() - .AddPublicRegistryModuleMetadataProviderServices() + .AddRegistryCatalogServices() .AddSingleton(); } } diff --git a/src/Bicep.RegistryModuleTool/packages.lock.json b/src/Bicep.RegistryModuleTool/packages.lock.json index f62b54da9fb..8eb4cbd5336 100644 --- a/src/Bicep.RegistryModuleTool/packages.lock.json +++ b/src/Bicep.RegistryModuleTool/packages.lock.json @@ -667,6 +667,20 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "4.7.0", @@ -1202,6 +1216,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.Tools.Benchmark/packages.lock.json b/src/Bicep.Tools.Benchmark/packages.lock.json index f59024d7608..1ed25c99c1b 100644 --- a/src/Bicep.Tools.Benchmark/packages.lock.json +++ b/src/Bicep.Tools.Benchmark/packages.lock.json @@ -1614,6 +1614,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/Bicep.Wasm/IServiceCollectionExtensions.cs b/src/Bicep.Wasm/IServiceCollectionExtensions.cs index accf5c8016e..37ef898c3e3 100644 --- a/src/Bicep.Wasm/IServiceCollectionExtensions.cs +++ b/src/Bicep.Wasm/IServiceCollectionExtensions.cs @@ -10,7 +10,7 @@ using Bicep.Core.FileSystem; using Bicep.Core.Registry; using Bicep.Core.Registry.Auth; -using Bicep.Core.Registry.PublicRegistry; +using Bicep.Core.Registry.Catalog; using Bicep.Core.Semantics.Namespaces; using Bicep.Core.TypeSystem.Providers; using Bicep.Core.Utils; @@ -38,7 +38,7 @@ public static IServiceCollection AddBicepCore(this IServiceCollection services) .AddSingleton() .AddSingleton() .AddSingleton() - .AddPublicRegistryModuleMetadataProviderServices() + .AddRegistryCatalogServices() .AddSingleton(); public static IServiceCollection AddBicepDecompiler(this IServiceCollection services) => services diff --git a/src/Bicep.Wasm/packages.lock.json b/src/Bicep.Wasm/packages.lock.json index 72f56ce01be..6bd7e3a338d 100644 --- a/src/Bicep.Wasm/packages.lock.json +++ b/src/Bicep.Wasm/packages.lock.json @@ -525,6 +525,20 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.VisualStudio.Threading": { + "type": "Transitive", + "resolved": "17.12.19", + "contentHash": "eLiGMkMYyaSguqHs3lsrFxy3tAWSLuPEL2pIWRcADMDVAs2xqm3dr1d9QYjiEusTgiClF9KD6OB2NdZP72Oy0Q==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Analyzers": "17.12.19", + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "4.7.0", @@ -1064,6 +1078,7 @@ "Microsoft.Extensions.Http": "[8.0.1, )", "Microsoft.Graph.Bicep.Types": "[0.1.7-preview, )", "Microsoft.PowerPlatform.ResourceStack": "[7.0.0.2080, )", + "Microsoft.VisualStudio.Threading": "[17.12.19, )", "Newtonsoft.Json": "[13.0.3, )", "Semver": "[3.0.0, )", "SharpYaml": "[2.1.1, )", diff --git a/src/vscode-bicep/.vscode/launch.json b/src/vscode-bicep/.vscode/launch.json index 71d7133bf4f..46f69a74eb1 100644 --- a/src/vscode-bicep/.vscode/launch.json +++ b/src/vscode-bicep/.vscode/launch.json @@ -12,7 +12,7 @@ "--extensionDevelopmentPath=${workspaceRoot}" ], "sourceMaps": true, - "preLaunchTask": "${defaultBuildTask}", + "$preLaunchTask": "${defaultBuildTask}", "env": { "BICEP_TRACING_ENABLED": "true", "BICEP_LANGUAGE_SERVER_PATH": "${workspaceRoot}/../Bicep.LangServer/bin/Debug/net8.0/Bicep.LangServer.dll", diff --git a/src/vscode-bicep/package.json b/src/vscode-bicep/package.json index fcc2ef10108..a84eb39c7a8 100644 --- a/src/vscode-bicep/package.json +++ b/src/vscode-bicep/package.json @@ -752,8 +752,8 @@ "test:snapshot": "jest --config jest.config.snapshot.js", "test:update-snapshot": "jest --config jest.config.snapshot.js --updateSnapshot", "testlocal:e2e": "(export BICEP_LANGUAGE_SERVER_PATH=${INIT_CWD}/../Bicep.LangServer/bin/Debug/net8.0/Bicep.LangServer.dll || set BICEP_LANGUAGE_SERVER_PATH=%INIT_CWD%/../Bicep.LangServer/bin/Debug/net8.0/Bicep.LangServer.dll) && npm run build && npm run build:e2e && npm run test:e2e", - "clean": "rimraf ./out ./coverage", - "package": "npm run clean && nbgv-setversion && vsce package --no-dependencies --githubBranch main --out ./vscode-bicep.vsix && nbgv-setversion --reset", + "clean": "", + "package": "nbgv-setversion && vsce package --no-dependencies --githubBranch main --out ./vscode-bicep.vsix && nbgv-setversion --reset", "package:local": "rimraf ./bicepLanguageServer && cp -r ../Bicep.LangServer/bin/Debug/net8.0 ./bicepLanguageServer && npm run package" }, "devDependencies": { From 20046e78152d36395990cca51474f6f9642f53f1 Mon Sep 17 00:00:00 2001 From: "Stephen Weatherford (MSFT)" Date: Fri, 24 Jan 2025 15:10:36 -0800 Subject: [PATCH 2/2] w --- .../MsGraphTypesViaRegistryTests.cs | 2 +- src/Bicep.Core.Samples/DataSetsExtensions.cs | 2 +- .../ApiVersion/ApiVersionProviderTests.cs | 2 +- ...xplicitValuesForLocationParamsRuleTests.cs | 2 +- .../Mock/Registry/RegistryCatalogMocks.cs | 16 -- .../PrivateAcrModuleMetadataProviderTests.cs | 231 +++--------------- .../Utils/RegistryHelper.cs | 4 +- ...stContainerRegistryClientFactoryBuilder.cs | 14 +- .../Registry/AzureContainerRegistryManager.cs | 5 + .../Catalog/BaseModuleMetadataProvider.cs | 2 +- .../IRegistryModuleMetadataProvider.cs | 2 +- .../Registry/Catalog/MightBeLazyAsync.cs | 2 - .../PrivateAcrModuleMetadataProvider.cs | 8 +- .../Catalog/PublicModuleMetadataProvider.cs | 4 +- .../Registry/Catalog/RegistryModuleCatalog.cs | 2 +- .../CompletionTests.cs | 2 - .../ModuleReferenceCompletionProvider.cs | 1 - .../Handlers/BicepCompletionHandler.cs | 2 +- .../AzureContainerRegistriesProvider.cs | 2 +- 19 files changed, 52 insertions(+), 253 deletions(-) diff --git a/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs b/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs index f9e7f5e3449..2c1d89bf96f 100644 --- a/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs +++ b/src/Bicep.Core.IntegrationTests/MsGraphTypesViaRegistryTests.cs @@ -111,7 +111,7 @@ public record ArtifactRegistryAddress(string RegistryAddress, string RepositoryP { public string ToSpecificationString(char delim) => $"br:{RegistryAddress}/{RepositoryPath}{delim}{ExtensionVersion}"; - public RepoDescriptor/*asdfg?*/ ClientDescriptor() => new(RegistryAddress, RepositoryPath, [ExtensionVersion]); + public RepoDescriptor ClientDescriptor() => new(RegistryAddress, RepositoryPath, [ExtensionVersion]); } [TestMethod] diff --git a/src/Bicep.Core.Samples/DataSetsExtensions.cs b/src/Bicep.Core.Samples/DataSetsExtensions.cs index 7a3a6860a0f..74ba8542008 100644 --- a/src/Bicep.Core.Samples/DataSetsExtensions.cs +++ b/src/Bicep.Core.Samples/DataSetsExtensions.cs @@ -72,7 +72,7 @@ public static IContainerRegistryClientFactory CreateMockRegistryClients( throw new InvalidOperationException($"Module '{moduleName}' has an invalid target reference '{target}'. Specify a reference to an OCI artifact."); } - clients.Add(new(targetReference.Registry, targetReference.Repository, [targetReference.Tag ?? "tagasdfg"])); + clients.Add(new(targetReference.Registry, targetReference.Repository, ["tag"])); } return RegistryHelper.CreateMockRegistryClients([.. clients, .. additionalClients]).factoryMock; diff --git a/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs b/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs index 87e267f8f2a..d17ab7ee646 100644 --- a/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs +++ b/src/Bicep.Core.UnitTests/ApiVersion/ApiVersionProviderTests.cs @@ -58,7 +58,7 @@ public void GetResourceTypeNames_ResourceGroup() } [DataTestMethod] - public void GetResourceTypeNames_SeparateScopes() //asdfg very slow, why? + public void GetResourceTypeNames_SeparateScopes() { var apiVersionProvider = CreateDefaultApiVersionProvider(); apiVersionProvider.InjectTypeReferences(ResourceScope.ResourceGroup, FakeResourceTypes.GetFakeResourceTypeReferences(FakeResourceTypes.ResourceScopeTypes)); diff --git a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/ExplicitValuesForLocationParamsRuleTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/ExplicitValuesForLocationParamsRuleTests.cs index 488494ac1e1..a94e65c2872 100644 --- a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/ExplicitValuesForLocationParamsRuleTests.cs +++ b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/ExplicitValuesForLocationParamsRuleTests.cs @@ -331,7 +331,7 @@ param location string }); } - [TestMethod] //asdfg null ref? + [TestMethod] public void If_Module_HasErrors_LocationParam_WithDefault_AndValuePassedIn_CaseInsensitive_ShouldPass() { var result = CompilationHelper.Compile( diff --git a/src/Bicep.Core.UnitTests/Mock/Registry/RegistryCatalogMocks.cs b/src/Bicep.Core.UnitTests/Mock/Registry/RegistryCatalogMocks.cs index 50041f6af45..e5dc825799a 100644 --- a/src/Bicep.Core.UnitTests/Mock/Registry/RegistryCatalogMocks.cs +++ b/src/Bicep.Core.UnitTests/Mock/Registry/RegistryCatalogMocks.cs @@ -18,7 +18,6 @@ namespace Bicep.Core.UnitTests.Mock.Registry { - //asdfg refactor? public static class RegistryCatalogMocks { private const string PublicRegistry = "mcr.microsoft.com"; @@ -143,20 +142,5 @@ string moduleAliasesJson { return ModuleAliasesConfiguration.Bind(JsonElementFactory.CreateElement(moduleAliasesJson), null); } - - //public static Mock MockConfigurationManager(RootConfiguration rootConfiguration) - //{ - // var configurationManager = StrictMock.Of(); - // configurationManager.Setup(x => x.GetConfiguration(It.IsAny())).Returns(rootConfiguration); - // return configurationManager; - //} - - //asdfg needed? - //public static ModuleAliasesConfiguration ModuleAliasesConfig2( //asdfg extension method? - // string moduleAliasesJson - //) - //{ - // return ModuleAliasesConfiguration.Bind(JsonElementFactory.CreateElement(moduleAliasesJson), null); - //} } } diff --git a/src/Bicep.Core.UnitTests/Registry/Catalog/PrivateAcrModuleMetadataProviderTests.cs b/src/Bicep.Core.UnitTests/Registry/Catalog/PrivateAcrModuleMetadataProviderTests.cs index ca578a72437..f6d9660e399 100644 --- a/src/Bicep.Core.UnitTests/Registry/Catalog/PrivateAcrModuleMetadataProviderTests.cs +++ b/src/Bicep.Core.UnitTests/Registry/Catalog/PrivateAcrModuleMetadataProviderTests.cs @@ -22,210 +22,8 @@ namespace Bicep.Core.UnitTests.Registry.Catalog { [TestClass] - public class PrivateAcrModuleMetadataProviderTests //asdfg2 - //asdfg: test after calling to get details, calling to get versions shouldn't require another call to the server - - + public class PrivateAcrModuleMetadataProviderTests { - //asdfg - //private IConfigurationManager ConfigManagerWithModuleAliases(string moduleAliasesJson) - //{ - // var configuration = BicepTestConstants.BuiltInConfiguration.With( - // moduleAliases: RegistryIndexerMocks.ModuleAliases(moduleAliasesJson)); - // return RegistryIndexerMocks.MockConfigurationManager(configuration).Object; - //} - - //private PublicModuleMetadataHttpClient CreateTypedClient() { //asdfg - // var httpClient = MockHttpMessageHandler.ToHttpClient(); - // return new PublicModuleMetadataHttpClient(httpClient); - //} - - - //private static readonly MockHttpMessageHandler MockHttpMessageHandler = new(); - - //[ClassInitialize] - //public static void ClassInitialize(TestContext _) - //{ - // MockHttpMessageHandler - // .When(HttpMethod.Get, "*") - // .Respond("application/json", "asdfg ModuleIndexJson"); - //} - - //asdfg: test after calling to get details, calling to get versions shouldn't require another call to the server - - //[TestMethod] - //public async Task Asdfg() - //{ - // //var moduleName = "module1"; - // //var registryStr = "example.com"; - // //var registryUri = new Uri($"https://{registryStr}"); - // //var repository = $"test/{moduleName}".ToLowerInvariant(); - - // //asdfg - // //var bicepModuleContents = "// hello"; - // //var documentationUri = "https://contoso.com/hep"; - - // //var (clientFactory, blobClients) = RegistryHelper.CreateMockRegistryClients((registryStr, repository)); - - // //var client = new MockContainerRegistryClient(); - - // //var blobClient = blobClients[(registryUri, repository)]; - - // //await RegistryHelper.PublishModuleToRegistryAsync(clientFactory, BicepTestConstants.FileSystem, "modulename", $"br:example.com/test/{moduleName}:v1", bicepModuleContents, publishSource: false, documentationUri); - - // //var manifest = blobClient.Manifests.Single().Value.ToObjectFromJson(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); - - - // //// compile and publish modules using throwaway file system - // //var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync(asdfg - // // new MockFileSystem(), - - // // [.. options.PublishedModules.Select(x => (x, "", true))]); - - // var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( - // new MockFileSystem(), - // [ - // ("br:registry.contoso.io/test/module1:v1", "param p1 bool", withSource: true), - // ("br:registry.contoso.io/test/module2:v1", "param p2 string", withSource: true), - // ("br:registry.contoso.io/test/module1:v2", "param p12 string", withSource: false), - // ]); - - - // //var clientFactory2 = StrictMock.Of(); - // //clientFactory2.Setup(m => m.CreateAuthenticatedRegistryClient(It.IsAny(), It.IsAny())).Returns(client); - - // //clientFactory2.Setup(m => m.) - - // //var acrManager = new AzureContainerRegistryManager(clientFactory); - - // //var asdfg = await acrManager.GetCatalogAsync(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); - // //client.MockRepositoryNames = ["abc", "def", "bicep/abc", "bicep/def"]; - // //var asdfg1 = acrManager.GetCatalogAsync(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); - - // //var indexer = RegistryIndexerMocks.CreateRegistryIndexer(null, - // // RegistryIndexerMocks.MockPrivateMetadataProvider( - // // "registry.contoso.io", - // // [ - // // ("bicep/abc", "description", "https://contoso.com/hep", [ ("1.0.0", "abc 1.0.0 description", "https://contoso.com/help/abc") ]), - // // ("bicep/def", "description", "https://contoso.com/hep", [ ("1.0.0", "def 1.0.0 description", "https://contoso.com/help/def") ]), - // // ])); - - // //var configuration = BicepTestConstants.BuiltInConfiguration.With( - // // moduleAliases: RegistryIndexerMocks.ModuleAliases( - // // """ - // // { - // // "br": { - // // "contoso": { - // // "registry": "private.contoso.io" - // // } - // // } - // // } - // // """)); - // //var configurationManager = StrictMock.Of(); //asdfg extract - // //configurationManager.Setup(x => x.GetConfiguration(It.IsAny())).Returns(configuration); - - // //var registry = indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); - // //registry.Should().NotBeNull(); - // //indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io").Should().BeSameAs(registry); //asdfg separate test - - // //var modules = await registry.TryGetModulesAsync(); - // //modules.Should().HaveCount(2); - - // //modules.Should().SatisfyRespectively( - // // x => - // // { - // // x.ModuleName.Should().Be("bicep/abc"); - // // (await x.TryGetVersionsAsync()).Should().HaveCount(1); - // // x.TryGetVersionsAsync().Result[0].Should().BeEquivalentTo( - // // new RegistryModuleVersionMetadata("1.0.0", new RegistryMetadataDetails("abc 1.0.0 description", "https://contoso.com/help/abc"))); - // // }, - // // x => x.ModuleName.Should().Be("bicep/def") - // //); - - // var provider = new PrivateAcrModuleMetadataProvider( - // BicepTestConstants.BuiltInConfiguration.Cloud, - // "registry.contoso.io", - // clientFactory); - // var modules = await provider.TryGetModulesAsync(); - // modules.Should().HaveCount(2); - //} - - //[TestMethod] - //public void Asdfg2() - //{ - // var provider = new PrivateAcrModuleMetadataProvider( - // BicepTestConstants.BuiltInConfiguration.Cloud, - // "registry.contoso.io", - // StrictMock.Of().Object); - // //var containerRegistryClientFactory = StrictMock.Of(); - - // //var provider = RegistryIndexerMocks.MockPrivateMetadataProvider(asdfg - // // "registry.contoso.io", - // // [ - // // ("bicep/abc", "description", "https://contoso.com/hep", [ ("1.0.0", "abc 1.0.0 description", "https://contoso.com/help/abc") ]), - // // ("bicep/def", "description", "https://contoso.com/hep", [ ("1.0.0", "def 1.0.0 description", "https://contoso.com/help/def") ]), - // // ]); - - // provider.Registry.Should().Be("registry.contoso.io"); - // ////var configManager = ConfigManagerWithModuleAliases(asdfg - // //// """ - // //// { - // //// "br": { - // //// "contoso": { - // //// "registry": "private.contoso.io" - // //// } - // //// } - // //// } - // //// """); - - // //var registry = indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); - // //registry.Should().NotBeNull(); - // //indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io").Should().BeSameAs(registry); //asdfg separate test - - // //var modules = await registry.TryGetModulesAsync(); - // //modules.Should().HaveCount(2); - //} - - //[TestMethod] - //public async Task Asdfg3() - //{ - // var provider = new PrivateAcrModuleMetadataProvider( - // BicepTestConstants.BuiltInConfiguration.Cloud, - // "registry.contoso.io", - // StrictMock.Of().Object); - // var containerRegistryClientFactory = StrictMock.Of(); - // containerRegistryClientFactory.Setup(x => x.CreateAuthenticatedContainerClient(It.IsAny(), It.IsAny())).Returns(new FakeContainerRegistryClient()); - - // //var provider = RegistryIndexerMocks.MockPrivateMetadataProvider(asdfg - // // "registry.contoso.io", - // // [ - // // ("bicep/abc", "description", "https://contoso.com/hep", [ ("1.0.0", "abc 1.0.0 description", "https://contoso.com/help/abc") ]), - // // ("bicep/def", "description", "https://contoso.com/hep", [ ("1.0.0", "def 1.0.0 description", "https://contoso.com/help/def") ]), - // // ]); - - // var modules = await provider.TryGetModulesAsync(); - // modules.Should().HaveCount(2); - // provider.GetCachedModules().Should().BeEmpty(); - - // ////var configManager = ConfigManagerWithModuleAliases(asdfg - // //// """ - // //// { - // //// "br": { - // //// "contoso": { - // //// "registry": "private.contoso.io" - // //// } - // //// } - // //// } - // //// """); - - // //var registry = indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io"); - // //registry.Should().NotBeNull(); - // //indexer.GetRegistry(BicepTestConstants.BuiltInConfiguration.Cloud, "registry.contoso.io").Should().BeSameAs(registry); //asdfg separate test - - // //var modules = await registry.TryGetModulesAsync(); - // //modules.Should().HaveCount(2); - //} - [TestMethod] public async Task TryGetModulesAsync() { @@ -405,5 +203,32 @@ public async Task GetDetails_ShouldAlsoCacheVersions_BecauseGettingModuleDetails var versions = await module!.TryGetVersionsAsync(); containerClient.CallsToGetAllManifestPropertiesAsync.Should().Be(1); } + + [TestMethod] + public async Task ModuleWithNoVersionsThatHaveDetails() + { + var containerClient = new FakeContainerRegistryClient(); + var clientFactory = await RegistryHelper.CreateMockRegistryClientWithPublishedModulesAsync( + new MockFileSystem(), + containerClient, + [ + new("br:registry.contoso.io/test/module1:v1", "metadata hello = 'this is module 1 version 1'\nparam p1 bool", WithSource: true), + new("br:registry.contoso.io/test/module2:v1", "metadata hello = 'this is module 2 version 1'\nparam p2 string", WithSource: true), + new("br:registry.contoso.io/test/module1:v2", "metadata hello = 'this is module 1 version 2'\nparam p12 string", WithSource: false), + ]); + + var provider = new PrivateAcrModuleMetadataProvider( + BicepTestConstants.BuiltInConfiguration.Cloud, + "registry.contoso.io", + clientFactory); + + var module = await provider.TryGetModuleAsync("test/module1"); + module.Should().NotBeNull(); + module!.GetCachedVersions().Should().BeEmpty(); + + var details = await module!.TryGetDetailsAsync(); + details.Description.Should().BeNull(); + details.DocumentationUri.Should().BeNull(); + } } } diff --git a/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs b/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs index 4dc39bcf25f..58107c71b56 100644 --- a/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs @@ -91,14 +91,14 @@ public static IContainerRegistryClientFactory CreateMockRegistryClient(RepoDescr } public static - (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient>, FakeContainerRegistryClient containerRegistryClient/*asdfg don't return?*/) + (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient>, FakeContainerRegistryClient containerRegistryClient) CreateMockRegistryClients(params RepoDescriptor[] clients) { return CreateMockRegistryClients(new FakeContainerRegistryClient(), clients); } public static - /* create type */ (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient>, FakeContainerRegistryClient containerRegistryClient/*asdfg don't return?*/) + /* create type */ (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient>, FakeContainerRegistryClient containerRegistryClient) CreateMockRegistryClients( FakeContainerRegistryClient containerRegistryClient, params RepoDescriptor[] repos) diff --git a/src/Bicep.Core.UnitTests/Utils/TestContainerRegistryClientFactoryBuilder.cs b/src/Bicep.Core.UnitTests/Utils/TestContainerRegistryClientFactoryBuilder.cs index 259c77238ba..353da04409d 100644 --- a/src/Bicep.Core.UnitTests/Utils/TestContainerRegistryClientFactoryBuilder.cs +++ b/src/Bicep.Core.UnitTests/Utils/TestContainerRegistryClientFactoryBuilder.cs @@ -18,19 +18,9 @@ public class TestContainerRegistryClientFactoryBuilder private readonly ImmutableDictionary<(Uri registryUri, string repository), MockRegistryBlobClient>.Builder blobClientsBuilder = ImmutableDictionary.CreateBuilder<(Uri registryUri, string repository), MockRegistryBlobClient>(); private FakeContainerRegistryClient containerClient = new(); - public TestContainerRegistryClientFactoryBuilder WithRepository(RepoDescriptor repo) - { - blobClientsBuilder.TryAdd((new Uri($"https://{repo.Registry}"), repo.Repository), new MockRegistryBlobClient()); - - if (!containerClient.FakeRepositories.ContainsKey(repo.Repository)) - { - containerClient.FakeRepositories.Add(repo.Repository, new(repo.Registry, repo.Repository, [.. repo.Tags.Select(t => t.Tag)])); - } - - return this; - } - public TestContainerRegistryClientFactoryBuilder WithRepository(RepoDescriptor repo, MockRegistryBlobClient client) //asdfg combine with above + public TestContainerRegistryClientFactoryBuilder WithRepository(RepoDescriptor repo, MockRegistryBlobClient? client = null) //asdfg combine with above { + client ??= new MockRegistryBlobClient(); blobClientsBuilder.TryAdd((new Uri($"https://{repo.Registry}"), repo.Repository), client); if (!containerClient.FakeRepositories.ContainsKey(repo.Repository)) diff --git a/src/Bicep.Core/Registry/AzureContainerRegistryManager.cs b/src/Bicep.Core/Registry/AzureContainerRegistryManager.cs index 17d2aa9b638..4c3f74162d3 100644 --- a/src/Bicep.Core/Registry/AzureContainerRegistryManager.cs +++ b/src/Bicep.Core/Registry/AzureContainerRegistryManager.cs @@ -146,6 +146,11 @@ async Task DownloadManifestInternalAsync(bool anonymousAccess Trace.WriteLine($"Attempting to pull artifact for module {artifactReference.FullyQualifiedReference} with anonymous authentication."); return await DownloadManifestInternalAsync(anonymousAccess: true); } + catch (InvalidArtifactException invalidArtifactException) + { + Trace.WriteLine($"Anonymous authentication failed with invalid artifact exception: {invalidArtifactException.Message}. Not retrying."); + throw; + } catch (RequestFailedException requestedFailedException) when (requestedFailedException.Status is 401 or 403) { Trace.WriteLine($"Anonymous authentication failed with status code {requestedFailedException.Status}. Retrying with authenticated client."); diff --git a/src/Bicep.Core/Registry/Catalog/BaseModuleMetadataProvider.cs b/src/Bicep.Core/Registry/Catalog/BaseModuleMetadataProvider.cs index aaed2d952b4..0c46ba35bc9 100644 --- a/src/Bicep.Core/Registry/Catalog/BaseModuleMetadataProvider.cs +++ b/src/Bicep.Core/Registry/Catalog/BaseModuleMetadataProvider.cs @@ -177,7 +177,7 @@ Task QueryData(bool initialDelay) } } - private bool IsCacheExpired() //asdfg test + private bool IsCacheExpired() { var expired = this.lastSuccessfulQuery.HasValue && this.lastSuccessfulQuery.Value + this.CacheValidFor < DateTime.Now; if (expired) diff --git a/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProvider.cs b/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProvider.cs index b6b1827240d..8fc9a54a2c1 100644 --- a/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProvider.cs +++ b/src/Bicep.Core/Registry/Catalog/IRegistryModuleMetadataProvider.cs @@ -10,7 +10,7 @@ namespace Bicep.Core.Registry.Catalog; public interface IRegistryModuleMetadata -{ //asdfg better name? asdfg combine asdfg interface? +{ public string Registry { get; init; } // e.g. "mcr.microsoft.com" public string ModuleName { get; init; } // e.g. "bicep/avm/app/dapr-containerapp" diff --git a/src/Bicep.Core/Registry/Catalog/MightBeLazyAsync.cs b/src/Bicep.Core/Registry/Catalog/MightBeLazyAsync.cs index dda4ea8f76d..9ca44ae1d98 100644 --- a/src/Bicep.Core/Registry/Catalog/MightBeLazyAsync.cs +++ b/src/Bicep.Core/Registry/Catalog/MightBeLazyAsync.cs @@ -25,8 +25,6 @@ public MightBeLazyAsync(Func> initializer) private bool IsLazy => lazy is not null; - //asdfg public bool HasValue => lazy is null || lazy.IsValueFactoryCompleted; - public bool TryGetValue([NotNullWhen(true)] out T? value) { if (IsLazy) diff --git a/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProvider.cs b/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProvider.cs index c83dbbf3ac4..715217dcc45 100644 --- a/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProvider.cs +++ b/src/Bicep.Core/Registry/Catalog/PrivateAcrModuleMetadataProvider.cs @@ -21,7 +21,7 @@ namespace Bicep.Core.Registry.Catalog; /// public class PrivateAcrModuleMetadataProvider : BaseModuleMetadataProvider, IRegistryModuleMetadataProvider { - private const int MaxReturnedModules = 10; //asdfg? + private const int MaxReturnedModules = 10000; //asdfg? private readonly CloudConfiguration cloud; private readonly IContainerRegistryClientFactory containerRegistryClientFactory; @@ -57,7 +57,7 @@ private async Task TryGetLiveModuleVersionMetadat try { AzureContainerRegistryManager acrManager = new(containerRegistryClientFactory); - var artifactResult = await acrManager.PullArtifactAsync(cloud, new OciArtifactReference(ArtifactType.Module, Registry, modulePath, version, null, new Uri("file://asdfg"))); + var artifactResult = await acrManager.PullArtifactAsync(cloud, new OciArtifactReference(ArtifactType.Module, Registry, modulePath, version, null, new Uri("file://noparent"))); var manifest = artifactResult.Manifest; string? description = null; string? documentationUri = null; @@ -82,7 +82,7 @@ protected override async Task> GetLiveDa Trace.WriteLine($"Retrieving catalog for registry {Registry}..."); AzureContainerRegistryManager acrManager = new(containerRegistryClientFactory); - var catalog = await acrManager.GetRepositoryNamesAsync(cloud, Registry, MaxReturnedModules); //asdfg limit? + var catalog = await acrManager.GetRepositoryNamesAsync(cloud, Registry, MaxReturnedModules); Trace.WriteLine($"Found {catalog.Length} repositories"); @@ -113,6 +113,6 @@ private RegistryMetadataDetails GetModuleDetails(ImmutableArray> GetLiveDataCoreAsync() @@ -45,7 +45,7 @@ protected override async Task> GetLiveDa )]); return new RegistryModuleMetadata( Registry, - $"{LanguageConstants.BicepPublicMcrPathPrefix}{m.ModulePath}", //asdfg remove prefix and get tests to fail + $"{LanguageConstants.BicepPublicMcrPathPrefix}{m.ModulePath}", new ComputedData(moduleDetails, versions)); })]; } diff --git a/src/Bicep.Core/Registry/Catalog/RegistryModuleCatalog.cs b/src/Bicep.Core/Registry/Catalog/RegistryModuleCatalog.cs index f45e17c5f83..2519232d3fd 100644 --- a/src/Bicep.Core/Registry/Catalog/RegistryModuleCatalog.cs +++ b/src/Bicep.Core/Registry/Catalog/RegistryModuleCatalog.cs @@ -42,7 +42,7 @@ public IRegistryModuleMetadataProvider GetProviderForRegistry(CloudConfiguration } provider = providerFactory.Create(cloud, registry, containerRegistryClientFactory); - registryProviders[registry] = provider; //asdfg threading + registryProviders[registry] = provider; return provider; } diff --git a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs index 0b100e44362..695d2af239d 100644 --- a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//asdfg make sure public versions still sorted correctly - using System.Diagnostics.CodeAnalysis; using System.IO.Abstractions.TestingHelpers; using System.Reflection; diff --git a/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs b/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs index 5cf7dedffe2..150d0f20862 100644 --- a/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/ModuleReferenceCompletionProvider.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//asdfg testpoint using System.Collections.Immutable; using System.Configuration; using System.Diagnostics; diff --git a/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs b/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs index c466312acc4..38e8c2e424d 100644 --- a/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepCompletionHandler.cs @@ -55,7 +55,7 @@ public override async Task Handle(CompletionParams request, Canc this.logger.LogError("Error with Completion in file {Uri} with {Context}. Underlying exception is: {Exception}", request.TextDocument.Uri, completionContext, e.ToString()); } - return new CompletionList(completions, isIncomplete: false/*asdfg*/); + return new CompletionList(completions, isIncomplete: false); } public override Task Handle(CompletionItem request, CancellationToken cancellationToken) diff --git a/src/Bicep.LangServer/Providers/AzureContainerRegistriesProvider.cs b/src/Bicep.LangServer/Providers/AzureContainerRegistriesProvider.cs index 52dba5af98e..14e1cdab481 100644 --- a/src/Bicep.LangServer/Providers/AzureContainerRegistriesProvider.cs +++ b/src/Bicep.LangServer/Providers/AzureContainerRegistriesProvider.cs @@ -33,7 +33,7 @@ public AzureContainerRegistriesProvider(ITokenCredentialFactory tokenCredentialF // Used for completions after typing "'br:" public async IAsyncEnumerable GetContainerRegistriesAccessibleFromAzure(CloudConfiguration cloud, [EnumeratorCancellation] CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested();//asdfg testpoint + cancellationToken.ThrowIfCancellationRequested(); var armClient = GetArmClient(cloud); TenantCollection tenants = armClient.GetTenants();