Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sourcing feature flags from bicepconfig.json #8559

Merged
merged 9 commits into from
Oct 4, 2022
30 changes: 4 additions & 26 deletions src/Bicep.Cli.IntegrationTests/RootCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,32 +128,10 @@ public async Task BicepThirdPartyNoticesShouldPrintNotices()
}

[TestMethod]
public async Task BicepHelpShouldIncludePublishWhenRegistryEnabled()
{
var featuresMock = Repository.Create<IFeatureProvider>();
featuresMock.Setup(m => m.RegistryEnabled).Returns(true);

var settings = CreateDefaultSettings() with { Features = featuresMock.Object };

var (output, error, result) = await Bicep(settings, "--help");

result.Should().Be(0);
error.Should().BeEmpty();

output.Should().NotBeEmpty();
output.Should().ContainAll(
"publish",
"Publishes",
"registry",
"reference",
"azurecr.io",
"br",
"--target");
}

[TestMethod]
public async Task BicepHelpShouldNotIncludePublishWhenRegistryDisabled()
public async Task BicepHelpShouldAlwaysIncludePublish()
{
// disable registry to ensure `bicep --help` is not consulting the feature provider before
// preparing the help text (as features can only be determined when an input file is specified)
var featuresMock = Repository.Create<IFeatureProvider>();
featuresMock.Setup(m => m.RegistryEnabled).Returns(false);

Expand All @@ -165,7 +143,7 @@ public async Task BicepHelpShouldNotIncludePublishWhenRegistryDisabled()
error.Should().BeEmpty();

output.Should().NotBeEmpty();
output.Should().NotContainAny(
output.Should().ContainAll(
"publish",
"Publishes",
"registry",
Expand Down
13 changes: 6 additions & 7 deletions src/Bicep.Cli.IntegrationTests/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Bicep.Core.FileSystem;
using Bicep.Core.Registry;
using Bicep.Core.Semantics;
using Bicep.Core.Semantics.Namespaces;
using Bicep.Core.Text;
using Bicep.Core.UnitTests;
using Bicep.Core.UnitTests.Utils;
Expand Down Expand Up @@ -41,7 +40,7 @@ protected record InvocationSettings(IFeatureProvider Features, IContainerRegistr
TestTypeHelper.CreateEmptyAzResourceTypeLoader(),
@out,
err,
features: settings.Features,
featureProviderFactory: IFeatureProviderFactory.WithStaticFeatureProvider(settings.Features),
clientFactory: settings.ClientFactory,
templateSpecRepositoryFactory: settings.TemplateSpecRepositoryFactory)).RunAsync(args));

Expand All @@ -55,9 +54,9 @@ protected static void AssertNoErrors(string error)

protected static IEnumerable<string> GetAllDiagnostics(string bicepFilePath, IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory)
{
var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager);
var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager);
var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath));
var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer);
var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer);

var output = new List<string>();
foreach (var (bicepFile, diagnostics) in compilation.GetAllDiagnosticsByBicepFile())
Expand All @@ -76,15 +75,15 @@ protected static IEnumerable<string> GetAllDiagnostics(string bicepFilePath, ICo
protected static IEnumerable<string> GetAllParamDiagnostics(string paramFilePath, IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory)
{
var configuration = BicepTestConstants.BuiltInConfiguration;
var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.Features, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager);
var dispatcher = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver, clientFactory, templateSpecRepositoryFactory, BicepTestConstants.FeatureProviderFactory, BicepTestConstants.BuiltInOnlyConfigurationManager), BicepTestConstants.BuiltInOnlyConfigurationManager);
var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, new Workspace(), PathHelper.FilePathToFileUrl(paramFilePath));
var compilation = new Compilation(BicepTestConstants.Features, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer);
var compilation = new Compilation(BicepTestConstants.FeatureProviderFactory, TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer);

var semanticModel = new ParamsSemanticModel(sourceFileGrouping, configuration, BicepTestConstants.Features, file => {
var compilationGrouping = new SourceFileGrouping(BicepTestConstants.FileResolver, file.FileUri, sourceFileGrouping.FileResultByUri, sourceFileGrouping.UriResultByModule, sourceFileGrouping.SourceFileParentLookup);


return new Compilation(BicepTestConstants.Features, BicepTestConstants.NamespaceProvider, compilationGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProvider, BicepTestConstants.LinterAnalyzer);
return new Compilation(BicepTestConstants.FeatureProviderFactory, BicepTestConstants.NamespaceProvider, compilationGrouping, BicepTestConstants.BuiltInOnlyConfigurationManager, BicepTestConstants.ApiVersionProviderFactory, BicepTestConstants.LinterAnalyzer);
});

