Skip to content

Commit

Permalink
DEVPROD-4608 Add stepback to the metadata of tasks (evergreen-ci#2299)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZackarySantana authored Mar 21, 2024
1 parent 3e7b983 commit fdf7d25
Show file tree
Hide file tree
Showing 18 changed files with 1,310 additions and 364 deletions.
11 changes: 9 additions & 2 deletions src/components/MetadataCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ApolloError } from "@apollo/client";
import styled from "@emotion/styled";
import { PolymorphicAs } from "@leafygreen-ui/polymorphic";
import { Body, BodyProps } from "@leafygreen-ui/typography";
import { Skeleton } from "antd";
import { ErrorWrapper } from "components/ErrorWrapper";
Expand Down Expand Up @@ -39,14 +40,20 @@ export const MetadataTitle: React.FC<{ children: React.ReactNode }> = ({
);

interface ItemProps {
as?: PolymorphicAs;
children: React.ReactNode;
"data-cy"?: string;
}

export const MetadataItem: React.FC<ItemProps> = ({
as = "p",
children,
"data-cy": dataCy,
}) => <Item data-cy={dataCy}>{children}</Item>;
}) => (
<Item data-cy={dataCy} as={as}>
{children}
</Item>
);

const Title = styled(Body)<BodyProps>`
font-size: 15px;
Expand All @@ -63,7 +70,7 @@ const Item = styled(Body)<BodyProps>`
line-height: 14px;
}
:not(:last-of-type) {
:not(:last-child) {
margin-bottom: 12px;
}
`;
67 changes: 67 additions & 0 deletions src/hooks/useBreakingTask/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useQuery } from "@apollo/client";
import { useToastContext } from "context/toast";
import {
BaseVersionAndTaskQuery,
BaseVersionAndTaskQueryVariables,
LastMainlineCommitQuery,
LastMainlineCommitQueryVariables,
} from "gql/generated/types";
import { BASE_VERSION_AND_TASK, LAST_MAINLINE_COMMIT } from "gql/queries";
import { useLastPassingTask } from "hooks/useLastPassingTask";
import { useParentTask } from "hooks/useParentTask";
import { TaskStatus } from "types/task";
import { string } from "utils";
import { getTaskFromMainlineCommitsQuery } from "utils/getTaskFromMainlineCommitsQuery";
import { isFailedTaskStatus } from "utils/statuses";

export const useBreakingTask = (taskId: string) => {
const dispatchToast = useToastContext();

const { data: taskData } = useQuery<
BaseVersionAndTaskQuery,
BaseVersionAndTaskQueryVariables
>(BASE_VERSION_AND_TASK, {
variables: { taskId },
});

const { buildVariant, displayName, projectIdentifier, status } =
taskData?.task ?? {};

const bvOptionsBase = {
tasks: [string.applyStrictRegex(displayName)],
variants: [string.applyStrictRegex(buildVariant)],
};

const { task: parentTask } = useParentTask(taskId);

const { task: lastPassingTask } = useLastPassingTask(taskId);
const passingOrderNumber = lastPassingTask?.order;

// The breaking commit is the first failing commit after the last passing commit.
// The skip order number should be the last passing commit's order number + 1.
// We use + 2 because internally the query does a less than comparison.
// https://github.com/evergreen-ci/evergreen/blob/f6751ac3194452d457c0a6fe1a9f9b30dd674c60/model/version.go#L518
const { data: breakingTaskData, loading } = useQuery<
LastMainlineCommitQuery,
LastMainlineCommitQueryVariables
>(LAST_MAINLINE_COMMIT, {
skip: !parentTask || !lastPassingTask || !isFailedTaskStatus(status),
variables: {
projectIdentifier,
skipOrderNumber: passingOrderNumber + 2,
buildVariantOptions: {
...bvOptionsBase,
statuses: [TaskStatus.Failed],
},
},
onError: (err) => {
dispatchToast.error(`Breaking commit unavailable: '${err.message}'`);
},
});
const task = getTaskFromMainlineCommitsQuery(breakingTaskData);

return {
task,
loading,
};
};
240 changes: 240 additions & 0 deletions src/hooks/useBreakingTask/useBreakingTask.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import { MockedProvider, MockedProviderProps } from "@apollo/client/testing";
import { RenderFakeToastContext } from "context/toast/__mocks__";
import {
BaseVersionAndTaskQuery,
BaseVersionAndTaskQueryVariables,
LastMainlineCommitQuery,
LastMainlineCommitQueryVariables,
} from "gql/generated/types";
import { BASE_VERSION_AND_TASK, LAST_MAINLINE_COMMIT } from "gql/queries";
import { renderHook, waitFor } from "test_utils";
import { ApolloMock } from "types/gql";
import { useBreakingTask } from ".";

interface ProviderProps {
mocks?: MockedProviderProps["mocks"];
children: React.ReactNode;
}
const ProviderWrapper: React.FC<ProviderProps> = ({ children, mocks = [] }) => (
<MockedProvider mocks={mocks}>{children}</MockedProvider>
);

describe("useBreakingTask", () => {
it("no breaking task is found when task is not found", () => {
const { dispatchToast } = RenderFakeToastContext();

const { result } = renderHook(() => useBreakingTask("t1"), {
wrapper: ({ children }) => ProviderWrapper({ children }),
});

expect(result.current.task).toBeUndefined();

// No error is dispatched when the task is not found.
expect(dispatchToast.error).toHaveBeenCalledTimes(0);
});
it("a breaking task is found when there is a previous failing task", async () => {
const { dispatchToast } = RenderFakeToastContext();

const { result } = renderHook(() => useBreakingTask("t1"), {
wrapper: ({ children }) =>
ProviderWrapper({
children,
mocks: [
getPatchTaskWithFailingBaseTask,
getLastPassingVersion,
getBreakingCommit,
],
}),
});

await waitFor(() => {
expect(result.current.task).toBeDefined();
});

expect(result.current.task.id).toBe("breaking_commit");

// No error is dispatched for success scenarios.
expect(dispatchToast.error).toHaveBeenCalledTimes(0);
});
it("a breaking task is not found due to an error in the query and a toast is dispatched", async () => {
const { dispatchToast } = RenderFakeToastContext();

const { result } = renderHook(() => useBreakingTask("t1"), {
wrapper: ({ children }) =>
ProviderWrapper({
children,
mocks: [
getPatchTaskWithFailingBaseTask,
getLastPassingVersion,
getBreakingCommitWithError,
],
}),
});

await waitFor(() => {
// An error is dispatched when the query fails.
expect(dispatchToast.error).toHaveBeenCalledTimes(1);
});

expect(result.current.task).toBeUndefined();
});
});

const baseTaskId =
"evergreen_lint_lint_agent_f4fe4814088e13b8ef423a73d65a6e0a5579cf93_21_11_29_17_55_27";

const getPatchTaskWithFailingBaseTask: ApolloMock<
BaseVersionAndTaskQuery,
BaseVersionAndTaskQueryVariables
> = {
request: {
query: BASE_VERSION_AND_TASK,
variables: {
taskId: "t1",
},
},
result: {
data: {
task: {
id: "evergreen_lint_lint_agent_patch_f4fe4814088e13b8ef423a73d65a6e0a5579cf93_61a8edf132f41750ab47bc72_21_12_02_16_01_54",
execution: 0,
displayName: "lint-agent",
buildVariant: "lint",
projectIdentifier: "evergreen",
status: "failed",
versionMetadata: {
baseVersion: {
id: "baseVersion",
order: 3676,
__typename: "Version",
},
isPatch: true,
id: "versionMetadataId",
__typename: "Version",
},
baseTask: {
id: baseTaskId,
execution: 0,
status: "failed",
__typename: "Task",
},
__typename: "Task",
},
},
},
};

const getLastPassingVersion: ApolloMock<
LastMainlineCommitQuery,
LastMainlineCommitQueryVariables
> = {
request: {
query: LAST_MAINLINE_COMMIT,
variables: {
projectIdentifier: "evergreen",
skipOrderNumber: 3676,
buildVariantOptions: {
tasks: ["^lint-agent$"],
variants: ["^lint$"],
statuses: ["success"],
},
},
},
result: {
data: {
mainlineCommits: {
versions: [
{
version: {
id: "evergreen_44110b57c6977bf3557009193628c9389772163f",
buildVariants: [
{
tasks: [
{
id: "last_passing_task",
execution: 0,
order: 3674,
status: "success",
__typename: "Task",
},
],
__typename: "GroupedBuildVariant",
},
],
__typename: "Version",
},
__typename: "MainlineCommitVersion",
},
],
__typename: "MainlineCommits",
},
},
},
};

const getBreakingCommit: ApolloMock<
LastMainlineCommitQuery,
LastMainlineCommitQueryVariables
> = {
request: {
query: LAST_MAINLINE_COMMIT,
variables: {
projectIdentifier: "evergreen",
skipOrderNumber: 3676,
buildVariantOptions: {
tasks: ["^lint-agent$"],
variants: ["^lint$"],
statuses: ["failed"],
},
},
},
result: {
data: {
mainlineCommits: {
versions: [
{
version: {
id: "evergreen_44110b57c6977bf3557009193628c9389772163f2",
buildVariants: [
{
tasks: [
{
id: "breaking_commit",
execution: 0,
order: 3676,
status: "failed",
__typename: "Task",
},
],
__typename: "GroupedBuildVariant",
},
],
__typename: "Version",
},
__typename: "MainlineCommitVersion",
},
],
__typename: "MainlineCommits",
},
},
},
};

const getBreakingCommitWithError: ApolloMock<
LastMainlineCommitQuery,
LastMainlineCommitQueryVariables
> = {
request: {
query: LAST_MAINLINE_COMMIT,
variables: {
projectIdentifier: "evergreen",
skipOrderNumber: 3676,
buildVariantOptions: {
tasks: ["^lint-agent$"],
variants: ["^lint$"],
statuses: ["failed"],
},
},
},
error: new Error("Matching task not found!"),
};
Loading

0 comments on commit fdf7d25

Please sign in to comment.