Skip to content

feat(cli): implements content-addressable store for the dev CLI build outputs, reducing disk usage#2725

Merged
ericallam merged 2 commits intomainfrom
ea-branch-105
Dec 3, 2025
Merged

feat(cli): implements content-addressable store for the dev CLI build outputs, reducing disk usage#2725
ericallam merged 2 commits intomainfrom
ea-branch-105

Conversation

@ericallam
Copy link
Member

@ericallam ericallam commented Dec 2, 2025

Implements a content-addressable store for dev CLI build outputs, significantly reducing disk usage when making iterative code changes during development.

Problem

When running trigger.dev dev, each code change creates a new build directory in .trigger/tmp/build-XXXXX/. Even minor changes resulted in ~22MB of duplicated files per build because esbuild outputs were being fully copied each time, despite most files (chunks, dependencies) being identical between builds.

Solution

Introduced a content-addressable store at .trigger/tmp/store/ that deduplicates build outputs using hardlinks:

  1. Content-addressable storage: Files are stored by their content hash (using esbuild's built-in outputFile.hash), so identical content is only stored once
  2. Hardlinks: Build directories contain hardlinks to store files instead of copies - same inode means zero additional disk space
  3. Hash caching: File hashes are stored in BuildManifest.outputHashes to avoid recomputation when copying to worker directories

Tested with 12 iterative builds in a reference project:

Metric Without Dedup With Dedup Savings
12 builds ~264 MB 68.7 MB 195 MB (74%)
  • First build: 22MB (populates the store)
  • Subsequent builds: ~1.7-4.3MB each (only changed files)
  • Savings increase with more builds as unchanged files are simply hardlinked

Backwards Compatibility

  • outputHashes is optional in BuildManifest - falls back to computing hashes if not present
  • Store is only used in dev mode, no impact on deploy builds
  • Store is cleared when dev session starts fresh (clearTmpDirs)

@changeset-bot
Copy link

changeset-bot bot commented Dec 2, 2025

🦋 Changeset detected

Latest commit: df5ef35

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 Patch
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 Patch
@trigger.dev/core Patch
@trigger.dev/python Patch
@trigger.dev/react-hooks Patch
@trigger.dev/redis-worker Patch
@trigger.dev/rsc Patch
@trigger.dev/schema-to-json Patch
@trigger.dev/sdk Patch
@trigger.dev/database Patch
@trigger.dev/otlp-importer Patch
@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

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 2, 2025

Walkthrough

This pull request adds a content-addressable store for CLI build outputs and threads an optional storeDir through the build and dev pipelines. Build outputs are hashed and can be written via a store-aware writer that hardlinks from a persistent .trigger/tmp/store location. Manifests gain an optional outputHashes map and copyManifestToDir can use the store to deduplicate files when copying manifests and outputs. Utilities for sanitizing hashes, computing file hashes, and resolving the store directory are included.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review createFileWithStore in packages/cli-v3/src/utilities/fileSystem.ts — hardlink/copy fallbacks, permissions, and atomicity.
  • Review copyDirWithStore and computeFileHash in packages/cli-v3/src/build/manifests.ts — traversal, symlink handling, hash lookup order, and failure fallbacks.
  • Verify propagation of storeDir through getBundleResultFromBuild, createBuildManifestFromBundle, bundleWorker, and dev session flows in packages/cli-v3/src/*.
  • Confirm BuildManifest schema change (outputHashes) in packages/core/src/v3/schemas/build.ts is consistent with manifest construction and consumers.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main feature: implementing a content-addressable store to reduce disk usage for dev CLI build outputs.
Description check ✅ Passed The PR description is comprehensive and follows the template structure with sections covering Problem, Solution, Test Results, and Backwards Compatibility, though the Checklist items are not explicitly marked as completed.
✨ 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 ea-branch-105

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

🧹 Nitpick comments (3)
packages/cli-v3/src/utilities/fileSystem.ts (1)

53-62: Consider atomic existence check or accept TOCTOU as acceptable risk.

The synchronous existsSync check followed by async unlink creates a potential time-of-check-to-time-of-use (TOCTOU) race condition. In practice, this is unlikely to cause issues in a single-process dev CLI, but for robustness consider either:

  1. Catching and ignoring ENOENT errors from unlink
  2. Using fs.rm with force: true which ignores missing files

Apply this diff for a more robust approach:

-  // Remove existing file at destination if it exists (hardlinks fail on existing files)
-  if (fsSync.existsSync(filePath)) {
-    await fsModule.unlink(filePath);
-  }
+  // Remove existing file at destination if it exists (hardlinks fail on existing files)
+  try {
+    await fsModule.unlink(filePath);
+  } catch (e: any) {
+    // Ignore ENOENT - file doesn't exist, which is fine
+    if (e.code !== 'ENOENT') throw e;
+  }
packages/cli-v3/src/build/manifests.ts (2)

54-56: Extract sanitizeHashForFilename to avoid duplication.

This function is identical to sanitizeHashForFilename in fileSystem.ts (lines 23-25). Consider extracting it to a shared location to maintain DRY principles.

Example refactor:

+// In fileSystem.ts, export the function:
-function sanitizeHashForFilename(hash: string): string {
+export function sanitizeHashForFilename(hash: string): string {
   return hash.replace(/\//g, "_").replace(/\+/g, "-");
 }

+// In manifests.ts, import and use it:
+import { sanitizeHashForFilename } from "../utilities/fileSystem.js";
+
-function sanitizeHashForFilename(hash: string): string {
-  return hash.replace(/\//g, "_").replace(/\+/g, "-");
-}

62-65: Consider collision risk with 16-character hash.

Using only the first 16 characters of a SHA-256 hash (64 bits) for content addressing may have collision risks in larger codebases. While unlikely in practice, the full hash or at least 32 characters (128 bits) would provide stronger collision resistance.

Consider using more hash characters:

-  return createHash("sha256").update(contents).digest("hex").slice(0, 16);
+  return createHash("sha256").update(contents).digest("hex"); // Full 64 chars, or .slice(0, 32) for 128 bits
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f27422 and 64cc995.

⛔ Files ignored due to path filters (1)
  • references/d3-chat/src/trigger/chat.ts is excluded by !references/**
📒 Files selected for processing (7)
  • .changeset/polite-eels-divide.md (1 hunks)
  • packages/cli-v3/src/build/bundle.ts (9 hunks)
  • packages/cli-v3/src/build/manifests.ts (2 hunks)
  • packages/cli-v3/src/dev/devSession.ts (5 hunks)
  • packages/cli-v3/src/utilities/fileSystem.ts (1 hunks)
  • packages/cli-v3/src/utilities/tempDirectories.ts (1 hunks)
  • packages/core/src/v3/schemas/build.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:

  • packages/core/src/v3/schemas/build.ts
  • packages/cli-v3/src/utilities/tempDirectories.ts
  • packages/cli-v3/src/utilities/fileSystem.ts
  • packages/cli-v3/src/build/manifests.ts
  • packages/cli-v3/src/dev/devSession.ts
  • packages/cli-v3/src/build/bundle.ts
{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/build.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Files:

  • packages/core/src/v3/schemas/build.ts
  • packages/cli-v3/src/utilities/tempDirectories.ts
  • packages/cli-v3/src/utilities/fileSystem.ts
  • packages/cli-v3/src/build/manifests.ts
  • packages/cli-v3/src/dev/devSession.ts
  • packages/cli-v3/src/build/bundle.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • packages/core/src/v3/schemas/build.ts
  • packages/cli-v3/src/utilities/tempDirectories.ts
  • packages/cli-v3/src/utilities/fileSystem.ts
  • packages/cli-v3/src/build/manifests.ts
  • packages/cli-v3/src/dev/devSession.ts
  • packages/cli-v3/src/build/bundle.ts
🧠 Learnings (11)
📚 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/core/src/v3/schemas/build.ts
  • packages/cli-v3/src/dev/devSession.ts
  • packages/cli-v3/src/build/bundle.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:

  • packages/cli-v3/src/utilities/tempDirectories.ts
  • packages/cli-v3/src/build/bundle.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:

  • .changeset/polite-eels-divide.md
  • packages/cli-v3/src/build/bundle.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: Run `npx trigger.devlatest dev` to start the Trigger.dev development server

Applied to files:

  • .changeset/polite-eels-divide.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 the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties

Applied to files:

  • packages/cli-v3/src/build/bundle.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/build/bundle.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/build/bundle.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 `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation

Applied to files:

  • packages/cli-v3/src/build/bundle.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 Trigger.dev project in `trigger.config.ts` using `defineConfig()` with project ref and task directories

Applied to files:

  • packages/cli-v3/src/build/bundle.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 logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks

Applied to files:

  • packages/cli-v3/src/build/bundle.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 every task, including subtasks

Applied to files:

  • packages/cli-v3/src/build/bundle.ts
🧬 Code graph analysis (2)
packages/cli-v3/src/dev/devSession.ts (2)
packages/cli-v3/src/utilities/tempDirectories.ts (1)
  • getStoreDir (67-72)
packages/cli-v3/src/build/bundle.ts (1)
  • getBundleResultFromBuild (237-327)
packages/cli-v3/src/build/bundle.ts (2)
packages/cli-v3/src/utilities/fileSystem.ts (2)
  • createFileWithStore (38-70)
  • createFile (9-17)
packages/cli-v3/src/build/manifests.ts (1)
  • copyManifestToDir (8-48)
⏰ 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 / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (13)
packages/cli-v3/src/utilities/tempDirectories.ts (1)

62-72: LGTM! Clean implementation of persistent store directory.

The getStoreDir function follows the same pattern as existing temporary directory utilities and correctly ensures the directory exists. The persistence across rebuilds (not cleaned up like getTmpDir directories) is appropriate for a content-addressable store.

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

23-25: Sanitization approach is correct.

The character replacements correctly handle base64 URL-unsafe characters (/ and +). This ensures hash-based filenames are filesystem-safe across platforms.

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

71-72: LGTM! Schema extension follows best practices.

The optional outputHashes field properly extends the BuildManifest schema for content-addressable deduplication. The use of z.record(z.string()) correctly models the path-to-hash mapping, and the optional nature ensures backward compatibility.

packages/cli-v3/src/dev/devSession.ts (3)

23-28: Clean integration of getStoreDir import.


61-62: Well-documented store directory initialization.

The comment clearly explains the purpose of the shared store directory for deduplication.


112-112: Consistent storeDir propagation throughout the build pipeline.

The storeDir is correctly threaded through all relevant function calls (createBuildManifestFromBundle, getBundleResultFromBuild, and bundleWorker), enabling content-addressable storage across the build lifecycle.

Also applies to: 142-148, 207-207

packages/cli-v3/src/build/manifests.ts (3)

14-20: Store-aware copying is well-structured.

The conditional logic cleanly separates store-based and regular copying paths, with appropriate fallback behavior.


107-110: Good handling of symbolic links.

Preserving symbolic links with verbatimSymlinks: true is the correct approach for maintaining directory structure integrity.


94-98: Verify that outputHashes keys match the paths reconstructed during directory traversal.

The lookup in copyDirWithStore reconstructs paths using join(source, entry.name) and uses them as keys in outputHashes. The keys were previously set in bundle.ts using outputFile.path from esbuild. Ensure these produce identical path strings—esbuild's outputFile.path with write: false should match the paths reconstructed by traversing the output directory. Path format inconsistencies (absolute vs. relative paths, or separator differences) could cause the hash lookup to fail and unnecessarily fall back to computing hashes at runtime.

packages/cli-v3/src/build/bundle.ts (4)

40-41: Well-documented BundleOptions extension.

The JSDoc comment clearly explains the purpose of the storeDir field for deduplication via hardlinks.


56-57: Appropriate addition of outputHashes to BundleResult.

The field correctly tracks content hashes for deduplication and is properly documented.


245-257: Clean implementation of content-addressable storage.

The logic correctly:

  1. Collects hashes for all output files using esbuild's built-in outputFile.hash
  2. Conditionally writes via createFileWithStore when storeDir is available
  3. Falls back to regular createFile otherwise

Note: The outputFile.path keys stored in outputHashes should match the path format expected in manifests.ts (line 95). Based on the code, both use absolute paths, so this should be consistent.


372-384: Consistent storeDir threading through manifest creation.

The storeDir parameter is properly:

  1. Added to the function signature with appropriate JSDoc
  2. Included in the BuildManifest via outputHashes
  3. Passed through to copyManifestToDir for store-aware copying

This completes the end-to-end integration of the content-addressable store.

Also applies to: 417-417, 424-424

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

🧹 Nitpick comments (4)
packages/cli-v3/src/build/manifests.ts (4)

9-21: Consider validating the storeDir parameter.

The function checks if storeDir is truthy, but an empty string would pass this check yet be invalid. Consider explicitly validating that storeDir is a non-empty string.

Apply this diff to add validation:

 ): Promise<BuildManifest> {
   // Copy the dir from source to destination
   // If storeDir is provided, create hardlinks for files that exist in the store
-  if (storeDir) {
+  if (storeDir && storeDir.trim()) {
     await copyDirWithStore(source, destination, storeDir, manifest.outputHashes);
   } else {
     await cp(source, destination, { recursive: true });
   }

55-58: Consider streaming hash computation for large files.

Reading the entire file into memory could be problematic for very large build outputs. Additionally, using only 16 hex characters (64 bits) provides limited collision resistance—acceptable for typical builds but potentially risky at scale.

Consider these improvements:

  1. Stream file contents for memory efficiency:
async function computeFileHash(filePath: string): Promise<string> {
  const stream = createReadStream(filePath);
  const hash = createHash("sha256");
  
  for await (const chunk of stream) {
    hash.update(chunk);
  }
  
  return hash.digest("hex").slice(0, 20); // 80 bits for better collision resistance
}
  1. Increase hash length to 20-24 characters for better collision resistance across many builds.

96-104: Clarify error handling for hardlink fallback.

The nested try-catch is confusing: if hardlinking fails but copying succeeds, the file is created but the disk space savings are lost. If both operations fail, rethrowing linkError may not be the most informative error.

Consider improving error clarity:

       if (existsSync(storePath)) {
         // Create hardlink to store file
         // Fall back to copy if hardlink fails (e.g., on Windows or cross-device)
         try {
           await link(storePath, destPath);
         } catch (linkError) {
+          logger.debug(`Hardlink failed for ${destPath}, falling back to copy`, { error: linkError });
           try {
             await cp(storePath, destPath);
           } catch (copyError) {
-            throw linkError; // Rethrow original error if copy also fails
+            throw new Error(
+              `Failed to link or copy file from store: ${storePath} -> ${destPath}. ` +
+              `Link error: ${linkError}. Copy error: ${copyError}`
+            );
           }
         }

105-108: Consider logging when files are missing from the store.

When a file isn't found in the store (line 106), it's copied directly from source. This might indicate store corruption, an incomplete build, or files generated outside the normal build process. Logging this case would help diagnose issues.

       } else {
         // File wasn't in the store - copy normally
+        logger.debug(`File not found in store, copying directly: ${sourcePath}`);
         await cp(sourcePath, destPath);
       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64cc995 and df5ef35.

📒 Files selected for processing (2)
  • packages/cli-v3/src/build/manifests.ts (2 hunks)
  • packages/cli-v3/src/utilities/fileSystem.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-v3/src/utilities/fileSystem.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/build/manifests.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Files:

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

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • packages/cli-v3/src/build/manifests.ts
🧬 Code graph analysis (1)
packages/cli-v3/src/build/manifests.ts (2)
packages/core/src/v3/schemas/build.ts (2)
  • BuildManifest (20-73)
  • BuildManifest (75-75)
packages/cli-v3/src/utilities/fileSystem.ts (2)
  • readFile (121-123)
  • sanitizeHashForFilename (23-25)
⏰ 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 (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
packages/cli-v3/src/build/manifests.ts (1)

2-7: LGTM: Import additions support new store functionality.

The new imports are appropriate for the content-addressable store implementation.

await copyDirWithStore(sourcePath, destPath, storeDir, outputHashes);
} else if (entry.isFile()) {
// Try to get hash from manifest first, otherwise compute it
const contentHash = outputHashes?.[sourcePath] ?? (await computeFileHash(sourcePath));
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n -A3 -B3 'outputHashes' --type=ts -g '!**/test/**' packages/cli-v3/src/build/

Repository: triggerdotdev/trigger.dev

Length of output: 5461


🏁 Script executed:

# Find where outputHashes is assigned/populated in bundle
rg -n 'outputHashes\s*[:=]' --type=ts -g '!**/test/**' packages/cli-v3/src/

Repository: triggerdotdev/trigger.dev

Length of output: 155


🏁 Script executed:

# Check the context of line 88 in manifests.ts to understand sourcePath format
sed -n '60,115p' packages/cli-v3/src/build/manifests.ts

Repository: triggerdotdev/trigger.dev

Length of output: 2142


Fix outputHashes key format mismatch at line 88.

The outputHashes keys are populated from outputFile.path (esbuild output, typically relative paths like "index.js"), but the lookup uses sourcePath (a full joined path like "/path/to/source/index.js"). These formats don't match, so the optional chain always falls back to computeFileHash, defeating the cache. Use consistent path formats—either normalize both to relative paths or ensure both are full paths.

🤖 Prompt for AI Agents
In packages/cli-v3/src/build/manifests.ts around line 88, the lookup uses
sourcePath (an absolute/joined path) against outputHashes keys (esbuild output
relative paths), causing a mismatch; normalize the key formats before lookup by
converting sourcePath to the same relative form used as outputHashes keys (e.g.,
const relativeSource = path.relative(process.cwd(), sourcePath) or
path.posix.normalize(path.relative(buildRoot, sourcePath))), then use
outputHashes[relativeSource] ?? await computeFileHash(sourcePath); ensure you
import/use node's path and handle separators consistently (posix if output keys
are posix-style).

@ericallam ericallam merged commit 5b7dfe2 into main Dec 3, 2025
31 checks passed
@ericallam ericallam deleted the ea-branch-105 branch December 3, 2025 10:17
@github-actions github-actions bot mentioned this pull request Dec 2, 2025
myftija pushed a commit that referenced this pull request Dec 5, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.


# Releases
## trigger.dev@4.2.0

### Minor Changes

- feat(cli): upgrade bun deployments to v1.3.3
([#2756](#2756))

### Patch Changes

- fix(otel): exported logs and spans will now have matching trace IDs
([#2724](#2724))
- The `--force-local-build` flag is now renamed to just `--local-build`
([#2702](#2702))
- fix(cli): header will always print the correct profile
([#2728](#2728))
- feat: add ability to set custom resource properties through
trigger.config.ts or via the OTEL_RESOURCE_ATTRIBUTES env var
([#2704](#2704))
- feat(cli): implements content-addressable store for the dev CLI build
outputs, reducing disk usage
([#2725](#2725))
- Added support for native build server builds in the deploy command
(`--native-build-server`)
([#2702](#2702))
-   Updated dependencies:
    -   `@trigger.dev/build@4.2.0`
    -   `@trigger.dev/core@4.2.0`
    -   `@trigger.dev/schema-to-json@4.2.0`

## @trigger.dev/build@4.2.0

### Patch Changes

- syncVercelEnvVars to skip API and read env vars directly from
env.process for Vercel build environments. New syncNeonEnvVars build
extension for syncing environment variablesfrom Neon database projects
to Trigger.dev. The extension automatically detects branches and builds
appropriate PostgreSQL connection strings for non-production, non-dev
environments (staging, preview).
([#2729](#2729))
-   Updated dependencies:
    -   `@trigger.dev/core@4.2.0`

## @trigger.dev/core@4.2.0

### Patch Changes

- fix: prevent ERR_IPC_CHANNEL_CLOSED errors from causing an unhandled
exception on TaskRunProcess
([#2743](#2743))
- Added support for native build server builds in the deploy command
(`--native-build-server`)
([#2702](#2702))

## @trigger.dev/python@4.2.0

### Patch Changes

-   Updated dependencies:
    -   `@trigger.dev/build@4.2.0`
    -   `@trigger.dev/sdk@4.2.0`
    -   `@trigger.dev/core@4.2.0`

## @trigger.dev/react-hooks@4.2.0

### Patch Changes

- fix: prevent infinite useEffect when passing an array of tags to
useRealtimeRunsWithTag
([#2705](#2705))
-   Updated dependencies:
    -   `@trigger.dev/core@4.2.0`

## @trigger.dev/redis-worker@4.2.0

### Patch Changes

-   Updated dependencies:
    -   `@trigger.dev/core@4.2.0`

## @trigger.dev/rsc@4.2.0

### Patch Changes

-   Updated dependencies:
    -   `@trigger.dev/core@4.2.0`

## @trigger.dev/schema-to-json@4.2.0

### Patch Changes

-   Updated dependencies:
    -   `@trigger.dev/core@4.2.0`

## @trigger.dev/sdk@4.2.0

### Patch Changes

- fix(sdk): Re-export schemaTask types to prevent the TypeScript error
TS2742: The inferred type of 'task' cannot be named without a reference
to '@trigger.dev/core/v3'. This is likely not portable.
([#2735](#2735))
- feat: add ability to set custom resource properties through
trigger.config.ts or via the OTEL_RESOURCE_ATTRIBUTES env var
([#2704](#2704))
-   Updated dependencies:
    -   `@trigger.dev/core@4.2.0`

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
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.

2 participants

Comments