Skip to content

Conversation

@myftija
Copy link
Member

@myftija myftija commented Nov 10, 2025

This PR adds logs streaming for deployments triggered by the GitHub
integration. We use S2 and its typescript sdk to do this. A short-lived
read-only token is used to read the stream directly on the client side.

logs-streaming.mov

@changeset-bot
Copy link

changeset-bot bot commented Nov 10, 2025

⚠️ No Changeset found

Latest commit: aeea736

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 10, 2025

Walkthrough

This pull request adds S2 (streamstore) support for streaming deployment logs (env schema additions, DeploymentPresenter S2 token retrieval and Redis-backed caching, loader now returns s2Logs, client-side streaming, and a LogsDisplay UI). It also introduces new DateTime props hour12?: boolean and hideDate?: boolean and threads hour12 through date/time formatting helpers, expands Paragraph visual variants with three "dimmed" sizes, adjusts a ResizablePanel min/max size, adds the @s2-dev/streamstore dependency, and enables the http2 polyfill in remix.config.js.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts: S2 initialization, Redis-backed token caching, token scopes/constraints, error handling, and added externalRef projection.
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx: loader return shape change, streaming lifecycle (AbortController), stream parsing/decoding, state management, and LogsDisplay integration.
  • apps/webapp/app/env.server.ts: new S2EnvSchema preprocess/default and discriminated union composition into EnvironmentSchema.
  • apps/webapp/app/components/primitives/DateTime.tsx: added hour12 and hideDate props and propagation through formatting helpers; verify Intl formatting behavior and defaulting.
  • apps/webapp/app/components/primitives/Paragraph.tsx: addition of new variant keys and associated styles.
  • apps/webapp/package.json and apps/webapp/remix.config.js: added dependency and polyfill configuration changes.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is largely incomplete. While it explains the feature, it lacks most required checklist items, testing details, and changelog section from the template. Complete the PR description by adding the checklist confirmations, detailed testing steps, and a comprehensive changelog entry describing the S2 integration and log streaming implementation.
Docstring Coverage ⚠️ Warning Docstring coverage is 30.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(deployments): stream build server logs' directly and clearly summarizes the main change - adding log streaming functionality for deployments.
✨ 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 build-server-logs

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: 3

🧹 Nitpick comments (2)
apps/webapp/app/components/primitives/Paragraph.tsx (1)

20-23: Remove duplicate variants and use existing ones.

The newly added variants are exact duplicates of existing variants:

  • "small/dimmed" (lines 20-23) is identical to "small" (lines 12-15)
  • "extra-small/dimmed" (lines 32-35) is identical to "extra-small" (lines 24-27)
  • "extra-small/dimmed/mono" (lines 36-39) is identical to "extra-small/mono" (lines 40-43)

Since the base variants already use text-text-dimmed, the /dimmed suffix adds no value and creates code duplication. Use the existing variant names directly in the consuming components.

</review_comment_end>

Also applies to: 32-39

apps/webapp/app/components/primitives/DateTime.tsx (1)

9-27: Remove unused hideDate prop from DateTime component's prop type or split prop types.

hideDate is included in DateTimeProps but is only used by DateTimeAccurate (line 206), not by DateTime (line 27), SmartDateTime (line 127), or DateTimeShort (line 267). Consider either removing hideDate from the shared DateTimeProps type or creating separate, more specific prop types for components that don't use this option.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fdf91a and f54ec87.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/webapp/app/components/primitives/DateTime.tsx (7 hunks)
  • apps/webapp/app/components/primitives/Paragraph.tsx (2 hunks)
  • apps/webapp/app/env.server.ts (1 hunks)
  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts (3 hunks)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (6 hunks)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx (1 hunks)
  • apps/webapp/package.json (1 hunks)
  • apps/webapp/remix.config.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations

Files:

  • apps/webapp/app/components/primitives/Paragraph.tsx
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

