Skip to content

feat(cli): Introduced start:flutter command for Flutter integration, added --write-env-file option to start#4972

Open
pavelgj wants to merge 1 commit intogenkit-ai:mainfrom
pavelgj:pj/flutter-start
Open

feat(cli): Introduced start:flutter command for Flutter integration, added --write-env-file option to start#4972
pavelgj wants to merge 1 commit intogenkit-ai:mainfrom
pavelgj:pj/flutter-start

Conversation

@pavelgj
Copy link
Member

@pavelgj pavelgj commented Mar 20, 2026

Checklist (if applicable):

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Genkit CLI by introducing dedicated support for Flutter development through a new start:flutter command. It also improves the flexibility of the general start command by allowing the export of environment variables to a file. These changes aim to streamline the developer experience for integrating Genkit with Flutter projects and provide better control over the development environment setup.

Highlights

  • New start:flutter command: A new CLI command, start:flutter, has been introduced to enable running Flutter applications in Genkit development mode. This command supports options for controlling the Dev UI, specifying ports, and configuring CORS.
  • New --write-env-file option for start command: The existing start command now includes a --write-env-file option, allowing users to export the generated development environment variables into a specified .env formatted file.
  • Refactored environment variable handling: The logic for generating development environment variables has been extracted into a new utility function, getDevEnvVars, improving modularity and reusability across CLI commands.
  • Simplified process standard I/O: The ProcessManager now uses stdio: 'inherit' for interactive processes, simplifying the handling of standard input/output and removing manual piping logic.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new start:flutter command for better integration with Flutter projects and adds a --write-env-file option to the existing start command. The changes look good overall, but there are a few areas for improvement regarding code duplication, correctness, and efficiency. I've identified a high-severity issue where the --noui flag is not handled in the new start:flutter command, and another high-severity efficiency issue where a function is called unnecessarily when using --write-env-file. Additionally, there are opportunities to clean up unused imports, simplify complex logic, and remove dead code. Please see my detailed comments for suggestions.

Comment on lines +81 to +94
let port: number;
if (options.port) {
port = Number(options.port);
if (isNaN(port) || port < 0) {
logger.error(`"${options.port}" is not a valid port number`);
return;
}
} else {
port = await getPort({ port: makeRange(4000, 4099) });
}
startServer(manager, port);
if (options.open) {
open(`http://localhost:${port}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The --noui option is defined for this command but it's not being handled. The Dev UI server is started unconditionally. This logic should be wrapped in a check for !options.noui to respect the user's choice, similar to how it's handled in the start command.

Suggested change
let port: number;
if (options.port) {
port = Number(options.port);
if (isNaN(port) || port < 0) {
logger.error(`"${options.port}" is not a valid port number`);
return;
}
} else {
port = await getPort({ port: makeRange(4000, 4099) });
}
startServer(manager, port);
if (options.open) {
open(`http://localhost:${port}`);
}
if (!options.noui) {
let port: number;
if (options.port) {
port = Number(options.port);
if (isNaN(port) || port < 0) {
logger.error(`"${options.port}" is not a valid port number`);
return;
}
} else {
port = await getPort({ port: makeRange(4000, 4099) });
}
startServer(manager, port);
if (options.open) {
open(`http://localhost:${port}`);
}
}

Comment on lines +71 to +84
let envVars: Record<string, string> | undefined;
if (options.writeEnvFile) {
const devEnv = await getDevEnvVars(projectRoot, {
disableRealtimeTelemetry: options.disableRealtimeTelemetry,
corsOrigin: options.corsOrigin,
experimentalReflectionV2: options.experimentalReflectionV2,
});
envVars = devEnv.envVars;
const content = Object.entries(envVars)
.map(([k, v]) => `${k}=${v}`)
.join('\n');
fs.writeFileSync(options.writeEnvFile, content);
logger.info(`Wrote environment variables to ${options.writeEnvFile}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When using the --write-env-file option, getDevEnvVars is called here, but only envVars is stored. The other returned values (reflectionV2Port and telemetryServerUrl) are discarded. This leads to startDevProcessManager calling getDevEnvVars again, which is inefficient. You should store all returned values and pass them to startDevProcessManager to avoid the redundant call.

Suggested change
let envVars: Record<string, string> | undefined;
if (options.writeEnvFile) {
const devEnv = await getDevEnvVars(projectRoot, {
disableRealtimeTelemetry: options.disableRealtimeTelemetry,
corsOrigin: options.corsOrigin,
experimentalReflectionV2: options.experimentalReflectionV2,
});
envVars = devEnv.envVars;
const content = Object.entries(envVars)
.map(([k, v]) => `${k}=${v}`)
.join('\n');
fs.writeFileSync(options.writeEnvFile, content);
logger.info(`Wrote environment variables to ${options.writeEnvFile}`);
}
let envVars: Record<string, string> | undefined;
let reflectionV2Port: number | undefined;
let telemetryServerUrl: string | undefined;
if (options.writeEnvFile) {
const devEnv = await getDevEnvVars(projectRoot, {
disableRealtimeTelemetry: options.disableRealtimeTelemetry,
corsOrigin: options.corsOrigin,
experimentalReflectionV2: options.experimentalReflectionV2,
});
envVars = devEnv.envVars;
reflectionV2Port = devEnv.reflectionV2Port;
telemetryServerUrl = devEnv.telemetryServerUrl;
const content = Object.entries(envVars)
.map(([k, v]) => `${k}=${v}`)
.join('\n');
fs.writeFileSync(options.writeEnvFile, content);
logger.info(`Wrote environment variables to ${options.writeEnvFile}`);
}

disableRealtimeTelemetry: options.disableRealtimeTelemetry,
corsOrigin: options.corsOrigin,
experimentalReflectionV2: options.experimentalReflectionV2,
envVars,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Following up on my other comment, to prevent the redundant call to getDevEnvVars, you should also pass reflectionV2Port and telemetryServerUrl to startDevProcessManager if they have been calculated. The options object should be updated to include them.

          envVars,
          reflectionV2Port,
          telemetryServerUrl,

Comment on lines +20 to +24
import fs from 'fs';
import getPort, { makeRange } from 'get-port';
import open from 'open';
import os from 'os';
import path from 'path';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These imports for fs, os, and path are not used within the file. To maintain code cleanliness, they should be removed.

Comment on lines +27 to +31
interface FlutterRunOptions {
port?: string;
open?: boolean;
corsOrigin?: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The FlutterRunOptions interface is incomplete. It's missing options defined for the command, such as noui and disableRealtimeTelemetry. This can lead to type safety issues and makes the code harder to understand. Please add the missing properties.

Suggested change
interface FlutterRunOptions {
port?: string;
open?: boolean;
corsOrigin?: string;
}
interface FlutterRunOptions {
noui?: boolean;
port?: string;
open?: boolean;
corsOrigin?: string;
disableRealtimeTelemetry?: boolean;
}

Comment on lines +128 to +145
const {
envVars: calculatedEnvVars,
reflectionV2Port: calculatedReflectionV2Port,
telemetryServerUrl: calculatedTelemetryServerUrl,
} = options?.envVars &&
options?.telemetryServerUrl &&
(!options?.experimentalReflectionV2 || options?.reflectionV2Port)
? {
envVars: options.envVars,
reflectionV2Port: options.reflectionV2Port,
telemetryServerUrl: options.telemetryServerUrl,
}
: await getDevEnvVars(projectRoot, options);

const envVars = options?.envVars || calculatedEnvVars;
const reflectionV2Port = options?.reflectionV2Port || calculatedReflectionV2Port;
const telemetryServerUrl =
options?.telemetryServerUrl || calculatedTelemetryServerUrl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for determining envVars, reflectionV2Port, and telemetryServerUrl is a bit complex and contains redundant assignments. This can be simplified to improve readability and maintainability by removing the intermediate calculated... variables and the subsequent || assignments.

Suggested change
const {
envVars: calculatedEnvVars,
reflectionV2Port: calculatedReflectionV2Port,
telemetryServerUrl: calculatedTelemetryServerUrl,
} = options?.envVars &&
options?.telemetryServerUrl &&
(!options?.experimentalReflectionV2 || options?.reflectionV2Port)
? {
envVars: options.envVars,
reflectionV2Port: options.reflectionV2Port,
telemetryServerUrl: options.telemetryServerUrl,
}
: await getDevEnvVars(projectRoot, options);
const envVars = options?.envVars || calculatedEnvVars;
const reflectionV2Port = options?.reflectionV2Port || calculatedReflectionV2Port;
const telemetryServerUrl =
options?.telemetryServerUrl || calculatedTelemetryServerUrl;
const {
envVars,
reflectionV2Port,
telemetryServerUrl,
} = (options?.envVars &&
options?.telemetryServerUrl &&
(!options?.experimentalReflectionV2 || options?.reflectionV2Port))
? {
envVars: options.envVars,
reflectionV2Port: options.reflectionV2Port,
telemetryServerUrl: options.telemetryServerUrl,
}
: await getDevEnvVars(projectRoot, options);

if (this.appProcess?.stdin) {
process.stdin.unpipe(this.appProcess.stdin);
}
this.originalStdIn = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The originalStdIn property is no longer used after refactoring the process spawning logic. This line, along with the property's declaration, can be removed to clean up dead code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant