Skip to content

Commit

Permalink
Add support for version in bicepconfig.json
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-c-martin committed Oct 4, 2024
1 parent 74551a5 commit 37da17f
Show file tree
Hide file tree
Showing 18 changed files with 170 additions and 6 deletions.
7 changes: 5 additions & 2 deletions src/Bicep.Cli/Commands/RootCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.IO.Compression;
using Bicep.Cli.Arguments;
using Bicep.Core.Exceptions;
using Bicep.Core.Utils;
using Environment = System.Environment;

namespace Bicep.Cli.Commands
{
Expand Down Expand Up @@ -265,10 +267,11 @@ private void PrintThirdPartyNotices()

private static string GetVersionString()
{
var versionSplit = ThisAssembly.AssemblyInformationalVersion.Split('+');
var version = BicepVersion.Instance.Value;
var commitHash = BicepVersion.Instance.CommitHash;

// <major>.<minor>.<patch> (<commmithash>)
return $"{versionSplit[0]} ({(versionSplit.Length > 1 ? versionSplit[1] : "custom")})";
return $"{version} ({(commitHash is {} ? commitHash : "custom")})";
}

private static void WriteEmbeddedResource(TextWriter writer, string streamName)
Expand Down
3 changes: 2 additions & 1 deletion src/Bicep.Cli/Rpc/CliJsonRpcServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Bicep.Core.Syntax;
using Bicep.Core.Text;
using Bicep.Core.TypeSystem;
using Bicep.Core.Utils;
using Bicep.Core.Workspaces;
using Newtonsoft.Json.Serialization;
using StreamJsonRpc;
Expand Down Expand Up @@ -42,7 +43,7 @@ public async Task<VersionResponse> Version(VersionRequest request, CancellationT
await Task.Yield();

return new(
ThisAssembly.AssemblyInformationalVersion.Split('+')[0]);
BicepVersion.Instance.Value);
}

/// <inheritdoc/>
Expand Down
51 changes: 51 additions & 0 deletions src/Bicep.Core.IntegrationTests/BicepConfigTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Bicep.Core.Diagnostics;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.UnitTests.Utils;
using Bicep.Core.Utils;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Bicep.Core.IntegrationTests;

[TestClass]
public class BicepConfigTests
{
[TestMethod]
public async Task Version_mismatch_should_block_compilation()
{
var result = await CompilationHelper.RestoreAndCompile(
("main.bicep", new("""
output foo string = ''
""")), ("../bicepconfig.json", new("""
{
"bicep": {
"version": "0.0.1"
}
}
""")));

result.ExcludingLinterDiagnostics().Should().HaveDiagnostics([
("BCP409", DiagnosticLevel.Error, $"""Bicep version "{BicepVersion.Instance.Value}" was used for compilation, but version "0.0.1" is required in configuration file "/path/bicepconfig.json"."""),
]);
}

[TestMethod]
public async Task Correct_version_should_permit_compilation()
{
var result = await CompilationHelper.RestoreAndCompile(
("main.bicep", new("""
output foo string = ''
""")), ("../bicepconfig.json", new($$"""
{
"bicep": {
"version": "{{BicepVersion.Instance.Value}}"
}
}
""")));

result.Should().NotHaveAnyDiagnostics();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public void RootConfiguration_LeadingTildeInCacheRootDirectory_ExpandPath(string
cacheRootDirectory,
BicepTestConstants.BuiltInConfiguration.ExperimentalFeaturesEnabled,
BicepTestConstants.BuiltInConfiguration.Formatting,
BicepTestConstants.BuiltInConfiguration.Bicep,
BicepTestConstants.BuiltInConfiguration.ConfigFileUri,
BicepTestConstants.BuiltInConfiguration.DiagnosticBuilders);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ original.ExperimentalFeaturesEnabled with
Extensibility = true,
},
original.Formatting,
original.Bicep,
null,
null);
}
Expand Down
22 changes: 22 additions & 0 deletions src/Bicep.Core.UnitTests/Utils/BicepVersionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Bicep.Core.Utils;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Bicep.Core.UnitTests.Utils;

