Skip to content

Commit 76cf8df

Browse files
committed
Initialize remote extensions
1 parent ee077dc commit 76cf8df

File tree

3 files changed

+241
-49
lines changed

3 files changed

+241
-49
lines changed

src/extension.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
88
import Log from './common/logger';
99
import GitpodAuthenticationProvider from './authentication';
1010
import RemoteConnector from './remoteConnector';
11-
import SettingsSync from './settingsSync';
11+
import { SettingsSync } from './settingsSync';
1212
import GitpodServer from './gitpodServer';
1313
import TelemetryReporter from './telemetryReporter';
1414
import { exportLogs } from './exportLogs';
@@ -42,10 +42,11 @@ export async function activate(context: vscode.ExtensionContext) {
4242
}
4343
}));
4444

45-
context.subscriptions.push(new SettingsSync(logger, telemetry));
45+
const settingsSync = new SettingsSync(logger, telemetry);
46+
context.subscriptions.push(settingsSync);
4647

4748
const authProvider = new GitpodAuthenticationProvider(context, logger, telemetry);
48-
remoteConnector = new RemoteConnector(context, experiments, logger, telemetry);
49+
remoteConnector = new RemoteConnector(context, settingsSync, experiments, logger, telemetry);
4950
context.subscriptions.push(authProvider);
5051
context.subscriptions.push(vscode.window.registerUriHandler({
5152
handleUri(uri: vscode.Uri) {

src/remoteConnector.ts

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ import TelemetryReporter from './telemetryReporter';
2525
import { addHostToHostFile, checkNewHostInHostkeys } from './ssh/hostfile';
2626
import { DEFAULT_IDENTITY_FILES } from './ssh/identityFiles';
2727
import { HeartbeatManager } from './heartbeat';
28-
import { getGitpodVersion, isFeatureSupported } from './featureSupport';
28+
import { getGitpodVersion, GitpodVersion, isFeatureSupported } from './featureSupport';
2929
import SSHConfiguration from './ssh/sshConfig';
3030
import { isWindows } from './common/platform';
3131
import { untildify } from './common/files';
3232
import { ExperimentalSettings, isUserOverrideSetting } from './experiments';
33+
import { ISyncExtension, NoSettingsSyncSession, NoSyncStoreError, parseSyncData, SettingsSync, SyncResource } from './settingsSync';
34+
import { retry } from './common/async';
3335

3436
interface SSHConnectionParams {
3537
workspaceId: string;
@@ -121,6 +123,7 @@ export default class RemoteConnector extends Disposable {
121123

122124
constructor(
123125
private readonly context: vscode.ExtensionContext,
126+
private readonly settingsSync: SettingsSync,
124127
private readonly experiments: ExperimentalSettings,
125128
private readonly logger: Log,
126129
private readonly telemetry: TelemetryReporter
@@ -900,12 +903,76 @@ export default class RemoteConnector extends Disposable {
900903
}
901904
}
902905

903-
private startHeartBeat(accessToken: string, connectionInfo: SSHConnectionParams) {
906+
private async startHeartBeat(accessToken: string, connectionInfo: SSHConnectionParams, gitpodVersion: GitpodVersion) {
904907
if (this.heartbeatManager) {
905908
return;
906909
}
907910

908911
this.heartbeatManager = new HeartbeatManager(connectionInfo.gitpodHost, connectionInfo.workspaceId, connectionInfo.instanceId, accessToken, this.logger, this.telemetry);
912+
913+
// gitpod remote extension installation is async so sometimes gitpod-desktop will activate before gitpod-remote
914+
// let's try a few times for it to finish install
915+
try {
916+
await retry(async () => {
917+
await vscode.commands.executeCommand('__gitpod.cancelGitpodRemoteHeartbeat');
918+
}, 3000, 15);
919+
this.telemetry.sendTelemetryEvent('vscode_desktop_heartbeat_state', { enabled: String(true), gitpodHost: connectionInfo.gitpodHost, workspaceId: connectionInfo.workspaceId, instanceId: connectionInfo.instanceId, gitpodVersion: gitpodVersion.raw });
920+
} catch {
921+
this.logger.error(`Could not execute '__gitpod.cancelGitpodRemoteHeartbeat' command`);
922+
this.telemetry.sendTelemetryEvent('vscode_desktop_heartbeat_state', { enabled: String(false), gitpodHost: connectionInfo.gitpodHost, workspaceId: connectionInfo.workspaceId, instanceId: connectionInfo.instanceId, gitpodVersion: gitpodVersion.raw });
923+
}
924+
}
925+
926+
private async initializeRemoteExtensions() {
927+
let syncData: { ref: string; content: string } | undefined;
928+
try {
929+
syncData = await this.settingsSync.readResource(SyncResource.Extensions);
930+
} catch (e) {
931+
if (e instanceof NoSyncStoreError) {
932+
const action = 'Settings Sync: Enable Sign In with Gitpod';
933+
const result = await vscode.window.showInformationMessage(`Couldn't initialize remote extensions, Settings Sync with Gitpod is required.`, action);
934+
if (result === action) {
935+
vscode.commands.executeCommand('gitpod.syncProvider.add');
936+
}
937+
} else if (e instanceof NoSettingsSyncSession) {
938+
const action = 'Enable Settings Sync';
939+
const result = await vscode.window.showInformationMessage(`Couldn't initialize remote extensions, please enable Settings Sync.`, action);
940+
if (result === action) {
941+
vscode.commands.executeCommand('workbench.userDataSync.actions.turnOn');
942+
}
943+
} else {
944+
this.logger.error('Error while fetching settings sync extension data', e);
945+
}
946+
return;
947+
}
948+
949+
const syncDataContent = parseSyncData(syncData.content);
950+
if (!syncDataContent) {
951+
this.logger.error('Error while parsing sync data');
952+
return;
953+
}
954+
955+
let extensions: ISyncExtension[];
956+
try {
957+
extensions = JSON.parse(syncDataContent.content);
958+
} catch {
959+
this.logger.error('Error while parsing settings sync extension data, malformed json');
960+
return;
961+
}
962+
963+
extensions = extensions.filter(e => e.installed);
964+
if (!extensions.length) {
965+
return;
966+
}
967+
968+
try {
969+
this.logger.trace(`Try installing extensions on remote: `, extensions.map(e => e.identifier.id).join('\n'));
970+
await retry(async () => {
971+
await vscode.commands.executeCommand('__gitpod.initializeRemoteExtensions', extensions);
972+
}, 3000, 15);
973+
} catch {
974+
this.logger.error(`Could not execute '__gitpod.initializeRemoteExtensions' command`);
975+
}
909976
}
910977

911978
private async onGitpodRemoteConnection() {
@@ -939,27 +1006,12 @@ export default class RemoteConnector extends Disposable {
9391006

9401007
const gitpodVersion = await getGitpodVersion(connectionInfo.gitpodHost, this.logger);
9411008
if (isFeatureSupported(gitpodVersion, 'localHeartbeat')) {
942-
// gitpod remote extension installation is async so sometimes gitpod-desktop will activate before gitpod-remote
943-
// let's try a few times for it to finish install
944-
let retryCount = 15;
945-
const tryStopRemoteHeartbeat = async () => {
946-
// Check for gitpod remote extension version to avoid sending heartbeat in both extensions at the same time
947-
const isGitpodRemoteHeartbeatCancelled = await cancelGitpodRemoteHeartbeat();
948-
if (isGitpodRemoteHeartbeatCancelled) {
949-
this.telemetry.sendTelemetryEvent('vscode_desktop_heartbeat_state', { enabled: String(true), gitpodHost: connectionInfo.gitpodHost, workspaceId: connectionInfo.workspaceId, instanceId: connectionInfo.instanceId, gitpodVersion: gitpodVersion.raw });
950-
} else if (retryCount > 0) {
951-
retryCount--;
952-
setTimeout(tryStopRemoteHeartbeat, 3000);
953-
} else {
954-
this.telemetry.sendTelemetryEvent('vscode_desktop_heartbeat_state', { enabled: String(false), gitpodHost: connectionInfo.gitpodHost, workspaceId: connectionInfo.workspaceId, instanceId: connectionInfo.instanceId, gitpodVersion: gitpodVersion.raw });
955-
}
956-
};
957-
958-
this.startHeartBeat(session.accessToken, connectionInfo);
959-
tryStopRemoteHeartbeat();
1009+
this.startHeartBeat(session.accessToken, connectionInfo, gitpodVersion);
9601010
} else {
9611011
this.logger.warn(`Local heatbeat not supported in ${connectionInfo.gitpodHost}, using version ${gitpodVersion.version}`);
9621012
}
1013+
1014+
this.initializeRemoteExtensions();
9631015
}
9641016

9651017
public override async dispose(): Promise<void> {
@@ -980,17 +1032,6 @@ function isGitpodRemoteWindow(context: vscode.ExtensionContext) {
9801032
return false;
9811033
}
9821034

983-
async function cancelGitpodRemoteHeartbeat() {
984-
let result = false;
985-
try {
986-
// Invoke command from gitpot-remote extension
987-
result = await vscode.commands.executeCommand('__gitpod.cancelGitpodRemoteHeartbeat');
988-
} catch {
989-
// Ignore if not found
990-
}
991-
return result;
992-
}
993-
9941035
function getServiceURL(gitpodHost: string): string {
9951036
return new URL(gitpodHost).toString().replace(/\/$/, '');
9961037
}

0 commit comments

Comments
 (0)