Skip to content

Commit

Permalink
Add support for sourcing feature flags from bicepconfig.json (#8559)
Browse files Browse the repository at this point in the history
* Add support for sourcing feature flags from bicepconfig.json

Also emit a deprecation notice when a feature flag is sourced from an environment variable.

* Leave only bicepconfig.json as a feature flag source

* Use fixed property names in Config.ExperimentalFeaturesEnabled

* Cleanup diff

* Fix failing tests

* Update bicepconfig.json schema
  • Loading branch information
jeskew authored Oct 4, 2022
1 parent 030248d commit 6715860
Show file tree
Hide file tree
Showing 88 changed files with 652 additions and 382 deletions.
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)
{
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,
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

0 comments on commit 6715860

Please sign in to comment.