Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new command run:services:update (gated behind the direct_cloud_run experiment) to update, rebuild, and redeploy Cloud Run services, specifically supporting setting or clearing a base image. Feedback on the changes suggests: (1) iterating through all configurations returned by getServiceConfigs to properly support multi-region setups instead of only checking the first one, (2) gracefully handling cases where config.region is undefined in missingServiceMessage to prevent printing 'undefined' to users, and (3) using defensive assignment (|| {}) when initializing client.run namespaces to avoid overwriting existing properties.
| const [config] = getServiceConfigs({ ...options, only }); | ||
| const existing = await getExistingService(projectId, config.region, serviceId); | ||
| if (!existing) { | ||
| throw new FirebaseError(`${missingServiceMessage(config)} Then you can update it.`); | ||
| } | ||
| if (clearBaseImage && !mainContainer(existing.template)?.baseImageUri) { | ||
| logBullet(`Service ${clc.bold(serviceId)} does not have a base image.`); | ||
| return; | ||
| } |
There was a problem hiding this comment.
The current implementation only checks the first configuration returned by getServiceConfigs. If a user has configured the same service in multiple regions (a common multi-region setup), this will only validate and clear the base image for the first region, while completely ignoring the others. Additionally, if config.region is missing from firebase.json, it will result in a malformed request or a generic crash. We should iterate through all configurations, validate that the region is specified, check if the service exists in each region, and determine if any of them have a base image before proceeding.
const configs = getServiceConfigs({ ...options, only });
let hasBaseImage = false;
for (const config of configs) {
if (!config.region) {
throw new FirebaseError("Region is missing for service ${serviceId} in firebase.json.");
}
const existing = await getExistingService(projectId, config.region, serviceId);
if (!existing) {
throw new FirebaseError(`${missingServiceMessage(config)} Then you can update it.`);
}
if (mainContainer(existing.template)?.baseImageUri) {
hasBaseImage = true;
}
}
if (clearBaseImage && !hasBaseImage) {
logBullet(`Service ${clc.bold(serviceId)} does not have a base image.`);
return;
}References
- Use strict null checks and handle undefined/null explicitly. (link)
| export function missingServiceMessage(config: RunSingle): string { | ||
| const how = `Create it with ${clc.bold("firebase init run")} or ${clc.bold(`firebase deploy --only run:${config.serviceId}`)}.`; | ||
| return `Cloud Run service ${config.serviceId} doesn't exist in ${config.region} yet. ${how}`; | ||
| } |
There was a problem hiding this comment.
If config.region is not defined in firebase.json, missingServiceMessage will print undefined in the error message (e.g., doesn't exist in undefined yet). We should handle the absence of config.region gracefully by omitting the region clause if it is not present.
| export function missingServiceMessage(config: RunSingle): string { | |
| const how = `Create it with ${clc.bold("firebase init run")} or ${clc.bold(`firebase deploy --only run:${config.serviceId}`)}.`; | |
| return `Cloud Run service ${config.serviceId} doesn't exist in ${config.region} yet. ${how}`; | |
| } | |
| export function missingServiceMessage(config: RunSingle): string { | |
| const regionStr = config.region ? ` in ${config.region}` : ""; | |
| const how = `Create it with ${clc.bold("firebase init run")} or ${clc.bold(`firebase deploy --only run:${config.serviceId}`)}.`; | |
| return `Cloud Run service ${config.serviceId} doesn't exist${regionStr} yet. ${how}`; | |
| } |
| if (experiments.isEnabled("direct_cloud_run")) { | ||
| client.run = {}; | ||
| client.run.services = {}; | ||
| client.run.services.update = loadCommand("run-services-update"); | ||
| } |
There was a problem hiding this comment.
To prevent accidentally overwriting any existing client.run or client.run.services namespaces that might be initialized elsewhere or in future updates, use defensive assignment (|| {}).
| if (experiments.isEnabled("direct_cloud_run")) { | |
| client.run = {}; | |
| client.run.services = {}; | |
| client.run.services.update = loadCommand("run-services-update"); | |
| } | |
| if (experiments.isEnabled("direct_cloud_run")) { | |
| client.run = client.run || {}; | |
| client.run.services = client.run.services || {}; | |
| client.run.services.update = loadCommand("run-services-update"); | |
| } |
51f4259 to
595ee57
Compare
1608f73 to
9a8b7ca
Compare
Description
Scenarios Tested
Sample Commands