Skip to content
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
1 change: 1 addition & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
- Fix `func pack --build-native-deps` failure on Windows for Python 3.13+ (#4742)
- Cleaned up `func --help` output and fixed validation errors when using the `--help` flag for specific commands (#4748)
- Improved `func init --help` output to better display options for each worker runtime (#4748)
- Fix F# project & template initialization via `func init | new` (#4749)
4 changes: 2 additions & 2 deletions src/Cli/func/Actions/LocalActions/CreateFunctionAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public override async Task RunAsync()
if (string.IsNullOrWhiteSpace(TemplateName))
{
SelectionMenuHelper.DisplaySelectionWizardPrompt("template");
TemplateName ??= SelectionMenuHelper.DisplaySelectionWizard(DotnetHelpers.GetTemplates(_workerRuntime));
TemplateName ??= SelectionMenuHelper.DisplaySelectionWizard(DotnetHelpers.GetTemplates(_workerRuntime, Language));
}
else
{
Expand Down Expand Up @@ -461,7 +461,7 @@ private bool InferAndUpdateLanguage(WorkerRuntime workerRuntime)
return true;
case WorkerRuntime.DotnetIsolated:
// use fsproj as an indication that we have a F# project
Language = FileSystemHelpers.GetFiles(Environment.CurrentDirectory, searchPattern: "*.fsproj").Any() ? Constants.Languages.FSharpIsolated : Constants.Languages.CSharpIsolated;
Language = FileSystemHelpers.GetFiles(Environment.CurrentDirectory, searchPattern: "*.fsproj").Any() ? Constants.Languages.FSharp : Constants.Languages.CSharp;
return true;
case WorkerRuntime.Node:
// use tsconfig.json as an indicator that we have a TypeScript project
Expand Down
10 changes: 7 additions & 3 deletions src/Cli/func/Actions/LocalActions/InitAction/InitAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ private async Task InitFunctionAppProject()
if (WorkerRuntimeLanguageHelper.IsDotnet(ResolvedWorkerRuntime) && !Csx)
{
await ShowEolMessage();
await DotnetHelpers.DeployDotnetProject(Utilities.SanitizeLiteral(Path.GetFileName(Environment.CurrentDirectory), allowed: "-"), Force, ResolvedWorkerRuntime, TargetFramework);
await DotnetHelpers.DeployDotnetProject(Utilities.SanitizeLiteral(Path.GetFileName(Environment.CurrentDirectory), allowed: "-"), Force, ResolvedWorkerRuntime, TargetFramework, ResolvedLanguage);
}
else
{
Expand Down Expand Up @@ -266,7 +266,9 @@ private static (WorkerRuntime WorkerRuntime, string WorkerLanguage) ResolveWorke
if (!string.IsNullOrEmpty(workerRuntimeString))
{
workerRuntime = WorkerRuntimeLanguageHelper.NormalizeWorkerRuntime(workerRuntimeString);
language = languageString ?? WorkerRuntimeLanguageHelper.NormalizeLanguage(workerRuntimeString);
language = !string.IsNullOrEmpty(languageString)
? WorkerRuntimeLanguageHelper.NormalizeLanguage(languageString)
: WorkerRuntimeLanguageHelper.NormalizeLanguage(workerRuntimeString);
}
else if (GlobalCoreToolsSettings.CurrentWorkerRuntimeOrNone == Helpers.WorkerRuntime.None)
{
Expand All @@ -282,7 +284,9 @@ private static (WorkerRuntime WorkerRuntime, string WorkerLanguage) ResolveWorke
else
{
workerRuntime = GlobalCoreToolsSettings.CurrentWorkerRuntime;
language = GlobalCoreToolsSettings.CurrentLanguageOrNull ?? languageString ?? WorkerRuntimeLanguageHelper.NormalizeLanguage(WorkerRuntimeLanguageHelper.GetRuntimeMoniker(workerRuntime));
language = GlobalCoreToolsSettings.CurrentLanguageOrNull
?? (!string.IsNullOrEmpty(languageString) ? WorkerRuntimeLanguageHelper.NormalizeLanguage(languageString) : null)
?? WorkerRuntimeLanguageHelper.NormalizeLanguage(WorkerRuntimeLanguageHelper.GetRuntimeMoniker(workerRuntime));
}

return (workerRuntime, language);
Expand Down
2 changes: 0 additions & 2 deletions src/Cli/func/Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,6 @@ public static class Languages
public const string Python = "python";
public const string CSharp = "c#";
public const string FSharp = "f#";
public const string CSharpIsolated = "c#-isolated";
public const string FSharpIsolated = "f#-isolated";
public const string Powershell = "powershell";
public const string Java = "java";
public const string Custom = "custom";
Expand Down
36 changes: 28 additions & 8 deletions src/Cli/func/Helpers/DotnetHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,22 @@ public static async Task<string> DetermineTargetFrameworkAsync(string workingDir
return SelectionMenuHelper.DisplaySelectionWizard(uniqueTfms.ToArray());
}

public static async Task DeployDotnetProject(string name, bool force, WorkerRuntime workerRuntime, string targetFramework = "")
public static async Task DeployDotnetProject(string name, bool force, WorkerRuntime workerRuntime, string targetFramework = "", string language = "")
{
await TemplateOperationAsync(
async () =>
{
var frameworkString = string.IsNullOrEmpty(targetFramework)
? string.Empty
: $"--Framework \"{targetFramework}\"";

var languageString = string.IsNullOrEmpty(language)
? string.Empty
: $"--language \"{language}\"";

var connectionString = $"--StorageConnectionStringValue \"{Constants.StorageEmulatorConnectionString}\"";
TryGetCustomHiveArg(workerRuntime, out string customHive);
var exe = new Executable("dotnet", $"new func {frameworkString} --AzureFunctionsVersion v4 --name {name} {connectionString} {(force ? "--force" : string.Empty)}{customHive}");
var exe = new Executable("dotnet", $"new func {frameworkString} {languageString} --AzureFunctionsVersion v4 --name {name} {connectionString} {(force ? "--force" : string.Empty)}{customHive}");
var exitCode = await exe.RunAsync(o => { }, e => ColoredConsole.Error.WriteLine(ErrorColor(e)));
if (exitCode != 0)
{
Expand Down Expand Up @@ -189,12 +194,27 @@ await TemplateOperationAsync(
_ => throw new ArgumentException($"Unknown template '{templateName}'", nameof(templateName))
};

internal static IEnumerable<string> GetTemplates(WorkerRuntime workerRuntime)
internal static IEnumerable<string> GetTemplates(WorkerRuntime workerRuntime, string language = "")
{
if (workerRuntime == WorkerRuntime.DotnetIsolated)
{
return new[]
if (language.Equals("F#", StringComparison.OrdinalIgnoreCase))
{
return
[
"HttpTrigger",
"BlobTrigger",
"QueueTrigger",
"TimerTrigger",
"EventHubTrigger",
"EventGridTrigger",
"CosmosDBTrigger"
];
}

return
[
"McpToolTrigger",
"QueueTrigger",
"HttpTrigger",
"BlobTrigger",
Expand All @@ -211,11 +231,11 @@ internal static IEnumerable<string> GetTemplates(WorkerRuntime workerRuntime)
"DurableFunctionsOrchestration",
"DurableFunctionsEntityClass",
"DurableFunctionsEntityFunction"
};
];
}

return new[]
{
return
[
"QueueTrigger",
"HttpTrigger",
"BlobTrigger",
Expand All @@ -236,7 +256,7 @@ internal static IEnumerable<string> GetTemplates(WorkerRuntime workerRuntime)
"DaprPublishOutputBinding",
"DaprServiceInvocationTrigger",
"DaprTopicTrigger",
};
];
}

public static bool CanDotnetBuild()
Expand Down
5 changes: 3 additions & 2 deletions src/Cli/func/Helpers/ProjectHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public static string FindProjectFile(string path, LoggingFilterHelper loggingFil
DirectoryInfo filePath = new DirectoryInfo(path);
do
{
var projectFiles = filePath.GetFiles("*.csproj");
// Look for both C# and F# project files
IEnumerable<FileInfo> projectFiles = filePath.GetFiles("*.csproj").Concat(filePath.GetFiles("*.fsproj"));
if (projectFiles.Any())
{
foreach (FileInfo file in projectFiles)
Expand All @@ -68,7 +69,7 @@ public static string FindProjectFile(string path, LoggingFilterHelper loggingFil

if (shouldLog)
{
logger.LogDebug($"Csproj not found in {path} directory tree. Skipping user secrets file configuration.");
logger.LogDebug($"csproj (or fsproj) not found in {path} directory tree. Skipping user secrets file configuration.");
}

return null;
Expand Down
15 changes: 8 additions & 7 deletions src/Cli/func/Helpers/WorkerRuntimeLanguageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static class WorkerRuntimeLanguageHelper
private static readonly IDictionary<WorkerRuntime, string> _workerToDefaultLanguageMap = new Dictionary<WorkerRuntime, string>
{
{ WorkerRuntime.Dotnet, Constants.Languages.CSharp },
{ WorkerRuntime.DotnetIsolated, Constants.Languages.CSharpIsolated },
{ WorkerRuntime.DotnetIsolated, Constants.Languages.CSharp },
{ WorkerRuntime.Node, Constants.Languages.JavaScript },
{ WorkerRuntime.Python, Constants.Languages.Python },
{ WorkerRuntime.Powershell, Constants.Languages.Powershell },
Expand All @@ -62,8 +62,10 @@ public static class WorkerRuntimeLanguageHelper
{ Constants.Languages.TypeScript, new[] { "ts" } },
{ Constants.Languages.Python, new[] { "py" } },
{ Constants.Languages.Powershell, new[] { "pwsh" } },
{ Constants.Languages.CSharp, new[] { "csharp", "dotnet" } },
{ Constants.Languages.CSharpIsolated, new[] { "dotnet-isolated", "dotnetIsolated" } },

// By default dotnet should map to csharp
{ Constants.Languages.CSharp, new[] { "csharp", "dotnet", "dotnet-isolated", "dotnetIsolated" } },
{ Constants.Languages.FSharp, new[] { "fsharp" } },
{ Constants.Languages.Java, new string[] { } },
{ Constants.Languages.Custom, new string[] { } }
};
Expand All @@ -77,17 +79,16 @@ public static class WorkerRuntimeLanguageHelper
{
{ WorkerRuntime.Node, new[] { Constants.Languages.JavaScript, Constants.Languages.TypeScript } },
{ WorkerRuntime.Dotnet, new[] { Constants.Languages.CSharp, Constants.Languages.FSharp } },
{ WorkerRuntime.DotnetIsolated, new[] { Constants.Languages.CSharpIsolated, Constants.Languages.FSharpIsolated } }
{ WorkerRuntime.DotnetIsolated, new[] { Constants.Languages.CSharp, Constants.Languages.FSharp } }
};

public static IEnumerable<WorkerRuntime> AvailableWorkersList => _availableWorkersRuntime.Keys
.Where(k => k != WorkerRuntime.Java);

public static string AvailableWorkersRuntimeString =>
string.Join(", ", _availableWorkersRuntime.Keys
.Where(k => (k != WorkerRuntime.Java))
.Select(s => s.ToString()))
.Replace(WorkerRuntime.DotnetIsolated.ToString(), "dotnet-isolated");
.Where(k => k != WorkerRuntime.Java)
.Select(s => GetRuntimeMoniker(s)));

public static string GetRuntimeMoniker(WorkerRuntime workerRuntime)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void GetTemplateShortName_ReturnsExpectedShortName(string input, string e

[Theory]
[InlineData(WorkerRuntime.Dotnet, 20)]
[InlineData(WorkerRuntime.DotnetIsolated, 16)]
[InlineData(WorkerRuntime.DotnetIsolated, 17)]
public void GetTemplates_ReturnsExpectedTemplates(WorkerRuntime runtime, int expectedCount)
{
var templates = DotnetHelpers.GetTemplates(runtime);
Expand Down
Loading