fix(site): show available logs consistently on template creation page (#19832)

This commit is contained in:
ケイラ
2025-09-16 14:33:57 -06:00
committed by GitHub
parent ea718084ff
commit f5fac293dc
5 changed files with 45 additions and 94 deletions
@@ -1,10 +1,16 @@
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import type { Interpolation, Theme } from "@emotion/react";
import type { ProvisionerJobLog, WorkspaceBuild } from "api/typesGenerated";
import type { Line } from "components/Logs/LogLine";
import { DEFAULT_LOG_LINE_SIDE_PADDING, Logs } from "components/Logs/Logs";
import dayjs from "dayjs";
import { type FC, Fragment, type HTMLAttributes, useMemo } from "react";
import { BODY_FONT_FAMILY, MONOSPACE_FONT_FAMILY } from "theme/constants";
import {
type FC,
Fragment,
type HTMLAttributes,
useLayoutEffect,
useRef,
} from "react";
import { BODY_FONT_FAMILY } from "theme/constants";
const Language = {
seconds: "seconds",
@@ -43,6 +49,7 @@ interface WorkspaceBuildLogsProps extends HTMLAttributes<HTMLDivElement> {
sticky?: boolean;
logs: ProvisionerJobLog[];
build?: WorkspaceBuild;
disableAutoscroll?: boolean;
}
export const WorkspaceBuildLogs: FC<WorkspaceBuildLogsProps> = ({
@@ -50,38 +57,24 @@ export const WorkspaceBuildLogs: FC<WorkspaceBuildLogsProps> = ({
sticky,
logs,
build,
disableAutoscroll,
className,
...attrs
}) => {
const theme = useTheme();
const _processedLogs = useMemo(() => {
const allLogs = logs || [];
// Add synthetic overflow message if needed
if (build?.job?.logs_overflowed) {
allLogs.push({
id: -1,
created_at: new Date().toISOString(),
log_level: "error",
log_source: "provisioner",
output:
"Provisioner logs exceeded the max size of 1MB. Will not continue to write provisioner logs for workspace build.",
stage: "overflow",
});
}
return allLogs;
}, [logs, build?.job?.logs_overflowed]);
const groupedLogsByStage = groupLogsByStage(logs);
const ref = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
if (disableAutoscroll || logs.length === 0) {
return;
}
ref.current?.scrollIntoView({ block: "end" });
}, [logs, disableAutoscroll]);
return (
<div
css={{
border: `1px solid ${theme.palette.divider}`,
borderRadius: 8,
fontFamily: MONOSPACE_FONT_FAMILY,
}}
ref={ref}
className="font-mono border border-border rounded-lg"
{...attrs}
>
{Object.entries(groupedLogsByStage).map(([stage, logs]) => {
@@ -11,7 +11,7 @@ import { AlertVariant } from "modules/provisioners/ProvisionerAlert";
import { ProvisionerStatusAlert } from "modules/provisioners/ProvisionerStatusAlert";
import { useWatchVersionLogs } from "modules/templates/useWatchVersionLogs";
import { WorkspaceBuildLogs } from "modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs";
import { type FC, useLayoutEffect, useRef } from "react";
import type { FC } from "react";
import { navHeight } from "theme/constants";
type BuildLogsDrawerProps = {
@@ -29,27 +29,6 @@ export const BuildLogsDrawer: FC<BuildLogsDrawerProps> = ({
...drawerProps
}) => {
const logs = useWatchVersionLogs(templateVersion);
const logsContainer = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
setTimeout(() => {
if (logsContainer.current) {
logsContainer.current.scrollTop = logsContainer.current.scrollHeight;
}
}, 0);
};
// biome-ignore lint/correctness/useExhaustiveDependencies: consider refactoring
useLayoutEffect(() => {
scrollToBottom();
}, [logs]);
// biome-ignore lint/correctness/useExhaustiveDependencies: consider refactoring
useLayoutEffect(() => {
if (drawerProps.open) {
scrollToBottom();
}
}, [drawerProps.open]);
const isMissingVariables =
error instanceof JobError &&
@@ -58,6 +37,7 @@ export const BuildLogsDrawer: FC<BuildLogsDrawerProps> = ({
const matchingProvisioners = templateVersion?.matched_provisioners?.count;
const availableProvisioners =
templateVersion?.matched_provisioners?.available;
const hasLogs = logs && logs.length > 0;
return (
<Drawer anchor="right" {...drawerProps}>
@@ -70,8 +50,6 @@ export const BuildLogsDrawer: FC<BuildLogsDrawerProps> = ({
</IconButton>
</header>
{}
{isMissingVariables ? (
<MissingVariablesBanner
onFillVariables={() => {
@@ -80,23 +58,28 @@ export const BuildLogsDrawer: FC<BuildLogsDrawerProps> = ({
});
const firstVariableInput =
variablesSectionRef.current?.querySelector("input");
setTimeout(() => firstVariableInput?.focus(), 0);
firstVariableInput?.focus();
drawerProps.onClose();
}}
/>
) : availableProvisioners && availableProvisioners > 0 && logs ? (
<section ref={logsContainer} css={styles.logs}>
<WorkspaceBuildLogs logs={logs} css={{ border: 0 }} />
</section>
) : (
<>
<ProvisionerStatusAlert
matchingProvisioners={matchingProvisioners}
availableProvisioners={availableProvisioners}
tags={templateVersion?.job.tags ?? {}}
variant={AlertVariant.Inline}
/>
<Loader />
{(matchingProvisioners === 0 || !hasLogs) && (
<ProvisionerStatusAlert
matchingProvisioners={matchingProvisioners}
availableProvisioners={availableProvisioners}
tags={templateVersion?.job.tags ?? {}}
variant={AlertVariant.Inline}
/>
)}
{hasLogs ? (
<section css={styles.logs}>
<WorkspaceBuildLogs logs={logs} className="border-0" />
</section>
) : (
<Loader />
)}
</>
)}
</div>
@@ -197,15 +197,6 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
const isEditorValueBinary =
typeof editorValue === "string" ? isBinaryData(editorValue) : false;
// Auto scroll
const logsContentRef = useRef<HTMLDivElement>(null);
// biome-ignore lint/correctness/useExhaustiveDependencies: consider refactoring
useEffect(() => {
if (logsContentRef.current) {
logsContentRef.current.scrollTop = logsContentRef.current.scrollHeight;
}
}, [buildLogs, resources]);
useLeaveSiteWarning(dirty);
const canBuild = !isBuilding;
@@ -596,10 +587,7 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
</div>
{selectedTab === "logs" && (
<div
css={[styles.logs, styles.tabContent]}
ref={logsContentRef}
>
<div css={[styles.logs, styles.tabContent]}>
{templateVersion.job.error ? (
<div>
<ProvisionerAlert
@@ -299,6 +299,7 @@ const BuildLogsContent: FC<{
}}
logs={sortLogsByCreatedAt(logs)}
build={build}
disableAutoscroll
/>
);
};
@@ -2,7 +2,7 @@ import { useTheme } from "@emotion/react";
import type { ProvisionerJobLog } from "api/typesGenerated";
import { Loader } from "components/Loader/Loader";
import { WorkspaceBuildLogs } from "modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs";
import { type FC, useEffect, useRef } from "react";
import type { FC } from "react";
interface WorkspaceBuildLogsSectionProps {
logs?: ProvisionerJobLog[];
@@ -11,22 +11,8 @@ interface WorkspaceBuildLogsSectionProps {
export const WorkspaceBuildLogsSection: FC<WorkspaceBuildLogsSectionProps> = ({
logs,
}) => {
const scrollRef = useRef<HTMLDivElement>(null);
const theme = useTheme();
// biome-ignore lint/correctness/useExhaustiveDependencies: reset scroll when logs change
useEffect(() => {
// Auto scrolling makes hard to snapshot test using Chromatic
if (process.env.STORYBOOK === "true") {
return;
}
const scrollEl = scrollRef.current;
if (scrollEl) {
scrollEl.scrollTop = scrollEl.scrollHeight;
}
}, [logs]);
return (
<div
css={{
@@ -50,7 +36,7 @@ export const WorkspaceBuildLogsSection: FC<WorkspaceBuildLogsSectionProps> = ({
>
Build logs
</header>
<div ref={scrollRef} css={{ height: "400px", overflowY: "auto" }}>
<div css={{ height: "400px", overflowY: "auto" }}>
{logs ? (
<WorkspaceBuildLogs
sticky