-
Notifications
You must be signed in to change notification settings - Fork 943
fix: let workspace pages download partial logs for unhealthy workspaces #13761
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
f1f5f88
fix: get basic fix in for preventing download logs from blowing up UI
Parkreiner edd6362
fix: make sure blob units can't go out of bounds
Parkreiner 901aa39
fix: make sure timeout is cleared on component unmount
Parkreiner e1d8113
fix: reduce risk of shared cache state breaking useAgentLogs
Parkreiner 87f57aa
fix: allow partial downloading of logs
Parkreiner 5bf2baf
fix: make sure useMemo cache is used properly
Parkreiner 5c2316b
wip: commit current progress on updated logs functionality
Parkreiner edd6569
docs: rewrite comment for clarity
Parkreiner 2eb7d6e
refactor: clean up current code
Parkreiner fa06515
fix: update styles for unavailable logs
Parkreiner 8fb4496
fix: resolve linter violations
Parkreiner 6935f50
fix: update type signature of getErrorDetail
Parkreiner f74eda4
fix: revert log/enabled logic for useAgentLogs
Parkreiner 7b76eb7
fix: remove memoization from DownloadLogsDialog
Parkreiner 07ff536
fix: update name of timeout state
Parkreiner 460aa53
refactor: make log web sockets logic more clear
Parkreiner 5a449b4
8000
docs: reword comment for clarity
Parkreiner eb4f88c
fix: commit current style update progress
Parkreiner 71692b6
fix: finish style updates
Parkreiner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix: make sure useMemo cache is used properly
- Loading branch information
commit 5bf2bafe7cbf89ddfe95201fd50bb5c1cf
8000
6f2fe5
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,13 @@ import Skeleton from "@mui/material/Skeleton"; | |
import { saveAs } from "file-saver"; | ||
import JSZip from "jszip"; | ||
import { useMemo, useState, type FC, useRef, useEffect } from "react"; | ||
import { useQueries, useQuery } from "react-query"; | ||
import { UseQueryOptions, useQueries, useQuery } from "react-query"; | ||
import { agentLogs, buildLogs } from "api/queries/workspaces"; | ||
import type { Workspace, WorkspaceAgent } from "api/typesGenerated"; | ||
import type { | ||
Workspace, | ||
WorkspaceAgent, | ||
WorkspaceAgentLog, | ||
} from "api/typesGenerated"; | ||
import { | ||
ConfirmDialog, | ||
type ConfirmDialogProps, | ||
|
@@ -32,50 +36,75 @@ type DownloadableFile = { | |
export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | ||
workspace, | ||
download = saveAs, | ||
...dialogProps | ||
open, | ||
onConfirm, | ||
onClose, | ||
}) => { | ||
const theme = useTheme(); | ||
const agents = workspace.latest_build.resources.flatMap( | ||
(resource) => resource.agents ?? [], | ||
); | ||
|
||
const agentLogResults = useQueries({ | ||
queries: agents.map((a) => ({ | ||
...agentLogs(workspace.id, a.id), | ||
enabled: dialogProps.open, | ||
})), | ||
}); | ||
|
||
const buildLogsQuery = useQuery({ | ||
...buildLogs(workspace), | ||
enabled: dialogProps.open, | ||
enabled: open, | ||
}); | ||
|
||
const downloadableFiles = useMemo<readonly DownloadableFile[]>(() => { | ||
const files: DownloadableFile[] = [ | ||
{ | ||
name: `${workspace.name}-build-logs.txt`, | ||
blob: buildLogsQuery.data | ||
? new Blob([buildLogsQuery.data.map((l) => l.output).join("\n")], { | ||
type: "text/plain", | ||
}) | ||
: undefined, | ||
}, | ||
const buildLogsFile = useMemo<DownloadableFile>(() => { | ||
return { | ||
name: `${workspace.name}-build-logs.txt`, | ||
blob: buildLogsQuery.data | ||
? new Blob([buildLogsQuery.data.map((l) => l.output).join("\n")], { | ||
type: "text/plain", | ||
}) | ||
: undefined, | ||
}; | ||
}, [workspace.name, buildLogsQuery.data]); | ||
|
||
// This is clunky, but we have to memoize in two steps to make sure that we | ||
// don't accidentally break the memo cache every render. We can't tuck | ||
// everything into a single memo call, because we need to set up React Query | ||
// state between processing the agents, and we can't violate rules of hooks | ||
type AgentInfo = Readonly<{ | ||
agents: readonly WorkspaceAgent[]; | ||
queries: readonly UseQueryOptions<readonly WorkspaceAgentLog[]>[]; | ||
}>; | ||
|
||
const { agents, queries } = useMemo<AgentInfo>(() => { | ||
const allAgents = workspace.latest_build.resources.flatMap( | ||
(resource) => resource.agents ?? [], | ||
); | ||
|
||
// Can't use the "new Set()" trick because we're not dealing with primitives | ||
const uniqueAgents = [ | ||
...new Map(allAgents.map((agent) => [agent.id, agent])).values(), | ||
]; | ||
|
||
return { | ||
agents: uniqueAgents, | ||
queries: uniqueAgents.map((agent) => { | ||
return { | ||
...agentLogs(workspace.id, agent.id), | ||
enabled: open, | ||
}; | ||
}), | ||
}; | ||
}, [workspace, open]); | ||
|
||
const agentLogResults = useQueries({ queries }); | ||
const allFiles = useMemo<readonly DownloadableFile[]>(() => { | ||
const files: DownloadableFile[] = [buildLogsFile]; | ||
|
||
agents.forEach((a, i) => { | ||
const name = `${a.name}-logs.txt`; | ||
const logs = agentLogResults[i].data; | ||
const txt = logs?.map((l) => l.output).join("\n"); | ||
const txt = agentLogResults[i]?.data?.map((l) => l.output).join("\n"); | ||
|
||
let blob: Blob | undefined; | ||
if (txt) { | ||
blob = new Blob([txt], { type: "text/plain" }); | ||
} | ||
|
||
files.push({ name, blob }); | ||
}); | ||
|
||
return files; | ||
}, [agentLogResults, agents, buildLogsQuery.data, workspace.name]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was a problem with the previous logic: because |
||
}, [agentLogResults, agents, buildLogsFile]); | ||
|
||
const [isDownloading, setIsDownloading] = useState(false); | ||
const timeoutIdRef = useRef<number | undefined>(undefined); | ||
|
@@ -88,11 +117,12 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
}, []); | ||
|
||
const isWorkspaceHealthy = workspace.health.healthy; | ||
const isLoadingFiles = downloadableFiles.some((f) => f.blob === undefined); | ||
const isLoadingFiles = allFiles.some((f) => f.blob === undefined); | ||
|
||
return ( | ||
<ConfirmDialog | ||
{...dialogProps} | ||
open={open} | ||
onClose={onClose} | ||
hideCancel={false} | ||
title="Download logs" | ||
confirmLoading={isDownloading} | ||
|
@@ -111,7 +141,7 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
onConfirm={async () => { | ||
setIsDownloading(true); | ||
const zip = new JSZip(); | ||
downloadableFiles.forEach((f) => { | ||
allFiles.forEach((f) => { | ||
if (f.blob) { | ||
zip.file(f.name, f.blob); | ||
} | ||
|
@@ -120,7 +150,7 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
try { | ||
const content = await zip.generateAsync({ type: "blob" }); | ||
download(content, `${workspace.name}-logs.zip`); | ||
dialogProps.onClose(); | ||
onClose(); | ||
|
||
timeoutIdRef.current = window.setTimeout(() => { | ||
setIsDownloading(false); | ||
|
@@ -148,7 +178,7 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
)} | ||
|
||
<ul css={styles.list}> | ||
{downloadableFiles.map((f) => ( | ||
{allFiles.map((f) => ( | ||
<li key={f.name} css={styles.listItem}> | ||
<span css={styles.listItemPrimary}>{f.name}</span> | ||
<span css={styles.listItemSecondary}> | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.