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

POC - Refactor .NET 8 detection to eliminate FUNCTIONS_INPROC_NET8_ENABLED DLL approach #4249

Closed
wants to merge 1 commit into from
Closed
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
34 changes: 8 additions & 26 deletions src/Azure.Functions.Cli/Actions/HostActions/StartHostAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,25 +353,7 @@ private void UpdateEnvironmentVariables(IDictionary<string, string> secrets)
}
}

/// <summary>
/// Check local.settings.json to determine whether in-proc .NET8 is enabled.
/// </summary>
private async Task<bool> IsInProcDotNet8Enabled()
{
var localSettingsJObject = await GetLocalSettingsJsonAsJObjectAsync();
var inProcDotNet8EnabledValue = localSettingsJObject?["Values"]?[Constants.InProcDotNet8EnabledSetting]?.Value<string>();
var isInProcDotNet8Enabled = string.Equals("1", inProcDotNet8EnabledValue, StringComparison.Ordinal);

if (VerboseLogging == true)
{
var message = isInProcDotNet8Enabled
? $"{Constants.InProcDotNet8EnabledSetting} app setting enabled in local.settings.json"
: $"{Constants.InProcDotNet8EnabledSetting} app setting is not enabled in local.settings.json";
ColoredConsole.WriteLine(VerboseColor(message));
}

return isInProcDotNet8Enabled;
}

private async Task<JObject> GetLocalSettingsJsonAsJObjectAsync()
{
Expand Down Expand Up @@ -400,7 +382,7 @@ public override async Task RunAsync()
{
await PreRunConditions();
var isVerbose = VerboseLogging.HasValue && VerboseLogging.Value;

// Return if running is delegated to another version of Core Tools
if (await TryHandleInProcDotNetLaunchAsync())
{
Expand Down Expand Up @@ -486,12 +468,12 @@ private async Task<bool> TryHandleInProcDotNetLaunchAsync()
else if (GlobalCoreToolsSettings.CurrentWorkerRuntime == WorkerRuntime.dotnet)
{
// Infer host runtime by checking if .NET 8 is enabled
var isDotNet8Project = await IsInProcDotNet8Enabled();
var isDotNet8Project = await DotnetHelpers.IsDotNet8();

var selectedRuntime = isDotNet8Project
? DotnetConstants.InProc8HostRuntime
: DotnetConstants.InProc6HostRuntime;

PrintVerboseForHostSelection(selectedRuntime);

if (Utilities.IsMinifiedVersion())
Expand All @@ -510,10 +492,8 @@ private async Task<bool> TryHandleInProcDotNetLaunchAsync()
}

internal async Task ValidateHostRuntimeAsync(WorkerRuntime currentWorkerRuntime,
Func<Task<bool>> validateDotNet8ProjectEnablement = null)
bool? validateDotNet8ProjectEnablement = null)
{
validateDotNet8ProjectEnablement ??= IsInProcDotNet8Enabled;

void ThrowCliException(string suffix)
{
throw new CliException($"The runtime argument value provided, '{HostRuntime}', is invalid. {suffix}");
Expand All @@ -534,11 +514,13 @@ void ThrowCliException(string suffix)
ThrowCliException($"The provided value is only valid for the worker runtime '{WorkerRuntime.dotnetIsolated}'.");
}

if (isInproc8ArgumentValue && !await validateDotNet8ProjectEnablement())
validateDotNet8ProjectEnablement ??= await DotnetHelpers.IsDotNet8();

if (isInproc8ArgumentValue && !validateDotNet8ProjectEnablement.Value)
{
ThrowCliException($"For the '{DotnetConstants.InProc8HostRuntime}' runtime, the '{Constants.InProcDotNet8EnabledSetting}' environment variable must be set. See https://aka.ms/azure-functions/dotnet/net8-in-process.");
}
else if (isInproc6ArgumentValue && await validateDotNet8ProjectEnablement())
else if (isInproc6ArgumentValue && validateDotNet8ProjectEnablement.Value)
{
ThrowCliException($"For the '{DotnetConstants.InProc6HostRuntime}' runtime, the '{Constants.InProcDotNet8EnabledSetting}' environment variable cannot be be set. See https://aka.ms/azure-functions/dotnet/net8-in-process.");
}
Expand Down
34 changes: 32 additions & 2 deletions src/Azure.Functions.Cli/Helpers/DotnetHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static void EnsureDotnet()
public static async Task<string> DetermineTargetFramework(string projectDirectory, string projectFilename = null)
{
EnsureDotnet();
if (projectFilename == null)
if (projectFilename == null)
{
var projectFilePath = ProjectHelpers.FindProjectFile(projectDirectory);
if (projectFilePath != null)
Expand Down Expand Up @@ -118,7 +118,7 @@ await TemplateOperation(async () =>
{
ColoredConsole.Error.WriteLine(ErrorColor(dotnetNewErrorMessage));
}

throw new CliException("Error creating function.");
}
}, workerRuntime);
Expand Down Expand Up @@ -265,6 +265,22 @@ public static string GetCsprojOrFsproj()
}
}

/// <summary>
/// Check dll to determine if the project is a .NET 8 project
/// </summary>
/// <returns></returns>
public static async Task<bool> IsDotNet8()
{
var dllPath = ExtensionsHelper.GetScriptFilePath();

var dotNetVersion = await GetTargetFramework(dllPath);

var result = dotNetVersion.Contains(".NETCoreApp,Version=v8.0") ||
dotNetVersion.Contains(".NET,Version=v8.0");

return result;
}

private static Task TemplateOperation(Func<Task> action, WorkerRuntime workerRuntime)
{
EnsureDotnet();
Expand Down Expand Up @@ -349,5 +365,19 @@ private static async Task DotnetTemplatesAction(string action, string templateDi
await exe.RunAsync();
}
}

private static async Task<string> GetTargetFramework(string dllPath)
{
var assembly = Assembly.LoadFrom(dllPath);
var targetFrameworkAttribute = (System.Runtime.Versioning.TargetFrameworkAttribute)
Attribute.GetCustomAttribute(assembly, typeof(System.Runtime.Versioning.TargetFrameworkAttribute));

if (targetFrameworkAttribute == null)
{
throw new CliException("Could not find the TargetFramework element in the .csproj file.");
}

return targetFrameworkAttribute.FrameworkName;
}
}
}
19 changes: 19 additions & 0 deletions src/Azure.Functions.Cli/Helpers/ExtensionsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,25 @@ private static IEnumerable<string> GetBindings()
return bindings;
}

