diff --git a/extension/schemas/aspire-global-settings.schema.json b/extension/schemas/aspire-global-settings.schema.json index 606b7ebd700..03ccb0aa580 100644 --- a/extension/schemas/aspire-global-settings.schema.json +++ b/extension/schemas/aspire-global-settings.schema.json @@ -258,4 +258,4 @@ } }, "additionalProperties": false -} +} \ No newline at end of file diff --git a/extension/schemas/aspire-settings.schema.json b/extension/schemas/aspire-settings.schema.json index 90cfd3abc95..b15d09198d7 100644 --- a/extension/schemas/aspire-settings.schema.json +++ b/extension/schemas/aspire-settings.schema.json @@ -262,4 +262,4 @@ } }, "additionalProperties": false -} +} \ No newline at end of file diff --git a/extension/src/commands/add.ts b/extension/src/commands/add.ts index e1e158d7b4b..a80dfccdb03 100644 --- a/extension/src/commands/add.ts +++ b/extension/src/commands/add.ts @@ -1,5 +1,8 @@ +import { AspireEditorCommandProvider } from '../editor/AspireEditorCommandProvider'; import { AspireTerminalProvider } from '../utils/AspireTerminalProvider'; +import { getAppHostArgs } from '../utils/appHostArgs'; -export async function addCommand(terminalProvider: AspireTerminalProvider) { - await terminalProvider.sendAspireCommandToAspireTerminal('add'); +export async function addCommand(terminalProvider: AspireTerminalProvider, editorCommandProvider: AspireEditorCommandProvider) { + const appHostArgs = await getAppHostArgs(editorCommandProvider); + await terminalProvider.sendAspireCommandToAspireTerminal('add', true, appHostArgs); } diff --git a/extension/src/commands/deploy.ts b/extension/src/commands/deploy.ts index 057d419f6ca..1cad80f24cc 100644 --- a/extension/src/commands/deploy.ts +++ b/extension/src/commands/deploy.ts @@ -1,5 +1,8 @@ +import { AspireEditorCommandProvider } from '../editor/AspireEditorCommandProvider'; import { AspireTerminalProvider } from '../utils/AspireTerminalProvider'; +import { getAppHostArgs } from '../utils/appHostArgs'; -export async function deployCommand(terminalProvider: AspireTerminalProvider) { - await terminalProvider.sendAspireCommandToAspireTerminal('deploy'); +export async function deployCommand(terminalProvider: AspireTerminalProvider, editorCommandProvider: AspireEditorCommandProvider) { + const appHostArgs = await getAppHostArgs(editorCommandProvider); + await terminalProvider.sendAspireCommandToAspireTerminal('deploy', true, appHostArgs); } diff --git a/extension/src/commands/publish.ts b/extension/src/commands/publish.ts index 276ea03a7a8..3df7b1594f6 100644 --- a/extension/src/commands/publish.ts +++ b/extension/src/commands/publish.ts @@ -1,5 +1,8 @@ +import { AspireEditorCommandProvider } from '../editor/AspireEditorCommandProvider'; import { AspireTerminalProvider } from '../utils/AspireTerminalProvider'; +import { getAppHostArgs } from '../utils/appHostArgs'; -export async function publishCommand(terminalProvider: AspireTerminalProvider) { - await terminalProvider.sendAspireCommandToAspireTerminal('publish'); +export async function publishCommand(terminalProvider: AspireTerminalProvider, editorCommandProvider: AspireEditorCommandProvider) { + const appHostArgs = await getAppHostArgs(editorCommandProvider); + await terminalProvider.sendAspireCommandToAspireTerminal('publish', true, appHostArgs); } diff --git a/extension/src/commands/update.ts b/extension/src/commands/update.ts index 23e8070920e..f5131303f2a 100644 --- a/extension/src/commands/update.ts +++ b/extension/src/commands/update.ts @@ -1,5 +1,8 @@ +import { AspireEditorCommandProvider } from '../editor/AspireEditorCommandProvider'; import { AspireTerminalProvider } from '../utils/AspireTerminalProvider'; +import { getAppHostArgs } from '../utils/appHostArgs'; -export async function updateCommand(terminalProvider: AspireTerminalProvider) { - await terminalProvider.sendAspireCommandToAspireTerminal('update'); +export async function updateCommand(terminalProvider: AspireTerminalProvider, editorCommandProvider: AspireEditorCommandProvider) { + const appHostArgs = await getAppHostArgs(editorCommandProvider); + await terminalProvider.sendAspireCommandToAspireTerminal('update', true, appHostArgs); } diff --git a/extension/src/editor/AspireEditorCommandProvider.ts b/extension/src/editor/AspireEditorCommandProvider.ts index 0d72b0d92e7..7b51b862300 100644 --- a/extension/src/editor/AspireEditorCommandProvider.ts +++ b/extension/src/editor/AspireEditorCommandProvider.ts @@ -70,7 +70,12 @@ export class AspireEditorCommandProvider implements vscode.Disposable { const fileText = await vscode.workspace.fs.readFile(vscode.Uri.file(filePath)).then(buffer => buffer.toString()); const lines = fileText.split(/\r?\n/); - return lines.some(line => line.startsWith('#:sdk Aspire.AppHost.Sdk')); + if (lines.some(line => line.startsWith('#:sdk Aspire.AppHost.Sdk'))) { + return true; + } + + const firstNonEmptyLine = lines.find(line => line.trim().length > 0)?.trim(); + return firstNonEmptyLine === 'var builder = DistributedApplication.CreateBuilder(args);'; } private onChangeAppHostPath(newPath: string | null) { @@ -113,15 +118,20 @@ export class AspireEditorCommandProvider implements vscode.Disposable { } } - public async tryExecuteRunAppHost(noDebug: boolean): Promise { - let appHostToRun: string; + /** + * Returns the resolved AppHost path from the active editor or workspace settings, or null if none is available. + */ + public async getAppHostPath(): Promise { if (vscode.window.activeTextEditor && await this.isAppHostCsFile(vscode.window.activeTextEditor.document.uri.fsPath)) { - appHostToRun = vscode.window.activeTextEditor.document.uri.fsPath; + return vscode.window.activeTextEditor.document.uri.fsPath; } - else if (this._workspaceAppHostPath) { - appHostToRun = this._workspaceAppHostPath; - } - else { + + return this._workspaceAppHostPath; + } + + public async tryExecuteRunAppHost(noDebug: boolean): Promise { + const appHostToRun = await this.getAppHostPath(); + if (!appHostToRun) { vscode.window.showErrorMessage(noAppHostInWorkspace); return; } diff --git a/extension/src/extension.ts b/extension/src/extension.ts index de001575696..f2e5fe88783 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -50,12 +50,12 @@ export async function activate(context: vscode.ExtensionContext) { const editorCommandProvider = new AspireEditorCommandProvider(); - const cliAddCommandRegistration = vscode.commands.registerCommand('aspire-vscode.add', () => tryExecuteCommand('aspire-vscode.add', terminalProvider, addCommand)); + const cliAddCommandRegistration = vscode.commands.registerCommand('aspire-vscode.add', () => tryExecuteCommand('aspire-vscode.add', terminalProvider, (tp) => addCommand(tp, editorCommandProvider))); const cliNewCommandRegistration = vscode.commands.registerCommand('aspire-vscode.new', () => tryExecuteCommand('aspire-vscode.new', terminalProvider, newCommand)); const cliInitCommandRegistration = vscode.commands.registerCommand('aspire-vscode.init', () => tryExecuteCommand('aspire-vscode.init', terminalProvider, initCommand)); - const cliDeployCommandRegistration = vscode.commands.registerCommand('aspire-vscode.deploy', () => tryExecuteCommand('aspire-vscode.deploy', terminalProvider, deployCommand)); - const cliPublishCommandRegistration = vscode.commands.registerCommand('aspire-vscode.publish', () => tryExecuteCommand('aspire-vscode.publish', terminalProvider, publishCommand)); - const cliUpdateCommandRegistration = vscode.commands.registerCommand('aspire-vscode.update', () => tryExecuteCommand('aspire-vscode.update', terminalProvider, updateCommand)); + const cliDeployCommandRegistration = vscode.commands.registerCommand('aspire-vscode.deploy', () => tryExecuteCommand('aspire-vscode.deploy', terminalProvider, (tp) => deployCommand(tp, editorCommandProvider))); + const cliPublishCommandRegistration = vscode.commands.registerCommand('aspire-vscode.publish', () => tryExecuteCommand('aspire-vscode.publish', terminalProvider, (tp) => publishCommand(tp, editorCommandProvider))); + const cliUpdateCommandRegistration = vscode.commands.registerCommand('aspire-vscode.update', () => tryExecuteCommand('aspire-vscode.update', terminalProvider, (tp) => updateCommand(tp, editorCommandProvider))); const openTerminalCommandRegistration = vscode.commands.registerCommand('aspire-vscode.openTerminal', () => tryExecuteCommand('aspire-vscode.openTerminal', terminalProvider, openTerminalCommand)); const configureLaunchJsonCommandRegistration = vscode.commands.registerCommand('aspire-vscode.configureLaunchJson', () => tryExecuteCommand('aspire-vscode.configureLaunchJson', terminalProvider, configureLaunchJsonCommand)); const settingsCommandRegistration = vscode.commands.registerCommand('aspire-vscode.settings', () => tryExecuteCommand('aspire-vscode.settings', terminalProvider, settingsCommand)); diff --git a/extension/src/utils/AspireTerminalProvider.ts b/extension/src/utils/AspireTerminalProvider.ts index 95ed6bf5426..edd482ca16e 100644 --- a/extension/src/utils/AspireTerminalProvider.ts +++ b/extension/src/utils/AspireTerminalProvider.ts @@ -58,7 +58,7 @@ export class AspireTerminalProvider implements vscode.Disposable { this._dcpServerConnectionInfo = value; } - async sendAspireCommandToAspireTerminal(subcommand: string, showTerminal: boolean = true) { + async sendAspireCommandToAspireTerminal(subcommand: string, showTerminal: boolean = true, additionalArgs?: string[]) { const cliPath = await this.getAspireCliExecutablePath(); // On Windows, use & to execute paths, especially those with special characters @@ -73,6 +73,19 @@ export class AspireTerminalProvider implements vscode.Disposable { command = `${quotedPath} ${subcommand}`; } + if (additionalArgs && additionalArgs.length > 0) { + const quotedArgs = additionalArgs.map(arg => { + if (process.platform === 'win32') { + // On Windows PowerShell, wrap in double quotes and escape inner double quotes + return `"${arg.replace(/"/g, '`"')}"`; + } else { + // On Unix, wrap in single quotes and escape inner single quotes + return `'${arg.replace(/'/g, "'\"'\"'")}'`; + } + }); + command += ' ' + quotedArgs.join(' '); + } + if (this.isCliDebugLoggingEnabled()) { command += ' --debug'; } diff --git a/extension/src/utils/appHostArgs.ts b/extension/src/utils/appHostArgs.ts new file mode 100644 index 00000000000..54fdfe2c04d --- /dev/null +++ b/extension/src/utils/appHostArgs.ts @@ -0,0 +1,14 @@ +import { AspireEditorCommandProvider } from '../editor/AspireEditorCommandProvider'; + +/** + * Returns CLI arguments to pass the resolved AppHost project path via --apphost, + * or undefined if no AppHost is currently available. + */ +export async function getAppHostArgs(editorCommandProvider: AspireEditorCommandProvider): Promise { + const appHostPath = await editorCommandProvider.getAppHostPath(); + if (!appHostPath) { + return undefined; + } + + return ['--apphost', appHostPath]; +}