mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): show available logs consistently on template creation page (#19832)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user