Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-console-interceptor-2900.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/core": patch
---

Fix: ConsoleInterceptor now delegates to original console methods to preserve log chain when other interceptors (like Sentry) are present. (#2900)
5 changes: 5 additions & 0 deletions .changeset/fix-docker-hub-rate-limit-2911.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli-v3": patch
---

Fix: Native build server failed with Docker Hub rate limits. Added support for checking checking `DOCKER_USERNAME` and `DOCKER_PASSWORD` in environment variables and logging into Docker Hub before building. (#2911)
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 | 🟡 Minor

Typo: "checking checking" — duplicate word.

-Fix: Native build server failed with Docker Hub rate limits. Added support for checking checking `DOCKER_USERNAME` and `DOCKER_PASSWORD` in environment variables and logging into Docker Hub before building. (`#2911`)
+Fix: Native build server failed with Docker Hub rate limits. Added support for checking `DOCKER_USERNAME` and `DOCKER_PASSWORD` in environment variables and logging into Docker Hub before building. (`#2911`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Fix: Native build server failed with Docker Hub rate limits. Added support for checking checking `DOCKER_USERNAME` and `DOCKER_PASSWORD` in environment variables and logging into Docker Hub before building. (#2911)
Fix: Native build server failed with Docker Hub rate limits. Added support for checking `DOCKER_USERNAME` and `DOCKER_PASSWORD` in environment variables and logging into Docker Hub before building. (`#2911`)
🤖 Prompt for AI Agents
In @.changeset/fix-docker-hub-rate-limit-2911.md at line 5, The changelog line
contains a duplicated word "checking checking"; edit the summary in
.changeset/fix-docker-hub-rate-limit-2911.md to remove the duplicate so it reads
"...support for checking `DOCKER_USERNAME` and `DOCKER_PASSWORD` in environment
variables and logging into Docker Hub before building." Ensure no other
duplicated words remain in that sentence.

5 changes: 5 additions & 0 deletions .changeset/fix-github-install-node-version-2913.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli-v3": patch
---

Fix: Ignore engine checks during deployment install phase to prevent failure on build server when Node version mismatch exists. (#2913)
5 changes: 5 additions & 0 deletions .changeset/fix-orphaned-workers-2909.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli-v3": patch
---

Fix: `trigger.dev dev` command left orphaned worker processes when exited via Ctrl+C (SIGINT). Added signal handlers to ensure proper cleanup of child processes and lockfiles. (#2909)
5 changes: 5 additions & 0 deletions .changeset/fix-sentry-oom-2920.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli-v3": patch
---

Fix Sentry OOM: Allow disabling `source-map-support` via `TRIGGER_SOURCE_MAPS=false`. Also supports `node` for native source maps. (#2920)
53 changes: 53 additions & 0 deletions PR_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Pull Request: Consolidated Fixes

## Title
fix: consolidated fixes for orphaned workers, Sentry OOM, and Docker Hub rate limits

## Description (copy this to GitHub PR)

### Summary
This PR consolidates several bug fixes for the CLI and core packages.

### Fixes Included
- **#2909**: Ensure worker cleanup on SIGINT/SIGTERM to prevent orphaned processes
- **#2920**: Allow disabling source-map-support to prevent OOM with Sentry
- **#2913**: Fix GitHub Actions node version compatibility during deploys
- **#2911**: Authenticate to Docker Hub to prevent rate limits
- **#2900**: Fix Sentry console log interception

### Changes

#### `packages/cli-v3/src/commands/dev.ts`
- Added SIGINT/SIGTERM signal handlers for proper worker cleanup on dev server exit

#### `packages/cli-v3/src/commands/update.ts`
- Cleaned up incompatible code for better maintainability

#### `packages/cli-v3/src/cli/common.ts`
- Added `ignoreEngines` option to CommonCommandOptions schema

#### `packages/cli-v3/src/commands/login.ts`
- Fixed missing `ignoreEngines` property in whoAmI calls

#### `packages/cli-v3/src/entryPoints/dev-run-worker.ts` & `managed-run-worker.ts`
- Added missing imports: `env`, `normalizeImportPath`, `VERSION`, `promiseWithResolvers`

#### `packages/core/src/v3/consoleInterceptor.ts`
- Fixed console interceptor to properly delegate to original methods (Sentry compatibility)

### Testing
- ✅ Local typecheck passes
- ✅ Unit tests pass for affected packages

---

## Instructions

1. Go to: https://github.com/deepshekhardas/trigger.dev
2. Click **"Contribute"** → **"Open pull request"**
3. Ensure:
- Base: `triggerdotdev/trigger.dev` : `main`
- Compare: `deepshekhardas/trigger.dev` : `main`
4. Copy the **Title** above into the PR title field
5. Copy the **Description** section above into the PR body
Comment on lines +1 to +52
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 | 🟡 Minor

This file should not be committed to the repository.

PR_DESCRIPTION.md contains PR creation instructions (e.g., "Go to GitHub", "Click Contribute") that are ephemeral meta-information. This belongs in the PR description on GitHub, not as a tracked file in the repo. Please remove it before merging.

🤖 Prompt for AI Agents
In `@PR_DESCRIPTION.md` around lines 1 - 52, PR_DESCRIPTION.md is an ephemeral PR
creation instruction file that should not be committed; remove PR_DESCRIPTION.md
from the branch (delete the file and stage the removal so it is not included in
the PR), update the commit/branch accordingly (amend or create a new commit),
and ensure no similar ephemeral instruction files are committed in future.

6. Click **"Create pull request"**
2 changes: 1 addition & 1 deletion apps/webapp/app/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ const EnvironmentSchema = z
REALTIME_MAXIMUM_CREATED_AT_FILTER_AGE_IN_MS: z.coerce
.number()
.int()
.default(24 * 60 * 60 * 1000), // 1 day in milliseconds
.default(30 * 24 * 60 * 60 * 1000), // 30 days in milliseconds

PUBSUB_REDIS_HOST: z
.string()
Expand Down
1 change: 0 additions & 1 deletion apps/webapp/test/realtimeClient.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { containerWithElectricAndRedisTest } from "@internal/testcontainers";
import { expect, describe } from "vitest";
import { RealtimeClient } from "../app/services/realtimeClient.server.js";
import Redis from "ioredis";
Expand Down
127 changes: 127 additions & 0 deletions apps/webapp/test/realtimeClientFilter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { describe, test, expect, vi, beforeEach } from "vitest";
import { RealtimeClient } from "../app/services/realtimeClient.server";

// Hoist mocks
const mocks = vi.hoisted(() => ({
longPollingFetch: vi.fn(),
createRedisClient: vi.fn(() => ({
defineCommand: vi.fn(),
on: vi.fn(),
})),
}));

vi.mock("~/utils/longPollingFetch", () => ({
longPollingFetch: mocks.longPollingFetch,
}));

vi.mock("~/redis.server", () => ({
createRedisClient: mocks.createRedisClient,
}));

vi.mock("../app/services/unkey/redisCacheStore.server", () => ({
RedisCacheStore: vi.fn(),
}));

vi.mock("@unkey/cache", () => ({
createCache: vi.fn(() => ({
get: vi.fn(),
set: vi.fn(),
})),
DefaultStatefulContext: vi.fn(),
Namespace: vi.fn(),
}));

// Mock env.server to set the limit to 30 days
vi.mock("~/env.server", () => ({
env: {
REALTIME_MAXIMUM_CREATED_AT_FILTER_AGE_IN_MS: 30 * 24 * 60 * 60 * 1000,
},
}));
Comment on lines +1 to +39
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Test relies heavily on mocks and imports env.server — contrary to project guidelines.

The coding guidelines state:

  1. "Tests should avoid mocks or stubs and use the helpers from @internal/testcontainers when Redis or Postgres are needed" — this file has 5 vi.mock calls.
  2. "For testable code in the webapp, never import env.server.ts in test files — pass configuration as options instead" — line 35-39 mocks env.server rather than injecting REALTIME_MAXIMUM_CREATED_AT_FILTER_AGE_IN_MS via a constructor/options parameter.

The recommended approach is to refactor RealtimeClient (or the filter-computing method) to accept the maximum age as a configuration option, then pass it directly in the test constructor instead of mocking the module. This would also let you drop most of the other mocks if the filtering logic were extracted into a standalone, pure function that's independently testable.

As per coding guidelines: "Test files should only import classes and functions from app/**/*.ts files and should not import env.server.ts directly or indirectly; pass configuration through options instead" and "Use testcontainers helpers instead of mocks".

🤖 Prompt for AI Agents
In `@apps/webapp/test/realtimeClientFilter.test.ts` around lines 1 - 39, Refactor
RealtimeClient so the created-at age limit is injected rather than read from
env.server: extract the filter logic into a pure function (e.g.,
computeCreatedAtFilter(maxAgeMs, referenceDate?)) or add a constructor/options
param (e.g., RealtimeClient({ maxCreatedAtAgeMs })) that
RealtimeClient.getCreatedAtFilter uses; update tests to call the pure function
directly or instantiate RealtimeClient with a numeric maxCreatedAtAgeMs and
remove the vi.mock of "~/env.server" and the related env constant
REALTIME_MAXIMUM_CREATED_AT_FILTER_AGE_IN_MS, and where Redis/Postgres are
needed replace mocks with `@internal/testcontainers` helpers so tests no longer
depend on module mocks.


describe("RealtimeClient Filter Logic", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.longPollingFetch.mockResolvedValue({
ok: true,
status: 200,
headers: new Map(),
json: async () => [],
text: async () => "",
});
Comment on lines +44 to +50
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 | 🟡 Minor

Mock response headers should be a Headers instance, not a Map.

If RealtimeClient (or longPollingFetch consumers) calls .get() on the response headers expecting the standard Headers API, a Map will work by coincidence for .get(), but .has(), iteration, and other Headers-specific methods differ. Use new Headers() to avoid subtle breakage.

Proposed fix
         mocks.longPollingFetch.mockResolvedValue({
             ok: true,
             status: 200,
-            headers: new Map(),
+            headers: new Headers(),
             json: async () => [],
             text: async () => "",
         });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mocks.longPollingFetch.mockResolvedValue({
ok: true,
status: 200,
headers: new Map(),
json: async () => [],
text: async () => "",
});
mocks.longPollingFetch.mockResolvedValue({
ok: true,
status: 200,
headers: new Headers(),
json: async () => [],
text: async () => "",
});
🤖 Prompt for AI Agents
In `@apps/webapp/test/realtimeClientFilter.test.ts` around lines 44 - 50, The mock
response currently sets headers to a Map which can mask bugs; update the mock
resolved value inside mocks.longPollingFetch.mockResolvedValue to use a proper
Headers instance (e.g., new Headers()) instead of new Map() so consumers like
RealtimeClient or longPollingFetch get the standard Headers API
(has/get/forEach/entries) and avoid subtle mismatch in header behavior.

});

test("should allow createdAt filter > 24h (e.g. 8d)", async () => {
const client = new RealtimeClient({
electricOrigin: "http://electric",
redis: { host: "localhost", port: 6379, tlsDisabled: true } as any,
keyPrefix: "test",
cachedLimitProvider: { getCachedLimit: async () => 100 },
});

// Request 8 days ago
await client.streamRuns(
"http://remix-app",
{ id: "env-1", organizationId: "org-1" },
{ createdAt: "8d" },
"2024-01-01"
);

const callArgs = mocks.longPollingFetch.mock.calls[0];
const urlToCheck = callArgs[0] as string;
const url = new URL(urlToCheck);
const where = url.searchParams.get("where");

// Check for "createdAt" > '...'
const match = where?.match(/"createdAt" > '([^']+)'/);
expect(match).toBeTruthy();

const dateStr = match?.[1];
const date = new Date(dateStr!);

const now = Date.now();
const diff = now - date.getTime();
const days = diff / (24 * 60 * 60 * 1000);

// It should be close to 8 days.
expect(days).toBeCloseTo(8, 0); // 0 digits precision is enough (e.g. 7.99 or 8.01)
expect(days).toBeGreaterThan(7.9);
expect(days).toBeLessThan(8.1);
});

test("should clamp createdAt filter > 30d", async () => {
const client = new RealtimeClient({
electricOrigin: "http://electric",
redis: { host: "localhost", port: 6379, tlsDisabled: true } as any,
keyPrefix: "test",
cachedLimitProvider: { getCachedLimit: async () => 100 },
});

// Request 60 days ago
await client.streamRuns(
"http://remix-app",
{ id: "env-1", organizationId: "org-1" },
{ createdAt: "60d" },
"2024-01-01"
);

const callArgs = mocks.longPollingFetch.mock.calls[0];
const urlToCheck = callArgs[0] as string;
const url = new URL(urlToCheck);
const where = url.searchParams.get("where");

const match = where?.match(/"createdAt" > '([^']+)'/);
expect(match).toBeTruthy();

const dateStr = match?.[1];
const date = new Date(dateStr!);

const now = Date.now();
const diff = now - date.getTime();
const days = diff / (24 * 60 * 60 * 1000);

// It should be clamped to 30 days.
expect(days).toBeCloseTo(30, 0);
expect(days).toBeGreaterThan(29.9);
expect(days).toBeLessThan(30.1);
});
});
Binary file added apps/webapp/test_output.txt
Binary file not shown.
40 changes: 40 additions & 0 deletions consolidated_pr_body.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Consolidated Bug Fixes

This PR combines fixes for several independent issues identified in the codebase, covering CLI stability, deployment/build reliability, and runtime correctness.

## Fixes

| Issue / Feature | Description |
|-----------------|-------------|
| **Orphaned Workers** | Fixes `trigger dev` leaving orphaned `trigger-dev-run-worker` processes by ensuring graceful shutdown on `SIGINT`/`SIGTERM` and robust process cleanup. |
| **Sentry Interception** | Fixes `ConsoleInterceptor` swallowing logs when Sentry (or other monkey-patchers) are present by delegating to the original preserved console methods. |
| **Engine Strictness** | Fixes deployment failures on GitHub Integration when `engines.node` is strict (e.g. "22") by passing `--no-engine-strict` (and equivalents) during the `trigger deploy` build phase. |
| **Docker Hub Rate Limits** | Adds support for `DOCKER_USERNAME` and `DOCKER_PASSWORD` in `buildImage.ts` to authenticate with Docker Hub and avoid rate limits during native builds. |
| **Dead Process Hang** | Fixes a hang in `TaskRunProcess.execute()` by checking specific process connectivity before attempting to send IPC messages. |
| **Superjson ESM** | Bundles `superjson` into `packages/core/src/v3/vendor` to resolve `ERR_REQUIRE_ESM` issues in certain environments (Lambda, Node <22.12). |
| **Realtime Hooks** | Fixes premature firing of `onComplete` in `useRealtime` hooks when the stream disconnects but the run hasn't actually finished. |
| **Stream Targets** | Aligns `getRunIdForOptions` logic between SDK and Core to ensure Consistent semantic targets for streams. |
| **Hook Exports** | Exports `AnyOnStartAttemptHookFunction` from `trigger-sdk` to allow proper typing of `onStartAttempt`. |

## Verification

### Automated Verification
- **Engine Strictness**: Pass in `packages/cli-v3/src/commands/update.test.ts`.
- **Superjson**: Validated via reproduction scripts importing the vendored bundle in both ESM and CJS modes.
- **Sentry**: Validated via `repro_2900_sentry.ts` script ensuring logs flow through Sentry patches.

### Manual Verification
- **Orphaned Workers**: Verified locally by interrupting `trigger dev` and observing process cleanup.
- **Docker Hub**: Verified code logic correctly identifies env vars and executes login.
- **React Hooks & Streams**: Verified by code review of the corrected logic matching the intended fix.

## Changesets
- `fix-orphaned-workers-2909`
- `fix-sentry-console-interceptor-2900`
- `fix-github-install-node-version-2913`
- `fix-docker-hub-rate-limit-2911`
- `fix-dead-process-execute-hang`
- `vendor-superjson-esm-fix`
- `calm-hooks-wait`
- `consistent-stream-targets`
- `export-start-attempt-hook-type`
7 changes: 4 additions & 3 deletions packages/cli-v3/src/cli/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const CommonCommandOptions = z.object({
logLevel: z.enum(["debug", "info", "log", "warn", "error", "none"]).default("log"),
skipTelemetry: z.boolean().default(false),
profile: z.string().default(readAuthConfigCurrentProfileName()),
ignoreEngines: z.boolean().default(false),
});

export type CommonCommandOptions = z.infer<typeof CommonCommandOptions>;
Expand All @@ -30,9 +31,9 @@ export function commonOptions(command: Command) {
.option("--skip-telemetry", "Opt-out of sending telemetry");
}

export class SkipLoggingError extends Error {}
export class SkipCommandError extends Error {}
export class OutroCommandError extends SkipCommandError {}
export class SkipLoggingError extends Error { }
export class SkipCommandError extends Error { }
export class OutroCommandError extends SkipCommandError { }

export async function handleTelemetry(action: () => Promise<void>) {
try {
Expand Down
Loading