Skip to content
Open
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- Validate task queue function names at deploy time, since Cloud Tasks queue IDs (derived from the function name) cannot contain underscores (#7365).
- Add `MCP-Protocol-Version`, `Mcp-Method`, and `Mcp-Name` HTTP headers to `OneMcpServer` requests per the MCP 0728 standard release candidate (https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/ and https://modelcontextprotocol.io/seps/2243-http-standardization).
- Fixes Storage Emulator to support JSON uploads larger than 100KB without hanging or throwing 413 error (#8355)
- Add `extdeprecationwarnings` experiment to display phased deprecation notices and guidance across `ext:*` CLI commands.
37 changes: 37 additions & 0 deletions src/deploy/functions/validate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

describe("functionNamesAreValid", () => {
it("should allow properly formatted function names", () => {
const functions: any[] = [

Check warning on line 49 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unexpected any. Specify a different type
{
id: "my-function-1",
},
Expand All @@ -55,7 +55,7 @@
},
];
expect(() => {
validate.functionIdsAreValid(functions);

Check warning on line 58 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe argument of type `any[]` assigned to a parameter of type `{ id: string; platform: string; }[]`
}).to.not.throw();
});

Expand Down Expand Up @@ -114,6 +114,43 @@
});
});

describe("taskQueueFunctionNamesAreValid", () => {
const ENDPOINT_BASE: backend.Endpoint = {
platform: "gcfv2",
id: "id",
region: "us-east1",
project: "project",
entryPoint: "id",
runtime: "nodejs16",
httpsTrigger: {},
};

it("should not throw on hyphenated task queue function names", () => {
const endpoints: backend.Endpoint[] = [
{ ...ENDPOINT_BASE, id: "my-task-function", taskQueueTrigger: {} },
];
expect(() => {
validate.taskQueueFunctionNamesAreValid(endpoints);
}).to.not.throw();
});

it("should throw on underscores in task queue function names", () => {
const endpoints: backend.Endpoint[] = [
{ ...ENDPOINT_BASE, id: "dummy_function", taskQueueTrigger: {} },
];
expect(() => {
validate.taskQueueFunctionNamesAreValid(endpoints);
}).to.throw(FirebaseError, /dummy_function/);
});

it("should ignore underscores in non-task-queue function names", () => {
const endpoints: backend.Endpoint[] = [{ ...ENDPOINT_BASE, id: "dummy_function" }];
expect(() => {
validate.taskQueueFunctionNamesAreValid(endpoints);
}).to.not.throw();
});
});

describe("endpointsAreValid", () => {
const ENDPOINT_BASE: backend.Endpoint = {
platform: "gcfv2",
Expand Down Expand Up @@ -710,7 +747,7 @@
});

it("passes validation with empty backend", () => {
expect(validate.secretsAreValid(project, backend.empty())).to.not.be.rejected;

Check warning on line 750 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
});

it("passes validation with no secret env vars", () => {
Expand All @@ -718,7 +755,7 @@
...ENDPOINT,
platform: "gcfv2",
});
expect(validate.secretsAreValid(project, b)).to.not.be.rejected;

Check warning on line 758 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
});

it("fails validation given non-existent secret version", () => {
Expand All @@ -735,7 +772,7 @@
},
],
});
expect(validate.secretsAreValid(project, b)).to.be.rejectedWith(

Check warning on line 775 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
FirebaseError,
/Failed to validate secret version/,
);
Expand All @@ -755,7 +792,7 @@
},
],
});
expect(validate.secretsAreValid(project, b)).to.be.rejectedWith(

Check warning on line 795 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
FirebaseError,
/Failed to validate secret versions/,
);
Expand All @@ -779,7 +816,7 @@
},
],
});
expect(validate.secretsAreValid(project, b)).to.be.rejectedWith(

Check warning on line 819 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
FirebaseError,
/Failed to validate secret versions/,
);
Expand All @@ -806,7 +843,7 @@
});

await validate.secretsAreValid(project, b);
expect(backend.allEndpoints(b)[0].secretEnvironmentVariables![0].version).to.equal("2");

Check warning on line 846 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Forbidden non-null assertion
}
});

Expand All @@ -833,7 +870,7 @@
});

await validate.secretsAreValid(project, b);
expect(backend.allEndpoints(b)[0].secretEnvironmentVariables![0].version).to.equal("1");

Check warning on line 873 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Forbidden non-null assertion
}
});

Expand All @@ -859,7 +896,7 @@
});

await validate.secretsAreValid(project, b);
expect(backend.allEndpoints(b)[0].secretEnvironmentVariables![0].version).to.equal("2");

Check warning on line 899 in src/deploy/functions/validate.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Forbidden non-null assertion
}
});

Expand Down
22 changes: 22 additions & 0 deletions src/deploy/functions/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export function endpointsAreValid(
validateLifecycleHooks(wantBackend, existingBackend);
const endpoints = backend.allEndpoints(wantBackend);
functionIdsAreValid(endpoints);
taskQueueFunctionNamesAreValid(endpoints);
validateTimeoutConfig(endpoints);
for (const ep of endpoints) {
validateScheduledTimeout(ep);
Expand Down Expand Up @@ -329,6 +330,27 @@ export function functionIdsAreValid(functions: { id: string; platform: string }[
}
}

/**
* Validate that task queue function names conform to Cloud Tasks queue ID naming rules.
* Unlike Cloud Functions, Cloud Tasks queue IDs (which we derive from the function name)
* cannot contain underscores. See
* https://cloud.google.com/tasks/docs/reference/rest/v2/projects.locations.queues#Queue
* @throws { FirebaseError } Task queue function names must be valid Cloud Tasks queue IDs.
*/
export function taskQueueFunctionNamesAreValid(endpoints: backend.Endpoint[]): void {
const queueId = /^[a-zA-Z0-9-]{1,100}$/;
const invalidIds = endpoints
.filter(backend.isTaskQueueTriggered)
.filter((ep) => !queueId.test(ep.id));
if (invalidIds.length !== 0) {
const msg =
`${invalidIds.map((f) => f.id).join(", ")} task queue function name(s) can only contain ` +
`letters, numbers, and hyphens (no underscores), and not exceed 100 characters in length. ` +
`This is because the function's name is used as the Cloud Tasks queue ID.`;
throw new FirebaseError(msg);
}
}

/**
* Validate secret environment variables setting, if any.
* A bad secret configuration can lead to a significant delay in function deploys.
Expand Down
Loading