[TestClass]
public class BicepVersionTests
{
[TestMethod]
public void BicepVersion_should_return_assembly_info()
{
var result = BicepVersion.Instance;
result.Value.Should().MatchRegex(@"^[0-9]+\.[0-9]+\.[0-9]+");
if (result.CommitHash is {})
{
result.CommitHash.Should().MatchRegex(@"^[0-9a-f]{7,40}$");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static RootConfiguration WithAnalyzersConfiguration(this RootConfiguratio
current.CacheRootDirectory,
current.ExperimentalFeaturesEnabled,
current.Formatting,
current.Bicep,
current.ConfigFileUri,
current.DiagnosticBuilders);

Expand Down
21 changes: 21 additions & 0 deletions src/Bicep.Core/Configuration/BicepConfigurationSection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json;
using Bicep.Core.Extensions;

namespace Bicep.Core.Configuration;

public record BicepConfiguration(
string? Version);

public class BicepConfigurationSection : ConfigurationSection<BicepConfiguration>
{
public BicepConfigurationSection(BicepConfiguration data)
: base(data)
{
}

public static BicepConfigurationSection Bind(JsonElement element)
=> new(element.ToNonNullObject<BicepConfiguration>());
}
1 change: 1 addition & 0 deletions src/Bicep.Core/Configuration/ConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ private static RootConfiguration WithLoadDiagnostics(RootConfiguration configura
configuration.CacheRootDirectory,
configuration.ExperimentalFeaturesEnabled,
configuration.Formatting,
configuration.Bicep,
configuration.ConfigFileUri,
diagnostics);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public static RootConfiguration WithExperimentalFeaturesConfiguration(this RootC
current.CacheRootDirectory,
featuresEnabled,
current.Formatting,
current.Bicep,
current.ConfigFileUri,
current.DiagnosticBuilders);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static RootConfiguration WithExtensions(this RootConfiguration rootConfig
rootConfiguration.CacheRootDirectory,
rootConfiguration.ExperimentalFeaturesEnabled,
rootConfiguration.Formatting,
rootConfiguration.Bicep,
rootConfiguration.ConfigFileUri,
rootConfiguration.DiagnosticBuilders);
}
Expand All @@ -39,6 +40,7 @@ public static RootConfiguration WithImplicitExtensions(this RootConfiguration ro
rootConfiguration.CacheRootDirectory,
rootConfiguration.ExperimentalFeaturesEnabled,
rootConfiguration.Formatting,
rootConfiguration.Bicep,
rootConfiguration.ConfigFileUri,
rootConfiguration.DiagnosticBuilders);
}
Expand Down
12 changes: 11 additions & 1 deletion src/Bicep.Core/Configuration/RootConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class RootConfiguration

public const string FormattingKey = "formatting";

public const string BicepKey = "bicep";

public RootConfiguration(
CloudConfiguration cloud,
ModuleAliasesConfiguration moduleAliases,
Expand All @@ -37,6 +39,7 @@ public RootConfiguration(
string? cacheRootDirectory,
ExperimentalFeaturesEnabled experimentalFeaturesEnabled,
FormattingConfiguration formatting,
BicepConfigurationSection bicep,
Uri? configFileUri,
IEnumerable<DiagnosticBuilder.DiagnosticBuilderDelegate>? diagnosticBuilders)
{
Expand All @@ -48,6 +51,7 @@ public RootConfiguration(
this.CacheRootDirectory = ExpandCacheRootDirectory(cacheRootDirectory);
this.ExperimentalFeaturesEnabled = experimentalFeaturesEnabled;
this.Formatting = formatting;
this.Bicep = bicep;
this.ConfigFileUri = configFileUri;
this.DiagnosticBuilders = diagnosticBuilders?.ToImmutableArray() ?? [];
}
Expand All @@ -60,11 +64,12 @@ public static RootConfiguration Bind(JsonElement element, Uri? configFileUri = n
var cacheRootDirectory = element.TryGetProperty(CacheRootDirectoryKey, out var e) ? e.GetString() : default;
var experimentalFeaturesEnabled = ExperimentalFeaturesEnabled.Bind(element.GetProperty(ExperimentalFeaturesEnabledKey));
var formatting = FormattingConfiguration.Bind(element.GetProperty(FormattingKey));
var bicep = BicepConfigurationSection.Bind(element.GetProperty(BicepKey));

var extensions = ExtensionsConfiguration.Bind(element.GetProperty(ExtensionsKey));
var implicitExtensions = ImplicitExtensionsConfiguration.Bind(element.GetProperty(ImplicitExtensionsKey));

return new(cloud, moduleAliases, extensions, implicitExtensions, analyzers, cacheRootDirectory, experimentalFeaturesEnabled, formatting, configFileUri, diagnosticBuilders);
return new(cloud, moduleAliases, extensions, implicitExtensions, analyzers, cacheRootDirectory, experimentalFeaturesEnabled, formatting, bicep, configFileUri, diagnosticBuilders);
}

public CloudConfiguration Cloud { get; }
Expand All @@ -83,6 +88,8 @@ public static RootConfiguration Bind(JsonElement element, Uri? configFileUri = n

public FormattingConfiguration Formatting { get; }

public BicepConfigurationSection Bicep { get; }

public Uri? ConfigFileUri { get; }

public ImmutableArray<DiagnosticBuilder.DiagnosticBuilderDelegate> DiagnosticBuilders { get; }
Expand Down Expand Up @@ -122,6 +129,9 @@ public string ToUtf8Json()
writer.WritePropertyName(FormattingKey);
this.Formatting.WriteTo(writer);

writer.WritePropertyName(BicepKey);
this.Bicep.WriteTo(writer);

writer.WriteEndObject();
}

Expand Down
1 change: 1 addition & 0 deletions src/Bicep.Core/Configuration/bicepconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This is the base configuration which provides the defaults for all values (end users don't see this file).
// Intellisense for bicepconfig.json is controlled by src/vscode-bicep/schemas/bicepconfig.schema.json

"bicep": {},
"cloud": {
"currentProfile": "AzureCloud",
"profiles": {
Expand Down
7 changes: 7 additions & 0 deletions src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,13 @@ public Diagnostic MicrosoftGraphBuiltinDeprecatedSoon(ExtensionDeclarationSyntax
public Diagnostic NameofInvalidOnUnnamedExpression() => CoreError(
"BCP408",
$"The \"{LanguageConstants.NameofFunctionName}\" function can only be used with an expression which has a name.");

public Diagnostic InvalidBicepVersion(string? configFilePath, string expectedVersion, string actualVersion) => CoreError(
"BCP409",
configFilePath switch {
{} => $"""Bicep version "{actualVersion}" was used for compilation, but version "{expectedVersion}" is required in configuration file "{configFilePath}".""",
_ => $"""Bicep version "{actualVersion}" was used for compilation, but version "{expectedVersion}" is required.""",
});
}

public static DiagnosticBuilderInternal ForPosition(TextSpan span)
Expand Down
13 changes: 12 additions & 1 deletion src/Bicep.Core/Emit/EmitLimitationCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public static EmitLimitationInfo Calculate(SemanticModel model)
ForSyntaxValidatorVisitor.Validate(model, diagnostics);
FunctionPlacementValidatorVisitor.Validate(model, diagnostics);
IntegerValidatorVisitor.Validate(model, diagnostics);


BlockIncorrectBicepVersion(model, diagnostics);
DetectDuplicateNames(model, diagnostics, resourceScopeData, moduleScopeData);
DetectIncorrectlyFormattedNames(model, diagnostics);
DetectUnexpectedResourceLoopInvariantProperties(model, diagnostics);
Expand All @@ -61,6 +62,16 @@ public static EmitLimitationInfo Calculate(SemanticModel model)
return new(diagnostics.GetDiagnostics(), moduleScopeData, resourceScopeData, paramAssignments);
}

private static void BlockIncorrectBicepVersion(SemanticModel model, IDiagnosticWriter diagnostics)
{
var actualVersion = ThisAssembly.AssemblyInformationalVersion.Split('+')[0];
if (model.Configuration.Bicep.Data.Version is {} expectedVersion &&
expectedVersion != actualVersion)
{
diagnostics.Write(TextSpan.TextDocumentStart, x => x.InvalidBicepVersion(model.Configuration.ConfigFileUri?.LocalPath, expectedVersion, actualVersion));
}
}

private static void DetectDuplicateNames(SemanticModel semanticModel, IDiagnosticWriter diagnosticWriter, ImmutableDictionary<DeclaredResourceMetadata, ScopeHelper.ScopeData> resourceScopeData, ImmutableDictionary<ModuleSymbol, ScopeHelper.ScopeData> moduleScopeData)
{
// TODO generalize or move into Az extension
Expand Down
19 changes: 19 additions & 0 deletions src/Bicep.Core/Utils/BicepVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Bicep.Core.Utils;

public record BicepVersion(
string Value,
string? CommitHash)
{
public static readonly BicepVersion Instance = GetVersion();

private static BicepVersion GetVersion()
{
var split = ThisAssembly.AssemblyInformationalVersion.Split('+');

return new(
split[0],
split.Length > 1 ? split[1] : null);
}
}
2 changes: 1 addition & 1 deletion src/Bicep.Core/Utils/Environment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ public class Environment : IEnvironment

public IEnumerable<string> GetVariableNames()
=> System.Environment.GetEnvironmentVariables().Keys.OfType<string>();
}
}
11 changes: 11 additions & 0 deletions src/vscode-bicep/schemas/bicepconfig.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,17 @@
}
}
},
"bicep": {
"type": "object",
"description": "Bicep configuration",
"properties": {
"version": {
"type": "string",
"format": "[0-9]+\\.[0-9]+\\.[0-9]+",
"description": "The version of Bicep to require for compilation. If this is specified and not met, compilation will fail."
}
}
},
"cacheRootDirectory": {
"type": "string",
"description": "The directory in which Bicep may cache templates downloaded from remote registries"
Expand Down

0 comments on commit 37da17f

Please sign in to comment.