public static string GetScriptFilePath()
{
// Ignore current implementation, I need help getting the script file path.

// Below should be the logic to get the script file path...

// If we don't have functions, this mean we don't have function.json
// If we don't have a function.json file, we can't determine the script file
var scriptFile =
FileSystemHelpers.GetFiles(Environment.CurrentDirectory, searchPattern: Constants.FunctionJsonFileName)
.Select(p =>
{
var functionJsonFileContent = FileSystemHelpers.ReadAllTextFromFile(p);
var functionMetadata = JsonConvert.DeserializeObject<FunctionMetadata>(functionJsonFileContent);
return functionMetadata.ScriptFile[1..];
}).FirstOrDefault();
return scriptFile;
}

public static IEnumerable<ExtensionPackage> GetExtensionPackages()
{
Dictionary<string, ExtensionPackage> packages = new Dictionary<string, ExtensionPackage>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,15 @@ public async Task ValidateHostRuntimeAsync_MatchesExpectedResults(WorkerRuntime
HostRuntime = hostRuntimeArgument
};

await startHostAction.ValidateHostRuntimeAsync(currentRuntime, () => Task.FromResult(validNet8Configuration));
await startHostAction.ValidateHostRuntimeAsync(currentRuntime, validNet8Configuration);
}
catch (CliException)
{
if (!expectException)
{
throw;
}

return;
}

Expand Down