提交 aede2434 编写于 作者: J João Moreno

fixes microsoft/vscode-remote-release#3180

上级 875c2ced
......@@ -8,12 +8,8 @@ import * as path from 'vs/base/common/path';
import * as cp from 'child_process';
import * as pfs from 'vs/base/node/pfs';
import * as extpath from 'vs/base/node/extpath';
import * as platform from 'vs/base/common/platform';
import { promisify } from 'util';
import { Action } from 'vs/base/common/actions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import product from 'vs/platform/product/common/product';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
......@@ -21,6 +17,9 @@ import Severity from 'vs/base/common/severity';
import { ILogService } from 'vs/platform/log/common/log';
import { FileAccess } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/productService';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IsMacNativeContext } from 'vs/platform/contextkey/common/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
function ignore<T>(code: string, value: T): (err: any) => Promise<T> {
return err => err.code === code ? Promise.resolve<T>(value) : Promise.reject<T>(err);
......@@ -39,45 +38,65 @@ function isAvailable(): Promise<boolean> {
return Promise.resolve(pfs.exists(getSource()));
}
class InstallAction extends Action {
const category = nls.localize('shellCommand', "Shell Command");
static readonly ID = 'workbench.action.installCommandLine';
static readonly LABEL = nls.localize('install', "Install '{0}' command in PATH", product.applicationName);
class InstallAction extends Action2 {
constructor(
id: string,
label: string,
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@ILogService private readonly logService: ILogService,
@IProductService private readonly productService: IProductService
) {
super(id, label);
constructor() {
super({
id: 'workbench.action.installCommandLine',
title: {
value: nls.localize('install', "Install '{0}' command in PATH", product.applicationName),
original: `Shell Command: Install \'${product.applicationName}\' command in PATH`
},
category,
f1: true,
precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', ''))
});
}
private get target(): string {
return `/usr/local/bin/${this.productService.applicationName}`;
}
run(accessor: ServicesAccessor): Promise<void> {
const productService = accessor.get(IProductService);
const notificationService = accessor.get(INotificationService);
const logService = accessor.get(ILogService);
const dialogService = accessor.get(IDialogService);
const target = `/usr/local/bin/${productService.applicationName}`;
run(): Promise<void> {
return isAvailable().then(isAvailable => {
if (!isAvailable) {
const message = nls.localize('not available', "This command is not available");
this.notificationService.info(message);
notificationService.info(message);
return undefined;
}
return this.isInstalled()
return this.isInstalled(target)
.then(isInstalled => {
if (!isAvailable || isInstalled) {
return Promise.resolve(null);
} else {
return pfs.unlink(this.target)
return pfs.unlink(target)
.then(undefined, ignore('ENOENT', null))
.then(() => pfs.symlink(getSource(), this.target))
.then(() => pfs.symlink(getSource(), target))
.then(undefined, err => {
if (err.code === 'EACCES' || err.code === 'ENOENT') {
return this.createBinFolderAndSymlinkAsAdmin();
return new Promise<void>((resolve, reject) => {
const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => {
switch (result.choice) {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + target + '\'\\" with administrator privileges"';
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'."))))
.then(() => resolve(), reject);
break;
case 1 /* Cancel */:
reject(new Error(nls.localize('aborted', "Aborted")));
break;
}
});
});
}
return Promise.reject(err);
......@@ -85,113 +104,84 @@ class InstallAction extends Action {
}
})
.then(() => {
this.logService.trace('cli#install', this.target);
this.notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", this.productService.applicationName));
logService.trace('cli#install', target);
notificationService.info(nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", productService.applicationName));
});
});
}
private isInstalled(): Promise<boolean> {
return pfs.lstat(this.target)
private isInstalled(target: string): Promise<boolean> {
return pfs.lstat(target)
.then(stat => stat.isSymbolicLink())
.then(() => extpath.realpath(this.target))
.then(() => extpath.realpath(target))
.then(link => link === getSource())
.then(undefined, ignore('ENOENT', false));
}
private createBinFolderAndSymlinkAsAdmin(): Promise<void> {
return new Promise<void>((resolve, reject) => {
const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
this.dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => {
switch (result.choice) {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + this.target + '\'\\" with administrator privileges"';
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'."))))
.then(() => resolve(), reject);
break;
case 1 /* Cancel */:
reject(new Error(nls.localize('aborted', "Aborted")));
break;
}
});
});
}
}
class UninstallAction extends Action {
static readonly ID = 'workbench.action.uninstallCommandLine';
static readonly LABEL = nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName);
constructor(
id: string,
label: string,
@INotificationService private readonly notificationService: INotificationService,
@ILogService private readonly logService: ILogService,
@IDialogService private readonly dialogService: IDialogService,
@IProductService private readonly productService: IProductService
) {
super(id, label);
class UninstallAction extends Action2 {
constructor() {
super({
id: 'workbench.action.uninstallCommandLine',
title: {
value: nls.localize('uninstall', "Uninstall '{0}' command from PATH", product.applicationName),
original: `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`
},
category,
f1: true,
precondition: ContextKeyExpr.and(IsMacNativeContext, ContextKeyExpr.equals('remoteName', ''))
});
}
private get target(): string {
return `/usr/local/bin/${this.productService.applicationName}`;
}
run(accessor: ServicesAccessor): Promise<void> {
const productService = accessor.get(IProductService);
const notificationService = accessor.get(INotificationService);
const logService = accessor.get(ILogService);
const dialogService = accessor.get(IDialogService);
const target = `/usr/local/bin/${productService.applicationName}`;
run(): Promise<void> {
return isAvailable().then(isAvailable => {
if (!isAvailable) {
const message = nls.localize('not available', "This command is not available");
this.notificationService.info(message);
notificationService.info(message);
return undefined;
}
const uninstall = () => {
return pfs.unlink(this.target)
return pfs.unlink(target)
.then(undefined, ignore('ENOENT', null));
};
return uninstall().then(undefined, err => {
if (err.code === 'EACCES') {
return this.deleteSymlinkAsAdmin();
return new Promise<void>(async (resolve, reject) => {
const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
const { choice } = await dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 });
switch (choice) {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"rm \'' + target + '\'\\" with administrator privileges"';
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", target))))
.then(() => resolve(), reject);
break;
case 1 /* Cancel */:
reject(new Error(nls.localize('aborted', "Aborted")));
break;
}
});
}
return Promise.reject(err);
}).then(() => {
this.logService.trace('cli#uninstall', this.target);
this.notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", this.productService.applicationName));
logService.trace('cli#uninstall', target);
notificationService.info(nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", productService.applicationName));
});
});
}
private deleteSymlinkAsAdmin(): Promise<void> {
return new Promise<void>(async (resolve, reject) => {
const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")];
const { choice } = await this.dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 });
switch (choice) {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"rm \'' + this.target + '\'\\" with administrator privileges"';
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", this.target))))
.then(() => resolve(), reject);
break;
case 1 /* Cancel */:
reject(new Error(nls.localize('aborted', "Aborted")));
break;
}
});
}
}
if (platform.isMacintosh) {
const category = nls.localize('shellCommand', "Shell Command");
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(InstallAction), `Shell Command: Install \'${product.applicationName}\' command in PATH`, category);
workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.from(UninstallAction), `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`, category);
}
registerAction2(InstallAction);
registerAction2(UninstallAction);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册