Skip to content

Conversation

@myftija
Copy link
Collaborator

@myftija myftija commented Dec 11, 2025

This PR makes our image builds deterministic and reproducible by ensuring that identical source code always produces the same image layers and image digest. This means that deployments where nothing has changed will no longer invalidate the image cache in our worker cluster nodes, thus avoid making the cold starts for runs worse.

Context
New deployments currently increase the cold start times for runs, as they generate a new image which needs to be pulled in the worker cluster where runs are executed. It happens also when the source code for the deployment has not changed due to non-deterministic steps in our build system. This addresses the latter issue by making builds reproducible.

Main changes

  • Avoided baking TRIGGER_DEPLOYMENT_ID and TRIGGER_DEPLOYMENT_VERSION in the image, we now pass these via the supervisor instead.
  • Used json-stable-stringify for consistent key ordering in the files we generate for the build, e.g., package.json, build.json, index.json.
  • Removed metafile.json from the image contents as it is not actually used in the container. This is only relevant for the analyze command.
  • Added SOURCE_DATE_EPOCH=0 and rewrite-timestamp=true to Docker builds to normalize file timestamps.
  • Removed some timings and outputHashes from build outputs and manifests.

The builds are now reproducible for both native build server and Depot paths. This should also lead to better image layer cache reuse in general.

@changeset-bot
Copy link

changeset-bot bot commented Dec 11, 2025

🦋 Changeset detected

Latest commit: bc0b18e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 26 packages
Name Type
trigger.dev Minor
@trigger.dev/core Minor
d3-chat Patch
references-d3-openai-agents Patch
references-nextjs-realtime Patch
references-realtime-hooks-test Patch
references-realtime-streams Patch
references-telemetry Patch
@trigger.dev/build Minor
@trigger.dev/python Minor
@trigger.dev/redis-worker Minor
@trigger.dev/schema-to-json Minor
@trigger.dev/sdk Minor
@internal/cache Patch
@internal/clickhouse Patch
@internal/redis Patch
@internal/replication Patch
@internal/run-engine Patch
@internal/schedule-engine Patch
@internal/testcontainers Patch
@internal/tracing Patch
@internal/zod-worker Patch
@trigger.dev/react-hooks Minor
@trigger.dev/rsc Minor
@trigger.dev/database Minor
@trigger.dev/otlp-importer Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

This PR threads deployment identifiers through the run and workload stack and makes build/manifest output more deterministic. Supervisor now passes deploymentId and deploymentVersion into WorkloadManager.create; Docker and Kubernetes workload launches set TRIGGER_DEPLOYMENT_ID and TRIGGER_DEPLOYMENT_VERSION env vars. The run-engine DequeuedMessage always returns string deployment fields and adds deployment.version. Build tooling adds SOURCE_DATE_EPOCH=0, enforces rewrite-timestamp, adjusts output/load handling, removes TRIGGER_DEPLOYMENT_VERSION as a build-arg, and toggles outputHashes for deploys. JSON writes use stable/pretty stringify; manifests exclude timings and externals are sorted.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Confirm WorkloadManager.create signature and all call sites updated to include deploymentId and deploymentVersion.
  • Check Docker and Kubernetes env var additions for correctness and potential information leakage.
  • Verify run-engine schema changes (required deployment.id, deployment.friendlyId, added deployment.version) are compatible with all consumers and serialization/deserialization boundaries.
  • Review build-image changes: SOURCE_DATE_EPOCH=0, rewrite-timestamp, removal of TRIGGER_DEPLOYMENT_VERSION build-arg, and new load handling in output options.
  • Inspect manifest-related changes: conditional omission of outputHashes, exclusion of timings when writing index.json, and deterministic sorting of externals.
  • Ensure json-stable-stringify/pretty output in writeJSONFile does not break tooling that expects previous formatting or ordering.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description provides comprehensive context, main changes, and justification but is missing the required checklist items, testing steps section, changelog section, and screenshots section from the template. Complete the checklist items, add a 'Testing' section describing how the changes were tested, ensure 'Changelog' section summarizes the changes, and include 'Screenshots' section if applicable (or explicitly mark as N/A).
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(cli): deterministic image builds for deployments' clearly and concisely summarizes the main objective of making builds deterministic and reproducible, which is the primary focus of the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch stable-builds

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cli-v3/src/deploy/buildImage.ts (1)

