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: { diff --git a/src/deploy/functions/build.ts b/src/deploy/functions/build.ts index a7f6792c97b..57ce7c9b778 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,41 @@ 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; + if (!secretParam.resourceId) { + continue; + } + + for (const endpoint of Object.values(build.endpoints)) { + for (const envVar of endpoint.secretEnvironmentVariables ?? []) { + 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; + } + } + } + } +} + /** * Parses any of the supported formats used to refer to a Secret in .env: * API_KEY=