Skip to content

Commit

Permalink
rsync support in deploySteps (#9808)
Browse files Browse the repository at this point in the history
  • Loading branch information
xisui-MSFT authored Sep 1, 2022
1 parent 08fd621 commit 466fb3b
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 46 deletions.
126 changes: 102 additions & 24 deletions Extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2333,21 +2333,21 @@
"markdownDescription": "%c_cpp.configuration.simplifyStructuredComments.markdownDescription%",
"scope": "application"
},
"C_Cpp.doxygen.generateOnType":{
"C_Cpp.doxygen.generateOnType": {
"type": "boolean",
"default": true,
"description": "%c_cpp.configuration.doxygen.generateOnType.description%",
"scope": "resource"
},
"C_Cpp.doxygen.generatedStyle": {
"type": "string",
"enum":[
"enum": [
"///",
"/**",
"/*!",
"//!"
],
"default":"///",
"default": "///",
"description": "%c_cpp.configuration.doxygen.generatedStyle.description%",
"scope": "resource"
},
Expand Down Expand Up @@ -2897,7 +2897,7 @@
"icon": "$(debug-configure)"
},
{
"command": "C_Cpp.GenerateDoxygenComment",
"command": "C_Cpp.GenerateDoxygenComment",
"title": "%c_cpp.command.GenerateDoxygenComment.title%",
"category": "C/C++"
}
Expand Down Expand Up @@ -3355,7 +3355,7 @@
"anyOf": [
{
"type": "object",
"description": "%c_cpp.debuggers.deploySteps.scp.description%",
"description": "%c_cpp.debuggers.deploySteps.copyFile.description%",
"default": {},
"required": [
"type",
Expand All @@ -3366,10 +3366,11 @@
"properties": {
"type": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.description%",
"description": "%c_cpp.debuggers.deploySteps.copyFile.description%",
"default": "",
"enum": [
"scp"
"scp",
"rsync"
]
},
"files": {
Expand All @@ -3384,7 +3385,7 @@
}
}
],
"description": "%c_cpp.debuggers.deploySteps.scp.files.description%",
"description": "%c_cpp.debuggers.deploySteps.copyFile.files.description%",
"default": ""
},
"host": {
Expand Down Expand Up @@ -3512,19 +3513,57 @@
},
"targetDir": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.targetDir.description%",
"description": "%c_cpp.debuggers.deploySteps.copyFile.targetDir.description%",
"default": ""
},
"scpPath": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.scpPath.description%",
"default": ""
"recursive": {
"type": "boolean",
"description": "%c_cpp.debuggers.deploySteps.copyFile.recursive.description%",
"default": "true"
},
"debug": {
"type": "boolean",
"description": "%c_cpp.debuggers.deploySteps.debug%"
}
}
},
"allOf": [
{
"if": {
"properties": {
"type": {
"const": "scp"
}
}
},
"then": {
"properties": {
"scpPath": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.copyFile.scpPath.description%",
"default": ""
}
}
}
},
{
"if": {
"properties": {
"type": {
"const": "rsync"
}
}
},
"then": {
"properties": {
"rsyncPath": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.copyFile.rsyncPath.description%",
"default": ""
}
}
}
}
]
},
{
"type": "object",
Expand Down Expand Up @@ -4034,7 +4073,7 @@
"anyOf": [
{
"type": "object",
"description": "%c_cpp.debuggers.deploySteps.scp.description%",
"description": "%c_cpp.debuggers.deploySteps.copyFile.description%",
"default": {},
"required": [
"type",
Expand All @@ -4045,10 +4084,11 @@
"properties": {
"type": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.description%",
"description": "%c_cpp.debuggers.deploySteps.copyFile.description%",
"default": "",
"enum": [
"scp"
"scp",
"rsync"
]
},
"files": {
Expand All @@ -4063,7 +4103,7 @@
}
}
],
"description": "%c_cpp.debuggers.deploySteps.scp.files.description%",
"description": "%c_cpp.debuggers.deploySteps.copyFile.files.description%",
"default": ""
},
"host": {
Expand Down Expand Up @@ -4191,19 +4231,57 @@
},
"targetDir": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.targetDir.description%",
"description": "%c_cpp.debuggers.deploySteps.copyFile.targetDir.description%",
"default": ""
},
"scpPath": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.scpPath.description%",
"default": ""
"recursive": {
"type": "boolean",
"description": "%c_cpp.debuggers.deploySteps.copyFile.recursive.description%",
"default": "true"
},
"debug": {
"type": "boolean",
"description": "%c_cpp.debuggers.deploySteps.debug%"
}
}
},
"allOf": [
{
"if": {
"properties": {
"type": {
"const": "scp"
}
}
},
"then": {
"properties": {
"scpPath": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.copyFile.scpPath.description%",
"default": ""
}
}
}
},
{
"if": {
"properties": {
"type": {
"const": "rsync"
}
}
},
"then": {
"properties": {
"rsyncPath": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.copyFile.rsyncPath.description%",
"default": ""
}
}
}
}
]
},
{
"type": "object",
Expand Down
10 changes: 6 additions & 4 deletions Extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,12 @@
"c_cpp.debuggers.host.localForward.localSocket.description": "Local socket",
"c_cpp.debuggers.host.localForward.remoteSocket.description": "Remote socket",
"c_cpp.debuggers.deploySteps.description": "Steps needed to deploy the application. Order matters.",
"c_cpp.debuggers.deploySteps.scp.description": "Copy files using SCP.",
"c_cpp.debuggers.deploySteps.scp.files.description": "Files to be copied via SCP. Supports path pattern.",
"c_cpp.debuggers.deploySteps.scp.targetDir.description": "Target directory.",
"c_cpp.debuggers.deploySteps.scp.scpPath.description": "Optional full path to SCP. Assumes SCP is on PATH if not specified",
"c_cpp.debuggers.deploySteps.copyFile.description": "Copy files using SCP or rsync.",
"c_cpp.debuggers.deploySteps.copyFile.files.description": "Files to be copied. Supports path pattern.",
"c_cpp.debuggers.deploySteps.copyFile.targetDir.description": "Target directory.",
"c_cpp.debuggers.deploySteps.copyFile.recursive.description": "If true, copies folders recursively.",
"c_cpp.debuggers.deploySteps.copyFile.scpPath.description": "Optional full path to SCP. Assumes SCP is on PATH if not specified",
"c_cpp.debuggers.deploySteps.copyFile.rsyncPath.description": "Optional full path to rsync. Assumes rsync is on PATH if not specified",
"c_cpp.debuggers.deploySteps.debug": "If true, skip when starting without debugging. If false, skip when starting debugging. If undefined, never skip.",
"c_cpp.debuggers.deploySteps.ssh.description": "SSH command step.",
"c_cpp.debuggers.deploySteps.ssh.command.description": "Command to be executed via SSH. The command after '-c' in SSH command.",
Expand Down
23 changes: 17 additions & 6 deletions Extension/src/Debugger/configurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { Environment, ParsedEnvironmentFile } from './ParsedEnvironmentFile';
import { CppSettings, OtherSettings } from '../LanguageServer/settings';
import { configPrefix } from '../LanguageServer/extension';
import { expandAllStrings, ExpansionOptions, ExpansionVars } from '../expand';
import { scp, ssh } from '../SSH/commands';
import { rsync, scp, ssh } from '../SSH/commands';
import * as glob from 'glob';
import { promisify } from 'util';
import { AttachItemsProvider, AttachPicker, RemoteAttachPicker } from './attachToProcess';
Expand All @@ -35,6 +35,7 @@ const localize: nls.LocalizeFunc = nls.loadMessageBundle();

enum StepType {
scp = 'scp',
rsync = 'rsync',
ssh = 'ssh',
shell = 'shell',
remoteShell = 'remoteShell',
Expand Down Expand Up @@ -1011,7 +1012,8 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
// Skip steps that doesn't match current launch mode. Explicit true/false check, since a step is always run when debug is undefined.
return true;
}
switch (step.type) {
const stepType: StepType = step.type;
switch (stepType) {
case StepType.command: {
// VS Code commands are the same regardless of which extension invokes them, so just invoke them here.
if (step.args && !Array.isArray(step.args)) {
Expand All @@ -1021,9 +1023,11 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
const returnCode: unknown = await vscode.commands.executeCommand(step.command, ...step.args);
return !returnCode;
}
case StepType.scp: {
case StepType.scp:
case StepType.rsync: {
const isScp: boolean = stepType === StepType.scp;
if (!step.files || !step.targetDir || !step.host) {
logger.getOutputChannelLogger().showErrorMessage(localize('missing.properties.scp', '"host", "files", and "targetDir" are required in scp steps.'));
logger.getOutputChannelLogger().showErrorMessage(localize('missing.properties.copyFile', '"host", "files", and "targetDir" are required in {0} steps.', isScp ? 'SCP' : 'rsync'));
return false;
}
const host: util.ISshHostInfo = { hostName: step.host.hostName, user: step.host.user, port: step.host.port };
Expand All @@ -1036,10 +1040,17 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
files = files.concat((await globAsync(fileGlob)).map(file => vscode.Uri.file(file)));
}
} else {
logger.getOutputChannelLogger().showErrorMessage(localize('incorrect.files.type.scp', '"files" must be a string or an array of strings in scp steps.'));
logger.getOutputChannelLogger().showErrorMessage(localize('incorrect.files.type.copyFile', '"files" must be a string or an array of strings in {0} steps.', isScp ? 'SCP' : 'rsync'));
return false;
}
const scpResult: util.ProcessReturnType = await scp(files, host, step.targetDir, config.scpPath, jumpHosts, cancellationToken);

let scpResult: util.ProcessReturnType;
if (isScp) {
scpResult = await scp(files, host, step.targetDir, config.scpPath, config.recursive, jumpHosts, cancellationToken);
} else {
scpResult = await rsync(files, host, step.targetDir, config.scpPath, config.recursive, jumpHosts, cancellationToken);
}

if (!scpResult.succeeded || cancellationToken?.isCancellationRequested) {
return false;
}
Expand Down
29 changes: 28 additions & 1 deletion Extension/src/SSH/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import { runSshTerminalCommandWithLogin } from './sshCommandRunner';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();

export async function scp(files: vscode.Uri[], host: ISshHostInfo, targetDir: string, scpPath?: string, jumpHosts?: ISshHostInfo[], cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
export async function scp(files: vscode.Uri[], host: ISshHostInfo, targetDir: string, recursive: boolean = true, scpPath?: string, jumpHosts?: ISshHostInfo[], cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
const args: string[] = [];
if (recursive) {
args.push('-r');
}
if (jumpHosts && jumpHosts.length > 0) {
args.push('-J', jumpHosts.map(getFullHostAddress).join(','));
}
Expand All @@ -26,6 +29,30 @@ export async function scp(files: vscode.Uri[], host: ISshHostInfo, targetDir: st
return runSshTerminalCommandWithLogin(host, { systemInteractor: defaultSystemInteractor, nickname: 'scp', command: `"${scpPath || 'scp'}" ${args.join(' ')}`, token: cancellationToken });
}

// Recursive is less important in rsync thank in SCP. SCP under recursive mode always follows symlinks, and potentially causes problems.
// In rsync, there are options to avoid this issue (-l, -K). To mitigate confusion, we still provide a recursive option here like in SCP.
export async function rsync(files: vscode.Uri[], host: ISshHostInfo, targetDir: string, recursive: boolean = true, rsyncPath?: string, jumpHosts?: ISshHostInfo[], cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
// --links, -l When symlinks are encountered, recreate the symlink on the destination.
// --keep-dirlinks, -K Treat symlinked dir on receiver as dir.
// --perms, -p Keep permissions.
// --verbose, -v Verbose.
// --compress, -z Compress file data during the transfer.
const args: string[] = ['-lKpvz'];
if (recursive) {
args.push('-r');
}
if (jumpHosts && jumpHosts.length > 0) {
args.push('-e', `ssh -J ${jumpHosts.map(getFullHostAddress).join(',')}`);
}
if (host.port) {
// upper case P
args.push(`--port=${host.port}`);
}
args.push(files.map(uri => `"${uri.fsPath}"`).join(' '), `${getFullHostAddressNoPort(host)}:${targetDir}`);

return runSshTerminalCommandWithLogin(host, { systemInteractor: defaultSystemInteractor, nickname: 'rsync', command: `"${rsyncPath || 'rsync'}" ${args.join(' ')}`, token: cancellationToken });
}

export function ssh(host: ISshHostInfo, command: string, sshPath?: string, jumpHosts?: ISshHostInfo[], localForwards?: ISshLocalForwardInfo[], continueOn?: string, cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
const args: string[] = [];
if (jumpHosts && jumpHosts.length > 0) {
Expand Down
Loading

0 comments on commit 466fb3b

Please sign in to comment.