Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions src/firebase_studio/migrate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@

describe("extractMetadata", () => {
beforeEach(() => {
sandbox.stub(fs, "readFile").callsFake(async (p: any) => {

Check warning on line 28 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
const pStr = p.toString();

Check warning on line 29 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value

Check warning on line 29 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .toString on an `any` value

Check warning on line 29 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
if (pStr.endsWith("metadata.json")) {

Check warning on line 30 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value

Check warning on line 30 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .endsWith on an `any` value
return JSON.stringify({ projectId: "original-project" });
}
if (pStr.endsWith("blueprint.md")) {

Check warning on line 33 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value

Check warning on line 33 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .endsWith on an `any` value
return "# **App Name**: Test App";
}
return "";
Expand Down Expand Up @@ -63,7 +63,7 @@
let spawnStub: sinon.SinonStub;

beforeEach(() => {
sandbox.stub(fs, "stat").resolves({ isDirectory: () => true } as any);

Check warning on line 66 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type

Check warning on line 66 in src/firebase_studio/migrate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `Stats | BigIntStats | undefined`
const cp = require("child_process");
sandbox.stub(cp, "spawnSync").returns({ status: 0 });
sandbox.stub(process, "platform").value("darwin");
Expand Down Expand Up @@ -724,18 +724,31 @@
expect(writeFileStub.calledWith(mcpConfigPath, sinon.match(/"dart":/))).to.be.true;
});

it("should prompt user for Dart MCP server and NOT configure if they decline", async () => {
accessStub.withArgs(sinon.match("pubspec.yaml")).resolves();
commandStub.withArgs("dart").returns(true);
confirmStub
.withArgs(sinon.match({ message: sinon.match(/Dart MCP server/) }))
.resolves(false);
it("should respect non-interactive mode and not prompt", async () => {
commandStub.withArgs("agy").returns(true);
commandStub.withArgs("npx").returns(true);

await migrate(testRoot);
await migrate(testRoot, { startAntigravity: true, nonInteractive: true });

const mcpConfigDir = path.join(require("os").homedir(), ".gemini", "antigravity");
const mcpConfigPath = path.join(mcpConfigDir, "mcp_config.json");
expect(writeFileStub.calledWith(mcpConfigPath, sinon.match(/"dart":/))).to.be.false;
// Verify that prompts were called with nonInteractive: true
expect(
confirmStub.calledWith(
sinon.match({ message: sinon.match(/Firebase MCP server/), nonInteractive: true }),
),
).to.be.true;
expect(
selectStub.calledWith(
sinon.match({
message: sinon.match(/install Firebase project skills/),
nonInteractive: true,
}),
),
).to.be.true;
expect(
confirmStub.calledWith(
sinon.match({ message: sinon.match(/open it in Antigravity now/), nonInteractive: true }),
),
).to.be.true;
});
});

Expand Down
25 changes: 18 additions & 7 deletions src/firebase_studio/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { installAgentSkills } from "../agentSkills";
export interface MigrateOptions {
project?: string;
startAntigravity?: boolean;
nonInteractive?: boolean;
}

interface McpServerConfig {
Expand All @@ -29,7 +30,11 @@ interface McpConfig {
mcpServers: Record<string, McpServerConfig>;
}

async function setupAntigravityMcpServer(rootPath: string, appType?: AppType): Promise<void> {
async function setupAntigravityMcpServer(
rootPath: string,
appType?: AppType,
nonInteractive?: boolean,
): Promise<void> {
const mcpConfigDir = path.join(os.homedir(), ".gemini", "antigravity");
const mcpConfigPath = path.join(mcpConfigDir, "mcp_config.json");

Expand Down Expand Up @@ -59,6 +64,7 @@ async function setupAntigravityMcpServer(rootPath: string, appType?: AppType): P
const confirmFirebase = await prompt.confirm({
message: "Would you like to enable the Firebase MCP server for Antigravity?",
default: true,
nonInteractive,
});

if (confirmFirebase) {
Expand All @@ -82,6 +88,7 @@ async function setupAntigravityMcpServer(rootPath: string, appType?: AppType): P
const confirmDart = await prompt.confirm({
message: "Would you like to enable the Dart MCP server for Antigravity?",
default: true,
nonInteractive,
});

if (confirmDart) {
Expand Down Expand Up @@ -284,6 +291,7 @@ async function injectAntigravityContext(
rootPath: string,
projectId: string | undefined,
appName: string,
nonInteractive?: boolean,
): Promise<void> {
const agentDir = path.join(rootPath, ".agents");
const rulesDir = path.join(agentDir, "rules");
Expand All @@ -302,7 +310,7 @@ async function injectAntigravityContext(
{ name: "Globally for all projects", value: "global" },
],
default: "local",
nonInteractive: process.env.NODE_ENV === "test",
nonInteractive: nonInteractive || process.env.NODE_ENV === "test",
});

await installAgentSkills({
Expand Down Expand Up @@ -386,6 +394,7 @@ async function getAgyCommand(startAgy?: boolean): Promise<string | undefined> {
async function createFirebaseConfigs(
rootPath: string,
projectId: string | undefined,
nonInteractive?: boolean,
): Promise<void> {
if (!projectId) {
return;
Expand Down Expand Up @@ -426,7 +435,7 @@ async function createFirebaseConfigs(
const confirmBackend = await prompt.confirm({
message: `Would you like to use the App Hosting backend "${selectedBackendId}"?`,
default: true,
nonInteractive: process.env.NODE_ENV === "test",
nonInteractive: nonInteractive || process.env.NODE_ENV === "test",
});

if (confirmBackend) {
Expand Down Expand Up @@ -697,6 +706,7 @@ async function askToOpenAntigravity(
rootPath: string,
appName: string,
startAntigravity?: boolean,
nonInteractive?: boolean,
): Promise<void> {
const agyCommand = await getAgyCommand(startAntigravity);

Expand All @@ -721,6 +731,7 @@ async function askToOpenAntigravity(
const answer = await prompt.confirm({
message: "Would you like to open it in Antigravity now?",
default: true,
nonInteractive,
});

if (answer) {
Expand Down Expand Up @@ -771,12 +782,12 @@ export async function migrate(
}

await updateReadme(rootPath, appType);
await createFirebaseConfigs(rootPath, projectId);
await createFirebaseConfigs(rootPath, projectId, options.nonInteractive);
await uploadSecrets(rootPath, projectId);
await upgradeGenkitVersion(rootPath);
await injectAntigravityContext(rootPath, projectId, appName);
await injectAntigravityContext(rootPath, projectId, appName, options.nonInteractive);
await writeAntigravityConfigs(rootPath, appType);
await setupAntigravityMcpServer(rootPath, appType);
await setupAntigravityMcpServer(rootPath, appType, options.nonInteractive);
await cleanupUnusedFiles(rootPath);

// Suggest renaming if we are in the 'download' folder
Expand All @@ -788,5 +799,5 @@ export async function migrate(
}

await track.trackGA4("firebase_studio_migrate", { app_type: appType, result: "success" });
await askToOpenAntigravity(rootPath, appName, options.startAntigravity);
await askToOpenAntigravity(rootPath, appName, options.startAntigravity, options.nonInteractive);
}
Loading