Skip to content

Commit d9e7296

Browse files
committed
[projects] Add Prebuild Events
1 parent 4311ed4 commit d9e7296

19 files changed

+405
-157
lines changed

components/dashboard/src/projects/Prebuild.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import moment from "moment";
8-
import { PrebuildInfo } from "@gitpod/gitpod-protocol";
8+
import { PrebuildWithStatus } from "@gitpod/gitpod-protocol";
99
import { useContext, useEffect, useState } from "react";
1010
import { useLocation, useRouteMatch } from "react-router";
1111
import Header from "../components/Header";
@@ -25,7 +25,7 @@ export default function () {
2525
const projectName = match?.params?.project;
2626
const prebuildId = match?.params?.prebuildId;
2727

28-
const [ prebuild, setPrebuild ] = useState<PrebuildInfo | undefined>();
28+
const [ prebuild, setPrebuild ] = useState<PrebuildWithStatus | undefined>();
2929

3030
useEffect(() => {
3131
if (!teams || !projectName || !prebuildId) {
@@ -52,7 +52,7 @@ export default function () {
5252
if (!prebuild) {
5353
return "unknown prebuild";
5454
}
55-
return (<h1 className="tracking-tight">{prebuild.branch} </h1>);
55+
return (<h1 className="tracking-tight">{prebuild.info.branch} </h1>);
5656
};
5757

5858
const renderSubtitle = () => {
@@ -61,19 +61,19 @@ export default function () {
6161
}
6262
const statusIcon = prebuildStatusIcon(prebuild.status);
6363
const status = prebuildStatusLabel(prebuild.status);
64-
const startedByAvatar = prebuild.startedByAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={prebuild.startedByAvatar || ''} alt={prebuild.startedBy} />;
64+
const startedByAvatar = prebuild.info.startedByAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={prebuild.info.startedByAvatar || ''} alt={prebuild.info.startedBy} />;
6565
return (<div className="flex">
6666
<div className="text-base text-gray-900 dark:text-gray-50 font-medium uppercase">
6767
<div className="inline-block align-text-bottom mr-2 w-4 h-4">{statusIcon}</div>
6868
{status}
6969
</div>
7070
<p className="mx-2 my-auto">·</p>
7171
<div className="my-auto">
72-
<p>{startedByAvatar}Triggered {moment(prebuild.startedAt).fromNow()}</p>
72+
<p>{startedByAvatar}Triggered {moment(prebuild.info.startedAt).fromNow()}</p>
7373
</div>
7474
<p className="mx-2 my-auto">·</p>
7575
<div className="my-auto">
76-
<p className="text-gray-500 dark:text-gray-50">{shortCommitMessage(prebuild.changeTitle)}</p>
76+
<p className="text-gray-500 dark:text-gray-50">{shortCommitMessage(prebuild.info.changeTitle)}</p>
7777
</div>
7878
</div>)
7979
};
@@ -84,7 +84,7 @@ export default function () {
8484
<Header title={renderTitle()} subtitle={renderSubtitle()} />
8585
<div className="lg:px-28 px-10 mt-8">
8686
<div className="h-96 rounded-xl overflow-hidden bg-gray-100 dark:bg-gray-800 flex flex-col">
87-
<PrebuildLogs workspaceId={prebuild?.buildWorkspaceId}/>
87+
<PrebuildLogs workspaceId={prebuild?.info?.buildWorkspaceId}/>
8888
</div>
8989
</div>
9090
</>;

components/dashboard/src/projects/Prebuilds.tsx

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import moment from "moment";
8-
import { PrebuildInfo, PrebuiltWorkspaceState, Project } from "@gitpod/gitpod-protocol";
8+
import { PrebuildInfo, PrebuildWithStatus, PrebuiltWorkspaceState, Project } from "@gitpod/gitpod-protocol";
99
import { useContext, useEffect, useState } from "react";
1010
import { useHistory, useLocation, useRouteMatch } from "react-router";
1111
import Header from "../components/Header";
@@ -26,14 +26,33 @@ export default function () {
2626
const match = useRouteMatch<{ team: string, resource: string }>("/:team/:resource");
2727
const projectName = match?.params?.resource;
2828

29-
// @ts-ignore
3029
const [project, setProject] = useState<Project | undefined>();
3130
const [defaultBranch, setDefaultBranch] = useState<string | undefined>();
3231

3332
const [searchFilter, setSearchFilter] = useState<string | undefined>();
3433
const [statusFilter, setStatusFilter] = useState<PrebuiltWorkspaceState | undefined>();
3534

36-
const [prebuilds, setPrebuilds] = useState<PrebuildInfo[]>([]);
35+
const [prebuilds, setPrebuilds] = useState<PrebuildWithStatus[]>([]);
36+
37+
useEffect(() => {
38+
if (!project) {
39+
return;
40+
}
41+
const registration = getGitpodService().registerClient({
42+
onPrebuildUpdate: (update: PrebuildWithStatus) => {
43+
setPrebuilds(prev => [update, ...prev.filter(p => p.info.id !== update.info.id)])
44+
}
45+
});
46+
47+
(async () => {
48+
const prebuilds = await getGitpodService().server.findPrebuilds({ projectId: project.id });
49+
setPrebuilds(prebuilds);
50+
})();
51+
52+
return () => {
53+
registration.dispose();
54+
}
55+
}, [project]);
3756

3857
useEffect(() => {
3958
if (!teams) {
@@ -44,31 +63,28 @@ export default function () {
4463
? await getGitpodService().server.getTeamProjects(team.id)
4564
: await getGitpodService().server.getUserProjects());
4665

47-
const project = projectName && projects.find(p => p.name === projectName);
48-
if (project) {
49-
setProject(project);
50-
51-
const prebuilds = await getGitpodService().server.findPrebuilds({ projectId: project.id });
52-
setPrebuilds(prebuilds);
66+
const newProject = projectName && projects.find(p => p.name === projectName);
67+
if (newProject) {
68+
setProject(newProject);
5369

54-
const details = await getGitpodService().server.getProjectOverview(project.id);
70+
const details = await getGitpodService().server.getProjectOverview(newProject.id);
5571
if (details?.branches) {
5672
setDefaultBranch(details.branches.find(b => b.isDefault)?.name);
5773
}
5874
}
5975
})();
60-
}, [ teams ]);
76+
}, [teams]);
6177

62-
const prebuildContextMenu = (p: PrebuildInfo) => {
78+
const prebuildContextMenu = (p: PrebuildWithStatus) => {
6379
const running = p.status === "building";
6480
const entries: ContextMenuEntry[] = [];
6581
entries.push({
6682
title: "View Prebuild",
67-
onClick: () => openPrebuild(p)
83+
onClick: () => openPrebuild(p.info)
6884
});
6985
entries.push({
7086
title: "Trigger Prebuild",
71-
onClick: () => triggerPrebuild(p.branch),
87+
onClick: () => triggerPrebuild(p.info.branch),
7288
separator: running
7389
});
7490
if (running) {
@@ -94,18 +110,16 @@ export default function () {
94110
return entries;
95111
}
96112

97-
const filter = (p: PrebuildInfo) => {
113+
const filter = (p: PrebuildWithStatus) => {
98114
if (statusFilter && statusFilter !== p.status) {
99115
return false;
100116
}
101-
if (searchFilter && `${p.changeTitle} ${p.branch}`.toLowerCase().includes(searchFilter.toLowerCase()) === false) {
117+
if (searchFilter && `${p.info.changeTitle} ${p.info.branch}`.toLowerCase().includes(searchFilter.toLowerCase()) === false) {
102118
return false;
103119
}
104120
return true;
105121
}
106122

107-
const filteredPrebuilds = prebuilds.filter(filter);
108-
109123
const openPrebuild = (pb: PrebuildInfo) => {
110124
history.push(`/${!!team ? team.slug : 'projects'}/${projectName}/${pb.id}`);
111125
}
@@ -149,25 +163,25 @@ export default function () {
149163
<ItemFieldContextMenu />
150164
</ItemField>
151165
</Item>
152-
{filteredPrebuilds.map((p: PrebuildInfo) => <Item className="grid grid-cols-3">
166+
{prebuilds.filter(filter).map((p, index) => <Item key={`prebuild-${p.info.id}`} className="grid grid-cols-3">
153167
<ItemField className="flex items-center">
154-
<div className="cursor-pointer" onClick={() => openPrebuild(p)}>
168+
<div className="cursor-pointer" onClick={() => openPrebuild(p.info)}>
155169
<div className="text-base text-gray-900 dark:text-gray-50 font-medium uppercase mb-1">
156170
<div className="inline-block align-text-bottom mr-2 w-4 h-4">{prebuildStatusIcon(p.status)}</div>
157171
{prebuildStatusLabel(p.status)}
158172
</div>
159-
<p>{p.startedByAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={p.startedByAvatar || ''} alt={p.startedBy} />}Triggered {formatDate(p.startedAt)}</p>
173+
<p>{p.info.startedByAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={p.info.startedByAvatar || ''} alt={p.info.startedBy} />}Triggered {formatDate(p.info.startedAt)}</p>
160174
</div>
161175
</ItemField>
162176
<ItemField className="flex items-center">
163177
<div>
164-
<div className="text-base text-gray-500 dark:text-gray-50 font-medium mb-1">{shortCommitMessage(p.changeTitle)}</div>
165-
<p>{p.changeAuthorAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={p.changeAuthorAvatar || ''} alt={p.changeAuthor} />}Authored {formatDate(p.changeDate)} · {p.changeHash?.substring(0, 8)}</p>
178+
<div className="text-base text-gray-500 dark:text-gray-50 font-medium mb-1">{shortCommitMessage(p.info.changeTitle)}</div>
179+
<p>{p.info.changeAuthorAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={p.info.changeAuthorAvatar || ''} alt={p.info.changeAuthor} />}Authored {formatDate(p.info.changeDate)} · {p.info.changeHash?.substring(0, 8)}</p>
166180
</div>
167181
</ItemField>
168182
<ItemField className="flex items-center">
169183
<div className="flex space-x-2">
170-
<span className="font-medium text-gray-500 dark:text-gray-50">{p.branch}</span>
184+
<span className="font-medium text-gray-500 dark:text-gray-50">{p.info.branch}</span>
171185
</div>
172186
<span className="flex-grow" />
173187
<ItemFieldContextMenu menuEntries={prebuildContextMenu(p)} />
@@ -179,7 +193,7 @@ export default function () {
179193
</>;
180194
}
181195

182-
export function prebuildStatusLabel(status: PrebuiltWorkspaceState) {
196+
export function prebuildStatusLabel(status: PrebuiltWorkspaceState | undefined) {
183197
switch (status) {
184198
case "aborted":
185199
return (<span className="font-medium text-red-500 uppercase">failed</span>);
@@ -193,7 +207,7 @@ export function prebuildStatusLabel(status: PrebuiltWorkspaceState) {
193207
break;
194208
}
195209
}
196-
export function prebuildStatusIcon(status: PrebuiltWorkspaceState) {
210+
export function prebuildStatusIcon(status: PrebuiltWorkspaceState | undefined) {
197211
switch (status) {
198212
case "aborted":
199213
return (<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">

components/dashboard/src/projects/Project.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import moment from "moment";
8-
import { PrebuildInfo, Project } from "@gitpod/gitpod-protocol";
8+
import { PrebuildInfo, PrebuildWithStatus, Project } from "@gitpod/gitpod-protocol";
99
import { useContext, useEffect, useState } from "react";
1010
import { useHistory, useLocation, useRouteMatch } from "react-router";
1111
import Header from "../components/Header";
@@ -29,7 +29,7 @@ export default function () {
2929
const [project, setProject] = useState<Project | undefined>();
3030

3131
const [branches, setBranches] = useState<Project.BranchDetails[]>([]);
32-
const [lastPrebuilds, setLastPrebuilds] = useState<Map<string, PrebuildInfo | undefined>>(new Map());
32+
const [lastPrebuilds, setLastPrebuilds] = useState<Map<string, PrebuildWithStatus | undefined>>(new Map());
3333
const [prebuildLoaders] = useState<Set<string>>(new Set());
3434

3535
const [searchFilter, setSearchFilter] = useState<string | undefined>();
@@ -160,8 +160,8 @@ export default function () {
160160
const prebuild = lastPrebuild(branch); // this might lazily trigger fetching of prebuild details
161161

162162
const avatar = branch.changeAuthorAvatar && <img className="rounded-full w-4 h-4 inline-block align-text-bottom mr-2" src={branch.changeAuthorAvatar || ''} alt={branch.changeAuthor} />;
163-
const statusIcon = prebuild?.status && prebuildStatusIcon(prebuild.status);
164-
const status = prebuild?.status && prebuildStatusLabel(prebuild.status);
163+
const statusIcon = prebuildStatusIcon(prebuild?.status);
164+
const status = prebuildStatusLabel(prebuild?.status);
165165

166166
return <Item key={`branch-${index}-${branchName}`} className="grid grid-cols-3 group">
167167
<ItemField className="flex items-center">
@@ -179,7 +179,7 @@ export default function () {
179179
</div>
180180
</ItemField>
181181
<ItemField className="flex items-center">
182-
<div className="text-base text-gray-900 dark:text-gray-50 font-medium uppercase mb-1 cursor-pointer" onClick={() => prebuild && openPrebuild(prebuild)}>
182+
<div className="text-base text-gray-900 dark:text-gray-50 font-medium uppercase mb-1 cursor-pointer" onClick={() => prebuild && openPrebuild(prebuild.info)}>
183183
{prebuild ? (<><div className="inline-block align-text-bottom mr-2 w-4 h-4">{statusIcon}</div>{status}</>) : (<span> </span>)}
184184
</div>
185185
<span className="flex-grow" />

components/dashboard/src/projects/Projects.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useContext, useEffect, useState } from "react";
1414
import { getGitpodService } from "../service/service";
1515
import { getCurrentTeam, TeamsContext } from "../teams/teams-context";
1616
import { ThemeContext } from "../theme-context";
17-
import { PrebuildInfo, Project } from "@gitpod/gitpod-protocol";
17+
import { PrebuildWithStatus, Project } from "@gitpod/gitpod-protocol";
1818
import { toRemoteURL } from "./render-utils";
1919
import ContextMenu from "../components/ContextMenu";
2020

@@ -25,7 +25,7 @@ export default function () {
2525
const { teams } = useContext(TeamsContext);
2626
const team = getCurrentTeam(location, teams);
2727
const [ projects, setProjects ] = useState<Project[]>([]);
28-
const [ lastPrebuilds, setLastPrebuilds ] = useState<Map<string, PrebuildInfo>>(new Map());
28+
const [ lastPrebuilds, setLastPrebuilds ] = useState<Map<string, PrebuildWithStatus>>(new Map());
2929

3030
const { isDark } = useContext(ThemeContext);
3131

@@ -132,8 +132,8 @@ export default function () {
132132
<div className={"my-auto rounded-full w-3 h-3 text-sm align-middle " + (true ? "bg-green-500" : "bg-gray-400")}>
133133
&nbsp;
134134
</div>
135-
<div className="my-auto">{lastPrebuilds.get(p.id)!.branch}</div>
136-
<div className="my-auto text-gray-400">{moment(lastPrebuilds.get(p.id)!.startedAt, "YYYYMMDD").fromNow()}</div>
135+
<div className="my-auto">{lastPrebuilds.get(p.id)?.info?.branch}</div>
136+
<div className="my-auto text-gray-400">{moment(lastPrebuilds.get(p.id)?.info.startedAt, "YYYYMMDD").fromNow()}</div>
137137
<div className="my-auto text-gray-400 flex-grow text-right">View All ⟶</div>
138138
</div>)
139139
: (<div className="flex h-full text-md">

components/dashboard/src/service/service-mock.ts

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -106,43 +106,45 @@ const gitpodServiceMock = createServiceMock({
106106
findPrebuilds: async (p) => {
107107
const { projectId } = p;
108108
return [{
109-
id: "pb1",
110-
branch: "main",
111-
buildWorkspaceId: "123",
112-
teamId: "t1",
113-
projectId,
114-
projectName: "pb1",
115-
cloneUrl: pr1.cloneUrl,
116-
startedAt: t1,
117-
startedBy: u1.id,
118-
startedByAvatar: u1.avatarUrl,
119-
status: "available",
120-
changeTitle: "[Comp] Add new functionality for",
121-
changeDate: t1,
122-
changeAuthor: u1.fullName!,
123-
changeAuthorAvatar: u1.avatarUrl,
124-
changePR: "4647",
125-
changeUrl: "https://github.com/gitpod-io/gitpod/pull/4738",
126-
changeHash: "2C0FFE"
109+
info: {
110+
id: "pb1",
111+
branch: "main",
112+
buildWorkspaceId: "123",
113+
teamId: "t1",
114+
projectId,
115+
projectName: "pb1",
116+
cloneUrl: pr1.cloneUrl,
117+
startedAt: t1,
118+
startedBy: u1.id,
119+
startedByAvatar: u1.avatarUrl,
120+
changeTitle: "[Comp] Add new functionality for",
121+
changeDate: t1,
122+
changeAuthor: u1.fullName!,
123+
changeAuthorAvatar: u1.avatarUrl,
124+
changePR: "4647",
125+
changeUrl: "https://github.com/gitpod-io/gitpod/pull/4738",
126+
changeHash: "2C0FFE"
127+
}, status: "available"
127128
}, {
128-
id: "pb1",
129-
branch: "foo/bar",
130-
buildWorkspaceId: "1234",
131-
teamId: "t1",
132-
projectId,
133-
projectName: "pb1",
134-
cloneUrl: pr1.cloneUrl,
135-
startedAt: t1,
136-
startedBy: u1.id,
137-
startedByAvatar: u1.avatarUrl,
138-
status: "aborted",
139-
changeTitle: "Fix Bug Nr 1",
140-
changeDate: t1,
141-
changeAuthor: u1.fullName!,
142-
changeAuthorAvatar: u1.avatarUrl,
143-
changePR: "4245",
144-
changeUrl: "https://github.com/gitpod-io/gitpod/pull/4738",
145-
changeHash: "1C0FFE"
129+
info: {
130+
id: "pb1",
131+
branch: "foo/bar",
132+
buildWorkspaceId: "1234",
133+
teamId: "t1",
134+
projectId,
135+
projectName: "pb1",
136+
cloneUrl: pr1.cloneUrl,
137+
startedAt: t1,
138+
startedBy: u1.id,
139+
startedByAvatar: u1.avatarUrl,
140+
changeTitle: "Fix Bug Nr 1",
141+
changeDate: t1,
142+
changeAuthor: u1.fullName!,
143+
changeAuthorAvatar: u1.avatarUrl,
144+
changePR: "4245",
145+
changeUrl: "https://github.com/gitpod-io/gitpod/pull/4738",
146+
changeHash: "1C0FFE"
147+
}, status: "available"
146148
}
147149
]
148150
},

0 commit comments

Comments
 (0)