зеркало из https://github.com/Azure/azure-dev.git
aspire: Consider Aspire Capability when detecting AppHost (#4447)
In addition to the `IsAspireHost` property we will now look at the project capabilities to see if an `Aspire` capability is listed and if treat the project as an AppHost project. This aligns `azd`'s behavior with other tooling like Visual Studio which uses the project capabilities to determine if the project is an App Host or not. The .NET Team asked us to include this in our sniffing logic (but to continue to check `IsAspireHost` as well). Fixes #4364
This commit is contained in:
Родитель
209fd014d3
Коммит
dd46ca9b18
|
@ -5,7 +5,6 @@ import (
|
|||
"io/fs"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/azure/azure-dev/cli/azd/pkg/tools/dotnet"
|
||||
)
|
||||
|
@ -26,7 +25,7 @@ func (ad *dotNetAppHostDetector) DetectProject(ctx context.Context, path string,
|
|||
switch ext {
|
||||
case ".csproj", ".fsproj", ".vbproj":
|
||||
projectPath := filepath.Join(path, name)
|
||||
if isAppHost, err := ad.isAppHostProject(ctx, filepath.Join(projectPath)); err != nil {
|
||||
if isAppHost, err := ad.dotnetCli.IsAspireHostProject(ctx, filepath.Join(projectPath)); err != nil {
|
||||
log.Printf("error checking if %s is an app host project: %v", projectPath, err)
|
||||
} else if isAppHost {
|
||||
return &Project{
|
||||
|
@ -40,14 +39,3 @@ func (ad *dotNetAppHostDetector) DetectProject(ctx context.Context, path string,
|
|||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// isAppHostProject returns true if the project at the given path has an MS Build Property named "IsAspireHost" which is
|
||||
// set to "true".
|
||||
func (ad *dotNetAppHostDetector) isAppHostProject(ctx context.Context, projectPath string) (bool, error) {
|
||||
value, err := ad.dotnetCli.GetMsBuildProperty(ctx, projectPath, "IsAspireHost")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(value) == "true", nil
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/azure/azure-dev/cli/azd/pkg/apphost"
|
||||
"github.com/azure/azure-dev/cli/azd/pkg/environment/azdcontext"
|
||||
|
@ -20,11 +19,11 @@ func appHostForProject(
|
|||
) (*project.ServiceConfig, error) {
|
||||
for _, service := range pc.Services {
|
||||
if service.Language == project.ServiceLanguageDotNet {
|
||||
isAppHost, err := dotnetCli.GetMsBuildProperty(ctx, service.Path(), "IsAspireHost")
|
||||
isAppHost, err := dotnetCli.IsAspireHostProject(ctx, service.Path())
|
||||
if err != nil {
|
||||
log.Printf("error checking if %s is an app host project: %v", service.Path(), err)
|
||||
}
|
||||
if strings.TrimSpace(isAppHost) == "true" {
|
||||
if isAppHost {
|
||||
return service, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ func (ai *DotNetImporter) CanImport(ctx context.Context, projectPath string) (bo
|
|||
return v.is, v.err
|
||||
}
|
||||
|
||||
value, err := ai.dotnetCli.GetMsBuildProperty(ctx, projectPath, "IsAspireHost")
|
||||
isAppHost, err := ai.dotnetCli.IsAspireHostProject(ctx, projectPath)
|
||||
if err != nil {
|
||||
ai.hostCheck[projectPath] = hostCheckResult{
|
||||
is: false,
|
||||
|
@ -94,11 +94,11 @@ func (ai *DotNetImporter) CanImport(ctx context.Context, projectPath string) (bo
|
|||
}
|
||||
|
||||
ai.hostCheck[projectPath] = hostCheckResult{
|
||||
is: strings.TrimSpace(value) == "true",
|
||||
is: isAppHost,
|
||||
err: nil,
|
||||
}
|
||||
|
||||
return strings.TrimSpace(value) == "true", nil
|
||||
return isAppHost, nil
|
||||
}
|
||||
|
||||
func (ai *DotNetImporter) ProjectInfrastructure(ctx context.Context, svcConfig *ServiceConfig) (*Infra, error) {
|
||||
|
|
|
@ -92,7 +92,7 @@ func TestImportManagerHasServiceErrorNoMultipleServicesWithAppHost(t *testing.T)
|
|||
slices.Contains(args.Args, "--getProperty:IsAspireHost")
|
||||
}).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) {
|
||||
return exec.RunResult{
|
||||
Stdout: "true",
|
||||
Stdout: aspireAppHostSniffResult,
|
||||
ExitCode: 0,
|
||||
}, nil
|
||||
})
|
||||
|
@ -145,7 +145,7 @@ func TestImportManagerHasServiceErrorAppHostMustTargetContainerApp(t *testing.T)
|
|||
slices.Contains(args.Args, "--getProperty:IsAspireHost")
|
||||
}).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) {
|
||||
return exec.RunResult{
|
||||
Stdout: "true",
|
||||
Stdout: aspireAppHostSniffResult,
|
||||
ExitCode: 0,
|
||||
}, nil
|
||||
})
|
||||
|
@ -278,7 +278,7 @@ func TestImportManagerProjectInfrastructureAspire(t *testing.T) {
|
|||
slices.Contains(args.Args, "--getProperty:IsAspireHost")
|
||||
}).RespondFn(func(args exec.RunArgs) (exec.RunResult, error) {
|
||||
return exec.RunResult{
|
||||
Stdout: "true",
|
||||
Stdout: aspireAppHostSniffResult,
|
||||
ExitCode: 0,
|
||||
}, nil
|
||||
})
|
||||
|
@ -462,3 +462,52 @@ func TestImportManager_SynthAllInfrastructure_FromResources(t *testing.T) {
|
|||
_, err = im.SynthAllInfrastructure(context.Background(), prjConfig)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// aspireAppHostSniffResult is mock data that would be returned by `dotnet msbuild` when fetching information about an
|
||||
// Aspire project. This is used to simulate the scenario where a project is an Aspire project. A real Aspire project would
|
||||
// have many entries in the ProjectCapability array (unrelated to the Aspire capability), but most have been omitted for
|
||||
// simplicity. An unrelated entry is included to ensure we are looking at the entire array of capabilities.
|
||||
// nolint: lll
|
||||
var aspireAppHostSniffResult string = `{
|
||||
"Properties": {
|
||||
"IsAspireHost": "true"
|
||||
},
|
||||
"Items": {
|
||||
"ProjectCapability": [
|
||||
{
|
||||
"Identity": "LocalUserSecrets",
|
||||
"FullPath": "/Users/matell/dd/ellismg/AspireBicep/AspireStarter/AspireStarter.AppHost/LocalUserSecrets",
|
||||
"RootDir": "/",
|
||||
"Filename": "LocalUserSecrets",
|
||||
"Extension": "",
|
||||
"RelativeDir": "",
|
||||
"Directory": "Users/matell/dd/ellismg/AspireBicep/AspireStarter/AspireStarter.AppHost/",
|
||||
"RecursiveDir": "",
|
||||
"ModifiedTime": "",
|
||||
"CreatedTime": "",
|
||||
"AccessedTime": "",
|
||||
"DefiningProjectFullPath": "/Users/matell/.nuget/packages/microsoft.extensions.configuration.usersecrets/8.0.0/buildTransitive/net6.0/Microsoft.Extensions.Configuration.UserSecrets.props",
|
||||
"DefiningProjectDirectory": "/Users/matell/.nuget/packages/microsoft.extensions.configuration.usersecrets/8.0.0/buildTransitive/net6.0/",
|
||||
"DefiningProjectName": "Microsoft.Extensions.Configuration.UserSecrets",
|
||||
"DefiningProjectExtension": ".props"
|
||||
},
|
||||
{
|
||||
"Identity": "Aspire",
|
||||
"FullPath": "/Users/matell/dd/ellismg/AspireBicep/AspireStarter/AspireStarter.AppHost/Aspire",
|
||||
"RootDir": "/",
|
||||
"Filename": "Aspire",
|
||||
"Extension": "",
|
||||
"RelativeDir": "",
|
||||
"Directory": "Users/matell/dd/ellismg/AspireBicep/AspireStarter/AspireStarter.AppHost/",
|
||||
"RecursiveDir": "",
|
||||
"ModifiedTime": "",
|
||||
"CreatedTime": "",
|
||||
"AccessedTime": "",
|
||||
"DefiningProjectFullPath": "/Users/matell/.nuget/packages/aspire.hosting.apphost/8.2.0/build/Aspire.Hosting.AppHost.targets",
|
||||
"DefiningProjectDirectory": "/Users/matell/.nuget/packages/aspire.hosting.apphost/8.2.0/build/",
|
||||
"DefiningProjectName": "Aspire.Hosting.AppHost",
|
||||
"DefiningProjectExtension": ".targets"
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
|
|
@ -303,6 +303,42 @@ func (cli *Cli) GetMsBuildProperty(ctx context.Context, project string, property
|
|||
return res.Stdout, nil
|
||||
}
|
||||
|
||||
// IsAspireHostProject returns true if the project at the given path has an MS Build Property named "IsAspireHost" which is
|
||||
// set to true or has a ProjectCapability named "Aspire".
|
||||
func (cli *Cli) IsAspireHostProject(ctx context.Context, projectPath string) (bool, error) {
|
||||
runArgs := newDotNetRunArgs("msbuild", projectPath, "--getProperty:IsAspireHost", "--getItem:ProjectCapability")
|
||||
res, err := cli.commandRunner.Run(ctx, runArgs)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("running dotnet msbuild on project '%s': %w", projectPath, err)
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Properties struct {
|
||||
IsAspireHost string `json:"IsAspireHost"`
|
||||
} `json:"Properties"`
|
||||
Items struct {
|
||||
ProjectCapability []struct {
|
||||
Identity string `json:"Identity"`
|
||||
} `json:"ProjectCapability"`
|
||||
} `json:"Items"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(res.Stdout), &result); err != nil {
|
||||
return false, fmt.Errorf("unmarshal dotnet msbuild output: %w", err)
|
||||
}
|
||||
|
||||
hasAspireCapability := false
|
||||
|
||||
for _, capability := range result.Items.ProjectCapability {
|
||||
if capability.Identity == "Aspire" {
|
||||
hasAspireCapability = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result.Properties.IsAspireHost == "true" || hasAspireCapability, nil
|
||||
}
|
||||
|
||||
func NewCli(commandRunner exec.CommandRunner) *Cli {
|
||||
return &Cli{
|
||||
commandRunner: commandRunner,
|
||||
|
|
Загрузка…
Ссылка в новой задаче