From efd3d2f33ab8f5194d7d90aed0efa4d74a86f27a Mon Sep 17 00:00:00 2001 From: Victor Fan Date: Wed, 22 Jul 2026 15:16:07 -0700 Subject: [PATCH 1/3] fix bug in new secret param creation for non-default IDs --- src/deploy/functions/build.ts | 36 ++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/deploy/functions/build.ts b/src/deploy/functions/build.ts index a7f6792c97b..566237638ab 100644 --- a/src/deploy/functions/build.ts +++ b/src/deploy/functions/build.ts @@ -361,6 +361,8 @@ export async function resolveBackend(opts: ResolveBackendOpts): Promise<{ opts.isEmulator, opts.force, ); + // build.secretEnvVars and build.params may be out of sync after param resolution, if new secret resources were created during it + matchSecretEnvVars(opts.build); return { backend: toBackend(opts.build, paramValues), envs: paramValues, secretRefs: secretRefs }; } @@ -804,7 +806,7 @@ export interface ParsedSecretRef { * /version can be omitted and will cause the secret to resolve to whatever the latest version was at time of deploy. * * For each binding imported from the .env file, - * 1) TODO: Check if a conflicting SecretParam with the same name exists. If so, override the param so that the prompting flow will look in the right place when deciding whether or not to create a new Secret. + * 1) Check if a conflicting SecretParam with the same name exists. If so, override the param so that the prompting flow will look in the right place when deciding whether or not to create a new Secret. * 2) Upsert the binding directly into the Build's SecretEnvVars, which will cause it to be actually available in process.ENV */ export function applyEnvSecretBindings( @@ -863,6 +865,38 @@ export function applyEnvSecretBindings( } } +/** + * Updates the SecretEnvVars of a Build to use the same backing resources as its Secret Params. + * + * This is usually ensured by applyEnvSecretBindings, which reconciles the state of SecretEnvVars and + * Secrets before param handling. However, param handling itself can change the Secrets in the case where + * the user is prompted to create a new Secret resource, and selects a non-default resource ID. + * + * This function is a no-op in all other cases. + */ +export function matchSecretEnvVars(build: Build): void { + for (const param of build.params) { + if (param.type !== "secret") { + continue; + } + const secretParam = param; + + for (const endpointName of Object.keys(build.endpoints)) { + const endpoint = build.endpoints[endpointName]; + for (const envVar of endpoint.secretEnvironmentVariables ?? []) { + if (envVar.key === secretParam.name) { + if (secretParam.resourceId && envVar.secret !== secretParam.resourceId) { + logger.debug( + `Inserted newly created secret into build.SecretEnvVars: ${envVar.key}=${secretParam.resourceId}`, + ); + envVar.secret = secretParam.resourceId; + } + } + } + } + } +} + /** * Parses any of the supported formats used to refer to a Secret in .env: * API_KEY= From 463bc07f4faf7cce96fb37841951e688040d0f84 Mon Sep 17 00:00:00 2001 From: Victor Fan Date: Wed, 22 Jul 2026 15:27:53 -0700 Subject: [PATCH 2/3] slop --- src/deploy/functions/build.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/deploy/functions/build.ts b/src/deploy/functions/build.ts index 566237638ab..57ce7c9b778 100644 --- a/src/deploy/functions/build.ts +++ b/src/deploy/functions/build.ts @@ -880,17 +880,20 @@ export function matchSecretEnvVars(build: Build): void { continue; } const secretParam = param; + if (!secretParam.resourceId) { + continue; + } - for (const endpointName of Object.keys(build.endpoints)) { - const endpoint = build.endpoints[endpointName]; + for (const endpoint of Object.values(build.endpoints)) { for (const envVar of endpoint.secretEnvironmentVariables ?? []) { - if (envVar.key === secretParam.name) { - if (secretParam.resourceId && envVar.secret !== secretParam.resourceId) { - logger.debug( - `Inserted newly created secret into build.SecretEnvVars: ${envVar.key}=${secretParam.resourceId}`, - ); - envVar.secret = secretParam.resourceId; - } + if (envVar.key.toUpperCase() !== secretParam.name.toUpperCase()) { + continue; + } + if (envVar.secret !== secretParam.resourceId) { + logger.debug( + `Inserted newly created secret into build.SecretEnvVars: ${envVar.key}=${secretParam.resourceId}`, + ); + envVar.secret = secretParam.resourceId; } } } From bc7287988f60deecd2830d2ba3b77286f6d25dd4 Mon Sep 17 00:00:00 2001 From: Victor Fan Date: Thu, 23 Jul 2026 16:30:24 -0700 Subject: [PATCH 3/3] test --- src/deploy/functions/build.spec.ts | 82 ++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/deploy/functions/build.spec.ts b/src/deploy/functions/build.spec.ts index 59392210200..475f0a23242 100644 --- a/src/deploy/functions/build.spec.ts +++ b/src/deploy/functions/build.spec.ts @@ -690,6 +690,88 @@ describe("applyEnvSecretBindings", () => { }); }); +describe("matchSecretEnvVars", () => { + it("updates secretEnvVars in a Build with resource IDs from Secret params", () => { + const testBuild: build.Build = { + endpoints: { + func: { + region: "us-central1", + project: "test-project", + platform: "gcfv2", + runtime: "nodejs18", + entryPoint: "func1", + httpsTrigger: {}, + secretEnvironmentVariables: [ + { + key: "foo", + secret: "foo", + projectId: "test-project", + }, + ], + }, + }, + params: [ + { + type: "secret", + name: "foo", + resourceId: "bar", + version: "2", + inLocalEnvironment: true, + }, + ], + requiredAPIs: [], + }; + build.matchSecretEnvVars(testBuild); + expect(testBuild.endpoints["func"].secretEnvironmentVariables).to.deep.equal([ + { + key: "foo", + secret: "bar", + projectId: "test-project", + }, + ]); + }); + + it("is case-insensitive when matching secret names and env vars", () => { + const testBuild: build.Build = { + endpoints: { + func: { + region: "us-central1", + project: "test-project", + platform: "gcfv2", + runtime: "nodejs18", + entryPoint: "func1", + httpsTrigger: {}, + secretEnvironmentVariables: [ + { + key: "foo", + secret: "foo", + projectId: "test-project", + }, + ], + }, + }, + params: [ + { + type: "secret", + name: "FOO", + resourceId: "bar", + version: "2", + inLocalEnvironment: true, + }, + ], + requiredAPIs: [], + }; + build.matchSecretEnvVars(testBuild); + expect(testBuild.endpoints["func"].secretEnvironmentVariables).to.deep.equal([ + { + key: "foo", + secret: "bar", + projectId: "test-project", + }, + ]); + }); +}); + describe("applyPrefix", () => { const createTestBuild = (): build.Build => ({ endpoints: {