Files
devstar_plugin/src/remote-container.ts

204 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import * as vscode from 'vscode';
import * as rd from 'readline'
const { NodeSSH } = require('node-ssh')
import * as utils from './utils';
import User from './user';
import DevstarAPIHandler from './devstar-api';
export default class RemoteContainer {
private user: User;
constructor(user: User) {
this.user = user
}
/**
* 第一次打开远程项目
*
* 远程环境先创建local窗口在通过命令行调用url打开目前仅支持vscode协议
* @param host 项目名称
* @param hostname ip
* @param port
* @param username
* @param path
* @param context 用于支持远程项目环境
*/
async firstOpenProject(host: string, hostname: string, port: number, username: string, path: string, context: vscode.ExtensionContext) {
if (vscode.env.remoteName) {
// 远程环境
vscode.commands.executeCommand('workbench.action.terminal.newLocal').then(() => {
const terminal = vscode.window.terminals[vscode.window.terminals.length - 1];
if (terminal) {
// vscode协议
// 根据系统+命令行版本确定命令
const semver = require('semver')
const powershellVersion = context.globalState.get('powershellVersion')
const powershell_semver_compatible_version = semver.coerce(powershellVersion)
if (powershellVersion === undefined)
terminal.sendText(`code --new-window && code --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`)
else if (semver.satisfies(powershell_semver_compatible_version, ">=5.1.26100")) {
// win & powershell >= 5.1.26100.0
terminal.sendText(`code --new-window ; code --% --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`)
} else {
// win & powershell < 5.1.26100.0
terminal.sendText(`code --new-window && code --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`)
}
}
})
} else {
await this.firstConnect(host, hostname, username, port)
.then((res) => {
if (res === 'success') {
// only success then open folder
this.openRemoteFolder(host, port, username, path);
}
})
}
}
/**
* local environment第一次连接其他项目
* @param host 项目名称
* @param hostname ip
* @param username
* @param port
* @returns 成功返回success
*/
// connect with key
async firstConnect(host: string, hostname: string, username: string, port: number): Promise<string> {
return new Promise(async (resolve) => {
const ssh = new NodeSSH();
vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: vscode.l10n.t("Installing vscode-server and devstar extension in container"),
cancellable: false
}, async (progress) => {
try {
// 检查公私钥是否存在,如果不存在,需要创建
if (!this.user.existUserPrivateKey() || !this.user.existUserPublicKey()) {
await this.user.createUserSSHKey()
// 上传公钥
const devstarAPIHandler = new DevstarAPIHandler()
const uploadResult = await devstarAPIHandler.uploadUserPublicKey(this.user)
if (uploadResult !== "ok") {
throw new Error('Upload public key failed.')
}
}
} catch (error) {
console.error("Failed to first connect container: ", error)
}
// 本地环境
try {
// connect with key
await ssh.connect({
host: hostname,
username: username,
port: port,
privateKeyPath: this.user.getUserPrivateKeyPath()
});
progress.report({ message: vscode.l10n.t("Connected! Start installation") });
// install vscode-server and devstar extension
const vscodeCommitId = await utils.getVsCodeCommitId()
if ("" != vscodeCommitId) {
const vscodeServerUrl = `https://vscode.download.prss.microsoft.com/dbazure/download/stable/${vscodeCommitId}/vscode-server-linux-x64.tar.gz`
const installVscodeServerScript = `
mkdir -p ~/.vscode-server/bin/${vscodeCommitId} && \\
if [ "$(ls -A ~/.vscode-server/bin/${vscodeCommitId})" ]; then
~/.vscode-server/bin/${vscodeCommitId}/bin/code-server --install-extension mengning.devstar
else
wget ${vscodeServerUrl} -O vscode-server-linux-x64.tar.gz && \\
mv vscode-server-linux-x64.tar.gz ~/.vscode-server/bin/${vscodeCommitId} && \\
cd ~/.vscode-server/bin/${vscodeCommitId} && \\
tar -xvzf vscode-server-linux-x64.tar.gz --strip-components 1 && \\
rm vscode-server-linux-x64.tar.gz && \\
~/.vscode-server/bin/${vscodeCommitId}/bin/code-server --install-extension mengning.devstar
fi
`;
await ssh.execCommand(installVscodeServerScript);
console.log("vscode-server and extension installed");
vscode.window.showInformationMessage(vscode.l10n.t('Installation completed!'));
}
await ssh.dispose();
// only connect successfully then save the host info
await this.storeProjectSSHInfo(host, hostname, port, username)
resolve('success')
} catch (error) {
console.error('Failed to install vscode-server and extension: ', error);
await ssh.dispose();
}
});
});
}
/**
* 本地环境保存项目的ssh连接信息
* @param host
* @param hostname
* @param port
* @param username
*/
async storeProjectSSHInfo(host: string, hostname: string, port: number, username: string): Promise<void> {
const sshConfigPath = path.join(os.homedir(), '.ssh', 'config');
// check if the host and related info exist in local ssh config file before saving
var canAppendSSHConfig = true
if (fs.existsSync(sshConfigPath)) {
var reader = rd.createInterface(fs.createReadStream(sshConfigPath))
for await (const line of reader) {
if (line.includes(`Host ${host}`)) {
// the container ssh info exists
canAppendSSHConfig = false
break;
}
}
}
if (canAppendSSHConfig) {
// save the host to the local ssh config file
const privateKeyPath = this.user.getUserPrivateKeyPath();
const newSShConfigContent =
`\nHost ${host}\n HostName ${hostname}\n Port ${port}\n User ${username}\n PreferredAuthentications publickey\n IdentityFile ${privateKeyPath}\n `;
fs.writeFileSync(sshConfigPath, newSShConfigContent, { encoding: 'utf8', flag: 'a' });
console.log('Host registered in local ssh config');
}
}
/**
* local env
* 仅支持已经成功连接并在ssh config file中存储ssh信息的项目连接。
*
* @host 表示project name
*/
openRemoteFolder(host: string, port: number, username: string, path: string): void {
let terminal = vscode.window.activeTerminal || vscode.window.createTerminal(`Ext Terminal`);
terminal.show(true);
// 在原窗口打开
terminal.sendText(`code --remote ssh-remote+${username}@${host}:${port} ${path} --reuse-window`);
}
}
/**
* 打开项目(无须插件登录)
* @param hostname 表示ip
* @param port
* @param username
* @param path
*/
export async function openProjectWithoutLogging(hostname: string, port: number, username: string, path: string): Promise<void> {
const command = `code --remote ssh-remote+${username}@${hostname}:${port} ${path} --reuse-window`
let terminal = vscode.window.activeTerminal || vscode.window.createTerminal(`Ext Terminal`);
terminal.show(true);
terminal.sendText(command);
}