Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
46 changes: 40 additions & 6 deletions src/Cli/func/Actions/LocalActions/InitAction/InitAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public InitAction(ITemplatesManager templatesManager, ISecretsManager secretsMan

Comment thread
harshivcodes marked this conversation as resolved.
public bool SkipNpmInstall { get; set; } = false;

public bool SkipGoModTidy { get; set; } = false;

public WorkerRuntime ResolvedWorkerRuntime { get; set; }

public string ResolvedLanguage { get; set; }
Expand Down Expand Up @@ -147,6 +149,10 @@ public override ICommandLineParserResult ParseArgs(string[] args)
.Setup<bool>("skip-npm-install")
.Callback(skip => SkipNpmInstall = skip);

Parser
.Setup<bool>("skip-go-mod-tidy")
.Callback(skip => SkipGoModTidy = skip);

Parser
.Setup<string>('m', "model")
.Callback(m => ProgrammingModel = m);
Expand Down Expand Up @@ -209,6 +215,11 @@ private async Task InitFunctionAppProject()
{
(ResolvedWorkerRuntime, ResolvedLanguage) = ResolveWorkerRuntimeAndLanguage(WorkerRuntime, Language);

if (ResolvedWorkerRuntime == Helpers.WorkerRuntime.Native)
{
ColoredConsole.WriteLine(WarningColor("[preview] The native worker runtime is currently in preview. Features and behavior may change in future releases."));
}
Comment thread
harshivcodes marked this conversation as resolved.
Outdated

// Order here is important: each language may have multiple runtimes, and each unique (language, worker-runtime) pair
// may have its own programming model. Thus, we assume that ResolvedLanguage and ResolvedWorkerRuntime are properly set
// before attempting to resolve the programming model.
Expand Down Expand Up @@ -238,7 +249,7 @@ private async Task InitFunctionAppProject()
else
{
bool managedDependenciesOption = ResolveManagedDependencies(ResolvedWorkerRuntime, ManagedDependencies);
await InitLanguageSpecificArtifacts(ResolvedWorkerRuntime, ResolvedLanguage, ResolvedProgrammingModel, managedDependenciesOption, GeneratePythonDocumentation);
await InitLanguageSpecificArtifacts(ResolvedWorkerRuntime, ResolvedLanguage, ResolvedProgrammingModel, managedDependenciesOption, GeneratePythonDocumentation, SkipGoModTidy);
Comment thread
harshivcodes marked this conversation as resolved.
Outdated
await WriteFiles();

await WriteHostJson(ResolvedWorkerRuntime, managedDependenciesOption, ExtensionBundle);
Expand Down Expand Up @@ -274,9 +285,18 @@ private static (WorkerRuntime WorkerRuntime, string WorkerLanguage) ResolveWorke
if (!string.IsNullOrEmpty(workerRuntimeString))
{
workerRuntime = WorkerRuntimeLanguageHelper.NormalizeWorkerRuntime(workerRuntimeString);
language = !string.IsNullOrEmpty(languageString)
? WorkerRuntimeLanguageHelper.NormalizeLanguage(languageString)
: WorkerRuntimeLanguageHelper.NormalizeLanguage(workerRuntimeString);
if (!string.IsNullOrEmpty(languageString))
{
language = WorkerRuntimeLanguageHelper.NormalizeLanguage(languageString);
}
else if (WorkerRuntimeLanguageHelper.TryNormalizeLanguage(workerRuntimeString, out var inferredLanguage))
{
language = inferredLanguage;
}
else
{
language = WorkerRuntimeLanguageHelper.GetDefaultTemplateLanguageFromWorker(workerRuntime);
}
}
else if (GlobalCoreToolsSettings.CurrentWorkerRuntimeOrNone == Helpers.WorkerRuntime.None)
{
Expand Down Expand Up @@ -304,7 +324,8 @@ private static string LanguageSelectionIfRelevant(WorkerRuntime workerRuntime)
{
if (workerRuntime == Helpers.WorkerRuntime.Node
|| workerRuntime == Helpers.WorkerRuntime.DotnetIsolated
|| workerRuntime == Helpers.WorkerRuntime.Dotnet)
|| workerRuntime == Helpers.WorkerRuntime.Dotnet
|| workerRuntime == Helpers.WorkerRuntime.Native)
{
if (WorkerRuntimeLanguageHelper.WorkerToSupportedLanguages.TryGetValue(workerRuntime, out IEnumerable<string> languages)
&& languages.Count() != 0)
Expand All @@ -324,7 +345,8 @@ private static async Task InitLanguageSpecificArtifacts(
string language,
ProgrammingModel programmingModel,
bool managedDependenciesOption,
bool generatePythonDocumentation = true)
bool generatePythonDocumentation = true,
bool skipGoModTidy = false)
{
switch (workerRuntime)
{
Expand Down Expand Up @@ -369,6 +391,13 @@ private static async Task InitLanguageSpecificArtifacts(
case Helpers.WorkerRuntime.Node:
await NodeJSHelpers.SetupProject(programmingModel, language);
break;
case Helpers.WorkerRuntime.Native:
await GoHelpers.SetupProject(
Utilities.SanitizeLiteral(Path.GetFileName(Environment.CurrentDirectory), allowed: "-_."),
Comment thread
harshivcodes marked this conversation as resolved.
Outdated
skipGoModTidy);
await FileSystemHelpers.WriteFileIfNotExists("main.go", await StaticResources.MainGo);
await FileSystemHelpers.WriteFileIfNotExists(Constants.FuncIgnoreFile, await StaticResources.FuncIgnore);
Comment thread
harshivcodes marked this conversation as resolved.
Outdated
break;
}
}

Expand Down Expand Up @@ -490,6 +519,11 @@ private static async Task WriteDockerfile(WorkerRuntime workerRuntime, string la
{
await FileSystemHelpers.WriteFileIfNotExists("Dockerfile", await StaticResources.DockerfileCustom);
}
else if (workerRuntime == Helpers.WorkerRuntime.Native)
{
ColoredConsole.WriteLine(WarningColor("Docker support for the native worker runtime is not yet available. Skipping Dockerfile creation."));
return;
}
else if (workerRuntime == Helpers.WorkerRuntime.None)
{
throw new CliException("Can't find WorkerRuntime None");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Fclp;

namespace Azure.Functions.Cli.Actions.LocalActions
{
[Action(Name = "init native", ParentCommandName = "init", ShowInHelp = true, HelpText = "Options specific to native runtime apps when running func init")]
internal class InitNativeSubcommandAction : BaseAction
{
public override ICommandLineParserResult ParseArgs(string[] args)
{
Parser
.Setup<string>('l', "language")
.WithDescription("The language for the function app. Options: golang.")
Comment thread
harshivcodes marked this conversation as resolved.
Outdated
.Callback(_ => { });

Parser
.Setup<bool>("skip-go-mod-tidy")
.WithDescription("Skip running 'go mod tidy' after project creation.")
Comment thread
harshivcodes marked this conversation as resolved.
Outdated
.Callback(_ => { });

return base.ParseArgs(args);
}

public override Task RunAsync()
{
// This method is never called - the main InitAction handles execution
return Task.CompletedTask;
}
}
}
4 changes: 3 additions & 1 deletion src/Cli/func/Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ internal static partial class Constants
{ WorkerRuntime.Dotnet, new[] { "mcr.microsoft.com/azure-functions/dotnet", "microsoft/azure-functions-dotnet-core2.0", "mcr.microsoft.com/azure-functions/base", "microsoft/azure-functions-base" } },
{ WorkerRuntime.Node, new[] { "mcr.microsoft.com/azure-functions/node", "microsoft/azure-functions-node8" } },
{ WorkerRuntime.Python, new[] { "mcr.microsoft.com/azure-functions/python", "microsoft/azure-functions-python3.6" } },
{ WorkerRuntime.Powershell, new[] { "mcr.microsoft.com/azure-functions/powershell", "microsoft/azure-functions-powershell" } }
{ WorkerRuntime.Powershell, new[] { "mcr.microsoft.com/azure-functions/powershell", "microsoft/azure-functions-powershell" } },
{ WorkerRuntime.Native, new string[] { } }
Comment thread
harshivcodes marked this conversation as resolved.
Outdated
};

public static readonly string[] TriggersWithoutStorage =
Expand Down Expand Up @@ -161,6 +162,7 @@ public static class Languages
public const string Powershell = "powershell";
public const string Java = "java";
public const string Custom = "custom";
public const string Golang = "golang";
}

public static class ArmConstants
Expand Down
130 changes: 130 additions & 0 deletions src/Cli/func/Helpers/GoHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Text;
using System.Text.RegularExpressions;
using Azure.Functions.Cli.Common;
using Colors.Net;
using static Azure.Functions.Cli.Common.OutputTheme;

namespace Azure.Functions.Cli.Helpers
{
public static class GoHelpers
{
private const int MinimumGoMajorVersion = 1;
private const int MinimumGoMinorVersion = 24;

public static async Task<WorkerLanguageVersionInfo> GetEnvironmentGoVersion()
{
return await GetVersion("go");
}

public static void AssertGoVersion(WorkerLanguageVersionInfo goVersion)
{
if (goVersion?.Version == null)
{
throw new CliException(
$"Could not find a Go installation. Go {MinimumGoMajorVersion}.{MinimumGoMinorVersion} or later is required. " +
"Please install Go from https://go.dev/dl/");
}

if (GlobalCoreToolsSettings.IsVerbose)
{
ColoredConsole.WriteLine(VerboseColor($"Found Go version {goVersion.Version} ({goVersion.ExecutablePath})."));
}

if (goVersion.Major == null || goVersion.Minor == null)
{
throw new CliException(
$"Unable to parse Go version '{goVersion.Version}'. " +
$"Go {MinimumGoMajorVersion}.{MinimumGoMinorVersion} or later is required.");
}

// Accept any major version > 1, or major == 1 with minor >= 24
if (goVersion.Major > MinimumGoMajorVersion)
{
return;
}

if (goVersion.Major == MinimumGoMajorVersion && goVersion.Minor >= MinimumGoMinorVersion)
{
return;
}

throw new CliException(
$"Go version {goVersion.Version} is not supported. " +
$"Go {MinimumGoMajorVersion}.{MinimumGoMinorVersion} or later is required. " +
"Please update Go from https://go.dev/dl/");
}

public static async Task SetupProject(string moduleName, bool skipGoModTidy)
{
var goVersion = await GetEnvironmentGoVersion();
AssertGoVersion(goVersion);

// Initialize: Run go mod init
var modInitExe = new Executable("go", $"mod init {moduleName}");
var modInitExitCode = await modInitExe.RunAsync(
l => ColoredConsole.WriteLine(l),
e => ColoredConsole.Error.WriteLine(ErrorColor(e)));
Comment thread
harshivcodes marked this conversation as resolved.
Outdated
if (modInitExitCode != 0)
{
throw new CliException($"Failed to initialize Go module. 'go mod init {moduleName}' exited with code {modInitExitCode}.");
}

// Fetch the Azure Functions Go worker dependency
var goGetExe = new Executable("go", "get github.com/azure/azure-functions-golang-worker");
var goGetExitCode = await goGetExe.RunAsync(
l => ColoredConsole.WriteLine(l),
e => ColoredConsole.Error.WriteLine(ErrorColor(e)));
if (goGetExitCode != 0)
{
throw new CliException("Failed to add Azure Functions Go worker dependency. 'go get' exited with a non-zero code.");
}

if (!skipGoModTidy)
{
var tidyExe = new Executable("go", "mod tidy");
var tidyExitCode = await tidyExe.RunAsync(
l => ColoredConsole.WriteLine(l),
e => ColoredConsole.Error.WriteLine(ErrorColor(e)));
if (tidyExitCode != 0)
{
ColoredConsole.WriteLine(WarningColor("Warning: 'go mod tidy' exited with a non-zero code. You may need to run it manually."));
}
}
else
{
ColoredConsole.WriteLine(AdditionalInfoColor("Skipped \"go mod tidy\". You must run \"go mod tidy\" manually."));
}
}

private static async Task<WorkerLanguageVersionInfo> GetVersion(string goExe)
{
try
{
var exe = new Executable(goExe, "version");
Comment thread
harshivcodes marked this conversation as resolved.
var sb = new StringBuilder();
var exitCode = await exe.RunAsync(l => sb.AppendLine(l), e => sb.AppendLine(e));

if (exitCode == 0)
{
var output = sb.ToString().Trim();

// Parse "go version go1.24.2 linux/amd64" format
var match = Regex.Match(output, @"go(\d+\.\d+(?:\.\d+)?)");
if (match.Success)
{
return new WorkerLanguageVersionInfo(WorkerRuntime.Native, match.Groups[1].Value, goExe);
}
}
}
catch (Exception)
{
// Go is not installed or not on PATH
Comment thread
harshivcodes marked this conversation as resolved.
Outdated
}

return null;
}
}
}
1 change: 1 addition & 0 deletions src/Cli/func/Helpers/LanguageWorkerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public static class LanguageWorkerHelper
{ WorkerRuntime.Powershell, "languageWorkers:powershell:arguments" },
{ WorkerRuntime.Dotnet, string.Empty },
{ WorkerRuntime.Custom, string.Empty },
{ WorkerRuntime.Native, string.Empty },
{ WorkerRuntime.None, string.Empty }
}
.Select(p => RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
Expand Down
Loading
Loading