Skip to content

Commit c938bfe

Browse files
authored
fix: prevent invalid render output for build logs (#17233)
## Changes made - Updated `Line` type in `LogLine.tsx` to support an ID value to prevent key conflicts during React rendering. Also deleted the `LineWithID` type, which became redundant after the change - Updated the `Logs` component to use the ID to avoid render key conflicts - Updated any component calls to add the ID as a prop ## Notes - This does prevent a bunch of extra `console.error` calls that React will automatically spit out, so this should help us a good bit in the future - Beyond being a little annoying, there was a chance (that was tiny for now) that React could accidentally mix up component instances during re-renders. That wasn't my main goal with this PR (I just wanted less noisy logs), but that should now be impossible
1 parent 0fe7346 commit c938bfe

File tree

9 files changed

+17
-20
lines changed

9 files changed

+17
-20
lines changed

site/src/components/Logs/LogLine.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { MONOSPACE_FONT_FAMILY } from "theme/constants";
66
export const DEFAULT_LOG_LINE_SIDE_PADDING = 24;
77

88
export interface Line {
9+
id: number;
910
time: string;
1011
output: string;
1112
level: LogLevel;

site/src/components/Logs/Logs.stories.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import type { Meta, StoryObj } from "@storybook/react";
22
import { chromatic } from "testHelpers/chromatic";
33
import { MockWorkspaceBuildLogs } from "testHelpers/entities";
4+
import type { Line } from "./LogLine";
45
import { Logs } from "./Logs";
56

67
const meta: Meta<typeof Logs> = {
78
title: "components/Logs",
89
parameters: { chromatic },
910
component: Logs,
1011
args: {
11-
lines: MockWorkspaceBuildLogs.map((log) => ({
12+
lines: MockWorkspaceBuildLogs.map<Line>((log) => ({
13+
id: log.id,
1214
level: log.log_level,
1315
time: log.created_at,
1416
output: log.output,

site/src/components/Logs/Logs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const Logs: FC<LogsProps> = ({
2020
<div css={styles.root} className={`${className} logs-container`}>
2121
<div css={{ minWidth: "fit-content" }}>
2222
{lines.map((line) => (
23-
<LogLine key={line.output} level={line.level}>
23+
<LogLine key={line.id} level={line.level}>
2424
{!hideTimestamps && (
2525
<LogLinePrefix>
2626
{dayjs(line.time).format("HH:mm:ss.SSS")}

site/src/modules/resources/AgentLogs/AgentLogLine.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,6 @@ import AnsiToHTML from "ansi-to-html";
33
import { type Line, LogLine, LogLinePrefix } from "components/Logs/LogLine";
44
import { type FC, type ReactNode, useMemo } from "react";
55

6-
// Logs are stored as the Line interface to make rendering
7-
// much more efficient. Instead of mapping objects each time, we're
8-
// able to just pass the array of logs to the component.
9-
export interface LineWithID extends Line {
10-
id: number;
11-
}
12-
136
// Approximate height of a log line. Used to control virtualized list height.
147
export const AGENT_LOG_LINE_HEIGHT = 20;
158

site/src/modules/resources/AgentLogs/AgentLogs.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import type { Interpolation, Theme } from "@emotion/react";
22
import Tooltip from "@mui/material/Tooltip";
33
import type { WorkspaceAgentLogSource } from "api/typesGenerated";
4+
import type { Line } from "components/Logs/LogLine";
45
import { type ComponentProps, forwardRef, useMemo } from "react";
56
import { FixedSizeList as List } from "react-window";
6-
import {
7-
AGENT_LOG_LINE_HEIGHT,
8-
AgentLogLine,
9-
type LineWithID,
10-
} from "./AgentLogLine";
7+
import { AGENT_LOG_LINE_HEIGHT, AgentLogLine } from "./AgentLogLine";
118

129
type AgentLogsProps = Omit<
1310
ComponentProps<typeof List>,
1411
"children" | "itemSize" | "itemCount"
1512
> & {
16-
logs: readonly LineWithID[];
13+
logs: readonly Line[];
1714
sources: readonly WorkspaceAgentLogSource[];
1815
};
1916

site/src/modules/resources/AgentLogs/mocks.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Those mocks are fetched from the Coder API in dev.coder.com
22

3-
import type { LineWithID } from "./AgentLogLine";
3+
import type { Line } from "components/Logs/LogLine";
44

55
export const MockSources = [
66
{
@@ -1128,4 +1128,4 @@ export const MockLogs = [
11281128
time: "2024-03-14T11:31:10.859531Z",
11291129
sourceId: "d9475581-8a42-4bce-b4d0-e4d2791d5c98",
11301130
},
1131-
] satisfies LineWithID[];
1131+
] satisfies Line[];

site/src/modules/resources/AgentRow.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
WorkspaceAgentMetadata,
1313
} from "api/typesGenerated";
1414
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
15+
import type { Line } from "components/Logs/LogLine";
1516
import { Stack } from "components/Stack/Stack";
1617
import { useProxy } from "contexts/ProxyContext";
1718
import {
@@ -318,7 +319,7 @@ export const AgentRow: FC<AgentRowProps> = ({
318319
width={width}
319320
css={styles.startupLogs}
320321
onScroll={handleLogScroll}
321-
logs={startupLogs.map((l) => ({
322+
logs={startupLogs.map<Line>((l) => ({
322323
id: l.id,
323324
level: l.level,
324325
output: l.output,

site/src/modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
22
import type { ProvisionerJobLog } from "api/typesGenerated";
3+
import type { Line } from "components/Logs/LogLine";
34
import { DEFAULT_LOG_LINE_SIDE_PADDING, Logs } from "components/Logs/Logs";
45
import dayjs from "dayjs";
56
import { type FC, Fragment, type HTMLAttributes } from "react";
@@ -63,7 +64,8 @@ export const WorkspaceBuildLogs: FC<WorkspaceBuildLogsProps> = ({
6364
>
6465
{Object.entries(groupedLogsByStage).map(([stage, logs]) => {
6566
const isEmpty = logs.every((log) => log.output === "");
66-
const lines = logs.map((log) => ({
67+
const lines = logs.map<Line>((log) => ({
68+
id: log.id,
6769
time: log.created_at,
6870
output: log.output,
6971
level: log.log_level,

site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
import { Alert } from "components/Alert/Alert";
88
import { ErrorAlert } from "components/Alert/ErrorAlert";
99
import { Loader } from "components/Loader/Loader";
10+
import type { Line } from "components/Logs/LogLine";
1011
import { Margins } from "components/Margins/Margins";
1112
import {
1213
FullWidthPageHeader,
@@ -302,7 +303,7 @@ const AgentLogsContent: FC<{ workspaceId: string; agent: WorkspaceAgent }> = ({
302303
return (
303304
<AgentLogs
304305
sources={agent.log_sources}
305-
logs={logs.map((l) => ({
306+
logs={logs.map<Line>((l) => ({
306307
id: l.id,
307308
output: l.output,
308309
time: l.created_at,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy