dotnet / aspire

Tools, templates, and packages to accelerate building observable, production-ready apps
https://learn.microsoft.com/dotnet/aspire
MIT License
3.87k stars 463 forks source link

Add annotation to manually specify project command line arguments. (Needed for AWS Lambda support) #6120

Open normj opened 1 month ago

normj commented 1 month ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

We are looking into how we can support adding a AWS Lambda Function to the DistributedApplicationBuilder. A Lambda function can be written as an executable or as a class library. The class library programming model being the more common approach.

The executable approach is not an issue for Aspire but for the class library we need to customize the execution arguments. Lambda functions coded as a class library are started with a host executable and the depsfile and runtimeconfig are overriden at startup. The class library is passed in as an argument to the host executable. For example if we had .NET project called "ClassLibraryLambdaFunction" that contained a Lambda function it would be executed using the following:

dotnet exec --depsfile ./ClassLibraryLambdaFunction.deps.json --runtimeconfig ./ClassLibraryLambdaFunction.runtimeconfig.json ../../../../AspireExtensions/LambdaHost/Amazon.Lambda.RuntimeSupport.dll ClassLibraryLambdaFunction::ClassLibraryLambdaFunction.Function::FunctionHandler

Having this configured in the launchSettings.json file by the user is not practical and as part of an AddAWSLambdaFunction method I want to be able to setup all of these command line arguments for the user and return a LambdaProjectResource that is a subclass of ProjectResource.

Describe the solution you'd like

When adding project to the DistributedApplicationBuilder you can only specify the launchSettings profile name for configuring how the project should be started. I would like to propose adding a LaunchSettingsOverrideAnnotation annotation where all of the command line arguments for the "dotnet" command can be specified. The ApplicationExecutor would look for the annotation and if exists use it for setting up the parameters for the process.

Additional context

I'm happy to contribute this work if the team agrees on it.

eerhardt commented 1 week ago

@normj - have you considered making the "class library" dotnet run-able? This can be done by adding MSBuild logic to set

See https://github.com/dotnet/sdk/blob/f7a5c4e3dc7f7ac35a2684192413530a43877a05/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets#L1111-L1171

These MSBuild properties are used by dotnet run and by VS (and thus by the Aspire AppHost) to launch projects.

In .NET 9, we added functionality to allow MSBuild <Target>s to set these properties, so you can run any logic to determine these values. See Extend dotnet run to invoke a run-command-producing Target (dotnet/sdk#42240).

normj commented 6 days ago

@eerhardt That is an interesting idea. If this was launched via the IDE would the IDE attach the debugger to it?

I do have the challenge that within a class library there could me multiple Lambda functions that need to be started up separately with different argument to identity the .NET method to call. Imagine in an Aspire AppHost the same project being added multiple times to the distributed application but with a different "handler" string that identifies what method to call. Maybe what I could do is have the MSBuild targets for running the class library look at an environment variable that the Aspire AppHost sets on the process. I'll play around and see what I can do.

eerhardt commented 6 days ago

If this was launched via the IDE would the IDE attach the debugger to it?

Yes. That is my understanding (although I have much less experience in the IDE tooling parts).

Look at the '$(UseAppHost)' != 'true' path:

https://github.com/dotnet/sdk/blob/f7a5c4e3dc7f7ac35a2684192413530a43877a05/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets#L1134-L1138

        <RunCommand Condition="'$(RunCommand)' == ''">dotnet</RunCommand>

        <_NetCoreRunArguments>exec &quot;$(TargetPath)&quot;</_NetCoreRunArguments>
        <RunArguments Condition="'$(RunArguments)' == '' and '$(StartArguments)' != ''">$(_NetCoreRunArguments) $(StartArguments)</RunArguments>
        <RunArguments Condition="'$(RunArguments)' == ''">$(_NetCoreRunArguments)</RunArguments>

This is basically the same thing you have. dotnet exec <some .dll>