196-214: Remove TRIGGER_DEPLOYMENT_ID from build args to achieve deterministic, deployment-agnostic images

While SOURCE_DATE_EPOCH=0 and rewrite-timestamp=true handle timestamp reproducibility, passing TRIGGER_DEPLOYMENT_ID as a build arg (line 200, 523) means the resulting image config will still vary per deployment, causing different digests even for identical code.

TRIGGER_DEPLOYMENT_VERSION is declared in the Dockerfile templates but is not passed as a build arg, so it doesn't affect the digest. However, TRIGGER_DEPLOYMENT_ID does and should be removed from the build args and set at runtime instead (e.g., via supervisor/kube env) to achieve the determinism goal.

Also applies to: 519-539, 731-752, 839-885

🧹 Nitpick comments (4)
internal-packages/run-engine/src/engine/systems/dequeueSystem.ts (1)

543-567: Empty-string deployment fields become the sentinel for “no deployment”

The new construction:

  • Always supplies string values for deployment.id, deployment.friendlyId, and deployment.version (using ?? ""), and
  • Adds deployment.version to match the core schema.

This is type-safe and compatible with the updated DequeuedMessage schema, but it also means “no deployment” (notably in development environments) is now represented as empty strings instead of undefined. If any downstream logic previously treated undefined specially or assumed non-empty values imply a valid deployment, it’s worth double-checking those call sites to ensure this sentinel change is acceptable.

apps/supervisor/src/index.ts (1)

247-264: Confirm deploymentId is meant to carry the friendly id, not the DB id

Here deploymentId is populated with message.deployment.friendlyId rather than message.deployment.id, while the type name suggests it might be the underlying DB id. That’s totally fine if the intent is “human‑readable deployment identifier” for logs/telemetry, but it’s easy for future readers to assume it’s the primary key.

If the friendly id is indeed what runners should see, consider either:

  • Leaving behavior as-is but clarifying in a comment, or
  • Renaming the option/env to something like deploymentFriendlyId to avoid ambiguity.

Otherwise, if the intent is to expose the DB id, this line should use message.deployment.id instead.

packages/cli-v3/src/build/buildWorker.ts (1)

15-16: Deterministic package.json generation looks good; verify dev/peer/scripts behavior

Refactoring writeDeployFiles to synthesize package.json from buildManifest.externals and write it via writeJSONFile(..., true) gives you a deterministic, minimal runtime manifest. Sorting trustedDependencies is also a nice touch for reproducibility.

Two things to double‑check:

  1. Runtime coverage: With dependencies now driven purely by buildManifest.externals, any package that’s only in the original devDependencies but still needed at runtime will no longer be installed in the image. Please verify your externals generation covers all true runtime dependencies.
  2. Scripts/dev/peer deps: You’re explicitly zeroing devDependencies, peerDependencies, and scripts. That’s good for slimming and determinism, but confirm you don’t rely on npm run-style scripts or peer metadata inside the container before merging.

Also applies to: 177-212

packages/cli-v3/src/deploy/buildImage.ts (1)

423-543: Local docker buildx: deterministic output flags are correct; consider push/load + inspect behavior

Using SOURCE_DATE_EPOCH=0 and --output type=image,name=${imageTag},rewrite-timestamp=true,... for the local docker buildx build path is consistent with the Depot path and should normalize timestamps in both the image config and layers.