var output = new List<string>();
Expand Down
15 changes: 11 additions & 4 deletions src/Bicep.Cli/Commands/BuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Bicep.Cli.Arguments;
using Bicep.Cli.Logging;
using Bicep.Cli.Services;
using Bicep.Core.Emit;
using Bicep.Core.Features;
using Bicep.Core.FileSystem;
using Microsoft.Extensions.Logging;

Expand All @@ -18,33 +20,38 @@ public class BuildCommand : ICommand
private readonly CompilationService compilationService;
private readonly CompilationWriter writer;
private readonly ParametersWriter paramsWriter;
private readonly IFeatureProviderFactory featureProviderFactory;

public BuildCommand(
ILogger logger,
IDiagnosticLogger diagnosticLogger,
InvocationContext invocationContext,
CompilationService compilationService,
CompilationWriter writer,
ParametersWriter paramsWriter)
ParametersWriter paramsWriter,
IFeatureProviderFactory featureProviderFactory)
{
this.logger = logger;
this.diagnosticLogger = diagnosticLogger;
this.invocationContext = invocationContext;
this.compilationService = compilationService;
this.writer = writer;
this.paramsWriter = paramsWriter;
this.featureProviderFactory = featureProviderFactory;
}

public async Task<int> RunAsync(BuildArguments args)
{
var inputPath = PathHelper.ResolvePath(args.InputFile);
var features = featureProviderFactory.GetFeatureProvider(PathHelper.FilePathToFileUrl(inputPath));
var emitterSettings = new EmitterSettings(features);

if (invocationContext.EmitterSettings.EnableSymbolicNames)
if (emitterSettings.EnableSymbolicNames)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a follow-up item, I think we should converge on a single message that covers enablement of all experimental features.

{
logger.LogWarning(CliResources.SymbolicNamesDisclaimerMessage);
}

if (invocationContext.Features.ResourceTypedParamsAndOutputsEnabled)
if (features.ResourceTypedParamsAndOutputsEnabled)
{
logger.LogWarning(CliResources.ResourceTypesDisclaimerMessage);
}
Expand Down Expand Up @@ -72,7 +79,7 @@ public async Task<int> RunAsync(BuildArguments args)
// return non-zero exit code on errors
return diagnosticLogger.ErrorCount > 0 ? 1 : 0;
}
else if (invocationContext.Features.ParamsFilesEnabled && IsBicepparamsFile(inputPath))
else if (features.ParamsFilesEnabled && IsBicepparamsFile(inputPath))
{
var model = await compilationService.CompileParams(inputPath, args.NoRestore);

Expand Down
13 changes: 10 additions & 3 deletions src/Bicep.Cli/Commands/GenerateParametersFileCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Bicep.Cli.Arguments;
using Bicep.Cli.Logging;
using Bicep.Cli.Services;
using Bicep.Core.Emit;
using Bicep.Core.Features;
using Bicep.Core.FileSystem;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
Expand All @@ -17,31 +19,36 @@ public class GenerateParametersFileCommand : ICommand
private readonly InvocationContext invocationContext;
private readonly CompilationService compilationService;
private readonly PlaceholderParametersWriter writer;
private readonly IFeatureProviderFactory featureProviderFactory;

public GenerateParametersFileCommand(
ILogger logger,
IDiagnosticLogger diagnosticLogger,
InvocationContext invocationContext,
CompilationService compilationService,
PlaceholderParametersWriter writer)
PlaceholderParametersWriter writer,
IFeatureProviderFactory featureProviderFactory)
{
this.logger = logger;
this.diagnosticLogger = diagnosticLogger;
this.invocationContext = invocationContext;
this.compilationService = compilationService;
this.writer = writer;
this.featureProviderFactory = featureProviderFactory;
}

