diff --git a/extension/src/debugger/languages/dotnet.ts b/extension/src/debugger/languages/dotnet.ts index 2009733dfcb..4bcf7da8f57 100644 --- a/extension/src/debugger/languages/dotnet.ts +++ b/extension/src/debugger/languages/dotnet.ts @@ -56,60 +56,6 @@ class DotNetService implements IDotNetService { } async buildDotNetProject(projectFile: string): Promise { - const isDevKitEnabled = await this.getAndActivateDevKit(); - if (isDevKitEnabled) { - this.writeToDebugConsole(lookingForDevkitBuildTask, 'stdout', true); - - const tasks = await vscode.tasks.fetchTasks(); - const buildTask = tasks.find(t => t.source === "dotnet" && t.name?.includes('build')); - - // The build task may not be registered if there are is no solution in the workspace or if there are no C# projects - // with .csproj files. - if (buildTask) { - // Modify the task to target the specific project - const projectName = path.basename(projectFile, '.csproj'); - - // Create a modified task definition with just the project file - const modifiedDefinition = { - ...buildTask.definition, - file: projectFile // This will make it build the specific project directly - }; - - // Create a new task with the modified definition - const modifiedTask = new vscode.Task( - modifiedDefinition, - buildTask.scope || vscode.TaskScope.Workspace, - `build ${projectName}`, - buildTask.source, - buildTask.execution, - buildTask.problemMatchers - ); - - extensionLogOutputChannel.info(`Executing build task: ${modifiedTask.name} for project: ${projectFile}`); - await vscode.tasks.executeTask(modifiedTask); - - let disposable: vscode.Disposable = { dispose: () => {} }; - return new Promise((resolve, reject) => { - disposable = vscode.tasks.onDidEndTaskProcess(async e => { - if (e.execution.task === modifiedTask) { - if (e.exitCode !== 0) { - reject(new Error(buildFailedWithExitCode(e.exitCode ?? 'unknown'))); - } - else { - return resolve(); - } - } - }); - }).finally(() => disposable.dispose()); - } - else { - this.writeToDebugConsole(noCsharpBuildTask, 'stdout', true); - } - } - else { - this.writeToDebugConsole(csharpDevKitNotInstalled, 'stdout', true); - } - return new Promise((resolve, reject) => { extensionLogOutputChannel.info(`Building .NET project: ${projectFile} using dotnet CLI`); @@ -325,10 +271,9 @@ export function createProjectDebuggerExtension(dotNetServiceProducer: (debugSess const runApiOutput = await dotNetService.getDotNetRunApiOutput(projectPath); const runApiConfig = getRunApiConfigFromOutput(runApiOutput); - // Build if the executable doesn't exist or forceBuild is requested - if ((!(await doesFileExist(runApiConfig.executablePath)) || launchOptions.forceBuild)) { - await dotNetService.buildDotNetProject(projectPath); - } + // There may be an older cached version of the file-based app, so we + // should force a build. + await dotNetService.buildDotNetProject(projectPath); debugConfiguration.program = runApiConfig.executablePath; diff --git a/src/Aspire.Hosting/Dcp/DcpExecutor.cs b/src/Aspire.Hosting/Dcp/DcpExecutor.cs index e82734ab6b4..090726cb53e 100644 --- a/src/Aspire.Hosting/Dcp/DcpExecutor.cs +++ b/src/Aspire.Hosting/Dcp/DcpExecutor.cs @@ -1383,6 +1383,8 @@ private void PrepareProjectExecutables() { var projectLaunchConfiguration = new ProjectLaunchConfiguration(); projectLaunchConfiguration.ProjectPath = projectMetadata.ProjectPath; + projectLaunchConfiguration.Mode = _configuration[KnownConfigNames.DebugSessionRunMode] + ?? (Debugger.IsAttached ? ExecutableLaunchMode.Debug : ExecutableLaunchMode.NoDebug); projectLaunchConfiguration.DisableLaunchProfile = project.TryGetLastAnnotation(out _); // Use the effective launch profile which has fallback logic diff --git a/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs b/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs index 1bfaee947fa..0fd59219924 100644 --- a/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs +++ b/tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs @@ -1421,6 +1421,39 @@ public async Task ProjectLaunchConfiguration_Populated_WhenLaunchProfileSpecifie Assert.Equal("http", plc.LaunchProfile); } + [Theory] + [InlineData("Debug", ExecutableLaunchMode.Debug)] + [InlineData("NoDebug", ExecutableLaunchMode.NoDebug)] + public async Task ProjectLaunchConfiguration_RespectsDebugSessionRunMode(string runMode, string expectedMode) + { + var builder = DistributedApplication.CreateBuilder(); + builder.AddProject("proj", launchProfileName: "http"); + + using var app = builder.Build(); + var model = app.Services.GetRequiredService(); + + var kubernetes = new TestKubernetesService(); + var configBuilder = new ConfigurationBuilder(); + configBuilder.AddInMemoryCollection(new Dictionary + { + [KnownConfigNames.DashboardOtlpGrpcEndpointUrl] = "http://localhost", + ["AppHost:BrowserToken"] = "token", + ["AppHost:OtlpApiKey"] = "otlp-key", + [DcpExecutor.DebugSessionPortVar] = "12345", + [KnownConfigNames.DebugSessionRunMode] = runMode + }); + var configuration = configBuilder.Build(); + + var executor = CreateAppExecutor(model, configuration: configuration, kubernetesService: kubernetes); + + await executor.RunApplicationAsync(); + + var exe = Assert.Single(kubernetes.CreatedResources.OfType()); + Assert.True(exe.TryGetProjectLaunchConfiguration(out var plc)); + Assert.NotNull(plc); + Assert.Equal(expectedMode, plc!.Mode); + } + [Fact] public async Task ProjectLaunchConfiguration_Disabled_WhenLaunchProfileExcluded_InDebugSession() {