One subtle point to confirm: when push is true and load ends up false (default from shouldLoad), type=image,...,push=true will typically not leave a local image in the engine. Your subsequent docker image inspect ${imageTag} to compute imageSizeBytes may then return no data, silently leaving imageSizeBytes = 0. If you rely on accurate image size metrics for pushed-only builds, you may want to:

  • either force load=true when push=true && options.load === undefined, or
  • skip the size inspection when you know the image is not loaded locally.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d287078 and 81a8811.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • apps/supervisor/src/index.ts (1 hunks)
  • apps/supervisor/src/workloadManager/docker.ts (1 hunks)
  • apps/supervisor/src/workloadManager/kubernetes.ts (1 hunks)
  • apps/supervisor/src/workloadManager/types.ts (1 hunks)
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts (1 hunks)
  • packages/cli-v3/package.json (2 hunks)
  • packages/cli-v3/src/build/buildWorker.ts (2 hunks)
  • packages/cli-v3/src/build/bundle.ts (1 hunks)
  • packages/cli-v3/src/deploy/buildImage.ts (4 hunks)
  • packages/cli-v3/src/entryPoints/managed-index-controller.ts (2 hunks)
  • packages/cli-v3/src/entryPoints/managed/env.ts (1 hunks)
  • packages/cli-v3/src/utilities/buildManifest.ts (1 hunks)
  • packages/cli-v3/src/utilities/fileSystem.ts (2 hunks)
  • packages/core/src/v3/schemas/runEngine.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

Files:

  • apps/supervisor/src/workloadManager/docker.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • packages/cli-v3/src/utilities/fileSystem.ts
  • packages/cli-v3/src/build/bundle.ts
  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • packages/cli-v3/src/build/buildWorker.ts
  • packages/cli-v3/src/entryPoints/managed-index-controller.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/cli-v3/src/entryPoints/managed/env.ts
  • packages/cli-v3/src/utilities/buildManifest.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Files:

  • apps/supervisor/src/workloadManager/docker.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • packages/cli-v3/src/utilities/fileSystem.ts
  • packages/cli-v3/src/build/bundle.ts
  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • packages/cli-v3/src/build/buildWorker.ts
  • packages/cli-v3/src/entryPoints/managed-index-controller.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/cli-v3/src/entryPoints/managed/env.ts
  • packages/cli-v3/src/utilities/buildManifest.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • apps/supervisor/src/workloadManager/docker.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • packages/cli-v3/src/utilities/fileSystem.ts
  • packages/cli-v3/src/build/bundle.ts
  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • packages/cli-v3/src/build/buildWorker.ts
  • packages/cli-v3/src/entryPoints/managed-index-controller.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/cli-v3/src/entryPoints/managed/env.ts
  • packages/cli-v3/src/utilities/buildManifest.ts
  • packages/cli-v3/package.json
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • packages/core/src/v3/schemas/runEngine.ts
🧠 Learnings (15)
📚 Learning: 2025-06-04T16:02:22.957Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2150
File: apps/supervisor/src/workloadManager/docker.ts:115-115
Timestamp: 2025-06-04T16:02:22.957Z
Learning: In the Trigger.dev codebase, the supervisor component uses DOCKER_ENFORCE_MACHINE_PRESETS while the docker provider component uses ENFORCE_MACHINE_PRESETS. These are separate components with separate environment variable configurations for the same logical concept of enforcing machine presets.

Applied to files:

  • apps/supervisor/src/workloadManager/docker.ts
📚 Learning: 2025-11-10T09:09:07.399Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2663
File: apps/webapp/app/env.server.ts:1205-1206
Timestamp: 2025-11-10T09:09:07.399Z
Learning: In the trigger.dev webapp, S2_ACCESS_TOKEN and S2_DEPLOYMENT_LOGS_BASIN_NAME environment variables must remain optional until an OSS version of S2 is available, to avoid breaking environments that don't have S2 provisioned.

Applied to files:

  • apps/supervisor/src/workloadManager/docker.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • packages/cli-v3/src/entryPoints/managed/env.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp

Applied to files:

  • apps/supervisor/src/workloadManager/docker.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • packages/cli-v3/src/entryPoints/managed/env.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • apps/supervisor/src/workloadManager/docker.ts
  • packages/cli-v3/src/build/bundle.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • packages/cli-v3/src/build/buildWorker.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • packages/cli-v3/src/entryPoints/managed/env.ts
  • packages/cli-v3/src/utilities/buildManifest.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run