public async Task<int> RunAsync(GenerateParametersFileArguments args)
{
var inputPath = PathHelper.ResolvePath(args.InputFile);
var features = featureProviderFactory.GetFeatureProvider(PathHelper.FilePathToFileUrl(inputPath));
var emitterSettings = new EmitterSettings(features);

if (invocationContext.EmitterSettings.EnableSymbolicNames)
if (emitterSettings.EnableSymbolicNames)
{
logger.LogWarning(CliResources.SymbolicNamesDisclaimerMessage);
}

if (invocationContext.Features.ResourceTypedParamsAndOutputsEnabled)
if (features.ResourceTypedParamsAndOutputsEnabled)
{
logger.LogWarning(CliResources.ResourceTypesDisclaimerMessage);
}
Expand Down
43 changes: 19 additions & 24 deletions src/Bicep.Cli/Commands/RootCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,6 @@ private void PrintHelp()
var exeName = ThisAssembly.AssemblyName;
var versionString = GetVersionString();

var registryText =
$@"
{exeName} publish <file> --target <ref>
Publishes the .bicep file to the module registry.

Arguments:
<file> The input file (can be a Bicep file or an ARM template file)
<ref> The module reference

Examples:
bicep publish file.bicep --target br:example.azurecr.io/hello/world:v1
bicep publish file.json --target br:example.azurecr.io/hello/world:v1

{exeName} restore <file>
Restores external modules from the specified Bicep file to the local module cache.

Arguments:
<file> The input file

";

var registryPlaceholder = this.invocationContext.Features.RegistryEnabled ? registryText : Environment.NewLine;

var output =
$@"Bicep CLI version {versionString}

Expand Down Expand Up @@ -136,7 +113,25 @@ bicep generate-params file.bicep --stdout
bicep generate-params file.bicep --outdir dir1
bicep generate-params file.bicep --outfile file.parameters.json

{registryPlaceholder} {exeName} [options]

{exeName} publish <file> --target <ref>
Publishes the .bicep file to the module registry.

Arguments:
<file> The input file (can be a Bicep file or an ARM template file)
<ref> The module reference

Examples:
bicep publish file.bicep --target br:example.azurecr.io/hello/world:v1
bicep publish file.json --target br:example.azurecr.io/hello/world:v1

{exeName} restore <file>
Restores external modules from the specified Bicep file to the local module cache.

Arguments:
<file> The input file

{exeName} [options]
Options:
--version -v Shows bicep version information
--help -h Shows this usage information
Expand Down
9 changes: 8 additions & 1 deletion src/Bicep.Cli/Helpers/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using Bicep.Cli.Commands;
using Bicep.Core.Features;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -52,7 +53,13 @@ public static IServiceCollection AddInvocationContext(this IServiceCollection se

// add contents of the context
services.AddSingleton(context.NamespaceProvider);
services.AddSingleton(context.Features);
if (context.FeatureProviderFactory is {} factory)
{
services.AddSingleton(factory);
} else
{
services.AddSingleton<IFeatureProviderFactory, FeatureProviderFactory>();
}
services.AddSingleton(context.ClientFactory);
services.AddSingleton(context.TemplateSpecRepositoryFactory);

Expand Down
9 changes: 3 additions & 6 deletions src/Bicep.Cli/InvocationContext.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Bicep.Core.Emit;
using Bicep.Core.Features;
using Bicep.Core.Registry;
using Bicep.Core.Registry.Auth;
Expand All @@ -17,14 +16,14 @@ public InvocationContext(
IAzResourceTypeLoader azResourceTypeLoader,
TextWriter outputWriter,
TextWriter errorWriter,
IFeatureProvider? features = null,
IFeatureProviderFactory? featureProviderFactory = null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be nullable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes; this parameter is a backdoor for the integration tests to toggle various feature flags and set the assembly version to a static string. It is never set on contexts created in the Bicep.Cli package, which instead relies on the DI container to instantiate the factory.

IContainerRegistryClientFactory? clientFactory = null,
ITemplateSpecRepositoryFactory? templateSpecRepositoryFactory = null)
{
// keep the list of services in this class in sync with the logic in the AddInvocationContext() extension method
OutputWriter = outputWriter;
ErrorWriter = errorWriter;
Features = features ?? new FeatureProvider();
FeatureProviderFactory = featureProviderFactory;
ClientFactory = clientFactory ?? new ContainerRegistryClientFactory(new TokenCredentialFactory());
TemplateSpecRepositoryFactory = templateSpecRepositoryFactory ?? new TemplateSpecRepositoryFactory(new TokenCredentialFactory());
NamespaceProvider = new DefaultNamespaceProvider(azResourceTypeLoader);
Expand All @@ -36,9 +35,7 @@ public InvocationContext(

public TextWriter ErrorWriter { get; }

public EmitterSettings EmitterSettings => new EmitterSettings(Features);

public IFeatureProvider Features { get; }
public IFeatureProviderFactory? FeatureProviderFactory { get; }

public IContainerRegistryClientFactory ClientFactory { get; }

Expand Down
3 changes: 1 addition & 2 deletions src/Bicep.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public static async Task<int> Main(string[] args)
new AzResourceTypeLoader(),
Console.Out,
Console.Error,
features: null,
clientFactory: null));

// this must be awaited so dispose of the listener occurs in the continuation
Expand Down Expand Up @@ -129,7 +128,7 @@ private ServiceProvider ConfigureServices()
.AddSingleton<IFileSystem, FileSystem>()
.AddSingleton<IConfigurationManager, ConfigurationManager>()
.AddSingleton<ITokenCredentialFactory, TokenCredentialFactory>()
.AddSingleton<IApiVersionProvider, ApiVersionProvider>()
.AddSingleton<IApiVersionProviderFactory, ApiVersionProviderFactory>()
.AddSingleton<IBicepAnalyzer, LinterAnalyzer>()
.AddSingleton<TemplateDecompiler>()
.AddSingleton<DecompilationWriter>()
Expand Down
Loading