diff --git a/.changeset/fluffy-weeks-sleep.md b/.changeset/fluffy-weeks-sleep.md new file mode 100644 index 0000000000..6235b335eb --- /dev/null +++ b/.changeset/fluffy-weeks-sleep.md @@ -0,0 +1,6 @@ +--- +"trigger.dev": patch +"@trigger.dev/core": patch +--- + +The new `triggeredVia` field is now populated in deployments via the CLI. diff --git a/apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts b/apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts index de2f25ec74..ea59c65722 100644 --- a/apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts @@ -1,4 +1,5 @@ import { + BuildServerMetadata, DeploymentErrorData, ExternalBuildData, prepareDeploymentError, @@ -154,17 +155,23 @@ export class DeploymentPresenter { avatarUrl: true, }, }, + buildServerMetadata: true, }, }); const gitMetadata = processGitMetadata(deployment.git); - const externalBuildData = deployment.externalBuildData ? ExternalBuildData.safeParse(deployment.externalBuildData) : undefined; + const buildServerMetadata = deployment.buildServerMetadata + ? BuildServerMetadata.safeParse(deployment.buildServerMetadata) + : undefined; let eventStream = undefined; - if (env.S2_ENABLED === "1" && gitMetadata?.source === "trigger_github_app") { + if ( + env.S2_ENABLED === "1" && + (buildServerMetadata || gitMetadata?.source === "trigger_github_app") + ) { const [error, accessToken] = await tryCatch(this.getS2AccessToken(project.externalRef)); if (error) { diff --git a/apps/webapp/app/v3/services/initializeDeployment.server.ts b/apps/webapp/app/v3/services/initializeDeployment.server.ts index e88d8b9e8f..52a968792c 100644 --- a/apps/webapp/app/v3/services/initializeDeployment.server.ts +++ b/apps/webapp/app/v3/services/initializeDeployment.server.ts @@ -1,4 +1,4 @@ -import { type InitializeDeploymentRequestBody } from "@trigger.dev/core/v3"; +import { BuildServerMetadata, type InitializeDeploymentRequestBody } from "@trigger.dev/core/v3"; import { customAlphabet } from "nanoid"; import { env } from "~/env.server"; import { type AuthenticatedEnvironment } from "~/services/apiAuth.server"; @@ -190,6 +190,21 @@ export class InitializeDeploymentService extends BaseService { isNativeBuild: payload.isNativeBuild, }); + const buildServerMetadata: BuildServerMetadata | undefined = + payload.isNativeBuild || payload.buildId + ? { + buildId: payload.buildId, + ...(payload.isNativeBuild + ? { + isNativeBuild: payload.isNativeBuild, + artifactKey: payload.artifactKey, + skipPromotion: payload.skipPromotion, + configFilePath: payload.configFilePath, + } + : {}), + } + : undefined; + const deployment = await this._prisma.workerDeployment.create({ data: { friendlyId: generateFriendlyId("deployment"), @@ -200,12 +215,14 @@ export class InitializeDeploymentService extends BaseService { environmentId: environment.id, projectId: environment.projectId, externalBuildData, + buildServerMetadata, triggeredById: triggeredBy?.id, type: payload.type, imageReference: imageRef, imagePlatform: env.DEPLOY_IMAGE_PLATFORM, git: payload.gitMeta ?? undefined, runtime: payload.runtime ?? undefined, + triggeredVia: payload.triggeredVia ?? undefined, startedAt: initialStatus === "BUILDING" ? new Date() : undefined, }, }); diff --git a/internal-packages/database/prisma/migrations/20251208112409_add_build_server_meta_to_deployments/migration.sql b/internal-packages/database/prisma/migrations/20251208112409_add_build_server_meta_to_deployments/migration.sql new file mode 100644 index 0000000000..1356966cc8 --- /dev/null +++ b/internal-packages/database/prisma/migrations/20251208112409_add_build_server_meta_to_deployments/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "public"."WorkerDeployment" ADD COLUMN "buildServerMetadata" JSONB; \ No newline at end of file diff --git a/internal-packages/database/prisma/migrations/20251208144643_add_triggered_via_to_deployments/migration.sql b/internal-packages/database/prisma/migrations/20251208144643_add_triggered_via_to_deployments/migration.sql new file mode 100644 index 0000000000..7e6218ea7c --- /dev/null +++ b/internal-packages/database/prisma/migrations/20251208144643_add_triggered_via_to_deployments/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "public"."WorkerDeployment" ADD COLUMN "triggeredVia" TEXT; diff --git a/internal-packages/database/prisma/schema.prisma b/internal-packages/database/prisma/schema.prisma index 140da0d710..50c2669935 100644 --- a/internal-packages/database/prisma/schema.prisma +++ b/internal-packages/database/prisma/schema.prisma @@ -1749,6 +1749,7 @@ model WorkerDeployment { imagePlatform String @default("linux/amd64") externalBuildData Json? + buildServerMetadata Json? status WorkerDeploymentStatus @default(PENDING) type WorkerDeploymentType @default(V1) @@ -1764,6 +1765,7 @@ model WorkerDeployment { triggeredBy User? @relation(fields: [triggeredById], references: [id], onDelete: SetNull, onUpdate: Cascade) triggeredById String? + triggeredVia String? startedAt DateTime? installedAt DateTime? diff --git a/packages/cli-v3/src/commands/deploy.ts b/packages/cli-v3/src/commands/deploy.ts index d387c83d65..818051d68d 100644 --- a/packages/cli-v3/src/commands/deploy.ts +++ b/packages/cli-v3/src/commands/deploy.ts @@ -6,6 +6,7 @@ import { GitMeta, DeploymentFinalizedEvent, DeploymentEventFromString, + DeploymentTriggeredVia, } from "@trigger.dev/core/v3/schemas"; import { Command, Option as CommandOption } from "commander"; import { join, relative, resolve } from "node:path"; @@ -48,7 +49,7 @@ import { import { loadDotEnvVars } from "../utilities/dotEnv.js"; import { isDirectory } from "../utilities/fileSystem.js"; import { setGithubActionsOutputAndEnvVars } from "../utilities/githubActions.js"; -import { createGitMeta } from "../utilities/gitMeta.js"; +import { createGitMeta, isGitHubActions } from "../utilities/gitMeta.js"; import { printStandloneInitialBanner } from "../utilities/initialBanner.js"; import { resolveLocalEnvVars } from "../utilities/localEnvVars.js"; import { logger } from "../utilities/logger.js"; @@ -382,6 +383,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { type: features.run_engine_v2 ? "MANAGED" : "V1", runtime: buildManifest.runtime, isNativeBuild: false, + triggeredVia: getTriggeredVia(), }, envVars.TRIGGER_EXISTING_DEPLOYMENT_ID ); @@ -893,6 +895,40 @@ async function initializeOrAttachDeployment( return newDeploymentOrError.data; } +function getTriggeredVia(): DeploymentTriggeredVia { + // Check specific CI providers first (most specific to least specific) + if (isGitHubActions()) { + return "cli:github_actions"; + } + if (process.env.GITLAB_CI === "true") { + return "cli:gitlab_ci"; + } + if (process.env.CIRCLECI === "true") { + return "cli:circleci"; + } + if (process.env.JENKINS_URL) { + return "cli:jenkins"; + } + if (process.env.TF_BUILD === "True") { + return "cli:azure_pipelines"; + } + if (process.env.BITBUCKET_BUILD_NUMBER) { + return "cli:bitbucket_pipelines"; + } + if (process.env.TRAVIS === "true") { + return "cli:travis_ci"; + } + if (process.env.BUILDKITE === "true") { + return "cli:buildkite"; + } + // Fallback for other/unknown CI environments + if (isCI) { + return "cli:ci_other"; + } + + return "cli:manual"; +} + async function handleNativeBuildServerDeploy({ apiClient, options, @@ -998,6 +1034,7 @@ async function handleNativeBuildServerDeploy({ artifactKey, skipPromotion: options.skipPromotion, configFilePath, + triggeredVia: getTriggeredVia(), }); if (!initializeDeploymentResult.success) { diff --git a/packages/cli-v3/src/utilities/gitMeta.ts b/packages/cli-v3/src/utilities/gitMeta.ts index 90ced43920..e3d2fc6477 100644 --- a/packages/cli-v3/src/utilities/gitMeta.ts +++ b/packages/cli-v3/src/utilities/gitMeta.ts @@ -105,7 +105,7 @@ function errorToString(err: unknown): string { return err instanceof Error ? err.message : String(err); } -function isGitHubActions() { +export function isGitHubActions() { // GH Actions CI sets these env variables return ( process.env.GITHUB_ACTIONS === "true" && diff --git a/packages/core/src/v3/schemas/api.ts b/packages/core/src/v3/schemas/api.ts index 7b328e6125..2fa9ba224a 100644 --- a/packages/core/src/v3/schemas/api.ts +++ b/packages/core/src/v3/schemas/api.ts @@ -400,6 +400,37 @@ export const ExternalBuildData = z.object({ export type ExternalBuildData = z.infer; +const anyString = z.custom((v) => typeof v === "string"); + +export const DeploymentTriggeredVia = z + .enum([ + "cli:manual", + "cli:ci_other", + "cli:github_actions", + "cli:gitlab_ci", + "cli:circleci", + "cli:jenkins", + "cli:azure_pipelines", + "cli:bitbucket_pipelines", + "cli:travis_ci", + "cli:buildkite", + "git_integration:github", + "dashboard", + ]) + .or(anyString); + +export type DeploymentTriggeredVia = z.infer; + +export const BuildServerMetadata = z.object({ + buildId: z.string().optional(), + isNativeBuild: z.boolean().optional(), + artifactKey: z.string().optional(), + skipPromotion: z.boolean().optional(), + configFilePath: z.string().optional(), +}); + +export type BuildServerMetadata = z.infer; + export const UpsertBranchRequestBody = z.object({ git: GitMeta.optional(), env: z.enum(["preview"]), @@ -462,6 +493,8 @@ export const InitializeDeploymentRequestBody = z type: z.enum(["MANAGED", "UNMANAGED", "V1"]).optional(), runtime: z.string().optional(), initialStatus: z.enum(["PENDING", "BUILDING"]).optional(), + triggeredVia: DeploymentTriggeredVia.optional(), + buildId: z.string().optional(), }) .and( z.preprocess( @@ -587,8 +620,6 @@ export const DeploymentLogEvent = z.object({ }), }); -const anyString = z.custom((v) => typeof v === "string"); - export const DeploymentFinalizedEvent = z.object({ type: z.literal("finalized"), data: z.object({