Applied to files:

  • apps/supervisor/src/workloadManager/docker.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • apps/supervisor/src/workloadManager/docker.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • packages/cli-v3/src/entryPoints/managed/env.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • apps/supervisor/src/workloadManager/docker.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings

Applied to files:

  • packages/cli-v3/src/build/bundle.ts
  • packages/cli-v3/src/build/buildWorker.ts
  • packages/cli-v3/src/entryPoints/managed-index-controller.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • packages/cli-v3/src/entryPoints/managed/env.ts
  • packages/cli-v3/src/utilities/buildManifest.ts
📚 Learning: 2025-08-20T07:41:13.973Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2424
File: apps/supervisor/src/workloadManager/docker.ts:175-178
Timestamp: 2025-08-20T07:41:13.973Z
Learning: dockerode createImage() supports multiple signatures including createImage(auth, options) -> Promise<ReadableStream> where auth is the first parameter (AuthConfig object) and options is the second parameter. Both createImage(auth, options) and createImage(options, {authconfig: auth}) are valid approaches.

Applied to files:

  • packages/cli-v3/src/deploy/buildImage.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Specify runtime environment (node or bun) in trigger.config.ts using the `runtime` property

Applied to files:

  • packages/cli-v3/src/entryPoints/managed/env.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure OpenTelemetry instrumentations and exporters in trigger.config.ts for enhanced logging

Applied to files:

  • packages/cli-v3/src/entryPoints/managed/env.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • packages/cli-v3/src/entryPoints/managed/env.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • packages/cli-v3/src/entryPoints/managed/env.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • packages/cli-v3/package.json
📚 Learning: 2025-11-27T16:27:48.109Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-27T16:27:48.109Z
Learning: pnpm version `10.23.0` and Node.js version `20.11.1` are required for development

Applied to files:

  • packages/cli-v3/package.json
🧬 Code graph analysis (3)
packages/cli-v3/src/utilities/fileSystem.ts (1)
packages/core/src/v3/apps/http.ts (1)
  • json (65-75)
packages/cli-v3/src/build/buildWorker.ts (1)
packages/cli-v3/src/utilities/fileSystem.ts (1)
  • writeJSONFile (162-164)
packages/cli-v3/src/entryPoints/managed-index-controller.ts (1)
packages/cli-v3/src/utilities/fileSystem.ts (1)
  • writeJSONFile (162-164)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
packages/cli-v3/src/build/bundle.ts (1)

417-419: Approve conditional outputHashes for deterministic deploy builds.

The change correctly omits outputHashes for deploy builds by providing an empty object while preserving it for dev builds. This ensures deterministic and reproducible deployments. The comment clearly explains the rationale—dev builds require the hash map for deduplication during rebuilds, while deploy builds omit it to achieve determinism.

Downstream consumers in copyDirWithStore are already designed to handle empty outputHashes via optional chaining and a fallback to computeFileHash, so the empty object will not cause issues.

packages/cli-v3/src/utilities/buildManifest.ts (1)

4-4: Destructuring looks good.

The explicit extraction of externals is appropriate for deterministic handling downstream.

packages/core/src/v3/schemas/runEngine.ts (1)

248-253: DequeuedMessage deployment fields now correctly reflect non-optional runtime contract

Making deployment.id, deployment.friendlyId, and deployment.version required strings in the schema matches how DequeueSystem now always supplies string values (with safe fallbacks) and avoids undefined leaking across the boundary. This is consistent and should help downstream consumers rely on deployment metadata being present.

apps/supervisor/src/workloadManager/docker.ts (1)

70-87: Deployment metadata correctly propagated into Docker container env

Adding TRIGGER_DEPLOYMENT_ID=${opts.deploymentId} and TRIGGER_DEPLOYMENT_VERSION=${opts.deploymentVersion} to envVars cleanly exposes deployment context to the runner while keeping it purely runtime (no impact on image determinism) and still avoiding env leakage in logs.

apps/supervisor/src/workloadManager/kubernetes.ts (1)

110-133: Kubernetes runner now has the same deployment context as Docker

The new TRIGGER_DEPLOYMENT_ID and TRIGGER_DEPLOYMENT_VERSION env vars for the run-controller container keep Kubernetes in sync with the Docker workload manager, ensuring runners in both orchestrators see consistent deployment metadata.

packages/cli-v3/src/entryPoints/managed/env.ts (1)

21-24: Deployment env vars correctly reclassified as runtime-only

Treating TRIGGER_DEPLOYMENT_ID and TRIGGER_DEPLOYMENT_VERSION as required runtime env vars (instead of build-time) matches the new supervisor behavior and removes deployment identity from image contents, which is important for deterministic builds.

apps/supervisor/src/workloadManager/types.ts (1)

20-38: WorkloadManagerCreateOptions correctly extended with deployment identity

Adding deploymentId and deploymentVersion as required fields on WorkloadManagerCreateOptions formalizes the need for deployment metadata at workload creation time and matches how Docker/Kubernetes managers now construct container env. This is a good, explicit contract extension—just ensure any alternative WorkloadManager implementations are updated accordingly.

packages/cli-v3/package.json (1)

95-120: Dependency changes for deterministic JSON output

Adding json-stable-stringify aligns with the new writeJSONFile implementation and is appropriate for deterministic JSON serialization. I don't see any immediate issues with this dependency wiring from the CLI side.

Please confirm that json-stable-stringify@^1.3.0 behaves as expected with your current Node/toolchain versions (especially around large objects and non-plain JSON values) and that no other packages still rely on @s2-dev/streamstore before finalizing this PR.

packages/cli-v3/src/utilities/fileSystem.ts (1)

2-3: Switch to json-stable-stringify in writeJSONFile looks correct

Using json-stable-stringify here gives deterministic key ordering across all JSON artifacts, with pretty controlling indentation only. The call pattern (stringify(json, pretty ? { space: 2 } : undefined)) matches the library API and is safe given the existing safeWriteFile behavior.

Please double‑check that all callers of writeJSONFile only pass JSON‑serializable data (no circular refs, functions, etc.), since any serialization error will now come from json-stable-stringify instead of the built‑in JSON.stringify.

Also applies to: 162-164

packages/cli-v3/src/entryPoints/managed-index-controller.ts (1)

13-14: Persisting a timings‑free, stably formatted index.json is a good fit for reproducibility

Dropping timings from the persisted index.json and routing the write through writeJSONFile(..., true) nicely removes a source of non‑determinism while standardizing JSON formatting.

Please confirm that no downstream consumer reads timings from index.json (as opposed to using workerManifest in‑memory) so this omission doesn’t silently change behavior in production.

Also applies to: 90-94

packages/cli-v3/src/deploy/buildImage.ts (1)

178-219: Depot remote build: timestamp normalization arguments look correct; confirm CLI support

Adding --build-arg SOURCE_DATE_EPOCH=0 and --output type=image,rewrite-timestamp=true should normalize file and image timestamps for Depot builds while keeping the rest of the arguments intact.

Please verify against the pinned @depot/cli version that:

  • --output type=image,rewrite-timestamp=true is supported in combination with --save, and
  • the behavior of --load (when set) with this --output configuration still matches your expectations for where the image ends up and how metadata.json is produced.

@myftija myftija changed the title feat(deployments): deterministic image builds feat(cli): deterministic image builds for deployments Dec 11, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cli-v3/src/deploy/buildImage.ts (1)

781-782: Remove unused TRIGGER_DEPLOYMENT_VERSION ARG declarations from the Dockerfile, or clarify if they're intentionally left as placeholders.

