From de337412d93d887be72846f7b39f9ba6fb28fd19 Mon Sep 17 00:00:00 2001 From: Julien Van Beveren Date: Tue, 19 Aug 2025 11:12:09 +0200 Subject: [PATCH 1/2] feat(build): add support for marking env vars as secrets in syncEnvVars - Added isSecret flag to SyncEnvVarsBody type - Updated CLI's syncEnvVarsWithServer to pass secret flags - Modified backend API endpoint to handle secret flags - Added documentation for the new feature --- .changeset/fresh-bats-travel.md | 6 +++++ ...ojects.$projectRef.envvars.$slug.import.ts | 2 ++ .../build/src/extensions/core/syncEnvVars.ts | 26 +++++++++++++++--- packages/cli-v3/src/commands/deploy.ts | 27 +++++++++---------- packages/core/src/v3/apiClient/types.ts | 12 +++++++++ packages/core/src/v3/schemas/api.ts | 2 ++ 6 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 .changeset/fresh-bats-travel.md diff --git a/.changeset/fresh-bats-travel.md b/.changeset/fresh-bats-travel.md new file mode 100644 index 0000000000..ebfda64542 --- /dev/null +++ b/.changeset/fresh-bats-travel.md @@ -0,0 +1,6 @@ +--- +"trigger.dev": minor +"@trigger.dev/build": minor +--- + +Added support for the secret flag on variables in the syncEnvVars extension diff --git a/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts b/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts index 803d99f28e..98f08aa5e3 100644 --- a/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts +++ b/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.import.ts @@ -44,6 +44,7 @@ export async function action({ params, request }: ActionFunctionArgs) { variables: Object.entries(body.variables).map(([key, value]) => ({ key, value, + isSecret: body.secrets?.[key] ?? false, })), }); @@ -55,6 +56,7 @@ export async function action({ params, request }: ActionFunctionArgs) { variables: Object.entries(body.parentVariables).map(([key, value]) => ({ key, value, + isSecret: body.parentSecrets?.[key] ?? false, })), }); diff --git a/packages/build/src/extensions/core/syncEnvVars.ts b/packages/build/src/extensions/core/syncEnvVars.ts index 4b43783561..3e2807bc95 100644 --- a/packages/build/src/extensions/core/syncEnvVars.ts +++ b/packages/build/src/extensions/core/syncEnvVars.ts @@ -2,7 +2,7 @@ import { BuildContext, BuildExtension } from "@trigger.dev/core/v3/build"; export type SyncEnvVarsBody = | Record - | Array<{ name: string; value: string; isParentEnv?: boolean }>; + | Array<{ name: string; value: string; isParentEnv?: boolean; isSecret?: boolean }>; export type SyncEnvVarsResult = | SyncEnvVarsBody @@ -151,9 +151,19 @@ async function callSyncEnvVarsFn( environment: string, branch: string | undefined, context: BuildContext -): Promise<{ env: Record; parentEnv?: Record } | undefined> { +): Promise<{ + env: Record; + secrets?: Record; + parentEnv?: Record; + parentEnvSecrets?: Record; +} | undefined> { if (syncEnvVarsFn && typeof syncEnvVarsFn === "function") { - let resolvedEnvVars: { env: Record; parentEnv?: Record } = { + let resolvedEnvVars: { + env: Record; + secrets?: Record; + parentEnv?: Record; + parentEnvSecrets?: Record; + } = { env: {}, }; let result; @@ -186,10 +196,20 @@ async function callSyncEnvVarsFn( if (item.isParentEnv) { if (!resolvedEnvVars.parentEnv) { resolvedEnvVars.parentEnv = {}; + resolvedEnvVars.parentEnvSecrets = {}; } resolvedEnvVars.parentEnv[item.name] = item.value; + if (item.isSecret && resolvedEnvVars.parentEnvSecrets) { + resolvedEnvVars.parentEnvSecrets[item.name] = true; + } } else { resolvedEnvVars.env[item.name] = item.value; + if (item.isSecret) { + if (!resolvedEnvVars.secrets) { + resolvedEnvVars.secrets = {}; + } + resolvedEnvVars.secrets[item.name] = true; + } } } } diff --git a/packages/cli-v3/src/commands/deploy.ts b/packages/cli-v3/src/commands/deploy.ts index b64e600014..41d1f1d40d 100644 --- a/packages/cli-v3/src/commands/deploy.ts +++ b/packages/cli-v3/src/commands/deploy.ts @@ -381,9 +381,8 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { const version = deployment.version; const rawDeploymentLink = `${authorization.dashboardUrl}/projects/v3/${resolvedConfig.project}/deployments/${deployment.shortCode}`; - const rawTestLink = `${authorization.dashboardUrl}/projects/v3/${ - resolvedConfig.project - }/test?environment=${options.env === "prod" ? "prod" : "stg"}`; + const rawTestLink = `${authorization.dashboardUrl}/projects/v3/${resolvedConfig.project + }/test?environment=${options.env === "prod" ? "prod" : "stg"}`; const deploymentLink = cliLink("View deployment", rawDeploymentLink); const testLink = cliLink("Test tasks", rawTestLink); @@ -562,8 +561,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { const taskCount = deploymentWithWorker.worker?.tasks.length ?? 0; outro( - `Version ${version} deployed with ${taskCount} detected task${taskCount === 1 ? "" : "s"} ${ - isLinksSupported ? `| ${deploymentLink} | ${testLink}` : "" + `Version ${version} deployed with ${taskCount} detected task${taskCount === 1 ? "" : "s"} ${isLinksSupported ? `| ${deploymentLink} | ${testLink}` : "" }` ); @@ -586,18 +584,16 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { TRIGGER_VERSION: version, TRIGGER_DEPLOYMENT_SHORT_CODE: deployment.shortCode, TRIGGER_DEPLOYMENT_URL: `${authorization.dashboardUrl}/projects/v3/${resolvedConfig.project}/deployments/${deployment.shortCode}`, - TRIGGER_TEST_URL: `${authorization.dashboardUrl}/projects/v3/${ - resolvedConfig.project - }/test?environment=${options.env === "prod" ? "prod" : "stg"}`, + TRIGGER_TEST_URL: `${authorization.dashboardUrl}/projects/v3/${resolvedConfig.project + }/test?environment=${options.env === "prod" ? "prod" : "stg"}`, }, outputs: { deploymentVersion: version, workerVersion: version, deploymentShortCode: deployment.shortCode, deploymentUrl: `${authorization.dashboardUrl}/projects/v3/${resolvedConfig.project}/deployments/${deployment.shortCode}`, - testUrl: `${authorization.dashboardUrl}/projects/v3/${ - resolvedConfig.project - }/test?environment=${options.env === "prod" ? "prod" : "stg"}`, + testUrl: `${authorization.dashboardUrl}/projects/v3/${resolvedConfig.project + }/test?environment=${options.env === "prod" ? "prod" : "stg"}`, needsPromotion: options.skipPromotion ? "true" : "false", }, }); @@ -608,11 +604,15 @@ export async function syncEnvVarsWithServer( projectRef: string, environmentSlug: string, envVars: Record, - parentEnvVars?: Record + parentEnvVars?: Record, + secrets?: Record, + parentEnvSecrets?: Record ) { return await apiClient.importEnvVars(projectRef, environmentSlug, { variables: envVars, parentVariables: parentEnvVars, + secrets, + parentSecrets: parentEnvSecrets, override: true, }); } @@ -640,8 +640,7 @@ async function failDeploy( checkLogsForErrors(logs); outro( - `${chalkError(`${prefix}:`)} ${ - error.message + `${chalkError(`${prefix}:`)} ${error.message }. Full build logs have been saved to ${logPath}` ); diff --git a/packages/core/src/v3/apiClient/types.ts b/packages/core/src/v3/apiClient/types.ts index 79baaf74ef..6a5f3c3c5a 100644 --- a/packages/core/src/v3/apiClient/types.ts +++ b/packages/core/src/v3/apiClient/types.ts @@ -13,6 +13,18 @@ export interface ImportEnvironmentVariablesParams { * To specify the variables, you can pass them in as a record of key-value pairs. e.g. `{ "key1": "value1", "key2": "value2" }` */ variables: Record; + /** + * Optional parent variables to be imported. These are used when dealing with branch environments. + */ + parentVariables?: Record; + /** + * Optional map of which variables should be marked as secrets. The keys should match the keys in `variables`. + */ + secrets?: Record; + /** + * Optional map of which parent variables should be marked as secrets. The keys should match the keys in `parentVariables`. + */ + parentSecrets?: Record; override?: boolean; } diff --git a/packages/core/src/v3/schemas/api.ts b/packages/core/src/v3/schemas/api.ts index 7fde77c41c..4fe76ab53a 100644 --- a/packages/core/src/v3/schemas/api.ts +++ b/packages/core/src/v3/schemas/api.ts @@ -799,6 +799,8 @@ export type UpdateEnvironmentVariableRequestBody = z.infer< export const ImportEnvironmentVariablesRequestBody = z.object({ variables: z.record(z.string()), parentVariables: z.record(z.string()).optional(), + secrets: z.record(z.boolean()).optional(), + parentSecrets: z.record(z.boolean()).optional(), override: z.boolean().optional(), }); From b00fc41244d3698994daa3ce76ce0e52d953e1cc Mon Sep 17 00:00:00 2001 From: Julien Van Beveren Date: Tue, 19 Aug 2025 11:27:35 +0200 Subject: [PATCH 2/2] implemented suggested refactor --- packages/build/src/extensions/core/syncEnvVars.ts | 10 +++++----- packages/cli-v3/src/commands/deploy.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/build/src/extensions/core/syncEnvVars.ts b/packages/build/src/extensions/core/syncEnvVars.ts index 3e2807bc95..f84b40dd90 100644 --- a/packages/build/src/extensions/core/syncEnvVars.ts +++ b/packages/build/src/extensions/core/syncEnvVars.ts @@ -155,14 +155,14 @@ async function callSyncEnvVarsFn( env: Record; secrets?: Record; parentEnv?: Record; - parentEnvSecrets?: Record; + parentSecrets?: Record; } | undefined> { if (syncEnvVarsFn && typeof syncEnvVarsFn === "function") { let resolvedEnvVars: { env: Record; secrets?: Record; parentEnv?: Record; - parentEnvSecrets?: Record; + parentSecrets?: Record; } = { env: {}, }; @@ -196,11 +196,11 @@ async function callSyncEnvVarsFn( if (item.isParentEnv) { if (!resolvedEnvVars.parentEnv) { resolvedEnvVars.parentEnv = {}; - resolvedEnvVars.parentEnvSecrets = {}; + resolvedEnvVars.parentSecrets = {}; } resolvedEnvVars.parentEnv[item.name] = item.value; - if (item.isSecret && resolvedEnvVars.parentEnvSecrets) { - resolvedEnvVars.parentEnvSecrets[item.name] = true; + if (item.isSecret && resolvedEnvVars.parentSecrets) { + resolvedEnvVars.parentSecrets[item.name] = true; } } else { resolvedEnvVars.env[item.name] = item.value; diff --git a/packages/cli-v3/src/commands/deploy.ts b/packages/cli-v3/src/commands/deploy.ts index 41d1f1d40d..44e6cbad24 100644 --- a/packages/cli-v3/src/commands/deploy.ts +++ b/packages/cli-v3/src/commands/deploy.ts @@ -606,13 +606,13 @@ export async function syncEnvVarsWithServer( envVars: Record, parentEnvVars?: Record, secrets?: Record, - parentEnvSecrets?: Record + parentSecrets?: Record ) { return await apiClient.importEnvVars(projectRef, environmentSlug, { variables: envVars, parentVariables: parentEnvVars, secrets, - parentSecrets: parentEnvSecrets, + parentSecrets, override: true, }); }