Skip to content

Commit

Permalink
Convert default command template values into ShellQuotedString format (
Browse files Browse the repository at this point in the history
…#4433)

* Convert default command template values into ShellQuotedString format

* Cmd doesn't like escaped strings, so switch to strong quotes

* Handle build image command

* Remove some duplicated code
  • Loading branch information
danegsta authored Dec 19, 2024
1 parent c64c35d commit 4799004
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
32 changes: 29 additions & 3 deletions src/commands/images/buildImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { IActionContext, UserCancelledError } from "@microsoft/vscode-azext-utils";
import { quoted } from "@microsoft/vscode-container-client";
import * as path from "path";
import * as vscode from "vscode";
import { ext } from "../../extensionVariables";
Expand Down Expand Up @@ -48,10 +49,10 @@ export async function buildImage(context: IActionContext, dockerFileUri: vscode.
contextPath
);

// Replace '${tag}' if needed. Tag is a special case because we don't want to prompt unless necessary, so must manually replace it.
if (tagRegex.test(terminalCommand.command)) {
const getImageName = async (): Promise<string> => {
const absFilePath: string = path.join(rootFolder.uri.fsPath, dockerFileItem.relativeFilePath);
const dockerFileKey = `buildTag_${absFilePath}`;

const prevImageName: string | undefined = ext.context.workspaceState.get(dockerFileKey);

// Get imageName based previous entries, else on name of subfolder containing the Dockerfile
Expand All @@ -65,7 +66,32 @@ export async function buildImage(context: IActionContext, dockerFileUri: vscode.
addImageTaggingTelemetry(context, imageName, '.after');

await ext.context.workspaceState.update(dockerFileKey, imageName);
terminalCommand.command = terminalCommand.command.replace(tagRegex, imageName);

return imageName;
};

// Replace '${tag}' if needed. Tag is a special case because we don't want to prompt unless necessary, so must manually replace it.
if (!terminalCommand.args || terminalCommand.args.length === 0) {
// This is a customized command, so parse the tag from the command
if (tagRegex.test(terminalCommand.command)) {
const imageName = await getImageName();
terminalCommand.command = terminalCommand.command.replace(tagRegex, imageName);
}
} else if (terminalCommand.args.some(arg => tagRegex.test(typeof (arg) === 'string' ? arg : arg.value))) {
// This is a default command, so look for ${tag} in the args
const imageName = await getImageName();

terminalCommand.args = terminalCommand.args.map(arg => {
if (typeof (arg) === 'string') {
if (tagRegex.test(arg)) {
arg = quoted(arg.replace(tagRegex, imageName));
}
} else if (tagRegex.test(arg.value)) {
arg = quoted(arg.value.replace(tagRegex, imageName));
}

return arg;
});
}

const client = await ext.runtimeManager.getClient();
Expand Down
33 changes: 27 additions & 6 deletions src/commands/selectCommandTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { IActionContext, IAzureQuickPickItem, IAzureQuickPickOptions, UserCancelledError } from '@microsoft/vscode-azext-utils';
import { PortBinding, VoidCommandResponse } from '@microsoft/vscode-container-client';
import { PortBinding, quoted, VoidCommandResponse } from '@microsoft/vscode-container-client';
import * as vscode from 'vscode';
import { ext } from '../extensionVariables';
import { isDockerComposeClient } from '../runtimes/OrchestratorRuntimeManager';
Expand Down Expand Up @@ -168,14 +168,35 @@ export async function selectCommandTemplate(
throw new Error(vscode.l10n.t('No command template was found for command \'{0}\'', command));
}

actionContext.telemetry.properties.isDefaultCommand = defaultTemplates.some(t => t.template === selectedTemplate.template) ? 'true' : 'false';
const isDefault = defaultTemplates.some(t => t.template === selectedTemplate.template);
actionContext.telemetry.properties.isDefaultCommand = isDefault ? 'true' : 'false';
actionContext.telemetry.properties.isCommandRegexMatched = selectedTemplate.match ? 'true' : 'false';

// This is not really ideal (putting the full command line into `command` instead of `command` + `args`), but parsing a string into a command + args like that is really hard
// Fortunately, `TaskCommandRunnerFactory` does not really care

let resolvedCommand = resolveVariables(selectedTemplate.template, folder, additionalVariables);

if (!isDefault) {
// This is not really ideal (putting the full command line into `command` instead of `command` + `args`), but parsing a string into a command + args like that is really hard
// Fortunately, `TaskCommandRunnerFactory` does not really care
return {
command: resolvedCommand,
args: undefined,
};
}

// For the default command, we can make assumptions that allow us to parse into command + args for better shell support

const argsRegex = /(?:"[^"]*")|(?:[^"\s]*)/g;
const commandAndArgs = resolvedCommand.match(argsRegex).filter(arg => arg.length !== 0);
if (commandAndArgs.length > 0) {
resolvedCommand = commandAndArgs[0].replace(/"/g, '');
}

const resolvedArgs = commandAndArgs.slice(1).map(arg => arg.startsWith('"') ? quoted(arg.replace(/"/g, '')) : arg);

return {
command: resolveVariables(selectedTemplate.template, folder, additionalVariables),
args: undefined,
command: resolvedCommand,
args: resolvedArgs,
};
}

Expand Down

0 comments on commit 4799004

Please sign in to comment.