From 82391fa093bb355062360b8cefdc13dba9dd52cb Mon Sep 17 00:00:00 2001 From: Chrison Simtian Date: Sat, 15 Aug 2026 11:30:55 +1200 Subject: [PATCH] Apply the run configuration on run, and add Run with Parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires the stored configuration into the two ways a target gets run. The inline ▶ stays silent: saved parameters are appended as arguments and secrets applied as environment variables, so the common case is one click and the configuration is simply in effect. "Run Target with Parameters…" opens an InputBox prefilled with the target and those same arguments, for a one-off tweak — an extra `--configuration Debug`, a different verbosity — without editing the saved configuration. Secrets still apply; they are not editable here, since the point of storing them in the keychain is that they never appear in a text field. runInTerminal disposes and recreates the Fallout terminal when an environment is supplied. A terminal's environment is fixed at creation, so reusing the existing one would silently run with a stale secret after a rotation — the failure would look like a bad credential rather than a stale terminal. Both commands are hidden from the command palette: they act on a tree item, and invoking them without one does nothing. Closes #14 Co-Authored-By: Claude Opus 5 --- package.json | 17 +++++++++++++++- src/extension.ts | 50 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 433da92..b5dd1d4 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,12 @@ "category": "Fallout", "icon": "$(play)" }, + { + "command": "fallout.runTargetWithParameters", + "title": "Run Target with Parameters…", + "category": "Fallout", + "icon": "$(run-all)" + }, { "command": "fallout.goToTarget", "title": "Go to Definition", @@ -145,9 +151,14 @@ "group": "inline@1" }, { - "command": "fallout.goToTarget", + "command": "fallout.runTargetWithParameters", "when": "(view == fallout.build || view == fallout.buildExplorer) && viewItem == falloutTarget", "group": "inline@2" + }, + { + "command": "fallout.goToTarget", + "when": "(view == fallout.build || view == fallout.buildExplorer) && viewItem == falloutTarget", + "group": "inline@3" } ], "commandPalette": [ @@ -155,6 +166,10 @@ "command": "fallout.runTarget", "when": "false" }, + { + "command": "fallout.runTargetWithParameters", + "when": "false" + }, { "command": "fallout.goToTarget", "when": "false" diff --git a/src/extension.ts b/src/extension.ts index aa5dcad..c28d0b1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -121,9 +121,16 @@ class DeploymentProvider implements vscode.TreeDataProvider { } } -function runInTerminal(root: string, args: string): void { - const existing = vscode.window.terminals.find(t => t.name === 'Fallout'); - const terminal = existing ?? vscode.window.createTerminal({ name: 'Fallout', cwd: root }); +function runInTerminal(root: string, args: string, env?: Record): void { + const hasEnv = env !== undefined && Object.keys(env).length > 0; + let terminal = vscode.window.terminals.find(t => t.name === 'Fallout'); + if (hasEnv) { + // Terminal env is fixed at creation; recreate so the current secrets apply. + terminal?.dispose(); + terminal = vscode.window.createTerminal({ name: 'Fallout', cwd: root, env }); + } else { + terminal ??= vscode.window.createTerminal({ name: 'Fallout', cwd: root }); + } terminal.show(); terminal.sendText(process.platform === 'win32' ? `./build.ps1 ${args}` : `./build.sh ${args}`); } @@ -133,11 +140,27 @@ export function activate(context: vscode.ExtensionContext): void { const provider = new FalloutTargetsProvider(extensionVersion); const runConfig = new RunConfigStore(context); - const runTarget = (name: string) => { - const root = provider.source?.root; - if (root) { - runInTerminal(root, name); - } + const workspaceRoot = () => provider.source?.root ?? vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + + // Silent run (▶): apply saved parameters as args and secrets as env. + const runTarget = async (name: string): Promise => { + const root = workspaceRoot(); + if (!root) { return; } + const args = [name, runConfig.buildArgs()].filter(Boolean).join(' '); + runInTerminal(root, args, await runConfig.buildEnv()); + }; + + // Prompt run: prefill with target + saved args, let the user tweak before running. + const runTargetWithParameters = async (name: string): Promise => { + const root = workspaceRoot(); + if (!root) { return; } + const edited = await vscode.window.showInputBox({ + title: `Run ${name}`, + prompt: 'Arguments passed to the Fallout build (secrets are still applied as environment variables)', + value: [name, runConfig.buildArgs()].filter(Boolean).join(' '), + }); + if (edited === undefined) { return; } // cancelled + runInTerminal(root, edited, await runConfig.buildEnv()); }; const refreshAll = () => { @@ -168,7 +191,12 @@ export function activate(context: vscode.ExtensionContext): void { vscode.commands.registerCommand('fallout.refreshTargets', refreshAll), vscode.commands.registerCommand('fallout.runTarget', (item?: TargetItem) => { if (item?.target) { - runTarget(item.target.name); + void runTarget(item.target.name); + } + }), + vscode.commands.registerCommand('fallout.runTargetWithParameters', (item?: TargetItem) => { + if (item?.target) { + void runTargetWithParameters(item.target.name); } }), vscode.commands.registerCommand('fallout.goToTarget', (item?: TargetItem) => { @@ -186,13 +214,13 @@ export function activate(context: vscode.ExtensionContext): void { provider.source ??= source; const graph = loadGraph(source); checkCompatibility(graph, extensionVersion); - GraphPanel.createOrShow(context.extensionUri, graph, runTarget); + GraphPanel.createOrShow(context.extensionUri, graph, name => void runTarget(name)); } catch (e) { void vscode.window.showWarningMessage(`Fallout: could not parse ${source.file}: ${e}`); } }), vscode.commands.registerCommand('fallout.planTarget', () => { - const root = provider.source?.root ?? vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + const root = workspaceRoot(); if (root) { runInTerminal(root, '--plan'); }