Skip to content

Commit

Permalink
DEVPROD-10188: Render waterfall (#412)
Browse files Browse the repository at this point in the history
  • Loading branch information
sophstad authored Oct 1, 2024
1 parent 4a1382f commit 4c2e5d2
Show file tree
Hide file tree
Showing 13 changed files with 383 additions and 37 deletions.
8 changes: 4 additions & 4 deletions .evergreen/evergreen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ buildvariants:
app_dir: apps/spruce
goroot: /opt/golang/go1.20
mongodb_tools_url: https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2204-x86_64-100.8.0.tgz
mongodb_url_2204: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-7.0.2.tgz
mongodb_url_2204: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-7.0.14.tgz
mongosh_url_2204: https://downloads.mongodb.com/compass/mongosh-2.0.2-linux-x64.tgz
node_version: 20.11.0
modules:
Expand All @@ -73,7 +73,7 @@ buildvariants:
app_dir: apps/parsley
goroot: /opt/golang/go1.20
mongodb_tools_url: https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2204-x86_64-100.8.0.tgz
mongodb_url_2204: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-7.0.2.tgz
mongodb_url_2204: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-7.0.14.tgz
mongosh_url_2204: https://downloads.mongodb.com/compass/mongosh-2.0.2-linux-x64.tgz
node_version: 20.11.0
run_on:
Expand Down Expand Up @@ -102,7 +102,7 @@ buildvariants:
app_dir: packages/lib
goroot: /opt/golang/go1.20
mongodb_tools_url: https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2204-x86_64-100.8.0.tgz
mongodb_url_2204: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-7.0.2.tgz
mongodb_url_2204: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-7.0.14.tgz
mongosh_url_2204: https://downloads.mongodb.com/compass/mongosh-2.0.2-linux-x64.tgz
node_version: 20.11.0
run_on:
Expand All @@ -121,7 +121,7 @@ buildvariants:
app_dir: packages/deploy-utils
goroot: /opt/golang/go1.20
mongodb_tools_url: https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2204-x86_64-100.8.0.tgz
mongodb_url_2204: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-7.0.2.tgz
mongodb_url_2204: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2204-7.0.14.tgz
mongosh_url_2204: https://downloads.mongodb.com/compass/mongosh-2.0.2-linux-x64.tgz
node_version: 20.11.0
run_on:
Expand Down
41 changes: 41 additions & 0 deletions apps/spruce/cypress/integration/waterfall/waterfall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
describe("waterfall page", () => {
beforeEach(() => {
cy.visit("/project/spruce/waterfall");
});

describe("version labels", () => {
it("shows a git tag label", () => {
cy.dataCy("version-labels")
.children()
.eq(4)
.contains("Git Tags: v2.28.5");
});
});

describe("inactive commits", () => {
it("renders an inactive version column", () => {
cy.dataCy("version-labels")
.children()
.eq(2)
.should("have.attr", "data-cy", "inactive-label");
cy.dataCy("build-group")
.first()
.children()
.eq(2)
.should("have.attr", "data-cy", "inactive-column");
});
});

describe("task grid", () => {
it("correctly renders child tasks", () => {
cy.dataCy("build-group").children().as("builds");

cy.get("@builds").eq(0).children().should("have.length", 1);
cy.get("@builds").eq(1).children().should("have.length", 8);
cy.get("@builds").eq(2).children().should("have.length", 0);
cy.get("@builds").eq(3).children().should("have.length", 1);
cy.get("@builds").eq(4).children().should("have.length", 8);
cy.get("@builds").eq(5).children().should("have.length", 8);
});
});
});
13 changes: 8 additions & 5 deletions apps/spruce/src/analytics/waterfall/useWaterfallAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import { useAnalyticsRoot } from "@evg-ui/lib/analytics/hooks";
import { AnalyticsIdentifier } from "analytics/types";
import { slugs } from "constants/routes";

type Action = {
name: "Clicked commit label";
link: "jira" | "githash" | "upstream project";
"commit.type": "active" | "inactive";
};
type Action =
| {
name: "Clicked commit label";
link: "jira" | "githash" | "upstream project";
"commit.type": "active" | "inactive";
}
| { name: "Clicked variant label" }
| { name: "Clicked task box"; "task.status": string };

export const useWaterfallAnalytics = () => {
const { [slugs.projectIdentifier]: projectIdentifier } = useParams();
Expand Down
19 changes: 18 additions & 1 deletion apps/spruce/src/gql/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9509,11 +9509,28 @@ export type WaterfallQuery = {
__typename?: "Query";
waterfall: {
__typename?: "Waterfall";
buildVariants: Array<{
__typename?: "WaterfallBuildVariant";
displayName: string;
id: string;
builds: Array<{
__typename?: "WaterfallBuild";
displayName: string;
id: string;
version: string;
tasks: Array<{
__typename?: "WaterfallTask";
displayName: string;
id: string;
status: string;
}>;
}>;
}>;
versions: Array<{
__typename?: "WaterfallVersion";
inactiveVersions?: Array<{ __typename?: "Version"; id: string }> | null;
version?: {
__typename?: "Version";
activated?: boolean | null;
author: string;
createTime: Date;
id: string;
Expand Down
18 changes: 17 additions & 1 deletion apps/spruce/src/gql/queries/waterfall.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@

query Waterfall($options: WaterfallOptions!) {
waterfall(options: $options) {
buildVariants {
builds {
displayName
id
tasks {
displayName
id
status
}
version
}
displayName
id
}
versions {
inactiveVersions {
id
}
version {
activated
author
createTime
gitTags {
Expand Down
173 changes: 173 additions & 0 deletions apps/spruce/src/pages/waterfall/BuildRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { memo, useCallback } from "react";
import styled from "@emotion/styled";
import { palette } from "@leafygreen-ui/palette";
import { Link } from "react-router-dom";
import { TaskStatus } from "@evg-ui/lib/types/task";
import { useWaterfallAnalytics } from "analytics";
import { StyledLink } from "components/styles";
import { getTaskRoute, getVariantHistoryRoute } from "constants/routes";
import { size } from "constants/tokens";
import {
WaterfallBuild,
WaterfallBuildVariant,
WaterfallQuery,
} from "gql/generated/types";
import {
BuildVariantTitle,
columnBasis,
gridGroupCss,
InactiveVersion,
Row,
} from "./styles";

const { black, gray, green, white } = palette;

export const BuildRow: React.FC<{
build: WaterfallBuildVariant;
projectIdentifier: string;
versions: WaterfallQuery["waterfall"]["versions"];
}> = ({ build, projectIdentifier, versions }) => {
const { sendEvent } = useWaterfallAnalytics();
const handleVariantClick = useCallback(
() => sendEvent({ name: "Clicked variant label" }),
[sendEvent],
);
const handleTaskClick = useCallback(
(status: string) => () =>
sendEvent({
name: "Clicked task box",
"task.status": status,
}),
[sendEvent],
);

const { builds, displayName } = build;
let buildIndex = 0;
return (
<Row>
<BuildVariantTitle>
<StyledLink
href={getVariantHistoryRoute(projectIdentifier, build.id)}
onClick={handleVariantClick}
>
{displayName}
</StyledLink>
</BuildVariantTitle>
<BuildGroup data-cy="build-group">
{versions.map(({ inactiveVersions, version }) => {
if (inactiveVersions?.length) {
return (
<InactiveVersion
key={inactiveVersions[0].id}
data-cy="inactive-column"
/>
);
}
/* The list of builds returned does not include a placeholder for inactive builds, so we need to check whether the build matches the version in the current column.
Builds are sorted in descending revision order and so match the versions' sort order. */
if (version && version.id === builds?.[buildIndex]?.version) {
const b = builds[buildIndex];
buildIndex += 1;
return (
<BuildGrid
key={b.id}
build={b}
handleTaskClick={handleTaskClick}
/>
);
}
return <Build key={version?.id} />;
})}
</BuildGroup>
</Row>
);
};

const BuildGrid: React.FC<{
build: WaterfallBuild;
handleTaskClick: (s: string) => () => void;
}> = ({ build, handleTaskClick }) => (
<Build
onClick={(event: React.MouseEvent) => {
handleTaskClick(
(event.target as HTMLDivElement)?.getAttribute("status") ?? "",
);
}}
>
{build.tasks.map(({ displayName, id, status }) => (
<SquareMemo
key={id}
data-tooltip={displayName}
status={status}
to={getTaskRoute(id)} // TODO DEVPROD-11734: use execution in task route
/>
))}
</Build>
);

const BuildGroup = styled.div`
${gridGroupCss}
border: 1px solid ${gray.light2};
border-radius: ${size.xs};
padding-bottom: ${size.xs};
padding-top: ${size.xs};
`;

const Build = styled.div`
${columnBasis}
`;

const SQUARE_SIZE = 16;

const Square = styled(Link)<{ status: string }>`
width: ${SQUARE_SIZE}px;
height: ${SQUARE_SIZE}px;
border: 1px solid ${white};
box-sizing: content-box;
float: left;
cursor: pointer;
position: relative;
/* TODO DEVPROD-11368: Render colors for all statuses. Could use background-image property to render icons. */
${({ status }) =>
status === TaskStatus.Succeeded
? `background-color: ${green.dark1};`
: `background-color: ${gray.light2};
`}
/* Tooltip */
:before {
content: attr(data-tooltip);
position: absolute;
bottom: calc(100% + 5px);
left: 50%;
transform: translate(-50%);
z-index: 1;
width: max-content;
max-width: 450px;
overflow-wrap: break-word;
padding: ${size.xs};
border-radius: 6px;
background: ${black};
color: ${white};
text-align: center;
display: none;
}
:hover:before {
display: block;
}
/* Tooltip caret */
:hover:after {
content: "";
position: absolute;
bottom: calc(100% - 5px);
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: ${black} transparent transparent transparent;
}
`;

const SquareMemo = memo(Square);
9 changes: 8 additions & 1 deletion apps/spruce/src/pages/waterfall/VersionLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { Body, InlineCode } from "@leafygreen-ui/typography";
import { Link } from "react-router-dom";
import { Unpacked } from "@evg-ui/lib/types/utils";
import { useWaterfallAnalytics } from "analytics";
import { StyledRouterLink } from "components/styles";
import { StyledRouterLink, wordBreakCss } from "components/styles";
import { getVersionRoute, getTriggerRoute } from "constants/routes";
import { WaterfallQuery } from "gql/generated/types";
import { useSpruceConfig, useDateFormat } from "hooks";
import { shortenGithash, jiraLinkify } from "utils/string";
import { columnBasis } from "./styles";

type VersionFields = NonNullable<
Unpacked<WaterfallQuery["waterfall"]["versions"]>["version"]
Expand Down Expand Up @@ -89,10 +90,16 @@ export const VersionLabel: React.FC<VersionFields> = ({
};

const VersionContainer = styled.div`
${columnBasis}
> * {
font-size: 12px;
line-height: 1.3;
}
p {
${wordBreakCss}
}
`;

const CommitMessage = styled(Body)`
Expand Down
Loading

0 comments on commit 4c2e5d2

Please sign in to comment.