Skip to content
This repository has been archived by the owner on Oct 7, 2024. It is now read-only.

V0.21.1 #80

Merged
merged 6 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .build/tasks/BicepNet.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ param (

[ValidateSet('Debug', 'Release')]
[string]
$Configuration = 'Debug',
$Configuration = 'Release',

[Switch]
$Full,
Expand Down
7 changes: 3 additions & 4 deletions .build/tasks/BicepNet.generateTestCases.build.ps1
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
task GenerateTestCases {
Import-Module (Convert-Path 'output/BicepNet.PS/2.1.0/BicepNet.PS.psd1')
$ModuleVersion = Split-ModuleVersion -ModuleVersion (Get-BuiltModuleVersion -OutputDirectory 'output' -ModuleName 'BicepNet.PS' -VersionedOutputDirectory)
$ModuleOutputPath = "output/BicepNet.PS/$($ModuleVersion.Version)/BicepNet.PS"
Import-Module (Convert-Path $ModuleOutputPath)
$ModuleCommands = Get-Command -Module 'BicepNet.PS'
# $CommandTable = @{}
$CommandList = foreach ($Command in $ModuleCommands) {
# $CommandTable[$Command.Name] = @{}
# $Command.parameters.Keys | ForEach-Object {$CommandTable[$Command.Name][$_] = $Command.parameters[$_].ParameterType.FullName}
[PSCustomObject]@{
CommandName = $Command.Name
Parameters = $Command.parameters.Keys | ForEach-Object {
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ runs:
id: assetscache
uses: actions/cache@v3
with:
path: outout/RequiredModules
path: output/RequiredModules
key: ${{ hashFiles('RequiredModules.psd1') }}

- name: Download required dependencies
Expand Down
30 changes: 4 additions & 26 deletions .github/actions/test-linux/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,10 @@ runs:
ref: ${{ github.head_ref }}
fetch-depth: 0

- name: Update PowerShell from LTS
shell: pwsh
run: |
# Most of the code is from https://aka.ms/install-powershell.ps1 but adapted for GITHUB_PATH
$Destination = "~/.powershell"
if (-not (Test-Path -Path $Destination)) { New-Item -ItemType Directory -Path $Destination }

Invoke-Expression "& { $(irm https://aka.ms/install-powershell.ps1) } -Destination $Destination"
$TargetPath = Join-Path -Path $Destination -ChildPath "pwsh"
$Symlink = "/usr/bin/pwsh"

$Uid = id -u
if ($Uid -ne "0") { $SUDO = "sudo" } else { $SUDO = "" }
# Make symbolic link point to installed path
& $SUDO ln -fs $TargetPath $Symlink

if ($LASTEXITCODE -ne 0) { throw "Could not add to GITHUB_PATH: failed to make '$Symlink' point to '$TargetPath'." }

# - name: Set PSModulePath
# shell: pwsh
# run: |
# $PSModulePath = @(
# [System.IO.Path]::Combine($Env:GITHUB_WORKSPACE, 'output', 'RequiredModules'),
# $Env:PSModulePath
# ) -join [System.IO.Path]::PathSeparator
# "PSModulePath=$PSModulePath" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
- name: Update PowerShell
uses: bjompen/UpdatePWSHAction@v1.0.0
with:
ReleaseVersion: 'stable'

- name: Download Build Artifact
uses: actions/download-artifact@v3
Expand Down
26 changes: 14 additions & 12 deletions BicepNet.Core/BicepWrapper.Build.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Bicep.Core;
using Bicep.Core.Emit;
using Bicep.Core.FileSystem;
using Bicep.Core.Semantics;
Expand All @@ -17,28 +18,24 @@ public partial class BicepWrapper
public async Task<IList<string>> BuildAsync(string bicepPath, string usingPath = "", bool noRestore = false)
{
var inputPath = PathHelper.ResolvePath(bicepPath);
var features = featureProviderFactory.GetFeatureProvider(PathHelper.FilePathToFileUrl(inputPath));
var emitterSettings = new EmitterSettings(features, BicepSourceFileKind.BicepFile);
var inputUri = PathHelper.FilePathToFileUrl(inputPath);

if (emitterSettings.EnableSymbolicNames)
if (!IsBicepFile(inputPath) && !IsBicepparamsFile(inputPath))
{
logger?.LogWarning("Symbolic name support in ARM is experimental, and should be enabled for testing purposes only.Do not enable this setting for any production usage, or you may be unexpectedly broken at any time!");
throw new InvalidOperationException($"Input file '{inputPath}' must have a .bicep or .bicepparam extension.");
}

if (features.ResourceTypedParamsAndOutputsEnabled)
{
logger?.LogWarning("Resource-typed parameters and outputs in ARM are experimental, and should be enabled for testing purposes only. Do not enable this setting for any production usage, or you may be unexpectedly broken at any time!");
}
var template = new List<string>();
var compilation = await compilationService.CompileAsync(inputPath, noRestore);

if (diagnosticLogger is not null && diagnosticLogger.ErrorCount > 0)
{
throw new InvalidOperationException($"Failed to compile file: {inputPath}");
}

var stream = new MemoryStream();
var fileKind = compilation.SourceFileGrouping.EntryPoint.FileKind;
var semanticModel = compilation.GetEntrypointSemanticModel();

var stream = new MemoryStream();
EmitResult emitresult = fileKind switch
{
BicepSourceFileKind.BicepFile => new TemplateEmitter(compilation.GetEntrypointSemanticModel()).Emit(stream),
Expand All @@ -54,11 +51,16 @@ public async Task<IList<string>> BuildAsync(string bicepPath, string usingPath =
stream.Position = 0;
using var reader = new StreamReader(stream);
var result = await reader.ReadToEndAsync();
template.Add(result);

var template = new List<string>
{
result
};
return template;
}

private static bool IsBicepFile(string inputPath) => PathHelper.HasBicepExtension(PathHelper.FilePathToFileUrl(inputPath));
private static bool IsBicepparamsFile(string inputPath) => PathHelper.HasBicepparamsExension(PathHelper.FilePathToFileUrl(inputPath));
private static EmitResult EmitParamsFile(Compilation compilation, string usingPath, Stream stream)
{
var bicepPath = PathHelper.ResolvePath(usingPath);
Expand All @@ -76,4 +78,4 @@ private static EmitResult EmitParamsFile(Compilation compilation, string usingPa
var emitter = new ParametersEmitter(paramsSemanticModel);
return emitter.Emit(stream);
}
}
}
11 changes: 3 additions & 8 deletions BicepNet.Core/BicepWrapper.ConvertResourceToBicep.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Bicep.Core.Diagnostics;
using Bicep.Core.Extensions;
using Bicep.Core.PrettyPrint;
using Bicep.Core.PrettyPrint.Options;
using Bicep.Core.Rewriters;
Expand All @@ -20,20 +19,16 @@ public string ConvertResourceToBicep(string resourceId, string resourceBody)
JsonElement resource = JsonSerializer.Deserialize<JsonElement>(resourceBody);

var template = AzureResourceProvider.GenerateBicepTemplate(id, matchedType, resource, includeTargetScope: true);
template = RewriteBicepTemplate(template);

return template;
return RewriteBicepTemplate(template);
}

public string RewriteBicepTemplate(string template)
{
BicepFile virtualBicepFile = SourceFileFactory.CreateBicepFile(new Uri($"inmemory:///generated.bicep"), template);

var workspace = new Workspace();
workspace.UpsertSourceFiles(virtualBicepFile.AsEnumerable());

var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, moduleDispatcher, workspace, virtualBicepFile.FileUri, forceModulesRestore: false);
var compilation = new Compilation(featureProviderFactory, namespaceProvider, sourceFileGrouping, configurationManager, bicepAnalyzer, null);
var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, moduleDispatcher, workspace, virtualBicepFile.FileUri, featureProviderFactory, false);
var compilation = new Compilation(featureProviderFactory, namespaceProvider, sourceFileGrouping, configurationManager, bicepAnalyzer, moduleDispatcher);

var bicepFile = RewriterHelper.RewriteMultiple(
compilation,
Expand Down
10 changes: 5 additions & 5 deletions BicepNet.Core/BicepWrapper.FindModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public IList<BicepRepository> FindModules(string inputString, bool isRegistryEnd
logger?.LogInformation("Searching file {inputString} for endpoints", inputString);
var inputUri = PathHelper.FilePathToFileUrl(inputString);

var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, moduleDispatcher, workspace, inputUri);
var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, moduleDispatcher, workspace, inputUri, featureProviderFactory, false);

var moduleReferences = moduleDispatcher.GetValidModuleReferences(sourceFileGrouping.GetModulesToRestore());
// FullyQualifiedReferences are already unwrapped from potential local aliases
Expand All @@ -57,8 +57,10 @@ public IList<BicepRepository> FindModules()
foreach (var directoryPath in directories)
{
var directoryName = Path.GetFileName(directoryPath);
logger?.LogInformation("Found endpoint {directoryName}", directoryName);
endpoints.Add(directoryName);
if(directoryName != "mcr.microsoft.com") {
logger?.LogInformation("Found endpoint {directoryName}", directoryName);
endpoints.Add(directoryName);
}
}

return FindModulesByEndpoints(endpoints);
Expand Down Expand Up @@ -90,8 +92,6 @@ private IList<BicepRepository> FindModulesByEndpoints(IList<string> endpoints)
var client = new ContainerRegistryClient(new Uri($"https://{endpoint}"), cred, options);
var repositoryNames = client.GetRepositoryNames();

logger?.LogInformation("Found modules:\n{joinedRepositoryNames}", string.Join("\n", repositoryNames));

foreach (var repositoryName in repositoryNames)
{
logger?.LogInformation("Searching module {repositoryName}", repositoryName);
Expand Down
40 changes: 23 additions & 17 deletions BicepNet.Core/BicepWrapper.Publish.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using Bicep.Core;
using Bicep.Core.Diagnostics;
using Bicep.Core.Emit;
using Bicep.Core.Exceptions;
using Bicep.Core.FileSystem;
using Bicep.Core.Modules;
using Bicep.Core.Registry;
using Bicep.Core.Semantics;
using Bicep.Core.Workspaces;
using System;
using System.IO;
using System.Threading.Tasks;
Expand All @@ -14,60 +12,68 @@ namespace BicepNet.Core;

public partial class BicepWrapper
{
public void Publish(string inputFilePath, string targetModuleReference) =>
joinableTaskFactory.Run(() => PublishAsync(inputFilePath, targetModuleReference));
public void Publish(string inputFilePath, string targetModuleReference, string? documentationUri, bool overwriteIfExists = false) =>
joinableTaskFactory.Run(() => PublishAsync(inputFilePath, targetModuleReference, documentationUri, overwriteIfExists = false));

public async Task PublishAsync(string inputFilePath, string targetModuleReference)
public async Task PublishAsync(string inputFilePath, string targetModuleReference, string? documentationUri, bool overwriteIfExists = false)
{
var inputPath = PathHelper.ResolvePath(inputFilePath);
var inputUri = PathHelper.FilePathToFileUrl(inputPath);
var moduleReference = ValidateReference(targetModuleReference, inputUri);
ArtifactReference? moduleReference = ValidateReference(targetModuleReference, inputUri);

if (PathHelper.HasArmTemplateLikeExtension(inputUri))
{
// Publishing an ARM template file.
using var armTemplateStream = fileSystem.FileStream.New(inputPath, FileMode.Open, FileAccess.Read);
await PublishModuleAsync(moduleReference, armTemplateStream);
await this.PublishModuleAsync(moduleReference, armTemplateStream, documentationUri, overwriteIfExists);
return;
}

var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, moduleDispatcher, workspace, inputUri);
var compilation = new Compilation(featureProviderFactory, namespaceProvider, sourceFileGrouping, configurationManager, bicepAnalyzer, modelLookup: null);
var bicepCompiler = new BicepCompiler(featureProviderFactory, namespaceProvider, configurationManager, bicepAnalyzer, fileResolver, moduleDispatcher);
var compilation = await bicepCompiler.CreateCompilation(inputUri, workspace);

if (LogDiagnostics(compilation))
{
var stream = new MemoryStream();
new TemplateEmitter(compilation.GetEntrypointSemanticModel()).Emit(stream);

stream.Position = 0;
await PublishModuleAsync(moduleReference, stream);
await PublishModuleAsync(moduleReference, stream, documentationUri, overwriteIfExists);
}
}

private ModuleReference ValidateReference(string targetModuleReference, Uri targetModuleUri)
// copied from PublishCommand.cs
private ArtifactReference ValidateReference(string targetModuleReference, Uri targetModuleUri)
{
if (!moduleDispatcher.TryGetModuleReference(targetModuleReference, targetModuleUri, out var moduleReference, out var failureBuilder))
if (!this.moduleDispatcher.TryGetModuleReference(targetModuleReference, targetModuleUri, out var moduleReference, out var failureBuilder))
{
// TODO: We should probably clean up the dispatcher contract so this sort of thing isn't necessary (unless we change how target module is set in this command)
var message = failureBuilder(DiagnosticBuilder.ForDocumentStart()).Message;

throw new BicepException(message);
}

if (!moduleDispatcher.GetRegistryCapabilities(moduleReference).HasFlag(RegistryCapabilities.Publish))
if (!this.moduleDispatcher.GetRegistryCapabilities(moduleReference).HasFlag(RegistryCapabilities.Publish))
{
throw new BicepException($"The specified module target \"{targetModuleReference}\" is not supported.");
}

return moduleReference;
}

private async Task PublishModuleAsync(ModuleReference target, Stream stream)
// copied from PublishCommand.cs
private async Task PublishModuleAsync(ArtifactReference target, Stream stream, string? documentationUri, bool overwriteIfExists)
{
try
{
await moduleDispatcher.PublishModule(target, stream, null);
// If we don't want to overwrite, ensure module doesn't exist
if (!overwriteIfExists && await this.moduleDispatcher.CheckModuleExists(target))
{
throw new BicepException($"The module \"{target.FullyQualifiedReference}\" already exists in registry. Use -Force to overwrite the existing module.");
}
await this.moduleDispatcher.PublishModule(target, stream, documentationUri);
}
catch (ExternalModuleException exception)
catch (ExternalArtifactException exception)
{
throw new BicepException($"Unable to publish module \"{target.FullyQualifiedReference}\": {exception.Message}");
}
Expand Down
32 changes: 19 additions & 13 deletions BicepNet.Core/BicepWrapper.Restore.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Bicep.Core;
using Bicep.Core.Diagnostics;
using Bicep.Core.FileSystem;
using Bicep.Core.Navigation;
using Bicep.Core.Registry;
using Bicep.Core.Syntax;
using Bicep.Core.Workspaces;
Expand All @@ -24,9 +26,11 @@ public async Task RestoreAsync(string inputFilePath, bool forceModulesRestore =
// Create separate configuration for the build, to account for custom rule changes
var buildConfiguration = configurationManager.GetConfiguration(inputUri);

var sourceFileGrouping = SourceFileGroupingBuilder.Build(fileResolver, moduleDispatcher, workspace, inputUri, forceModulesRestore);
var originalModulesToRestore = sourceFileGrouping.GetModulesToRestore().ToImmutableHashSet();
// Restore valid references, don't log any errors
var bicepCompiler = new BicepCompiler(featureProviderFactory, namespaceProvider, configurationManager, bicepAnalyzer, fileResolver, moduleDispatcher);
var compilation = await bicepCompiler.CreateCompilation(inputUri, workspace);

var originalModulesToRestore = compilation.SourceFileGrouping.GetModulesToRestore().ToImmutableHashSet();

// RestoreModules() does a distinct but we'll do it also to prevent duplicates in processing and logging
var modulesToRestoreReferences = moduleDispatcher.GetValidModuleReferences(originalModulesToRestore)
.Distinct()
Expand All @@ -35,8 +39,8 @@ public async Task RestoreAsync(string inputFilePath, bool forceModulesRestore =
// restore is supposed to only restore the module references that are syntactically valid
await moduleDispatcher.RestoreModules(modulesToRestoreReferences, forceModulesRestore);

// update the errors based on if the restore was successful
sourceFileGrouping = SourceFileGroupingBuilder.Rebuild(moduleDispatcher, workspace, sourceFileGrouping);
// update the errors based on restore status
var sourceFileGrouping = SourceFileGroupingBuilder.Rebuild(featureProviderFactory, this.moduleDispatcher, this.workspace, compilation.SourceFileGrouping);

LogDiagnostics(GetModuleRestoreDiagnosticsByBicepFile(sourceFileGrouping, originalModulesToRestore, forceModulesRestore));

Expand All @@ -51,25 +55,27 @@ public async Task RestoreAsync(string inputFilePath, bool forceModulesRestore =
}
}

private ImmutableDictionary<BicepSourceFile, ImmutableArray<IDiagnostic>> GetModuleRestoreDiagnosticsByBicepFile(SourceFileGrouping sourceFileGrouping, ImmutableHashSet<ModuleSourceResolutionInfo> originalModulesToRestore, bool forceModulesRestore)
private static ImmutableDictionary<BicepSourceFile, ImmutableArray<IDiagnostic>> GetModuleRestoreDiagnosticsByBicepFile(SourceFileGrouping sourceFileGrouping, ImmutableHashSet<ArtifactResolutionInfo> originalModulesToRestore, bool forceModulesRestore)
{
static IDiagnostic? DiagnosticForModule(SourceFileGrouping grouping, ModuleDeclarationSyntax module)
=> grouping.TryGetErrorDiagnostic(module) is { } errorBuilder ? errorBuilder(DiagnosticBuilder.ForPosition(module.Path)) : null;
static IDiagnostic? DiagnosticForModule(SourceFileGrouping grouping, IArtifactReferenceSyntax moduleDeclaration)
=> grouping.TryGetErrorDiagnostic(moduleDeclaration) is { } errorBuilder ? errorBuilder(DiagnosticBuilder.ForPosition(moduleDeclaration.SourceSyntax)) : null;

static IEnumerable<(BicepSourceFile, IDiagnostic)> GetDiagnosticsForModulesToRestore(SourceFileGrouping grouping, ImmutableHashSet<ModuleSourceResolutionInfo> originalModulesToRestore)
static IEnumerable<(BicepFile, IDiagnostic)> GetDiagnosticsForModulesToRestore(SourceFileGrouping grouping, ImmutableHashSet<ArtifactResolutionInfo> originalArtifactsToRestore)
{
var originalModulesToRestore = originalArtifactsToRestore.OfType<ArtifactResolutionInfo>();
foreach (var (module, sourceFile) in originalModulesToRestore)
{
if (sourceFile is BicepSourceFile bicepFile && DiagnosticForModule(grouping, module) is { } diagnostic)
if (sourceFile is BicepFile bicepFile &&
DiagnosticForModule(grouping, module) is { } diagnostic)
{
yield return (bicepFile, diagnostic);
}
}
}

static IEnumerable<(BicepSourceFile, IDiagnostic)> GetDiagnosticsForAllModules(SourceFileGrouping grouping)
static IEnumerable<(BicepFile, IDiagnostic)> GetDiagnosticsForAllModules(SourceFileGrouping grouping)
{
foreach (var bicepFile in grouping.SourceFiles.OfType<BicepSourceFile>())
foreach (var bicepFile in grouping.SourceFiles.OfType<BicepFile>())
{
foreach (var module in bicepFile.ProgramSyntax.Declarations.OfType<ModuleDeclarationSyntax>())
{
Expand All @@ -85,6 +91,6 @@ private ImmutableDictionary<BicepSourceFile, ImmutableArray<IDiagnostic>> GetMod

return diagnosticsByFile
.ToLookup(t => t.Item1, t => t.Item2)
.ToImmutableDictionary(g => g.Key, g => g.ToImmutableArray());
.ToImmutableDictionary(g => (BicepSourceFile)g.Key, g => g.ToImmutableArray());
}
}
Loading
Loading