diff --git a/app/src/lib/ci-checks/ci-checks.ts b/app/src/lib/ci-checks/ci-checks.ts index f1be7a964e..83afb11a27 100644 --- a/app/src/lib/ci-checks/ci-checks.ts +++ b/app/src/lib/ci-checks/ci-checks.ts @@ -336,7 +336,7 @@ export function isIncomplete(check: IRefCheck) { } /** Whether the check has failed (failure or requires action) */ -export function isFailure(check: IRefCheck) { +export function isFailure(check: IRefCheck | IAPIWorkflowJobStep) { if (check.status === 'completed') { switch (check.conclusion) { case 'failure': diff --git a/app/src/ui/check-runs/ci-check-run-actions-logs.tsx b/app/src/ui/check-runs/ci-check-run-actions-logs.tsx index c1b6834412..6dc46b58fd 100644 --- a/app/src/ui/check-runs/ci-check-run-actions-logs.tsx +++ b/app/src/ui/check-runs/ci-check-run-actions-logs.tsx @@ -16,6 +16,7 @@ import { IAPIWorkflowJobStep } from '../../lib/api' import { getFormattedCheckRunDuration, IRefCheckOutput, + isFailure, RefCheckOutputType, } from '../../lib/ci-checks/ci-checks' @@ -33,6 +34,7 @@ interface ICICheckRunActionLogsProps { interface ICICheckRunActionLogsState { readonly openSections: ReadonlySet + readonly firstFailedSectionIndex: number } export class CICheckRunActionLogs extends React.PureComponent< @@ -43,8 +45,48 @@ export class CICheckRunActionLogs extends React.PureComponent< public constructor(props: ICICheckRunActionLogsProps) { super(props) + + const openSections = new Set() + const firstFailedSectionIndex = + props.output.type === RefCheckOutputType.Actions + ? props.output.steps.findIndex(isFailure) + : -1 + + openSections.add(firstFailedSectionIndex) + this.state = { - openSections: new Set(), + openSections, + firstFailedSectionIndex, + } + } + + public componentDidUpdate(prevProps: ICICheckRunActionLogsProps) { + if ( + prevProps.output.type === this.props.output.type || + this.props.output.type !== RefCheckOutputType.Actions + ) { + return + } + + const openSections = new Set() + const firstFailedSectionIndex = this.props.output.steps.findIndex(isFailure) + + openSections.add(firstFailedSectionIndex) + + this.setState({ + openSections, + firstFailedSectionIndex, + }) + } + + private onStepHeaderRef = (stepIndex: number) => { + return (stepHeaderRef: HTMLDivElement | null) => { + if ( + stepIndex === this.state.firstFailedSectionIndex && + stepHeaderRef !== null + ) { + stepHeaderRef.scrollIntoView({ behavior: 'smooth', block: 'start' }) + } } } @@ -257,7 +299,11 @@ export class CICheckRunActionLogs extends React.PureComponent< }) return ( -
+
{!isSkipped ? (