Skip to content

Commit 79586a8

Browse files
authored
Merge branch 'master' into caot-remove-ailogic-flag
2 parents 6c71d2b + 3dfb5d3 commit 79586a8

File tree

5 files changed

+44
-22
lines changed

5 files changed

+44
-22
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
- Added 'hosting' to the 'firebase_init' MCP tool.

npm-shrinkwrap.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "firebase-tools",
3-
"version": "14.21.0",
3+
"version": "14.22.0",
44
"description": "Command-Line Interface for Firebase",
55
"main": "./lib/index.js",
66
"bin": {

src/deploy/functions/params.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import * as sinon from "sinon";
33

44
import * as prompt from "../../prompt";
55
import * as params from "./params";
6+
import * as secretManager from "../../gcp/secretManager";
7+
import { FirebaseError } from "../../error";
68

79
const expect = chai.expect;
810
const fakeConfig = {
@@ -298,4 +300,30 @@ describe("resolveParams", () => {
298300
input.resolves("22");
299301
await expect(params.resolveParams(paramsToResolve, fakeConfig, {})).to.eventually.be.rejected;
300302
});
303+
304+
it("does not throw in non-interactive mode if secret exists in cloud", async () => {
305+
const paramsToResolve: params.Param[] = [{ name: "MY_SECRET", type: "secret" }];
306+
const getSecretMetadataStub = sinon.stub(secretManager, "getSecretMetadata").resolves({
307+
secret: { name: "MY_SECRET", projectId: "foo", labels: {}, replication: {} },
308+
secretVersion: { versionId: "1", state: "ENABLED", secret: {} as any },
309+
});
310+
311+
await expect(params.resolveParams(paramsToResolve, fakeConfig, {}, true)).to.be.fulfilled;
312+
313+
getSecretMetadataStub.restore();
314+
});
315+
316+
it("throws in non-interactive mode if secret is missing in cloud", async () => {
317+
const paramsToResolve: params.Param[] = [{ name: "MY_SECRET", type: "secret" }];
318+
const getSecretMetadataStub = sinon.stub(secretManager, "getSecretMetadata").resolves({
319+
secret: undefined,
320+
});
321+
322+
await expect(params.resolveParams(paramsToResolve, fakeConfig, {}, true)).to.be.rejectedWith(
323+
FirebaseError,
324+
/In non-interactive mode but have no value for the secret/,
325+
);
326+
327+
getSecretMetadataStub.restore();
328+
});
301329
});

src/deploy/functions/params.ts

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -395,26 +395,10 @@ export async function resolveParams(
395395

396396
const [needSecret, needPrompt] = partition(outstanding, (param) => param.type === "secret");
397397

398-
// Check for missing secrets in non-interactive mode
399-
if (nonInteractive && needSecret.length > 0) {
400-
const secretNames = needSecret.map((p) => p.name).join(", ");
401-
const commands = needSecret
402-
.map(
403-
(p) =>
404-
`\tfirebase functions:secrets:set ${p.name}${(p as SecretParam).format === "json" ? " --format=json --data-file <file.json>" : ""}`,
405-
)
406-
.join("\n");
407-
throw new FirebaseError(
408-
`In non-interactive mode but have no value for the following secrets: ${secretNames}\n\n` +
409-
"Set these secrets before deploying:\n" +
410-
commands,
411-
);
412-
}
413-
414398
// The functions emulator will handle secrets
415399
if (!isEmulator) {
416400
for (const param of needSecret) {
417-
await handleSecret(param as SecretParam, firebaseConfig.projectId);
401+
await handleSecret(param as SecretParam, firebaseConfig.projectId, nonInteractive);
418402
}
419403
}
420404

@@ -481,9 +465,20 @@ function populateDefaultParams(config: FirebaseConfig): Record<string, ParamValu
481465
* to read its environment variables. They are instead provided through GCF's own
482466
* Secret Manager integration.
483467
*/
484-
async function handleSecret(secretParam: SecretParam, projectId: string): Promise<void> {
468+
async function handleSecret(
469+
secretParam: SecretParam,
470+
projectId: string,
471+
nonInteractive?: boolean,
472+
): Promise<void> {
485473
const metadata = await secretManager.getSecretMetadata(projectId, secretParam.name, "latest");
486474
if (!metadata.secret) {
475+
if (nonInteractive) {
476+
throw new FirebaseError(
477+
`In non-interactive mode but have no value for the secret: ${secretParam.name}\n\n` +
478+
"Set this secret before deploying:\n" +
479+
`\tfirebase functions:secrets:set ${secretParam.name}${secretParam.format === "json" ? " --format=json --data-file <file.json>" : ""}`,
480+
);
481+
}
487482
const promptMessage = `This secret will be stored in Cloud Secret Manager (https://cloud.google.com/secret-manager/pricing) as ${
488483
secretParam.name
489484
}. Enter ${secretParam.format === "json" ? "a JSON value" : "a value"} for ${

0 commit comments

Comments
 (0)