First version of cancelable and reportable commands
This commit is contained in:
Родитель
de6d2810b2
Коммит
470ddc98ee
|
@ -67,4 +67,4 @@ jspm_packages/
|
|||
*.vsix
|
||||
|
||||
# VS Code tests
|
||||
.vscode-test/**
|
||||
.vscode-test/
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
"outFiles": [
|
||||
"${workspaceRoot}/packages/*/out/src/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "Watch"
|
||||
"preLaunchTask": "Compile"
|
||||
},
|
||||
{
|
||||
"name": "Launch SalesforceDX VS Code Core Tests",
|
||||
|
@ -42,7 +42,7 @@
|
|||
"outFiles": [
|
||||
"${workspaceRoot}/packages/*/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "Watch"
|
||||
"preLaunchTask": "Compile"
|
||||
},
|
||||
{
|
||||
"name": "Launch SalesforceDX VS Code Apex Tests",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"outFiles": [
|
||||
"${workspaceRoot}/packages/*/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "Watch"
|
||||
"preLaunchTask": "Compile"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -31,6 +31,18 @@
|
|||
],
|
||||
"isBackground": true,
|
||||
"problemMatcher": "$tsc-watch"
|
||||
},
|
||||
{
|
||||
"taskName": "Compile",
|
||||
"command": "lerna",
|
||||
"isShellCommand": true,
|
||||
"showOutput": "silent",
|
||||
"args": [
|
||||
"run",
|
||||
"compile"
|
||||
],
|
||||
"isBackground": false,
|
||||
"problemMatcher": "$tsc-watch"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -32,8 +32,6 @@ You would usually do the following each time you close/reopen VS Code:
|
|||
|
||||
1. Open the Command Palette > Tasks: Run Task > Bootstrap (this essentially
|
||||
runs `lerna bootstrap`).
|
||||
1. Open the Command Palette > Tasks: Run Task > Watch (this essentially runs
|
||||
`lerna run --parallel watch`).
|
||||
1. In VS Code, open the debug view (Ctrl+Shift+D or Cmd+Shift+D on Mac) and from
|
||||
the launch configuration dropdown, pick "Launch Extensions".
|
||||
1. In VS Code, open the debug view (Ctrl+Shift+D or Cmd+Shift+D on Mac) and from
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"Other"
|
||||
],
|
||||
"dependencies": {
|
||||
"rxjs": "^5.4.1"
|
||||
"rxjs": "^5.4.1",
|
||||
"tree-kill": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.0.0",
|
||||
|
@ -29,4 +30,4 @@
|
|||
"coverage": "./node_modules/.bin/nyc npm test"
|
||||
},
|
||||
"main": "./out/src/"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,12 @@ export class Command {
|
|||
this.description = builder.description;
|
||||
this.args = builder.args;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this.description
|
||||
? this.description
|
||||
: `${this.command} ${this.args.join(' ')}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandBuilder {
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { Command } from './';
|
||||
import { spawn, ChildProcess, ExecOptions } from 'child_process';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import 'rxjs/add/observable/fromEvent';
|
||||
import 'rxjs/add/observable/interval';
|
||||
|
||||
const kill = require('tree-kill');
|
||||
|
||||
import { Command } from './';
|
||||
|
||||
export interface CancellationToken {
|
||||
isCancellationRequested: boolean;
|
||||
}
|
||||
|
@ -24,19 +27,25 @@ export class CliCommandExecutor {
|
|||
this.command.args,
|
||||
this.options
|
||||
);
|
||||
return new CommandExecution(childProcess, cancellationToken);
|
||||
return new CommandExecution(this.command, childProcess, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
export class CommandExecution {
|
||||
public readonly command: Command;
|
||||
public readonly cancellationToken?: CancellationToken;
|
||||
public readonly processExitSubject: Observable<number | string>;
|
||||
public readonly stdoutSubject: Observable<Buffer | string>;
|
||||
public readonly stderrSubject: Observable<Buffer | string>;
|
||||
|
||||
constructor(
|
||||
command: Command,
|
||||
childProcess: ChildProcess,
|
||||
cancellationToken?: CancellationToken
|
||||
) {
|
||||
this.command = command;
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
let timerSubscriber: Subscription | null;
|
||||
|
||||
// Process
|
||||
|
@ -54,15 +63,26 @@ export class CommandExecution {
|
|||
// Cancellation watcher
|
||||
if (cancellationToken) {
|
||||
const timer = Observable.interval(1000);
|
||||
timerSubscriber = timer.subscribe(next => {
|
||||
timerSubscriber = timer.subscribe(async next => {
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
try {
|
||||
childProcess.kill();
|
||||
await killPromise(childProcess.pid);
|
||||
} catch (e) {
|
||||
// This is best effort, by the time we get here, the process might have been killed.
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is required because of https://github.com/nodejs/node/issues/6052
|
||||
// Basically if a child process spawns it own children processes, those
|
||||
// children (grandchildren) processes are not necessarily killed
|
||||
async function killPromise(processId: number) {
|
||||
return new Promise((resolve, reject) => {
|
||||
kill(processId, 'SIGKILL', (err: any) => {
|
||||
err ? reject(err) : resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
"Other"
|
||||
],
|
||||
"dependencies": {
|
||||
"@salesforce/salesforcedx-utils-vscode": "0.1.0"
|
||||
"@salesforce/salesforcedx-utils-vscode": "0.1.0",
|
||||
"vscode-nls": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.0.0",
|
||||
|
@ -79,4 +80,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import * as vscode from 'vscode';
|
||||
|
||||
import { CommandExecution } from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
|
||||
export const DEFAULT_SFDX_CHANNEL = vscode.window.createOutputChannel(
|
||||
'SalesforceDX - CLI'
|
||||
);
|
||||
|
||||
export function streamCommandOutput(execution: CommandExecution) {
|
||||
DEFAULT_SFDX_CHANNEL.append('Starting ');
|
||||
DEFAULT_SFDX_CHANNEL.appendLine(execution.command.toString());
|
||||
DEFAULT_SFDX_CHANNEL.appendLine('');
|
||||
|
||||
execution.stderrSubject.subscribe(data =>
|
||||
DEFAULT_SFDX_CHANNEL.append(data.toString())
|
||||
);
|
||||
execution.stdoutSubject.subscribe(data =>
|
||||
DEFAULT_SFDX_CHANNEL.append(data.toString())
|
||||
);
|
||||
|
||||
execution.processExitSubject.subscribe(data => {
|
||||
DEFAULT_SFDX_CHANNEL.append(execution.command.toString());
|
||||
DEFAULT_SFDX_CHANNEL.appendLine(
|
||||
'ended' + data == null ? ` with exit code ${data.toString()}` : ''
|
||||
);
|
||||
});
|
||||
}
|
|
@ -1,196 +0,0 @@
|
|||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as status from './status';
|
||||
import {
|
||||
CliCommandExecutor,
|
||||
SfdxCommandBuilder
|
||||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
|
||||
const channel = vscode.window.createOutputChannel('SalesforceDX - CLI');
|
||||
|
||||
export function forceAuthWebLogin() {
|
||||
channel.appendLine('force:auth:web:login --setdefaultdevhubusername');
|
||||
|
||||
status.showStatus('Authenticating');
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:auth:web:login')
|
||||
.withArg('--setdefaultdevhubusername')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
|
||||
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
}
|
||||
|
||||
export function forceOrgCreate() {
|
||||
channel.appendLine('force:org:create');
|
||||
|
||||
vscode.workspace.findFiles('config/*.json', '').then(files => {
|
||||
const fileItems: vscode.QuickPickItem[] = files.map(file => {
|
||||
return {
|
||||
label: path.basename(file.toString()),
|
||||
description: file.fsPath
|
||||
};
|
||||
});
|
||||
vscode.window.showQuickPick(fileItems).then(selection => {
|
||||
if (selection) {
|
||||
status.showStatus('Creating org');
|
||||
const rootPath = vscode.workspace.rootPath!;
|
||||
const selectionPath = path.relative(
|
||||
rootPath,
|
||||
selection.description.toString()
|
||||
);
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:org:create')
|
||||
.withFlag('-f', `${selectionPath}`)
|
||||
.withArg('--setdefaultusername')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
|
||||
execution.stderrSubject.subscribe(data =>
|
||||
channel.append(data.toString())
|
||||
);
|
||||
execution.stdoutSubject.subscribe(data =>
|
||||
channel.append(data.toString())
|
||||
);
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function forceOrgOpen() {
|
||||
channel.appendLine('force:org:open');
|
||||
|
||||
status.showStatus('Opening org');
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder().withArg('force:org:open').build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
|
||||
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
}
|
||||
|
||||
export function forceSourcePull() {
|
||||
channel.appendLine('force:source:pull');
|
||||
|
||||
status.showStatus('Pulling from org...');
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder().withArg('force:source:pull').build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
|
||||
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
}
|
||||
|
||||
export function forceSourcePush() {
|
||||
channel.appendLine('force:source:push');
|
||||
|
||||
status.showStatus('Pushing to org');
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder().withArg('force:source:push').build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
|
||||
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
}
|
||||
|
||||
export function forceSourceStatus() {
|
||||
channel.appendLine('force:source:status');
|
||||
|
||||
status.showStatus('Checking status against org');
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder().withArg('force:source:status').build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
|
||||
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
}
|
||||
|
||||
export function forceApexTestRun(testClass?: string) {
|
||||
channel.appendLine('force:apex:test:run');
|
||||
|
||||
if (testClass) {
|
||||
status.showStatus('Running apex tests');
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:apex:test:run')
|
||||
.withFlag('--classnames', `${testClass}`)
|
||||
.withFlag('--resultformat', 'human')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
|
||||
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
} else {
|
||||
vscode.workspace.findFiles('**/*.testSuite-meta.xml', '').then(files => {
|
||||
const fileItems: vscode.QuickPickItem[] = files.map(file => {
|
||||
return {
|
||||
label: path
|
||||
.basename(file.toString())
|
||||
.replace('.testSuite-meta.xml', ''),
|
||||
description: file.fsPath
|
||||
};
|
||||
});
|
||||
|
||||
fileItems.push({
|
||||
label: 'All tests',
|
||||
description: 'Runs all tests in the current workspace'
|
||||
});
|
||||
|
||||
vscode.window.showQuickPick(fileItems).then(selection => {
|
||||
if (selection) {
|
||||
status.showStatus('Running apex tests');
|
||||
if (selection.label === 'All tests') {
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:apex:test:run')
|
||||
.withFlag('--resultformat', 'human')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
execution.stderrSubject.subscribe(data =>
|
||||
channel.append(data.toString())
|
||||
);
|
||||
execution.stdoutSubject.subscribe(data =>
|
||||
channel.append(data.toString())
|
||||
);
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
} else {
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:apex:test:run')
|
||||
.withFlag('--suitenames', `${selection.label}`)
|
||||
.withFlag('--resultformat', 'human')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute();
|
||||
|
||||
execution.stderrSubject.subscribe(data =>
|
||||
channel.append(data.toString())
|
||||
);
|
||||
execution.stdoutSubject.subscribe(data =>
|
||||
channel.append(data.toString())
|
||||
);
|
||||
execution.processExitSubject.subscribe(data => status.hideStatus());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import {
|
||||
CliCommandExecutor,
|
||||
SfdxCommandBuilder
|
||||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
import { streamCommandOutput } from '../channels';
|
||||
import { reportExecutionStatus } from '../notifications';
|
||||
import { CancellableStatusBar } from '../statuses';
|
||||
|
||||
export function forceApexTestRun(testClass?: string) {
|
||||
if (testClass) {
|
||||
runTestClass(testClass);
|
||||
} else {
|
||||
vscode.workspace.findFiles('**/*.testSuite-meta.xml', '').then(files => {
|
||||
const fileItems: vscode.QuickPickItem[] = files.map(file => {
|
||||
return {
|
||||
label: path
|
||||
.basename(file.toString())
|
||||
.replace('.testSuite-meta.xml', ''),
|
||||
description: file.fsPath
|
||||
};
|
||||
});
|
||||
|
||||
fileItems.push({
|
||||
label: 'All tests',
|
||||
description: 'Runs all tests in the current workspace'
|
||||
});
|
||||
|
||||
vscode.window.showQuickPick(fileItems).then(selection => {
|
||||
if (selection) {
|
||||
if (selection.label === 'All tests') {
|
||||
runAllTests();
|
||||
} else {
|
||||
runTestSuite(selection.label);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function runTestClass(testClass: string) {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:apex:test:run')
|
||||
.withFlag('--classnames', `${testClass}`)
|
||||
.withFlag('--resultformat', 'human')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute(cancellationToken);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
||||
|
||||
function runAllTests() {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:apex:test:run')
|
||||
.withFlag('--resultformat', 'human')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute(cancellationToken);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
||||
|
||||
function runTestSuite(testSuiteName: string) {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:apex:test:run')
|
||||
.withFlag('--suitenames', `${testSuiteName}`)
|
||||
.withFlag('--resultformat', 'human')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute(cancellationToken);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import * as vscode from 'vscode';
|
||||
import {
|
||||
CliCommandExecutor,
|
||||
SfdxCommandBuilder
|
||||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
import { streamCommandOutput } from '../channels';
|
||||
import { reportExecutionStatus } from '../notifications';
|
||||
import { CancellableStatusBar } from '../statuses';
|
||||
|
||||
export function forceAuthWebLogin() {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:auth:web:login')
|
||||
.withArg('--setdefaultdevhubusername')
|
||||
.build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute(cancellationTokenSource.token);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import {
|
||||
CliCommandExecutor,
|
||||
SfdxCommandBuilder
|
||||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
import { streamCommandOutput } from '../channels';
|
||||
import { reportExecutionStatus } from '../notifications';
|
||||
import { CancellableStatusBar } from '../statuses';
|
||||
|
||||
export function forceOrgCreate() {
|
||||
vscode.workspace.findFiles('config/*.json', '').then(files => {
|
||||
const fileItems: vscode.QuickPickItem[] = files.map(file => {
|
||||
return {
|
||||
label: path.basename(file.toString()),
|
||||
description: file.fsPath
|
||||
};
|
||||
});
|
||||
vscode.window.showQuickPick(fileItems).then(selection => {
|
||||
if (selection) {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
|
||||
const rootPath = vscode.workspace.rootPath!;
|
||||
const selectionPath = path.relative(
|
||||
rootPath,
|
||||
selection.description.toString()
|
||||
);
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder()
|
||||
.withArg('force:org:create')
|
||||
.withFlag('-f', `${selectionPath}`)
|
||||
.withArg('--setdefaultusername')
|
||||
.build(),
|
||||
{ cwd: rootPath }
|
||||
).execute(cancellationToken);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import * as vscode from 'vscode';
|
||||
import {
|
||||
CliCommandExecutor,
|
||||
SfdxCommandBuilder
|
||||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
import { streamCommandOutput } from '../channels';
|
||||
import { reportExecutionStatus } from '../notifications';
|
||||
import { CancellableStatusBar } from '../statuses';
|
||||
|
||||
export function forceOrgOpen() {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder().withArg('force:org:open').build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute(cancellationToken);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import * as vscode from 'vscode';
|
||||
import {
|
||||
CliCommandExecutor,
|
||||
SfdxCommandBuilder
|
||||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
import { streamCommandOutput } from '../channels';
|
||||
import { reportExecutionStatus } from '../notifications';
|
||||
import { CancellableStatusBar } from '../statuses';
|
||||
|
||||
export function forceSourcePull() {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder().withArg('force:source:pull').build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute(cancellationToken);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import * as vscode from 'vscode';
|
||||
import {
|
||||
CliCommandExecutor,
|
||||
SfdxCommandBuilder
|
||||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
import { streamCommandOutput } from '../channels';
|
||||
import { reportExecutionStatus } from '../notifications';
|
||||
import { CancellableStatusBar } from '../statuses';
|
||||
|
||||
export function forceSourcePush() {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder().withArg('force:source:push').build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute(cancellationToken);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import * as vscode from 'vscode';
|
||||
import {
|
||||
CliCommandExecutor,
|
||||
SfdxCommandBuilder
|
||||
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
import { streamCommandOutput } from '../channels';
|
||||
import { reportExecutionStatus } from '../notifications';
|
||||
import { CancellableStatusBar } from '../statuses';
|
||||
|
||||
export function forceSourceStatus() {
|
||||
const cancellationTokenSource = new vscode.CancellationTokenSource();
|
||||
const cancellationToken = cancellationTokenSource.token;
|
||||
|
||||
const execution = new CliCommandExecutor(
|
||||
new SfdxCommandBuilder().withArg('force:source:status').build(),
|
||||
{ cwd: vscode.workspace.rootPath }
|
||||
).execute(cancellationToken);
|
||||
|
||||
streamCommandOutput(execution);
|
||||
reportExecutionStatus(execution, cancellationToken);
|
||||
CancellableStatusBar.show(execution, cancellationTokenSource);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export { forceAuthWebLogin } from './forceAuthWebLogin';
|
||||
export { forceApexTestRun } from './forceApexTestRun';
|
||||
export { forceOrgCreate } from './forceOrgCreate';
|
||||
export { forceOrgOpen } from './forceOrgOpen';
|
||||
export { forceSourcePull } from './forceSourcePull';
|
||||
export { forceSourcePush } from './forceSourcePush';
|
||||
export { forceSourceStatus } from './forceSourceStatus';
|
|
@ -1,37 +1,54 @@
|
|||
import * as vscode from 'vscode';
|
||||
|
||||
import * as scratchOrgDecorator from './scratch-org-decorator';
|
||||
import * as commands from './commands';
|
||||
import { CANCEL_EXECUTION_COMMAND, cancelCommandExecution } from './statuses';
|
||||
import {
|
||||
forceApexTestRun,
|
||||
forceAuthWebLogin,
|
||||
forceOrgCreate,
|
||||
forceOrgOpen,
|
||||
forceSourcePull,
|
||||
forceSourcePush,
|
||||
forceSourceStatus
|
||||
} from './commands';
|
||||
|
||||
function registerCommands(): vscode.Disposable {
|
||||
// Customer-facing commands
|
||||
const forceAuthWebLoginCmd = vscode.commands.registerCommand(
|
||||
'sfdx.force.auth.web.login',
|
||||
commands.forceAuthWebLogin
|
||||
forceAuthWebLogin
|
||||
);
|
||||
const forceOrgCreateCmd = vscode.commands.registerCommand(
|
||||
'sfdx.force.org.create',
|
||||
commands.forceOrgCreate
|
||||
forceOrgCreate
|
||||
);
|
||||
const forceOrgOpenCmd = vscode.commands.registerCommand(
|
||||
'sfdx.force.org.open',
|
||||
commands.forceOrgOpen
|
||||
forceOrgOpen
|
||||
);
|
||||
const forceSourcePullCmd = vscode.commands.registerCommand(
|
||||
'sfdx.force.source.pull',
|
||||
commands.forceSourcePull
|
||||
forceSourcePull
|
||||
);
|
||||
const forceSourcePushCmd = vscode.commands.registerCommand(
|
||||
'sfdx.force.source.push',
|
||||
commands.forceSourcePush
|
||||
forceSourcePush
|
||||
);
|
||||
const forceSourceStatusCmd = vscode.commands.registerCommand(
|
||||
'sfdx.force.source.status',
|
||||
commands.forceSourceStatus
|
||||
forceSourceStatus
|
||||
);
|
||||
const forceApexTestRunCmd = vscode.commands.registerCommand(
|
||||
'sfdx.force.apex.test.run',
|
||||
commands.forceApexTestRun
|
||||
forceApexTestRun
|
||||
);
|
||||
|
||||
// Internal commands
|
||||
const internalCancelCommandExecution = vscode.commands.registerCommand(
|
||||
CANCEL_EXECUTION_COMMAND,
|
||||
cancelCommandExecution
|
||||
);
|
||||
|
||||
return vscode.Disposable.from(
|
||||
forceAuthWebLoginCmd,
|
||||
forceOrgCreateCmd,
|
||||
|
@ -39,7 +56,8 @@ function registerCommands(): vscode.Disposable {
|
|||
forceSourcePullCmd,
|
||||
forceSourcePushCmd,
|
||||
forceSourceStatusCmd,
|
||||
forceApexTestRunCmd
|
||||
forceApexTestRunCmd,
|
||||
internalCancelCommandExecution
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { CommandExecution } from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
import { DEFAULT_SFDX_CHANNEL } from '../channels';
|
||||
|
||||
export function reportExecutionStatus(
|
||||
execution: CommandExecution,
|
||||
cancellationToken?: vscode.CancellationToken
|
||||
) {
|
||||
execution.processExitSubject.subscribe(async data => {
|
||||
if (data !== null && data !== 'undefined' && data.toString() === '0') {
|
||||
const selection = await vscode.window.showInformationMessage(
|
||||
`Successfully executed ${execution.command}`,
|
||||
'Show'
|
||||
);
|
||||
if (selection && selection === 'Show') {
|
||||
DEFAULT_SFDX_CHANNEL.show();
|
||||
}
|
||||
} else {
|
||||
if (cancellationToken && cancellationToken.isCancellationRequested) {
|
||||
vscode.window.showWarningMessage(`${execution.command} canceled`);
|
||||
DEFAULT_SFDX_CHANNEL.show();
|
||||
} else {
|
||||
vscode.window.showErrorMessage(
|
||||
`Failed to execute ${execution.command}`
|
||||
);
|
||||
DEFAULT_SFDX_CHANNEL.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -7,7 +7,7 @@ export function showStatus(status: string) {
|
|||
if (!statusBarItem) {
|
||||
statusBarItem = window.createStatusBarItem(StatusBarAlignment.Left, -10);
|
||||
}
|
||||
statusBarItem.text = `$(beaker) ${status}`;
|
||||
statusBarItem.text = `$(clock) ${status}`;
|
||||
statusTimer = setInterval(
|
||||
() => (statusBarItem.text = statusBarItem.text + '.'),
|
||||
1000
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export {
|
||||
CANCEL_EXECUTION_COMMAND,
|
||||
PassiveStatusBar,
|
||||
CancellableStatusBar,
|
||||
cancelCommandExecution
|
||||
} from './status';
|
|
@ -0,0 +1,101 @@
|
|||
import {
|
||||
CancellationTokenSource,
|
||||
StatusBarItem,
|
||||
StatusBarAlignment,
|
||||
window
|
||||
} from 'vscode';
|
||||
|
||||
import { CommandExecution } from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
|
||||
|
||||
export const CANCEL_EXECUTION_COMMAND = 'internal.cancel.execution.command';
|
||||
const ALIGNMENT = StatusBarAlignment.Left;
|
||||
const PRIORITY = -10;
|
||||
|
||||
const statusBarItem: StatusBarItem = window.createStatusBarItem(
|
||||
ALIGNMENT,
|
||||
PRIORITY
|
||||
);
|
||||
let statusTimer: NodeJS.Timer | null;
|
||||
let cancellationTokenSource: CancellationTokenSource | null;
|
||||
|
||||
export class PassiveStatusBar {
|
||||
public static show(execution: CommandExecution) {
|
||||
return new PassiveStatusBar(execution);
|
||||
}
|
||||
|
||||
private constructor(execution: CommandExecution) {
|
||||
resetStatusBarItem();
|
||||
|
||||
statusBarItem.text = `$(clock) ${execution.command}`;
|
||||
|
||||
if (statusTimer) {
|
||||
clearInterval(statusTimer);
|
||||
}
|
||||
statusTimer = setInterval(
|
||||
() => (statusBarItem.text = statusBarItem.text + '.'),
|
||||
1000
|
||||
);
|
||||
execution.processExitSubject.subscribe(data => {
|
||||
if (statusTimer) {
|
||||
clearInterval(statusTimer);
|
||||
}
|
||||
statusBarItem.hide();
|
||||
});
|
||||
|
||||
statusBarItem.show();
|
||||
}
|
||||
}
|
||||
|
||||
export class CancellableStatusBar {
|
||||
public static show(
|
||||
execution: CommandExecution,
|
||||
token: CancellationTokenSource
|
||||
) {
|
||||
return new CancellableStatusBar(execution, token);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
execution: CommandExecution,
|
||||
token: CancellationTokenSource
|
||||
) {
|
||||
resetStatusBarItem();
|
||||
|
||||
statusBarItem.text = `$(x) ${execution.command}`;
|
||||
statusBarItem.tooltip = 'Click to cancel the command';
|
||||
statusBarItem.command = CANCEL_EXECUTION_COMMAND;
|
||||
|
||||
cancellationTokenSource = token;
|
||||
|
||||
if (statusTimer) {
|
||||
clearInterval(statusTimer);
|
||||
}
|
||||
statusTimer = setInterval(
|
||||
() => (statusBarItem.text = statusBarItem.text + '.'),
|
||||
1000
|
||||
);
|
||||
execution.processExitSubject.subscribe(data => {
|
||||
if (statusTimer) {
|
||||
clearInterval(statusTimer);
|
||||
}
|
||||
statusBarItem.hide();
|
||||
});
|
||||
|
||||
statusBarItem.show();
|
||||
}
|
||||
}
|
||||
|
||||
function resetStatusBarItem() {
|
||||
statusBarItem.text = '';
|
||||
statusBarItem.tooltip = '';
|
||||
statusBarItem.command = undefined;
|
||||
}
|
||||
|
||||
export function cancelCommandExecution() {
|
||||
if (cancellationTokenSource) {
|
||||
cancellationTokenSource.cancel();
|
||||
resetStatusBarItem();
|
||||
if (statusTimer) {
|
||||
clearInterval(statusTimer);
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче