Fix: treat "version is the current active version" as a successful deploy (#461)#462
Fix: treat "version is the current active version" as a successful deploy (#461)#462tuesd4y wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds handling for Firebase's 'already active version' error, treating it as a successful no-op deployment rather than a failure. For preview deploys, it retrieves the existing channel details, while for production deploys, it returns a success status directly. The review feedback highlights two important improvements: first, ensuring the 'already active version' check is not bypassed when debugging is enabled, and second, wrapping the channel list retrieval in a try-catch block to prevent transient errors from failing an otherwise successful deployment.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (!debug) { | ||
| console.log( | ||
| "Retrying deploy with the --debug flag for better error output" | ||
| ); | ||
| await execWithCredentials(args, projectId, gacFilename, { | ||
| debug: true, | ||
| firebaseToolsVersion, | ||
| force, | ||
| }); | ||
| if ( | ||
| isAlreadyActiveVersionError(output) || | ||
| isAlreadyActiveVersionError(e.message) | ||
| ) { | ||
| console.log( | ||
| "The deployed version is already the current active version; treating as a successful no-op deploy." | ||
| ); | ||
| } else { | ||
| console.log( | ||
| "Retrying deploy with the --debug flag for better error output" | ||
| ); | ||
| await execWithCredentials(args, projectId, gacFilename, { | ||
| debug: true, | ||
| firebaseToolsVersion, | ||
| force, | ||
| }); | ||
| } | ||
| } else { | ||
| throw e; | ||
| } |
There was a problem hiding this comment.
If debug is true (for example, if the action is run with debug options or during a retry), the catch block will immediately execute the else branch and throw the error, completely bypassing the isAlreadyActiveVersionError check. This means an "already active version" error will still cause the deployment to fail when debugging is active.
We should check isAlreadyActiveVersionError first, before checking if (!debug).
if (
isAlreadyActiveVersionError(output) ||
isAlreadyActiveVersionError(e.message)
) {
console.log(
"The deployed version is already the current active version; treating as a successful no-op deploy."
);
} else if (!debug) {
console.log(
"Retrying deploy with the --debug flag for better error output"
);
await execWithCredentials(args, projectId, gacFilename, {
debug: true,
firebaseToolsVersion,
force,
});
} else {
throw e;
}| async function getExistingChannel( | ||
| gacFilename: string, | ||
| deployConfig: ChannelDeployConfig | ||
| ): Promise<ChannelSuccessResult> { | ||
| const { projectId, channelId, target, firebaseToolsVersion } = deployConfig; | ||
|
|
||
| const listText = await execWithCredentials( | ||
| ["hosting:channel:list", ...(target ? ["--site", target] : [])], | ||
| projectId, | ||
| gacFilename, | ||
| { firebaseToolsVersion } | ||
| ); | ||
|
|
||
| const list = JSON.parse(listText.trim()) as ChannelListResult | ErrorResult; | ||
|
|
||
| const channel = | ||
| list.status === "success" | ||
| ? list.result.channels.find((c) => | ||
| c.name.endsWith(`/channels/${channelId}`) | ||
| ) | ||
| : undefined; | ||
|
|
||
| if (!channel) { | ||
| console.log( | ||
| `Could not find channel "${channelId}" when reading back the already-active deploy; reporting success without URL details.` | ||
| ); | ||
| } | ||
|
|
||
| const site = | ||
| channel?.name.match(/\/sites\/([^/]+)\//)?.[1] ?? target ?? channelId; | ||
|
|
||
| return { | ||
| status: "success", | ||
| result: { | ||
| [site]: { | ||
| site, | ||
| ...(target ? { target } : {}), | ||
| url: channel?.url ?? "", | ||
| expireTime: channel?.expireTime ?? "", | ||
| }, | ||
| }, | ||
| }; | ||
| } |
There was a problem hiding this comment.
If hosting:channel:list fails (due to transient network issues, permission errors, etc.) or returns invalid JSON, getExistingChannel will throw an unhandled exception. This will cause the entire deployment action to fail, even though the actual deployment succeeded (since the "already active version" error indicates the content is already live).
To make this more robust, we should wrap the channel list retrieval in a try-catch block and fall back to returning a successful result with empty URL details if it fails.
async function getExistingChannel(
gacFilename: string,
deployConfig: ChannelDeployConfig
): Promise<ChannelSuccessResult> {
const { projectId, channelId, target, firebaseToolsVersion } = deployConfig;
const site = target ?? channelId;
try {
const listText = await execWithCredentials(
["hosting:channel:list", ...(target ? ["--site", target] : [])],
projectId,
gacFilename,
{ firebaseToolsVersion }
);
const list = JSON.parse(listText.trim()) as ChannelListResult | ErrorResult;
const channel =
list.status === "success"
? list.result.channels.find((c) =>
c.name.endsWith(`/channels/${channelId}`)
)
: undefined;
if (!channel) {
console.log(
`Could not find channel "${channelId}" when reading back the already-active deploy; reporting success without URL details.`
);
if (list.status === "error") {
console.log(`Channel list error: ${list.error}`);
}
}
const resolvedSite =
channel?.name.match(/\/sites\/([^/]+)\/\//)?.[1] ?? site;
return {
status: "success",
result: {
[resolvedSite]: {
site: resolvedSite,
...(target ? { target } : {}),
url: channel?.url ?? "",
expireTime: channel?.expireTime ?? "",
},
},
};
} catch (e) {
console.log(
`Failed to retrieve channel list: ${e.message}. Reporting success without URL details.`
);
return {
status: "success",
result: {
[site]: {
site,
...(target ? { target } : {}),
url: "",
expireTime: "",
},
},
};
}
}
Fixes #461. A Firebase Hosting deploy can fully succeed with created version, uploaded files, version finalized, site serving the new content but the action might still exit with an error when the version being released is already the live/active version:
FAILED_PRECONDITION
Can't release to projects/-/sites//channels/live:
supplied version projects/.../versions/xxxx is the current active version
Note that this is not only for the live channel, but can happen for all channels.
This is effectively a successful no-op, but the CLI returns a non-zero exit code, so the workflow fails. This PR detects that specific error and treats it as a successful deploy.