Skip to content

Commit

Permalink
Ensure language server refreshes modules on force refresh (#13278)
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-c-martin authored Feb 9, 2024
1 parent 69631e3 commit c0ad57d
Show file tree
Hide file tree
Showing 14 changed files with 182 additions and 85 deletions.
4 changes: 2 additions & 2 deletions src/Bicep.Cli.IntegrationTests/PublishCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -450,11 +450,11 @@ public async Task Publish_BicepModule_WithDescriptionAndDocUri_ShouldPlaceDescri
var registryUri = new Uri($"https://{registryStr}");
var repository = $"test/{moduleName}".ToLowerInvariant();

var (clientFactory, blobClients) = DataSetsExtensions.CreateMockRegistryClients((registryStr, repository));
var (clientFactory, blobClients) = RegistryHelper.CreateMockRegistryClients((registryStr, repository));

var blobClient = blobClients[(registryUri, repository)];

await DataSetsExtensions.PublishModuleToRegistryAsync(clientFactory, "modulename", $"br:example.com/test/{moduleName}:v1", bicepModuleContents, publishSource: false, documentationUri);
await RegistryHelper.PublishModuleToRegistry(clientFactory, "modulename", $"br:example.com/test/{moduleName}:v1", bicepModuleContents, publishSource: false, documentationUri);

var manifest = blobClient.Manifests.Single().Value.ToObjectFromJson<OciManifest>(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public async Task Publish_provider_should_succeed()
var repository = $"test/provider";
var version = "0.0.1";

var (clientFactory, blobClientMocks) = DataSetsExtensions.CreateMockRegistryClients((registryStr, repository));
var (clientFactory, blobClientMocks) = RegistryHelper.CreateMockRegistryClients((registryStr, repository));
var mockBlobClient = blobClientMocks[(registryUri, repository)];

var indexPath = Path.Combine(outputDirectory, "index.json");
Expand Down
8 changes: 4 additions & 4 deletions src/Bicep.Core.IntegrationTests/DynamicAzTypesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ private async Task<ServiceBuilder> GetServices()

var services = new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: true, DynamicTypeLoadingEnabled: true, CacheRootDirectory: cacheRoot))
.WithContainerRegistryClientFactory(DataSetsExtensions.CreateOciClientForAzProvider());
.WithContainerRegistryClientFactory(RegistryHelper.CreateOciClientForAzProvider());

await DataSetsExtensions.PublishAzProvider(services.Build(), indexJson);
await RegistryHelper.PublishAzProvider(services.Build(), indexJson);

return services;
}
Expand Down Expand Up @@ -125,12 +125,12 @@ public async Task Bicep_module_artifact_specified_in_provider_declaration_syntax
{
// ARRANGE
var testArtifact = new ArtifactRegistryAddress(LanguageConstants.BicepPublicMcrRegistry, "bicep/providers/az", "0.2.661");
var clientFactory = DataSetsExtensions.CreateMockRegistryClients((testArtifact.RegistryAddress, testArtifact.RepositoryPath)).factoryMock;
var clientFactory = RegistryHelper.CreateMockRegistryClients((testArtifact.RegistryAddress, testArtifact.RepositoryPath)).factoryMock;
var services = new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: true, DynamicTypeLoadingEnabled: true))
.WithContainerRegistryClientFactory(clientFactory);

await DataSetsExtensions.PublishModuleToRegistryAsync(
await RegistryHelper.PublishModuleToRegistry(
clientFactory,
moduleName: "az",
target: testArtifact.ToSpecificationString(':'),
Expand Down
4 changes: 2 additions & 2 deletions src/Bicep.Core.IntegrationTests/ImportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ private async Task<ServiceBuilder> GetServices()

var services = new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: true, DynamicTypeLoadingEnabled: true))
.WithContainerRegistryClientFactory(DataSetsExtensions.CreateOciClientForAzProvider())
.WithContainerRegistryClientFactory(RegistryHelper.CreateOciClientForAzProvider())
.WithMockFileSystem(fileSystem)
.WithAzResourceTypeLoader(azTypeLoaderLazy.Value);

await DataSetsExtensions.PublishAzProvider(services.Build(), "/types/index.json");
await RegistryHelper.PublishAzProvider(services.Build(), "/types/index.json");

return services;
}
Expand Down
10 changes: 5 additions & 5 deletions src/Bicep.Core.IntegrationTests/RegistryProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class RegistryProviderTests : TestBase
{
private static ServiceBuilder GetServiceBuilder(IFileSystem fileSystem, string registryHost, string repositoryPath, bool extensibilityEnabledBool, bool providerRegistryBool)
{
var (clientFactory, _) = DataSetsExtensions.CreateMockRegistryClients((registryHost, repositoryPath));
var clientFactory = RegistryHelper.CreateMockRegistryClient(registryHost, repositoryPath);

return new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: extensibilityEnabledBool, ProviderRegistry: providerRegistryBool))
Expand All @@ -38,7 +38,7 @@ public async Task Providers_published_to_a_registry_can_be_compiled()

var services = GetServiceBuilder(fileSystem, registry, repository, true, true);

await DataSetsExtensions.PublishProviderToRegistryAsync(services.Build(), "/types/index.json", $"br:{registry}/{repository}:1.2.3");
await RegistryHelper.PublishProviderToRegistryAsync(services.Build(), "/types/index.json", $"br:{registry}/{repository}:1.2.3");

var result = await CompilationHelper.RestoreAndCompile(services, """
provider 'br:example.azurecr.io/test/provider/[email protected]'
Expand Down Expand Up @@ -69,7 +69,7 @@ public async Task Third_party_namespace_errors_with_configuration()

var services = GetServiceBuilder(fileSystem, registry, repository, true, true);

await DataSetsExtensions.PublishProviderToRegistryAsync(services.Build(), "/types/index.json", $"br:{registry}/{repository}:1.2.3");
await RegistryHelper.PublishProviderToRegistryAsync(services.Build(), "/types/index.json", $"br:{registry}/{repository}:1.2.3");

var result = await CompilationHelper.RestoreAndCompile(services, """
provider 'br:example.azurecr.io/test/provider/[email protected]' with {}
Expand Down Expand Up @@ -101,7 +101,7 @@ public async Task Third_party_imports_are_enabled_when_feature_is_enabled()

var services = GetServiceBuilder(fileSystem, registry, repository, true, true);

await DataSetsExtensions.PublishProviderToRegistryAsync(services.Build(), "/types/index.json", $"br:{registry}/{repository}:1.2.3");
await RegistryHelper.PublishProviderToRegistryAsync(services.Build(), "/types/index.json", $"br:{registry}/{repository}:1.2.3");

var result = await CompilationHelper.RestoreAndCompile(services, @$"
provider 'br:example.azurecr.io/test/provider/[email protected]'
Expand Down Expand Up @@ -147,7 +147,7 @@ public async Task Third_party_imports_are_disabled_unless_feature_is_enabled()

var services = GetServiceBuilder(fileSystem, registry, repository, false, false);

await DataSetsExtensions.PublishProviderToRegistryAsync(services.Build(), "/types/index.json", $"br:{registry}/{repository}:1.2.3");
await RegistryHelper.PublishProviderToRegistryAsync(services.Build(), "/types/index.json", $"br:{registry}/{repository}:1.2.3");

var result = await CompilationHelper.RestoreAndCompile(services, @$"
provider 'br:example.azurecr.io/test/provider/[email protected]'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public async Task ProviderNameAndVersionAreUsedAsCacheKeys()
var repositoryPath = $"test/provider";
var repositoryNames = new[] { "foo", "bar" };

var (clientFactory, _) = DataSetsExtensions.CreateMockRegistryClients(repositoryNames.Select(name => (registry, $"{repositoryPath}/{name}")).ToArray());
var (clientFactory, _) = RegistryHelper.CreateMockRegistryClients(repositoryNames.Select(name => (registry, $"{repositoryPath}/{name}")).ToArray());

var services = new ServiceBuilder()
.WithFeatureOverrides(new(ExtensibilityEnabled: true, ProviderRegistry: true))
Expand All @@ -38,7 +38,7 @@ public async Task ProviderNameAndVersionAreUsedAsCacheKeys()
foreach (var repoName in repositoryNames)
{
var indexJsonPath = Path.Combine(outputDirectory, "types", "index.json");
await DataSetsExtensions.PublishProviderToRegistryAsync(services.Build(), indexJsonPath, $"br:{registry}/{repositoryPath}/{repoName}:1.2.3");
await RegistryHelper.PublishProviderToRegistryAsync(services.Build(), indexJsonPath, $"br:{registry}/{repositoryPath}/{repoName}:1.2.3");
}

var result = await CompilationHelper.RestoreAndCompile(
Expand Down
63 changes: 2 additions & 61 deletions src/Bicep.Core.Samples/DataSetsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,7 @@ public static IContainerRegistryClientFactory CreateMockRegistryClients(Immutabl
clients.Add((targetReference.Registry, targetReference.Repository));
}

return CreateMockRegistryClients(clients.Concat(additionalClients).ToArray()).factoryMock;
}

public static (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient> blobClientMocks) CreateMockRegistryClients(params (string, string)[] clients)
{
var containerRegistryFactoryBuilder = new TestContainerRegistryClientFactoryBuilder();

foreach (var (registryHost, repository) in clients)
{
containerRegistryFactoryBuilder.RegisterMockRepositoryBlobClient(registryHost, repository);

}

return containerRegistryFactoryBuilder.Build();
return RegistryHelper.CreateMockRegistryClients(clients.Concat(additionalClients).ToArray()).factoryMock;
}

public static ITemplateSpecRepositoryFactory CreateEmptyTemplateSpecRepositoryFactory(bool enablePublishSource = false)
Expand Down Expand Up @@ -139,56 +126,10 @@ public static async Task PublishModulesToRegistryAsync(ImmutableDictionary<strin
{
foreach (var (moduleName, publishInfo) in registryModules)
{
await PublishModuleToRegistryAsync(clientFactory, moduleName, publishInfo.Metadata.Target, publishInfo.ModuleSource, publishSource, null);
}
}

public static async Task PublishModuleToRegistryAsync(IContainerRegistryClientFactory clientFactory, string moduleName, string target, string moduleSource, bool publishSource, string? documentationUri = null)
{
var featureProviderFactory = BicepTestConstants.CreateFeatureProviderFactory(new FeatureProviderOverrides(PublishSourceEnabled: publishSource));
var dispatcher = ServiceBuilder.Create(s => s.WithDisabledAnalyzersConfiguration()
.AddSingleton(clientFactory)
.AddSingleton(BicepTestConstants.TemplateSpecRepositoryFactory)
.AddSingleton(featureProviderFactory)
).Construct<IModuleDispatcher>();

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 result = CompilationHelper.Compile(moduleSource);
if (result.Template is null)
{
throw new InvalidOperationException($"Module {moduleName} failed to produce a template.");
await RegistryHelper.PublishModuleToRegistry(clientFactory, moduleName, publishInfo.Metadata.Target, publishInfo.ModuleSource, publishSource, null);
}

var features = featureProviderFactory.GetFeatureProvider(result.BicepFile.FileUri);
BinaryData? sourcesStream = publishSource ? BinaryData.FromStream(SourceArchive.PackSourcesIntoStream(result.Compilation.SourceFileGrouping, features.CacheRootDirectory)) : null;
await dispatcher.PublishModule(targetReference, BinaryData.FromString(result.Template.ToString()), sourcesStream, documentationUri);
}

public static async Task PublishProviderToRegistryAsync(IDependencyHelper services, string pathToIndexJson, string target)
{
var dispatcher = services.Construct<IModuleDispatcher>();
var fileSystem = services.Construct<IFileSystem>();

var targetReference = dispatcher.TryGetArtifactReference(ArtifactType.Provider, target, PathHelper.FilePathToFileUrl(pathToIndexJson)).IsSuccess(out var @ref) ? @ref
: throw new InvalidOperationException($"Invalid target reference '{target}'. Specify a reference to an OCI artifact.");

var tgzStream = await TypesV1Archive.GenerateProviderTarStream(fileSystem, pathToIndexJson);

await dispatcher.PublishProvider(targetReference, tgzStream);
}

private static Uri RandomFileUri() => PathHelper.FilePathToFileUrl(Path.GetTempFileName());

public static async Task PublishAzProvider(IDependencyHelper services, string pathToIndexJson)
{
var version = BicepTestConstants.BuiltinAzProviderVersion;
var repository = "bicep/providers/az";
await PublishProviderToRegistryAsync(services, pathToIndexJson, $"br:{LanguageConstants.BicepPublicMcrRegistry}/{repository}:{version}");
}

public static IContainerRegistryClientFactory CreateOciClientForAzProvider()
=> CreateMockRegistryClients((LanguageConstants.BicepPublicMcrRegistry, $"bicep/providers/az")).factoryMock;
}
}
85 changes: 85 additions & 0 deletions src/Bicep.Core.UnitTests/Utils/RegistryHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Immutable;
using System.IO.Abstractions;
using Bicep.Core.FileSystem;
using Bicep.Core.Registry;
using Bicep.Core.Registry.Providers;
using Bicep.Core.SourceCode;
using Bicep.Core.UnitTests.Features;
using Bicep.Core.UnitTests.Registry;
using Microsoft.Extensions.DependencyInjection;

namespace Bicep.Core.UnitTests.Utils;

public static class RegistryHelper
{
public static IContainerRegistryClientFactory CreateMockRegistryClient(string registry, string repository)
{
return new TestContainerRegistryClientFactoryBuilder()
.RegisterMockRepositoryBlobClient(registry, repository)
.Build().clientFactory;
}

public static (IContainerRegistryClientFactory factoryMock, ImmutableDictionary<(Uri, string), MockRegistryBlobClient> blobClientMocks) CreateMockRegistryClients(params (string, string)[] clients)
{
var containerRegistryFactoryBuilder = new TestContainerRegistryClientFactoryBuilder();

foreach (var (registryHost, repository) in clients)
{
containerRegistryFactoryBuilder.RegisterMockRepositoryBlobClient(registryHost, repository);

}

return containerRegistryFactoryBuilder.Build();
}

public static async Task PublishModuleToRegistry(IContainerRegistryClientFactory clientFactory, string moduleName, string target, string moduleSource, bool publishSource, string? documentationUri = null)
{
var featureProviderFactory = BicepTestConstants.CreateFeatureProviderFactory(new FeatureProviderOverrides(PublishSourceEnabled: publishSource));
var dispatcher = ServiceBuilder.Create(s => s.WithDisabledAnalyzersConfiguration()
.AddSingleton(clientFactory)
.AddSingleton(BicepTestConstants.TemplateSpecRepositoryFactory)
.AddSingleton(featureProviderFactory)
).Construct<IModuleDispatcher>();

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 result = CompilationHelper.Compile(moduleSource);
if (result.Template is null)
{
throw new InvalidOperationException($"Module {moduleName} failed to produce a template.");
}

var features = featureProviderFactory.GetFeatureProvider(result.BicepFile.FileUri);
BinaryData? sourcesStream = publishSource ? BinaryData.FromStream(SourceArchive.PackSourcesIntoStream(result.Compilation.SourceFileGrouping, features.CacheRootDirectory)) : null;
await dispatcher.PublishModule(targetReference, BinaryData.FromString(result.Template.ToString()), sourcesStream, documentationUri);
}

public static async Task PublishProviderToRegistryAsync(IDependencyHelper services, string pathToIndexJson, string target)
{
var dispatcher = services.Construct<IModuleDispatcher>();
var fileSystem = services.Construct<IFileSystem>();

var targetReference = dispatcher.TryGetArtifactReference(ArtifactType.Provider, target, PathHelper.FilePathToFileUrl(pathToIndexJson)).IsSuccess(out var @ref) ? @ref
: throw new InvalidOperationException($"Invalid target reference '{target}'. Specify a reference to an OCI artifact.");

var tgzStream = await TypesV1Archive.GenerateProviderTarStream(fileSystem, pathToIndexJson);

await dispatcher.PublishProvider(targetReference, tgzStream);
}

private static Uri RandomFileUri() => PathHelper.FilePathToFileUrl(Path.GetTempFileName());

public static async Task PublishAzProvider(IDependencyHelper services, string pathToIndexJson)
{
var version = BicepTestConstants.BuiltinAzProviderVersion;
var repository = "bicep/providers/az";
await PublishProviderToRegistryAsync(services, pathToIndexJson, $"br:{LanguageConstants.BicepPublicMcrRegistry}/{repository}:{version}");
}

public static IContainerRegistryClientFactory CreateOciClientForAzProvider()
=> CreateMockRegistryClients((LanguageConstants.BicepPublicMcrRegistry, $"bicep/providers/az")).factoryMock;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ public class TestContainerRegistryClientFactoryBuilder
{
private readonly ImmutableDictionary<(Uri registryUri, string repository), MockRegistryBlobClient>.Builder clientsBuilder = ImmutableDictionary.CreateBuilder<(Uri registryUri, string repository), MockRegistryBlobClient>();

public void RegisterMockRepositoryBlobClient(string registryHost, string repository)
public TestContainerRegistryClientFactoryBuilder RegisterMockRepositoryBlobClient(string registryHost, string repository)
{
clientsBuilder.TryAdd((new Uri($"https://{registryHost}"), repository), new MockRegistryBlobClient());

return this;
}

public void RegisterMockRepositoryBlobClient(string registryHost, string repository, MockRegistryBlobClient client)
Expand Down
4 changes: 2 additions & 2 deletions src/Bicep.Core/Workspaces/SourceFileGrouping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ public SourceFileGrouping(IFileResolver fileResolver,

public ImmutableDictionary<ISourceFile, ImmutableHashSet<ISourceFile>> SourceFileParentLookup { get; }

public IEnumerable<ArtifactResolutionInfo> GetArtifactsToRestore()
public IEnumerable<ArtifactResolutionInfo> GetArtifactsToRestore(bool force = false)
{
foreach (var (sourceFile, artifactResults) in FileUriResultByArtifactReference)
{
foreach (var (syntax, result) in artifactResults)
{
if (!result.IsSuccess(out _, out var failure) && failure.RequiresRestore)
if (force || !result.IsSuccess(out _, out var failure) && failure.RequiresRestore)
{
yield return new(syntax, sourceFile);
}
Expand Down
Loading

0 comments on commit c0ad57d

Please sign in to comment.