Skip to content
Draft
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
- Ensured the Firebase Hosting API is enabled before deploying and running other Hosting commands.
- Improved the error shown when a Hosting rewrite targets a function in the wrong region. It now names the function, the rewrite region, and the regions where the function was found. (#11289)
78 changes: 77 additions & 1 deletion src/deploy/hosting/convertConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,83 @@ describe("convertConfig", () => {
version: "14",
},
),
).to.eventually.be.rejectedWith(FirebaseError);
).to.eventually.be.rejectedWith(
FirebaseError,
`Rewrite for function \`${FUNCTION_ID}\` in firebase.json targets region \`asia-northeast1\`, ` +
`but \`${FUNCTION_ID}\` was only found in \`europe-west1\`. Update the rewrite's \`region\` ` +
`in firebase.json to that region.`,
);
});
it("should name the deployed region when rewrite points to an existing function in the wrong region", async () => {
existingBackendStub.resolves(
backend.of({
id: FUNCTION_ID,
project: PROJECT_ID,
entryPoint: FUNCTION_ID,
runtime: "nodejs22",
region: "europe-west1",
platform: "gcfv2",
httpsTrigger: {},
}),
);
await expect(
convertConfig(
{ projectId: "1" },
{},
{
config: {
site: "foo",
rewrites: [
{ glob: "/foo", function: { functionId: FUNCTION_ID, region: "us-central1" } },
],
},
version: "14",
},
),
).to.eventually.be.rejectedWith(
FirebaseError,
`Rewrite for function \`${FUNCTION_ID}\` in firebase.json targets region \`us-central1\`, ` +
`but \`${FUNCTION_ID}\` was only found in \`europe-west1\`. Update the rewrite's \`region\` ` +
`in firebase.json to that region.`,
);
});
it("should list every region when rewrite matches none of a multi-region function", async () => {
const endpointIn = (region: string): backend.Endpoint => ({
id: FUNCTION_ID,
project: PROJECT_ID,
entryPoint: FUNCTION_ID,
runtime: "nodejs22",
region,
platform: "gcfv2",
httpsTrigger: {},
});
await expect(
convertConfig(
{ projectId: "1" },
{
functions: {
default: {
wantBackend: backend.of(endpointIn("europe-west1"), endpointIn("us-east1")),
haveBackend: backend.empty(),
},
},
},
{
config: {
site: "foo",
rewrites: [
{ glob: "/foo", function: { functionId: FUNCTION_ID, region: "asia-northeast1" } },
],
},
version: "14",
},
),
).to.eventually.be.rejectedWith(
FirebaseError,
`Rewrite for function \`${FUNCTION_ID}\` in firebase.json targets region \`asia-northeast1\`, ` +
`but \`${FUNCTION_ID}\` was only found in \`europe-west1\`, \`us-east1\`. Update the rewrite's ` +
`\`region\` in firebase.json to one of these regions.`,
);
});
it("should throw when rewrite points to function being deleted", async () => {
await expect(
Expand Down
28 changes: 11 additions & 17 deletions src/deploy/hosting/convertConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@
if (endpoints.length === 0) {
return { matchingEndpoint: undefined, foundMatchingId: false };
}
if (endpoints.length === 1) {
if (region && region !== endpoints[0].region) {
return { matchingEndpoint: undefined, foundMatchingId: true };
}
if (!region && endpoints.length === 1) {
return { matchingEndpoint: endpoints[0], foundMatchingId: true };
}
if (!region) {
Expand All @@ -81,10 +78,16 @@
);
return { matchingEndpoint: us, foundMatchingId: true };
}
return {
matchingEndpoint: endpoints.find((e) => e.region === region),
foundMatchingId: true,
};
const matchingEndpoint = endpoints.find((e) => e.region === region);
if (!matchingEndpoint) {
const regions = endpoints.map((e) => `\`${e.region}\``).join(", ");
throw new FirebaseError(
`Rewrite for function \`${id}\` in firebase.json targets region \`${region}\`, ` +
`but \`${id}\` was only found in ${regions}. Update the rewrite's \`region\` ` +
`in firebase.json to ${endpoints.length === 1 ? "that region" : "one of these regions"}.`,
);
Comment on lines +84 to +88

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

Instead of using the generic phrase "that region" when there is only one matching region, we can make the error message even more precise and actionable by outputting the actual region name (which is already formatted in the regions variable). This provides the user with a direct, copy-pasteable value for their firebase.json configuration.

Note: If you apply this suggestion, you will also need to update the corresponding unit tests in src/deploy/hosting/convertConfig.spec.ts to match the updated error message.

    throw new FirebaseError(
      `Rewrite for function \\\\`${id}\\\\\` in firebase.json targets region \\\\`${region}\\\\\`, ` +
        `but \\\\`${id}\\\\\` was only found in ${regions}. Update the rewrite's \\\\`region\\\\\` ` +
        `in firebase.json to ${endpoints.length === 1 ? regions : "one of these regions"}.`,
    );
References
  1. Error messages should clearly explain what failed and why; when actionable, provide concrete remediation guidance. (link)

}
return { matchingEndpoint, foundMatchingId: true };
}

/**
Expand Down Expand Up @@ -165,15 +168,6 @@
: existingEndpointSearch?.matchingEndpoint;

if (!endpoint) {
// If we find a function matching the function ID we are looking for in either
// existing or currently-deploying backends, we consider it a firebase function.
// In this case, we throw an error if the rewrite doesn't point to a valid region.
if (deployingEndpointSearch.foundMatchingId || existingEndpointSearch?.foundMatchingId) {
throw new FirebaseError(
`Unable to find a valid endpoint for function. Functions matching the rewrite
are present but in the wrong region.`,
);
}
// This could possibly succeed if there has been a function written
// outside firebase tooling. But it will break in v2. We might need to
// revisit this.
Expand Down Expand Up @@ -263,7 +257,7 @@
// kind of rewrite and we haven't yet handled it.
try {
assertExhaustive(rewrite);
} catch (e: any) {

Check warning on line 260 in src/deploy/hosting/convertConfig.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unexpected any. Specify a different type
throw new FirebaseError(
"Invalid hosting rewrite config in firebase.json. " +
"A rewrite config must specify 'destination', 'function', 'dynamicLinks', or 'run'",
Expand Down
Loading