feat(cli): implements content-addressable store for the dev CLI build outputs, reducing disk usage#2725
feat(cli): implements content-addressable store for the dev CLI build outputs, reducing disk usage#2725
Conversation
… outputs, reducing disk usage
🦋 Changeset detectedLatest commit: df5ef35 The changes in this PR will be included in the next version bump. This PR includes changesets to release 26 packages
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 |
WalkthroughThis pull request adds a content-addressable store for CLI build outputs and threads an optional Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
existsSynccheck followed by asyncunlinkcreates 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:
- Catching and ignoring ENOENT errors from
unlink- Using
fs.rmwithforce: truewhich ignores missing filesApply 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
sanitizeHashForFilenameinfileSystem.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
⛔ Files ignored due to path filters (1)
references/d3-chat/src/trigger/chat.tsis 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.tspackages/cli-v3/src/utilities/tempDirectories.tspackages/cli-v3/src/utilities/fileSystem.tspackages/cli-v3/src/build/manifests.tspackages/cli-v3/src/dev/devSession.tspackages/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.tspackages/cli-v3/src/utilities/tempDirectories.tspackages/cli-v3/src/utilities/fileSystem.tspackages/cli-v3/src/build/manifests.tspackages/cli-v3/src/dev/devSession.tspackages/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.tspackages/cli-v3/src/utilities/tempDirectories.tspackages/cli-v3/src/utilities/fileSystem.tspackages/cli-v3/src/build/manifests.tspackages/cli-v3/src/dev/devSession.tspackages/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.tspackages/cli-v3/src/dev/devSession.tspackages/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.tspackages/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.mdpackages/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
getStoreDirfunction follows the same pattern as existing temporary directory utilities and correctly ensures the directory exists. The persistence across rebuilds (not cleaned up likegetTmpDirdirectories) 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
outputHashesfield properly extends the BuildManifest schema for content-addressable deduplication. The use ofz.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
storeDiris correctly threaded through all relevant function calls (createBuildManifestFromBundle,getBundleResultFromBuild, andbundleWorker), 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: trueis the correct approach for maintaining directory structure integrity.
94-98: Verify that outputHashes keys match the paths reconstructed during directory traversal.The lookup in
copyDirWithStorereconstructs paths usingjoin(source, entry.name)and uses them as keys inoutputHashes. The keys were previously set inbundle.tsusingoutputFile.pathfrom esbuild. Ensure these produce identical path strings—esbuild'soutputFile.pathwithwrite: falseshould 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
storeDirfield 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:
- Collects hashes for all output files using esbuild's built-in
outputFile.hash- Conditionally writes via
createFileWithStorewhenstoreDiris available- Falls back to regular
createFileotherwiseNote: The
outputFile.pathkeys stored inoutputHashesshould match the path format expected inmanifests.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
storeDirparameter is properly:
- Added to the function signature with appropriate JSDoc
- Included in the BuildManifest via
outputHashes- Passed through to
copyManifestToDirfor store-aware copyingThis completes the end-to-end integration of the content-addressable store.
Also applies to: 417-417, 424-424
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/cli-v3/src/build/manifests.ts (4)
9-21: Consider validating thestoreDirparameter.The function checks if
storeDiris truthy, but an empty string would pass this check yet be invalid. Consider explicitly validating thatstoreDiris 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:
- 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 }
- 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
linkErrormay 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
📒 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)); |
There was a problem hiding this comment.
🧩 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.tsRepository: 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).
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>
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:outputFile.hash), so identical content is only stored onceBuildManifest.outputHashesto avoid recomputation when copying to worker directoriesTested with 12 iterative builds in a reference project:
Backwards Compatibility
outputHashesis optional inBuildManifest- falls back to computing hashes if not presentclearTmpDirs)