We use zod a lot in packages/core and in the webapp

Files:

  • apps/webapp/app/components/primitives/Paragraph.tsx
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

When importing from @trigger.dev/core in the webapp, never import the root package path; always use one of the documented subpath exports from @trigger.dev/core’s package.json

Files:

  • apps/webapp/app/components/primitives/Paragraph.tsx
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx
  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
{apps/webapp/app/**/*.server.{ts,tsx},apps/webapp/app/routes/**/*.ts}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access environment variables only via the env export from app/env.server.ts; do not reference process.env directly

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
apps/webapp/app/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Modules intended for test consumption under apps/webapp/app/**/*.ts must not read environment variables; accept configuration via options instead

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
🧠 Learnings (6)
📚 Learning: 2025-08-29T10:06:49.293Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-08-29T10:06:49.293Z
Learning: Applies to {apps/webapp/app/**/*.server.{ts,tsx},apps/webapp/app/routes/**/*.ts} : Access environment variables only via the env export from app/env.server.ts; do not reference process.env directly

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-08-29T10:06:49.293Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-08-29T10:06:49.293Z
Learning: Applies to apps/webapp/app/**/*.ts : Modules intended for test consumption under apps/webapp/app/**/*.ts must not read environment variables; accept configuration via options instead

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-08-14T12:13:20.455Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2392
File: packages/cli-v3/src/utilities/gitMeta.ts:195-218
Timestamp: 2025-08-14T12:13:20.455Z
Learning: In the GitMeta schema (packages/core/src/v3/schemas/common.ts), all fields are intentionally optional to handle partial data from various deployment contexts (local, GitHub Actions, GitHub App). Functions like getGitHubAppMeta() are designed to work with missing environment variables rather than validate their presence.

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-08-18T10:07:17.368Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-08-18T10:07:17.368Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Import Trigger.dev APIs from "trigger.dev/sdk/v3" when writing tasks or related utilities

Applied to files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
📚 Learning: 2025-08-18T10:07:17.368Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-08-18T10:07:17.368Z
Learning: Applies to trigger.config.ts : Provide a valid Trigger.dev configuration using defineConfig with project ref and dirs (e.g., ["./trigger"]; tests/specs auto-excluded)

Applied to files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
📚 Learning: 2025-10-08T11:48:12.327Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2593
File: packages/core/src/v3/workers/warmStartClient.ts:168-170
Timestamp: 2025-10-08T11:48:12.327Z
Learning: The trigger.dev runners execute only in Node 21 and 22 environments, so modern Node.js APIs like AbortSignal.any (introduced in v20.3.0) are supported.

Applied to files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
🧬 Code graph analysis (3)
apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts (1)
apps/webapp/app/env.server.ts (1)
  • env (1211-1211)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx (1)
apps/webapp/app/components/primitives/Resizable.tsx (1)
  • ResizablePanel (46-46)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (4)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/Paragraph.tsx (1)
  • Paragraph (88-107)
apps/webapp/app/components/primitives/Tooltip.tsx (4)
  • TooltipProvider (128-128)
  • Tooltip (128-128)
  • TooltipTrigger (128-128)
  • TooltipContent (128-128)
apps/webapp/app/components/primitives/DateTime.tsx (1)
  • DateTimeAccurate (200-249)
