Skip to content

Commit b0c9bd5

Browse files
authored
Make GCA installation optional (#9097)
* Make GCA installation optional * remove console log * address gemini review * changelog * await * fix test
1 parent 84439ed commit b0c9bd5

File tree

8 files changed

+143
-15
lines changed

8 files changed

+143
-15
lines changed

firebase-vscode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## NEXT
22

3+
- [Changed] Gemini Code Assist is now optionally installed when using the "Build with AI" feature
4+
35
## 1.7.0
46

57
- Update internal `firebase-tools` dependency to 14.15.2

firebase-vscode/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"repository": "https://github.com/firebase/firebase-tools",
1212
"sideEffects": false,
1313
"extensionDependencies": [
14-
"google.geminicodeassist",
1514
"graphql.vscode-graphql-syntax"
1615
],
1716
"categories": [
@@ -217,7 +216,7 @@
217216
"test-compile": "npm run copyfiles && webpack --config src/test/webpack.test.js",
218217
"lint": "eslint src --ext ts",
219218
"test": "npm run test:unit && npm run test:e2e",
220-
"install:extensions": "code-server --install-extension Google.geminicodeassist --extensions-dir ./prebuilt-extensions/ && code-server --install-extension graphql.vscode-graphql-syntax --extensions-dir ./prebuilt-extensions/",
219+
"install:extensions": "code-server --install-extension graphql.vscode-graphql-syntax --extensions-dir ./prebuilt-extensions/",
221220
"pretest:e2e": "curl -fsSL https://code-server.dev/install.sh | sh -s -- --edge && npm run install:extensions",
222221
"pretest:unit": "npm run test-compile && tsc -p src/test/tsconfig.test.json",
223222
"test:unit": "node ./dist/test/firebase-vscode/src/test/runTest.js",

firebase-vscode/src/data-connect/ai-tools/firebase-mcp.ts

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,74 @@
1-
import { gemini, gemini as geminiToolModule } from "../../../../src/init/features/aitools/gemini";
1+
import {
2+
gemini,
3+
gemini as geminiToolModule,
4+
} from "../../../../src/init/features/aitools/gemini";
25
import * as vscode from "vscode";
36
import { firebaseConfig } from "../config";
47
import { ExtensionBrokerImpl } from "../../extension-broker";
58
import { AnalyticsLogger, DATA_CONNECT_EVENT_NAME } from "../../analytics";
69

10+
const GEMINI_EXTENSION_ID = "google.geminicodeassist";
711

12+
async function ensureGeminiExtension(): Promise<boolean> {
13+
let geminiExtension = vscode.extensions.getExtension(GEMINI_EXTENSION_ID);
814

9-
export function registerFirebaseMCP(broker: ExtensionBrokerImpl, analyticsLogger: AnalyticsLogger): vscode.Disposable {
15+
if (geminiExtension) {
16+
if (!geminiExtension.isActive) {
17+
await geminiExtension.activate();
18+
}
19+
return true;
20+
}
21+
22+
const selection = await vscode.window.showInformationMessage(
23+
"The Firebase Assistant requires the Gemini Code Assist extension. Do you want to install it?",
24+
"Yes",
25+
"No",
26+
);
27+
28+
if (selection !== "Yes") {
29+
vscode.window.showWarningMessage(
30+
"Cannot open Firebase Assistant without the Gemini Code Assist extension.",
31+
);
32+
return false;
33+
}
34+
35+
const disposable = vscode.extensions.onDidChange(async () => {
36+
geminiExtension = vscode.extensions.getExtension(GEMINI_EXTENSION_ID);
37+
if (geminiExtension) {
38+
await openGeminiChat();
39+
disposable.dispose();
40+
}
41+
});
42+
vscode.commands.executeCommand(
43+
"workbench.extensions.installExtension",
44+
GEMINI_EXTENSION_ID,
45+
);
46+
47+
return false;
48+
}
49+
50+
// Writes MCP config, then opens up Gemini with a new chat
51+
async function openGeminiChat() {
52+
writeToGeminiConfig();
53+
await vscode.commands.executeCommand("cloudcode.gemini.chatView.focus");
54+
await vscode.commands.executeCommand("geminicodeassist.agent.chat.new");
55+
}
56+
57+
export function registerFirebaseMCP(
58+
broker: ExtensionBrokerImpl,
59+
analyticsLogger: AnalyticsLogger,
60+
): vscode.Disposable {
1061
const geminiActivateSub = broker.on("firebase.activate.gemini", async () => {
1162
analyticsLogger.logger.logUsage(
1263
DATA_CONNECT_EVENT_NAME.TRY_FIREBASE_AGENT_CLICKED,
1364
);
14-
writeToGeminiConfig();
15-
await vscode.commands.executeCommand("cloudcode.gemini.chatView.focus");
16-
await vscode.commands.executeCommand("geminicodeassist.agent.chat.new"); // opens a new chat when an old one exists;
65+
66+
const geminiReady = await ensureGeminiExtension();
67+
68+
if (!geminiReady) {
69+
return;
70+
}
71+
await openGeminiChat();
1772
});
1873

1974
const mcpDocsSub = broker.on("docs.mcp.clicked", () => {
@@ -32,13 +87,14 @@ export function registerFirebaseMCP(broker: ExtensionBrokerImpl, analyticsLogger
3287

3388
// Writes the Firebase MCP server to the gemini code assist config file
3489
export function writeToGeminiConfig() {
35-
3690
const config = firebaseConfig.value?.tryReadValue;
3791
if (!config) {
3892
vscode.window.showErrorMessage("Could not read firebase.json");
3993
// TODO: Consider writing to HOME_DIR in case of this failure
4094
return;
4195
}
4296

43-
geminiToolModule.configure(config, config.projectDir, [/** TODO: Create "dataconnect" .md file */]);
97+
geminiToolModule.configure(config, config.projectDir, [
98+
/** TODO: Create "dataconnect" .md file */
99+
]);
44100
}

firebase-vscode/src/test/default_wdio.conf.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,7 @@ export const config: WebdriverIO.Config = {
4242
logLevel: "debug",
4343

4444
beforeTest: async function () {
45-
await browser.pause(3000); // give some time for extension dependencies to load their README page
46-
await browser.executeWorkbench((vscode) => {
47-
vscode.commands.executeCommand("workbench.action.closeAllEditors"); // close GCA home page
48-
});
45+
await browser.pause(1000); // give some time for extension dependency to load
4946
},
5047

5148
afterTest: async function (test) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { browser, expect } from "@wdio/globals";
2+
import { firebaseSuite, firebaseTest } from "../../utils/test_hooks";
3+
import { FirebaseCommands } from "../../utils/page_objects/commands";
4+
import { Workbench, Notification } from "wdio-vscode-service";
5+
import { Notifications } from "../../utils/page_objects/notifications";
6+
import { FirebaseSidebar } from "../../utils/page_objects/sidebar";
7+
8+
firebaseSuite("Gemini Install", async function () {
9+
firebaseTest(
10+
"should prompt to install Gemini and open chat view",
11+
async function () {
12+
const workbench = await browser.getWorkbench();
13+
14+
const sidebar = new FirebaseSidebar(workbench);
15+
await sidebar.openExtensionSidebar();
16+
17+
await sidebar.runInStudioContext(async (studio) => {
18+
await studio.geminiButton.waitForDisplayed();
19+
await studio.geminiButton.click();
20+
});
21+
22+
const notificationUtil = new Notifications(workbench);
23+
const installNotification =
24+
await notificationUtil.getGeminiInstallNotification();
25+
expect(installNotification).toExist();
26+
27+
// Click "Yes"
28+
await notificationUtil.clickYesFromGeminiInstallNotification(
29+
installNotification!, // verified in expect statement above,
30+
);
31+
32+
// Verify that the Gemini chat view is focused
33+
const chatView = await workbench.getEditorView().webView$;
34+
await chatView.waitForExist({ timeout: 50000 });
35+
const chatViewTitle = await chatView.getTitle();
36+
expect(chatViewTitle).toBe(
37+
"[Extension Development Host] Gemini Code Assist - Welcome — fishfood",
38+
);
39+
40+
await browser.executeWorkbench((vscode) => {
41+
vscode.commands.executeCommand(
42+
"workbench.extensions.uninstallExtension",
43+
"google.geminicodeassist",
44+
);
45+
});
46+
},
47+
);
48+
});

firebase-vscode/src/test/utils/install-extensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { execSync } from "child_process";
22

33
async function installExtensions() {
44
// List of extensions to install
5-
const extensions: string[] = ["graphql.vscode-graphql-syntax", "google.geminicodeassist"];
5+
const extensions: string[] = ["graphql.vscode-graphql-syntax"];
66

77
// Install each extension
88
extensions.forEach((extension) => {

firebase-vscode/src/test/utils/page_objects/notifications.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,31 @@ export class Notifications {
3333

3434
async editVariablesFromNotification(notification: Notification) {
3535
// takeAction doesn't work in wdio vscode
36-
const editButton = await notification.elem.$(".monaco-button=Edit variables");
36+
const editButton = await notification.elem.$(
37+
".monaco-button=Edit variables",
38+
);
3739
if (editButton) {
3840
await editButton.click();
3941
}
4042
}
43+
44+
async getGeminiInstallNotification() {
45+
const notifications = await this.workbench.getNotifications();
46+
return notifications.find(async (n) => {
47+
const message = await n.getMessage();
48+
return message.includes(
49+
"The Firebase Assistant requires the Gemini Code Assist extension",
50+
);
51+
});
52+
}
53+
54+
async clickYesFromGeminiInstallNotification(notification: Notification) {
55+
// takeAction doesn't work in wdio vscode
56+
const yesButton = await notification.elem.$(
57+
".monaco-button=Yes",
58+
);
59+
if (yesButton) {
60+
await yesButton.click();
61+
}
62+
}
4163
}

firebase-vscode/src/test/utils/page_objects/sidebar.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ export class StudioView {
140140
get fdcDeployElement() {
141141
return $(`vscode-button=${TEXT.DEPLOY_FDC_ENABLED}`);
142142
}
143+
144+
get geminiButton() {
145+
return $("vscode-button=Build your schema and queries with AI");
146+
}
143147
}
144148

145149
export class SchemaExplorerView {

0 commit comments

Comments
 (0)