Skip to content

Commit 8a758b7

Browse files
committed
quiet installation of local extensions
Since many users ignore this notification, only 40% of users who noticed it managed to convert, and around 1% of all users never come back if they try to use we decided to quite this notitication on the connection. Instead it logs and we provide an explicit `Install Local Extensions` command to force sync at any time which will prompt a user.
1 parent 9e34892 commit 8a758b7

File tree

4 files changed

+92
-40
lines changed

4 files changed

+92
-40
lines changed

package.json

+17-8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"onCommand:gitpod.exportLogs",
3838
"onCommand:gitpod.api.autoTunnel",
3939
"onCommand:gitpod.showReleaseNotes",
40+
"onCommand:gitpod.installLocalExtensions",
4041
"onAuthenticationRequest:gitpod",
4142
"onUri",
4243
"onStartupFinished"
@@ -63,12 +64,6 @@
6364
"description": "Use the local companion app to connect to a remote workspace.\nWarning: Connecting to a remote workspace using local companion app will be removed in the near future.",
6465
"default": false,
6566
"scope": "application"
66-
},
67-
"gitpod.remote.syncExtensions": {
68-
"type": "boolean",
69-
"description": "Automatically install sync extensions from Gitpod Sync Server on the remote workspace.",
70-
"default": true,
71-
"scope": "application"
7267
}
7368
}
7469
},
@@ -105,8 +100,22 @@
105100
"command": "gitpod.showReleaseNotes",
106101
"category": "Gitpod",
107102
"title": "Show Release Notes"
103+
},
104+
{
105+
"command": "gitpod.installLocalExtensions",
106+
"title": "Gitpod: Install Local Extensions...",
107+
"enablement": "gitpod.inWorkspace == true"
108108
}
109-
]
109+
],
110+
"menus": {
111+
"statusBar/remoteIndicator": [
112+
{
113+
"command": "gitpod.installLocalExtensions",
114+
"group": "remote_00_gitpod_navigation@01",
115+
"when": "gitpod.inWorkspace == true"
116+
}
117+
]
118+
}
110119
},
111120
"main": "./out/extension.js",
112121
"segmentKey": "YErmvd89wPsrCuGcVnF2XAl846W9WIGl",
@@ -164,4 +173,4 @@
164173
"uuid": "8.1.0",
165174
"yazl": "^2.5.1"
166175
}
167-
}
176+
}

scripts/prepare-release-build.js

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const releasePackageJson = JSON.parse(fs.readFileSync('./package.json').toString
66

77
const releaseDefaultConfig = new Map([
88
["gitpod.remote.useLocalApp", true],
9-
["gitpod.remote.syncExtensions", false],
109
]);
1110

1211
const gitpodConfig = releasePackageJson.contributes.configuration.find(e => e.title.toLowerCase() === 'gitpod');

src/experiments.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import * as semver from 'semver';
1010
import Log from './common/logger';
1111

1212
const EXPERTIMENTAL_SETTINGS = [
13-
'gitpod.remote.useLocalApp',
14-
'gitpod.remote.syncExtensions'
13+
'gitpod.remote.useLocalApp'
1514
];
1615

1716
export class ExperimentalSettings {

src/remoteConnector.ts

+74-29
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { v4 as uuid } from 'uuid';
67
import { AutoTunnelRequest, ResolveSSHConnectionRequest, ResolveSSHConnectionResponse } from '@gitpod/local-app-api-grpcweb/lib/localapp_pb';
78
import { LocalAppClient } from '@gitpod/local-app-api-grpcweb/lib/localapp_pb_service';
89
import { NodeHttpTransport } from '@improbable-eng/grpc-web-node-http-transport';
@@ -736,7 +737,7 @@ export default class RemoteConnector extends Disposable {
736737
throw new Error('SSH password modal dialog, Canceled');
737738
}
738739

739-
private async ensureValidGitpodHost(gitpodHost: string, flow: UserFlowTelemetry): Promise<boolean>{
740+
private async ensureValidGitpodHost(gitpodHost: string, flow: UserFlowTelemetry): Promise<boolean> {
740741
const config = vscode.workspace.getConfiguration('gitpod');
741742
const currentGitpodHost = config.get<string>('host')!;
742743
if (new URL(gitpodHost).host !== new URL(currentGitpodHost).host) {
@@ -951,48 +952,84 @@ export default class RemoteConnector extends Disposable {
951952
}
952953
}
953954

954-
private async initializeRemoteExtensions(flow: UserFlowTelemetry) {
955+
private async initializeRemoteExtensions(flow: UserFlowTelemetry & { quiet: boolean, flowId: string }) {
956+
this.telemetry.sendUserFlowStatus('enabled', flow);
955957
let syncData: { ref: string; content: string } | undefined;
956958
try {
957959
syncData = await this.settingsSync.readResource(SyncResource.Extensions);
958960
} catch (e) {
959961
if (e instanceof NoSyncStoreError) {
960-
const addSyncProvider = 'Settings Sync: Enable Sign In with Gitpod';
961-
const action = await this.notifications.showInformationMessage(`Could not install local extensions on remote workspace. Please enable [Settings Sync](https://www.gitpod.io/docs/ides-and-editors/settings-sync#enabling-settings-sync-in-vs-code-desktop) with Gitpod.`, { flow, id: 'no_sync_store' }, addSyncProvider);
962-
if (action === addSyncProvider) {
963-
vscode.commands.executeCommand('gitpod.syncProvider.add');
962+
const msg = `Could not install local extensions on remote workspace. Please enable [Settings Sync](https://www.gitpod.io/docs/ides-and-editors/settings-sync#enabling-settings-sync-in-vs-code-desktop) with Gitpod.`;
963+
this.logger.error(msg);
964+
965+
const status = 'no_sync_store';
966+
if (flow.quiet) {
967+
this.telemetry.sendUserFlowStatus(status, flow);
968+
} else {
969+
const addSyncProvider = 'Settings Sync: Enable Sign In with Gitpod';
970+
const action = await this.notifications.showInformationMessage(msg, { flow, id: status }, addSyncProvider);
971+
if (action === addSyncProvider) {
972+
vscode.commands.executeCommand('gitpod.syncProvider.add');
973+
}
964974
}
965975
} else if (e instanceof NoSettingsSyncSession) {
966-
const enableSettingsSync = 'Enable Settings Sync';
967-
const action = await this.notifications.showInformationMessage(`Could not install local extensions on remote workspace. Please enable [Settings Sync](https://www.gitpod.io/docs/ides-and-editors/settings-sync#enabling-settings-sync-in-vs-code-desktop).`, { flow, id: 'no_settings_sync' }, enableSettingsSync);
968-
if (action === enableSettingsSync) {
969-
vscode.commands.executeCommand('workbench.userDataSync.actions.turnOn');
976+
const msg = `Could not install local extensions on remote workspace. Please enable [Settings Sync](https://www.gitpod.io/docs/ides-and-editors/settings-sync#enabling-settings-sync-in-vs-code-desktop) with Gitpod.`;
977+
this.logger.error(msg);
978+
979+
const status = 'no_settings_sync';
980+
if (flow.quiet) {
981+
this.telemetry.sendUserFlowStatus(status, flow);
982+
} else {
983+
const enableSettingsSync = 'Enable Settings Sync';
984+
const action = await this.notifications.showInformationMessage(msg, { flow, id: status }, enableSettingsSync);
985+
if (action === enableSettingsSync) {
986+
vscode.commands.executeCommand('workbench.userDataSync.actions.turnOn');
987+
}
970988
}
971989
} else {
972990
this.logger.error('Error while fetching settings sync extension data:', e);
973991

974-
const seeLogs = 'See Logs';
975-
const action = await this.notifications.showErrorMessage(`Error while fetching settings sync extension data.`, { flow, id: 'failed_to_fetch' }, seeLogs);
976-
if (action === seeLogs) {
977-
this.logger.show();
992+
const status = 'failed_to_fetch';
993+
if (flow.quiet) {
994+
this.telemetry.sendUserFlowStatus(status, flow);
995+
} else {
996+
const seeLogs = 'See Logs';
997+
const action = await this.notifications.showErrorMessage(`Error while fetching settings sync extension data.`, { flow, id: status }, seeLogs);
998+
if (action === seeLogs) {
999+
this.logger.show();
1000+
}
9781001
}
9791002
}
9801003
return;
9811004
}
9821005

9831006
const syncDataContent = parseSyncData(syncData.content);
9841007
if (!syncDataContent) {
985-
this.telemetry.sendUserFlowStatus('failed_to_parse_content', flow);
986-
this.logger.error('Error while parsing sync data');
1008+
const msg = `Error while parsing settings sync extension data.`;
1009+
this.logger.error(msg);
1010+
1011+
const status = 'failed_to_parse_content';
1012+
if (flow.quiet) {
1013+
this.telemetry.sendUserFlowStatus(status, flow);
1014+
} else {
1015+
await this.notifications.showErrorMessage(msg, { flow, id: status });
1016+
}
9871017
return;
9881018
}
9891019

9901020
let extensions: ISyncExtension[];
9911021
try {
9921022
extensions = JSON.parse(syncDataContent.content);
9931023
} catch {
994-
this.telemetry.sendUserFlowStatus('failed_to_parse_json', flow);
995-
this.logger.error('Error while parsing settings sync extension data, malformed json');
1024+
const msg = `Error while parsing settings sync extension data, malformed JSON.`;
1025+
this.logger.error(msg);
1026+
1027+
const status = 'failed_to_parse_json';
1028+
if (flow.quiet) {
1029+
this.telemetry.sendUserFlowStatus(status, flow);
1030+
} else {
1031+
await this.notifications.showErrorMessage(msg, { flow, id: status });
1032+
}
9961033
return;
9971034
}
9981035

@@ -1020,10 +1057,18 @@ export default class RemoteConnector extends Disposable {
10201057
});
10211058
this.telemetry.sendUserFlowStatus('synced', flow);
10221059
} catch {
1023-
const seeLogs = 'See Logs';
1024-
const action = await this.notifications.showErrorMessage(`Error while installing local extensions on remote.`, { flow, id: 'failed' }, seeLogs);
1025-
if (action === seeLogs) {
1026-
this.logger.show();
1060+
const msg = `Error while installing local extensions on remote.`;
1061+
this.logger.error(msg);
1062+
1063+
const status = 'failed';
1064+
if (flow.quiet) {
1065+
this.telemetry.sendUserFlowStatus(status, flow);
1066+
} else {
1067+
const seeLogs = 'See Logs';
1068+
const action = await this.notifications.showErrorMessage(msg, { flow, id: status }, seeLogs);
1069+
if (action === seeLogs) {
1070+
this.logger.show();
1071+
}
10271072
}
10281073
}
10291074
}
@@ -1066,13 +1111,13 @@ export default class RemoteConnector extends Disposable {
10661111
this.logger.warn(`Local heartbeat not supported in ${connectionInfo.gitpodHost}, using version ${gitpodVersion.raw}`);
10671112
}
10681113

1069-
const syncExtensions = (await this.experiments.get<boolean>('gitpod.remote.syncExtensions', session.account.id, { gitpodHost: connectionInfo.gitpodHost }))!;
1070-
const userOverride = String(isUserOverrideSetting('gitpod.remote.syncExtensions'));
1071-
const syncExtFlow = { ...connectionInfo, gitpodVersion: gitpodVersion.raw, userId: session.account.id, flow: 'sync_local_extensions', userOverride };
1072-
this.telemetry.sendUserFlowStatus(syncExtensions ? 'enabled' : 'disabled', syncExtFlow);
1073-
if (syncExtensions) {
1074-
this.initializeRemoteExtensions(syncExtFlow);
1075-
}
1114+
const syncExtFlow = { ...connectionInfo, gitpodVersion: gitpodVersion.raw, userId: session.account.id, flow: 'sync_local_extensions' };
1115+
this.initializeRemoteExtensions({ ...syncExtFlow, quiet: true, flowId: uuid() });
1116+
this.context.subscriptions.push(vscode.commands.registerCommand("gitpod.installLocalExtensions", () => {
1117+
this.initializeRemoteExtensions({ ...syncExtFlow, quiet: false, flowId: uuid() });
1118+
}));
1119+
1120+
vscode.commands.executeCommand('setContext', 'gitpod.inWorkspace', true);
10761121
}
10771122

10781123
public override async dispose(): Promise<void> {

0 commit comments

Comments
 (0)