⏰ 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 (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 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 (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 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 / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 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 (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
apps/webapp/app/components/primitives/DateTime.tsx (4)

65-82: LGTM: Consistent 24-hour format enforcement.

Adding hour12: false ensures consistent 24-hour time formatting across the application.


188-198: Behavioral change: hour format now uses leading zeros.

The hour format changed from "numeric" to "2-digit" (line 190), which will display times as "09:30" instead of "9:30". This affects all components using time-only formatting (e.g., SmartDateTime and DateTimeAccurate). Verify this aligns with the desired UX.


200-249: LGTM: hideDate implementation is correct.

The conditional logic properly handles the hideDate flag by prioritizing it over the previousDate comparison. When hideDate is true, time-only formatting is consistently applied.


173-185: LGTM: Comprehensive 24-hour format enforcement.

The hour12: false addition across formatSmartDateTime, formatDateTimeAccurate, and formatDateTimeShort ensures consistent 24-hour time formatting throughout all date-time display components.

Also applies to: 251-265, 281-293

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

♻️ Duplicate comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (1)

88-156: Reset isStreaming to true when the effect re-runs.

The effect correctly includes s2Logs?.accessToken in the dependency array (line 156), but it never sets isStreaming back to true when the token rotates and a new stream session begins. This leaves the UI showing "No logs yet" (line 562) instead of "Waiting for logs..." even though streaming is active.

Apply this diff to reset the streaming state at the start of each effect run:

   useEffect(() => {
     const abortController = new AbortController();

     setLogs([]);
     setStreamError(null);
+    setIsStreaming(true);

     const streamLogs = async () => {
🧹 Nitpick comments (3)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (1)

446-450: Consider checking scroll position before auto-scrolling.

The current implementation always scrolls to the bottom when new logs arrive, which can be disruptive if users scroll up to review earlier entries. A common pattern is to auto-scroll only when the user is already near the bottom.

Example implementation:

useEffect(() => {
  if (logsContainerRef.current) {
    const { scrollTop, scrollHeight, clientHeight } = logsContainerRef.current;
    const isNearBottom = scrollHeight - scrollTop - clientHeight < 50; // 50px threshold
    if (isNearBottom) {
      logsContainerRef.current.scrollTop = scrollHeight;
    }
  }
}, [logs]);
apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts (2)

166-179: Consider adding upfront checks for S2 configuration values.

While the tryCatch wrapper prevents the page from failing, the code attempts S2 operations even when required values might be missing:

  • project.externalRef could be null (used on lines 168, 236, 244)
  • env.S2_DEPLOYMENT_LOGS_BASIN_NAME could be undefined (used on lines 174, 241)
  • env.S2_ACCESS_TOKEN could be undefined despite S2_ENABLED being "1"

Adding upfront validation avoids unnecessary API calls and produces clearer logs.

Apply this diff:

     let s2Logs = undefined;
-    if (env.S2_ENABLED === "1" && gitMetadata?.source === "trigger_github_app") {
+    if (
+      env.S2_ENABLED === "1" &&
+      env.S2_ACCESS_TOKEN &&
+      env.S2_DEPLOYMENT_LOGS_BASIN_NAME &&
+      project.externalRef &&
+      gitMetadata?.source === "trigger_github_app"
+    ) {
       const [error, accessToken] = await tryCatch(this.getS2AccessToken(project.externalRef));

Based on learnings


236-236: Use Date.now() for cleaner timestamp generation.

-      id: `${projectRef}-${new Date().getTime()}`,
+      id: `${projectRef}-${Date.now()}`,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 285a5fa and 7d0ef6e.

📒 Files selected for processing (2)
  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts (4 hunks)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations

Files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

We use zod a lot in packages/core and in the webapp

Files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

When importing from @trigger.dev/core in the webapp, never import the root package path; always use one of the documented subpath exports from @trigger.dev/core’s package.json

Files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
{apps/webapp/app/**/*.server.{ts,tsx},apps/webapp/app/routes/**/*.ts}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access environment variables only via the env export from app/env.server.ts; do not reference process.env directly

Files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
apps/webapp/app/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Modules intended for test consumption under apps/webapp/app/**/*.ts must not read environment variables; accept configuration via options instead

Files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2663
File: apps/webapp/app/env.server.ts:1205-1206
Timestamp: 2025-11-10T09:09:07.392Z
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.
📚 Learning: 2025-11-10T09:09:07.392Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2663
File: apps/webapp/app/env.server.ts:1205-1206
Timestamp: 2025-11-10T09:09:07.392Z
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/webapp/app/presenters/v3/DeploymentPresenter.server.ts
📚 Learning: 2025-08-18T10:07:17.368Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-08-18T10:07:17.368Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Inside tasks, prefer logger.debug/log/info/warn/error over ad-hoc console logging for structured logs

Applied to files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
📚 Learning: 2025-08-18T10:07:17.368Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-08-18T10:07:17.368Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Import Trigger.dev APIs from "trigger.dev/sdk/v3" when writing tasks or related utilities

Applied to files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
📚 Learning: 2025-10-08T11:48:12.327Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2593
File: packages/core/src/v3/workers/warmStartClient.ts:168-170
Timestamp: 2025-10-08T11:48:12.327Z
Learning: The trigger.dev runners execute only in Node 21 and 22 environments, so modern Node.js APIs like AbortSignal.any (introduced in v20.3.0) are supported.

Applied to files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
📚 Learning: 2025-08-14T12:13:20.455Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2392
File: packages/cli-v3/src/utilities/gitMeta.ts:195-218
Timestamp: 2025-08-14T12:13:20.455Z
Learning: In the GitMeta schema (packages/core/src/v3/schemas/common.ts), all fields are intentionally optional to handle partial data from various deployment contexts (local, GitHub Actions, GitHub App). Functions like getGitHubAppMeta() are designed to work with missing environment variables rather than validate their presence.

Applied to files:

  • apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
🧬 Code graph analysis (2)
apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts (2)
apps/webapp/app/env.server.ts (1)
  • env (1230-1230)
apps/webapp/app/presenters/v3/BranchesPresenter.server.ts (1)
  • processGitMetadata (198-254)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (2)
apps/webapp/app/components/primitives/Tooltip.tsx (4)
  • TooltipProvider (128-128)
  • Tooltip (128-128)
  • TooltipTrigger (128-128)
  • TooltipContent (128-128)
apps/webapp/app/components/primitives/DateTime.tsx (1)
  • DateTimeAccurate (200-249)
⏰ 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 (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • 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 (4)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (2)

44-67: LGTM! Loader correctly returns s2Logs alongside deployment.

The loader properly destructures both deployment and s2Logs from the presenter and returns them via typedjson. This enables the client-side streaming logic to access S2 credentials when available.


425-609: LGTM! LogsDisplay component is well-structured.

The component provides a clean UI for streaming logs with:

  • Collapse/expand functionality tied to deployment status
  • Copy-to-clipboard with visual feedback
  • Error and warning counts with color-coded indicators
  • Level-based styling (error/warn/info) with appropriate colors
  • Proper timestamp rendering using DateTimeAccurate with hideDate
apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts (2)

1-31: LGTM! S2 client initialization is properly guarded.

The conditional creation of the S2 client (line 31) based on env.S2_ENABLED and the Redis-backed token cache setup follow good practices for optional feature integration.


223-256: LGTM! Token caching strategy is sound.

The Redis-backed caching with a 59-minute TTL (slightly shorter than the 1-hour token validity) prevents cache/token skew and reduces API calls to S2. The scope restrictions (read-only, basin/stream prefix constraints) follow the principle of least privilege.

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

🧹 Nitpick comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (1)

568-601: Consider using a stable identifier instead of index for the key prop.

While using index as a key (line 571) is safe for append-only logs, it's generally discouraged as it can cause issues if logs are ever filtered, reordered, or have items removed. Consider using a combination of timestamp and index, or add a unique identifier to each log entry.

Example:

-                <div
-                  key={index}
+                <div
+                  key={`${log.timestamp.getTime()}-${index}`}
                  className={cn(

Alternatively, if S2 provides a sequence number in the record, use that:

type LogEntry = {
  message: string;
  timestamp: Date;
  level: "info" | "error" | "warn";
  seqNum: number; // Add sequence number from S2 record
};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d0ef6e and 1daa07e.

📒 Files selected for processing (1)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

We use zod a lot in packages/core and in the webapp

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

When importing from @trigger.dev/core in the webapp, never import the root package path; always use one of the documented subpath exports from @trigger.dev/core’s package.json

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
🧬 Code graph analysis (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (4)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/Paragraph.tsx (1)
  • Paragraph (88-107)
apps/webapp/app/components/primitives/Tooltip.tsx (4)
  • TooltipProvider (128-128)
  • Tooltip (128-128)
  • TooltipTrigger (128-128)
  • TooltipContent (128-128)
apps/webapp/app/components/primitives/DateTime.tsx (1)
  • DateTimeAccurate (200-249)
⏰ 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). (17)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (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 (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: typecheck / typecheck
🔇 Additional comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (1)

88-157: LGTM! Past feedback has been addressed.

The streaming effect now correctly:

  • Includes s2Logs?.accessToken in the dependency array (line 157), ensuring the stream restarts when the token rotates
  • Calls setIsStreaming(true) at the start (line 95), ensuring the UI reflects an active stream

The implementation properly handles cleanup with AbortController, uses functional state updates for concurrent safety, and gracefully handles edge cases (stream not found, aborted requests).

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)
apps/webapp/app/components/primitives/DateTime.tsx (1)

40-47: Propagate hour12 into tooltip to keep display consistent.

The tooltip always renders 12‑hour (defaults) regardless of the parent’s hour12, causing mismatched formats between the button and tooltip. Pass hour12 into TooltipContent and through its formatDateTime calls.

Apply these diffs (call sites):

   const tooltipContent = (
     <TooltipContent
       realDate={realDate}
       timeZone={timeZone}
       localTimeZone={localTimeZone}
       locales={locales}
+      hour12={hour12}
     />
   );
   const tooltipContent = (
     <TooltipContent
       realDate={realDate}
       timeZone={timeZone}
       localTimeZone={localTimeZone}
       locales={locales}
+      hour12={hour12}
     />
   );

And update TooltipContent:

 function TooltipContent({
   realDate,
   timeZone,
   localTimeZone,
   locales,
+  hour12,
 }: {
   realDate: Date;
   timeZone?: string;
   localTimeZone: string;
   locales: string[];
+  hour12?: boolean;
 }) {
   return (
     <div className="flex flex-col gap-1">
       <div className="flex flex-col gap-2.5 pb-1">
         {timeZone && timeZone !== "UTC" && (
           <DateTimeTooltipContent
             title={timeZone}
-            dateTime={formatDateTime(realDate, timeZone, locales, true, true)}
+            dateTime={formatDateTime(realDate, timeZone, locales, true, true, hour12)}
             isoDateTime={formatDateTimeISO(realDate, timeZone)}
             icon={<GlobeAmericasIcon className="size-4 text-purple-500" />}
           />
         )}
         <DateTimeTooltipContent
           title="UTC"
-          dateTime={formatDateTime(realDate, "UTC", locales, true, true)}
+          dateTime={formatDateTime(realDate, "UTC", locales, true, true, hour12)}
           isoDateTime={formatDateTimeISO(realDate, "UTC")}
           icon={<GlobeAltIcon className="size-4 text-blue-500" />}
         />
         <DateTimeTooltipContent
           title="Local"
-          dateTime={formatDateTime(realDate, localTimeZone, locales, true, true)}
+          dateTime={formatDateTime(realDate, localTimeZone, locales, true, true, hour12)}
           isoDateTime={formatDateTimeISO(realDate, localTimeZone)}
           icon={<Laptop className="size-4 text-green-500" />}
         />

Also applies to: 239-245

♻️ Duplicate comments (1)
apps/webapp/app/components/primitives/DateTime.tsx (1)

18-19: Make hour12 opt‑in; don’t override locale defaults by default.

Defaulting hour12 = true forces 12‑hour clocks globally (regression for 24h locales). Instead, keep hour12 optional and only include it in Intl.DateTimeFormat when explicitly provided. Also propagate this change consistently across all helpers. This keeps backward compatibility for callers that pass hour12, while preserving locale behavior when they don’t.
(Impacts all places timestamps render.)

Apply these diffs:

- export const DateTime = ({
+ export const DateTime = ({
   date,
   timeZone,
   includeSeconds = true,
   includeTime = true,
   showTimezone = false,
   showTooltip = true,
-  hour12 = true,
+  hour12,
 }: DateTimeProps) => {
-export function formatDateTime(
+export function formatDateTime(
   date: Date,
   timeZone: string,
   locales: string[],
   includeSeconds: boolean,
-  includeTime: boolean,
-  hour12: boolean = true
+  includeTime: boolean,
+  hour12?: boolean
 ): string {
-  return new Intl.DateTimeFormat(locales, {
+  return new Intl.DateTimeFormat(locales, {
     year: "numeric",
     month: "short",
     day: "numeric",
     hour: includeTime ? "numeric" : undefined,
     minute: includeTime ? "numeric" : undefined,
     second: includeTime && includeSeconds ? "numeric" : undefined,
     timeZone,
-    hour12,
+    ...(hour12 !== undefined ? { hour12 } : {}),
   }).format(date);
 }
-export const SmartDateTime = ({ date, previousDate = null, timeZone = "UTC", hour12 = true }: DateTimeProps) => {
+export const SmartDateTime = ({ date, previousDate = null, timeZone = "UTC", hour12 }: DateTimeProps) => {
-function formatSmartDateTime(date: Date, timeZone: string, locales: string[], hour12: boolean = true): string {
+function formatSmartDateTime(date: Date, timeZone: string, locales: string[], hour12?: boolean): string {
   return new Intl.DateTimeFormat(locales, {
     month: "short",
     day: "numeric",
     hour: "numeric",
     minute: "numeric",
     second: "numeric",
     timeZone,
     // @ts-ignore fractionalSecondDigits works in most modern browsers
     fractionalSecondDigits: 3,
-    hour12,
+    ...(hour12 !== undefined ? { hour12 } : {}),
   }).format(date);
 }
-function formatTimeOnly(date: Date, timeZone: string, locales: string[], hour12: boolean = true): string {
+function formatTimeOnly(date: Date, timeZone: string, locales: string[], hour12?: boolean): string {
   return new Intl.DateTimeFormat(locales, {
     hour: "2-digit",
     minute: "numeric",
     second: "numeric",
     timeZone,
     // @ts-ignore fractionalSecondDigits works in most modern browsers
     fractionalSecondDigits: 3,
-    hour12,
+    ...(hour12 !== undefined ? { hour12 } : {}),
   }).format(date);
 }
 export const DateTimeAccurate = ({
   date,
   timeZone = "UTC",
   previousDate = null,
   showTooltip = true,
   hideDate = false,
-  hour12 = true,
+  hour12,
 }: DateTimeProps) => {
-function formatDateTimeAccurate(date: Date, timeZone: string, locales: string[], hour12: boolean = true): string {
+function formatDateTimeAccurate(date: Date, timeZone: string, locales: string[], hour12?: boolean): string {
   const formattedDateTime = new Intl.DateTimeFormat(locales, {
     month: "short",
     day: "numeric",
     hour: "numeric",
     minute: "numeric",
     second: "numeric",
     timeZone,
     // @ts-ignore fractionalSecondDigits works in most modern browsers
     fractionalSecondDigits: 3,
-    hour12,
+    ...(hour12 !== undefined ? { hour12 } : {}),
   }).format(date);
-export const DateTimeShort = ({ date, timeZone = "UTC", hour12 = true }: DateTimeProps) => {
+export const DateTimeShort = ({ date, timeZone = "UTC", hour12 }: DateTimeProps) => {
-function formatDateTimeShort(date: Date, timeZone: string, locales: string[], hour12: boolean = true): string {
+function formatDateTimeShort(date: Date, timeZone: string, locales: string[], hour12?: boolean): string {
   const formattedDateTime = new Intl.DateTimeFormat(locales, {
     hour: "numeric",
     minute: "numeric",
     second: "numeric",
     timeZone,
     // @ts-ignore fractionalSecondDigits works in most modern browsers
     fractionalSecondDigits: 3,
-    hour12,
+    ...(hour12 !== undefined ? { hour12 } : {}),
   }).format(date);
 }

Also applies to: 28-29, 74-76, 84-85, 131-131, 177-178, 187-188, 192-193, 200-201, 209-211, 256-258, 266-267, 272-272, 286-287, 294-295

🧹 Nitpick comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (2)

105-105: Consider tracking the last sequence number to avoid re-fetching all logs.

Starting from seq_num: 0 on every reconnection (e.g., token rotation) means re-reading all logs from the beginning. For short build logs this is acceptable, but you could optimize by tracking the last sequence number read and resuming from there.


571-571: Consider using a more stable key for log entries.

Using index as the key works for append-only logs, but could cause React to reuse DOM nodes incorrectly when logs are cleared and refilled (line 93). Consider using a composite key like ${log.timestamp.getTime()}-${index} for better stability, though the current implementation should function correctly since all content is derived from state.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1daa07e and aeea736.

📒 Files selected for processing (2)
  • apps/webapp/app/components/primitives/DateTime.tsx (14 hunks)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations

Files:

  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

We use zod a lot in packages/core and in the webapp

Files:

  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

When importing from @trigger.dev/core in the webapp, never import the root package path; always use one of the documented subpath exports from @trigger.dev/core’s package.json

Files:

  • apps/webapp/app/components/primitives/DateTime.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx
🧬 Code graph analysis (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (2)
apps/webapp/app/components/primitives/Tooltip.tsx (4)
  • TooltipProvider (128-128)
  • Tooltip (128-128)
  • TooltipTrigger (128-128)
  • TooltipContent (128-128)
apps/webapp/app/components/primitives/DateTime.tsx (1)
  • DateTimeAccurate (204-254)
⏰ 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 (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 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 (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx (7)

4-6: LGTM!

The new imports support the S2 streaming integration and logs UI. All additions are used appropriately in the streaming logic and LogsDisplay component.

Also applies to: 28-33


44-67: LGTM!

The loader correctly extends the return payload to include s2Logs from the presenter, enabling client-side log streaming.


69-73: LGTM!

The LogEntry type is well-defined with a discriminated level union, providing type safety for log parsing and rendering. Correctly uses type over interface as per guidelines.


88-157: Previous review concerns addressed.

The effect now correctly includes s2Logs.accessToken in the dependency array (line 157) and sets isStreaming(true) at the start (line 95), ensuring the stream restarts with fresh tokens and the UI reflects the active streaming state.


140-141: Verify silent handling of missing streams is intentional.

The code silently ignores stream_not_found errors, which may occur when the build hasn't started producing logs yet. Please confirm this is the expected behavior and that users won't be confused by the absence of error feedback in legitimate failure scenarios.


250-262: LGTM!

The conditional rendering ensures logs are only displayed when the feature is enabled. The initialCollapsed logic provides a sensible default: collapsing logs for successful or pending deployments while expanding them for failed states to highlight errors.


426-610: LGTM!

The LogsDisplay component is well-implemented with thoughtful UX:

  • Auto-scrolls to new logs for live streaming
  • Copy-to-clipboard with visual feedback
  • Color-coded log levels (info/warn/error) with counts
  • Collapse/expand controls with smooth transitions
  • Appropriate use of DateTimeAccurate with hideDate and hour12={false} for 24-hour log timestamps

@myftija myftija merged commit 42f53b1 into main Nov 10, 2025
31 checks passed
@myftija myftija deleted the build-server-logs branch November 10, 2025 11:42
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