The Dockerfile declares ARG TRIGGER_DEPLOYMENT_VERSION (lines 782, 890) and sets it as an ENV variable in the indexer stage (lines 792, 900), but the build commands do not pass this value via --build-arg. The variable is required at runtime—it's used in controller.ts and taskRunProcessProvider.ts—and the supervisor correctly provides it via the -e flag during container execution (apps/supervisor/src/workloadManager/docker.ts:76 and kubernetes.ts:131).

Since TRIGGER_DEPLOYMENT_VERSION is always supplied at runtime by the supervisor (not baked into the image), either remove these unused ARG/ENV declarations for clarity, or add a comment explaining they're kept as placeholders for consistency with other deployment variables.

Applies also to: 791-792, 889-890, 899-900

🧹 Nitpick comments (1)
packages/cli-v3/src/deploy/buildImage.ts (1)

16-56: Consider using type instead of interface.

As per coding guidelines, prefer types over interfaces for TypeScript. This applies to BuildImageOptions, DepotBuildImageOptions, and SelfHostedBuildImageOptions defined in this file.

Apply this pattern:

-export interface BuildImageOptions {
+export type BuildImageOptions = {
   // ... properties
-}
+};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c49f68 and bc0b18e.

📒 Files selected for processing (3)
  • .changeset/five-pens-fly.md (1 hunks)
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts (1 hunks)
  • packages/cli-v3/src/deploy/buildImage.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

Files:

  • packages/cli-v3/src/deploy/buildImage.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Files:

  • packages/cli-v3/src/deploy/buildImage.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • packages/cli-v3/src/deploy/buildImage.ts
🧠 Learnings (8)
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • packages/cli-v3/src/deploy/buildImage.ts
  • .changeset/five-pens-fly.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure build process in trigger.config.ts using `build` object with external packages, extensions, and JSX settings

Applied to files:

  • packages/cli-v3/src/deploy/buildImage.ts
📚 Learning: 2025-08-20T07:41:13.973Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2424
File: apps/supervisor/src/workloadManager/docker.ts:175-178
Timestamp: 2025-08-20T07:41:13.973Z
Learning: dockerode createImage() supports multiple signatures including createImage(auth, options) -> Promise<ReadableStream> where auth is the first parameter (AuthConfig object) and options is the second parameter. Both createImage(auth, options) and createImage(options, {authconfig: auth}) are valid approaches.

Applied to files:

  • packages/cli-v3/src/deploy/buildImage.ts
📚 Learning: 2025-11-10T09:09:07.399Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2663
File: apps/webapp/app/env.server.ts:1205-1206
Timestamp: 2025-11-10T09:09:07.399Z
Learning: In the trigger.dev webapp, S2_ACCESS_TOKEN and S2_DEPLOYMENT_LOGS_BASIN_NAME environment variables must remain optional until an OSS version of S2 is available, to avoid breaking environments that don't have S2 provisioned.

Applied to files:

  • packages/cli-v3/src/deploy/buildImage.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution

Applied to files:

  • .changeset/five-pens-fly.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Run `npx trigger.devlatest init` to initialize a Trigger.dev project

Applied to files:

  • .changeset/five-pens-fly.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Run `npx trigger.devlatest dev` to start the Trigger.dev development server

Applied to files:

  • .changeset/five-pens-fly.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • .changeset/five-pens-fly.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
.changeset/five-pens-fly.md (1)

1-6: LGTM!

The changeset correctly documents the feature addition with appropriate minor version bumps for the affected packages.

packages/cli-v3/src/deploy/buildImage.ts (3)

222-223: LGTM! Deterministic build timestamp.

Adding SOURCE_DATE_EPOCH=0 correctly normalizes file timestamps for reproducible builds.

Also applies to: 572-573


201-208: LGTM! Load parameter properly propagated.

The load option is correctly threaded through both remote and local build flows into getOutputOptions.

Also applies to: 534-541


1119-1163: LGTM! Output options correctly enhanced for deterministic builds.

The changes properly add:

  • rewrite-timestamp=true to normalize timestamps in all output
  • Conditional load=true when requested
  • Proper parameter typing

These align with the PR's reproducible build objectives.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants