Skip to content

Add firebase deploy --only run for remote Cloud Build source deploys - #11205

Open
falahat wants to merge 5 commits into
bapi_init_runfrom
bapi_deploy_run
Open

falahat wants to merge 5 commits into
bapi_init_runfrom
bapi_deploy_run

Conversation

@falahat

@falahat falahat commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Description

Scenarios Tested

Sample Commands

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

Copy link
Copy Markdown
Contributor

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 support for deploying Cloud Run services directly via the Firebase CLI under the 'direct_cloud_run' experiment, adding the 'run' deploy target, integration with 'firebase init', and comprehensive tests. The review feedback suggests several key improvements: handling expected user-facing errors by importing and throwing 'FirebaseError', avoiding non-null assertions on the main container template to prevent runtime errors, ensuring temporary local archives are cleaned up in a 'finally' block to avoid disk leaks, parallelizing service preparation using 'Promise.all' for better performance, and adding defensive checks for potentially undefined configuration properties.

Comment thread src/deploy/run/deploy.ts
import * as artifactregistry from "../../gcp/artifactregistry";
import * as runv2 from "../../gcp/runv2";
import * as gcs from "../../gcp/storage";
import { getProjectNumber } from "../../getProjectNumber";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Import FirebaseError from ../../error to use it for throwing user-facing errors, adhering to the repository style guide.

Suggested change
import { getProjectNumber } from "../../getProjectNumber";
import { FirebaseError } from "../../error";
import { getProjectNumber } from "../../getProjectNumber";
References
  1. Throw FirebaseError for expected, user-facing errors. (link)

Comment thread src/deploy/run/deploy.ts Outdated
const template = svc.existing
? copyTemplate(svc.existing)
: { containers: [{ name: serviceId, image: "" }] };
const container = mainContainer(template)!;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Avoid using the non-null assertion operator !. If mainContainer(template) returns undefined, accessing container.image will throw a runtime error. Instead, add a defensive check and throw a FirebaseError.

  const container = mainContainer(template);
  if (!container) {
    throw new FirebaseError(`No container found in the template for service ${serviceId}`);
  }
References
  1. Use strict null checks and handle undefined/null explicitly. (link)

Comment thread src/deploy/run/deploy.ts Outdated
Comment on lines +101 to +107
logLabeledBullet("run", `Uploading source for service ${serviceId}...`);
const { bucket, object } = await gcs.uploadObject(
{ file: archive, stream: fs.createReadStream(archive) },
bucketName,
gcs.ContentType.ZIP,
);
return { bucket, object };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The temporary archive file created by createSourceDeployArchive is uploaded to GCS but never deleted from the local disk. To prevent leaking disk space, ensure the temporary archive is cleaned up in a finally block after the upload completes.

Suggested change
logLabeledBullet("run", `Uploading source for service ${serviceId}...`);
const { bucket, object } = await gcs.uploadObject(
{ file: archive, stream: fs.createReadStream(archive) },
bucketName,
gcs.ContentType.ZIP,
);
return { bucket, object };
logLabeledBullet("run", `Uploading source for service ${serviceId}...`);
try {
const { bucket, object } = await gcs.uploadObject(
{ file: archive, stream: fs.createReadStream(archive) },
bucketName,
gcs.ContentType.ZIP,
);
return { bucket, object };
} finally {
try {
fs.rmSync(archive, { force: true });
} catch {
// Ignore cleanup errors to avoid masking the original upload error
}
}

Comment thread src/deploy/run/prepare.ts
Comment thread src/deploy/run/util.ts Outdated
* Returns the Cloud Run services in firebase.json that match the --only filter.
*/
export function getServiceConfigs(options: Options): RunSingle[] {
const configs = [options.config.src.run || []].flat();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Defensively handle cases where options.config or options.config.src might be undefined (e.g., in unit tests or custom environments) by using optional chaining.

Suggested change
const configs = [options.config.src.run || []].flat();
const configs = [options.config?.src?.run || []].flat();
References
  1. Use strict null checks and handle undefined/null explicitly. (link)

Comment thread src/deploy/run/util.ts
@falahat

falahat commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@falahat
falahat added this pull request to stack #11209 September 30, 2026 04:19

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

Copy link
Copy Markdown
Contributor

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 support for deploying Cloud Run services directly from local source using the Firebase CLI. It adds the new run deploy target, implements its prepare, deploy, and release phases, integrates it into the initialization flow, and adds comprehensive unit and end-to-end tests. The review feedback highlights several key improvements: adhering to strict null checks by avoiding the non-null assertion operator on the main container, cleaning up temporary local archive files after upload to prevent disk space leaks, verifying the existence of the deployed service URI before logging, and using npx mocha in the test script for better environment robustness.

Comment thread src/deploy/run/deploy.ts
Comment on lines +10 to +11
import { Context, Payload, ServiceDeploy } from "./args";
import {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To support explicit null/undefined handling and avoid using the non-null assertion operator, we need to import FirebaseError from ../../error.

Suggested change
import { Context, Payload, ServiceDeploy } from "./args";
import {
import { FirebaseError } from "../../error";
import { Context, Payload, ServiceDeploy } from "./args";
import {

Comment thread src/deploy/run/deploy.ts Outdated
const template = svc.existing
? copyTemplate(svc.existing)
: { containers: [{ name: serviceId, image: "" }] };
const container = mainContainer(template)!;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using the non-null assertion operator ! violates the repository style guide rule to use strict null checks and handle undefined/null explicitly. Please check if the container is defined and throw a FirebaseError if it is not.

  const container = mainContainer(template);
  if (!container) {
    throw new FirebaseError(`No container found in the template for service ${serviceId}`);
  }
References
  1. Use strict null checks and handle undefined/null explicitly. (link)

Comment thread src/deploy/run/deploy.ts Outdated
Comment on lines +97 to +107
const archive = await createSourceDeployArchive(
cfg,
path.join(options.config.projectDir, cfg.rootDir),
);
logLabeledBullet("run", `Uploading source for service ${serviceId}...`);
const { bucket, object } = await gcs.uploadObject(
{ file: archive, stream: fs.createReadStream(archive) },
bucketName,
gcs.ContentType.ZIP,
);
return { bucket, object };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The temporary archive file created by createSourceDeployArchive is uploaded to GCS but never deleted from the local disk. To prevent leaking disk space in the temporary directory, wrap the upload in a try...finally block and clean up the archive file.

Suggested change
const archive = await createSourceDeployArchive(
cfg,
path.join(options.config.projectDir, cfg.rootDir),
);
logLabeledBullet("run", `Uploading source for service ${serviceId}...`);
const { bucket, object } = await gcs.uploadObject(
{ file: archive, stream: fs.createReadStream(archive) },
bucketName,
gcs.ContentType.ZIP,
);
return { bucket, object };
const archive = await createSourceDeployArchive(
cfg,
path.join(options.config.projectDir, cfg.rootDir),
);
logLabeledBullet("run", `Uploading source for service ${serviceId}...`);
try {
const { bucket, object } = await gcs.uploadObject(
{ file: archive, stream: fs.createReadStream(archive) },
bucketName,
gcs.ContentType.ZIP,
);
return { bucket, object };
} finally {
try {
fs.rmSync(archive, { force: true });
} catch (err) {
// ignore
}
}

Comment thread src/deploy/run/release.ts
Comment on lines +12 to +14
for (const { config, deployed } of payload.run?.services || []) {
logLabeledSuccess("run", `Deployed service ${config.serviceId} to ${deployed?.uri}`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If deployed or deployed.uri is undefined, this will log Deployed service <id> to undefined. We should explicitly check that deployed?.uri is defined before logging the success message.

  for (const { config, deployed } of payload.run?.services || []) {
    if (deployed?.uri) {
      logLabeledSuccess("run", `Deployed service ${config.serviceId} to ${deployed.uri}`);
    }
  }

Comment thread scripts/run-deploy-tests/run.sh Outdated
@falahat
falahat removed this pull request from stack #11209 September 30, 2026 21:42

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants