Skip to content

Allow enabling/disabling Incremental Prebuilds in Project Settings #7031

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions components/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { trackButtonOrAnchor, trackPathChange, trackLocation } from './Analytics
import { User } from '@gitpod/gitpod-protocol';
import * as GitpodCookie from '@gitpod/gitpod-protocol/lib/util/gitpod-cookie';
import { Experiment } from './experiments';
import ProjectSettings from './projects/ProjectSettings';

const Setup = React.lazy(() => import(/* webpackPrefetch: true */ './Setup'));
const Workspaces = React.lazy(() => import(/* webpackPrefetch: true */ './workspaces/Workspaces'));
Expand Down Expand Up @@ -298,6 +299,9 @@ function App() {
<Route exact path="/projects" component={Projects} />
<Route exact path="/projects/:projectName/:resourceOrPrebuild?" render={(props) => {
const { resourceOrPrebuild } = props.match.params;
if (resourceOrPrebuild === "settings") {
return <ProjectSettings />;
}
if (resourceOrPrebuild === "configure") {
return <ConfigureProject />;
}
Expand Down Expand Up @@ -337,6 +341,9 @@ function App() {
if (resourceOrPrebuild === "configure") {
return <ConfigureProject />;
}
if (resourceOrPrebuild === "settings") {
return <ProjectSettings />;
}
if (resourceOrPrebuild === "workspaces") {
return <Workspaces />;
}
Expand Down
38 changes: 21 additions & 17 deletions components/dashboard/src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* See License-AGPL.txt in the project root for license information.
*/

import { User, TeamMemberInfo } from "@gitpod/gitpod-protocol";
import { User, TeamMemberInfo, Project } from "@gitpod/gitpod-protocol";
import { useContext, useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { useLocation, useRouteMatch } from "react-router";
Expand All @@ -21,6 +21,8 @@ import ContextMenu from "./components/ContextMenu";
import Separator from "./components/Separator";
import PillMenuItem from "./components/PillMenuItem";
import TabMenuItem from "./components/TabMenuItem";
import { getTeamSettingsMenu } from "./teams/TeamSettings";
import { getProjectSettingsMenu } from "./projects/ProjectSettings";

interface Entry {
title: string,
Expand All @@ -35,14 +37,14 @@ export default function Menu() {
const location = useLocation();

const match = useRouteMatch<{ segment1?: string, segment2?: string, segment3?: string }>("/(t/)?:segment1/:segment2?/:segment3?");
const projectName = (() => {
const projectSlug = (() => {
const resource = match?.params?.segment2;
if (resource && !["projects", "members", "users", "workspaces", "settings"].includes(resource)) {
return resource;
}
})();
const prebuildId = (() => {
const resource = projectName && match?.params?.segment3;
const resource = projectSlug && match?.params?.segment3;
if (resource !== "workspaces" && resource !== "prebuilds" && resource !== "settings" && resource !== "configure") {
return resource;
}
Expand Down Expand Up @@ -89,24 +91,25 @@ export default function Menu() {
const teamOrUserSlug = !!team ? '/t/' + team.slug : '/projects';
const leftMenu: Entry[] = (() => {
// Project menu
if (projectName) {
if (projectSlug) {
return [
{
title: 'Branches',
link: `${teamOrUserSlug}/${projectName}`
link: `${teamOrUserSlug}/${projectSlug}`,
},
{
title: 'Workspaces',
link: `${teamOrUserSlug}/${projectName}/workspaces`
link: `${teamOrUserSlug}/${projectSlug}/workspaces`,
},
{
title: 'Prebuilds',
link: `${teamOrUserSlug}/${projectName}/prebuilds`
link: `${teamOrUserSlug}/${projectSlug}/prebuilds`,
},
{
title: 'Configuration',
link: `${teamOrUserSlug}/${projectName}/configure`
}
title: 'Settings',
link: `${teamOrUserSlug}/${projectSlug}/settings`,
alternatives: getProjectSettingsMenu({ slug: projectSlug } as Project, team).flatMap(e => e.link),
},
];
}
// Team menu
Expand All @@ -132,6 +135,7 @@ export default function Menu() {
teamSettingsList.push({
title: 'Settings',
link: `/t/${team.slug}/settings`,
alternatives: getTeamSettingsMenu(team).flatMap(e => e.link),
})
}

Expand Down Expand Up @@ -174,7 +178,7 @@ export default function Menu() {
const renderTeamMenu = () => {
return (
<div className="flex p-1 pl-3 ">
{ projectName && <div className="flex h-full rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 px-2 py-1">
{ projectSlug && <div className="flex h-full rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 px-2 py-1">
<Link to={team ? `/t/${team.slug}/projects` : `/projects`}>
<span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{team?.name || userFullName}</span>
</Link>
Expand Down Expand Up @@ -214,21 +218,21 @@ export default function Menu() {
}
]}>
<div className="flex h-full px-2 py-1 space-x-3.5">
{ !projectName && <span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{team?.name || userFullName}</span>}
<img className="filter-grayscale" style={{marginTop: 5, marginBottom: 5}} src={CaretUpDown} />
{ !projectSlug && <span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{team?.name || userFullName}</span>}
<img alt="" aria-label="Toggle team selection menu" className="filter-grayscale" style={{marginTop: 5, marginBottom: 5}} src={CaretUpDown} />
</div>
</ContextMenu>
</div>
{ projectName && (
{ projectSlug && (
<div className="flex h-full rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 px-2 py-1">
<Link to={`${teamOrUserSlug}/${projectName}${prebuildId ? "/prebuilds" : ""}`}>
<span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{projectName}</span>
<Link to={`${teamOrUserSlug}/${projectSlug}${prebuildId ? "/prebuilds" : ""}`}>
<span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{projectSlug}</span>
</Link>
</div>
)}
{ prebuildId && (
<div className="flex h-full ml-2 py-1">
<img className="mr-3 filter-grayscale m-auto transform -rotate-90" src={CaretDown} />
<img alt="" className="mr-3 filter-grayscale m-auto transform -rotate-90" src={CaretDown} />
<span className="text-base text-gray-600 dark:text-gray-400 font-semibold">{prebuildId}</span>
</div>
)}
Expand Down
4 changes: 2 additions & 2 deletions components/dashboard/src/components/CheckBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
function CheckBox(props: {
name?: string,
title: string | React.ReactNode,
desc: string,
desc: string | React.ReactNode,
checked: boolean,
disabled?: boolean,
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void
Expand All @@ -24,7 +24,7 @@ function CheckBox(props: {

const checkboxId = `checkbox-${props.title}-${String(Math.random())}`;

return <div className="flex mt-4">
return <div className="flex mt-4 max-w-2xl">
<input className={"h-4 w-4 focus:ring-0 mt-1 rounded cursor-pointer bg-transparent border-2 dark:filter-invert border-gray-800 dark:border-gray-900 focus:border-gray-900 dark:focus:border-gray-800 " + (props.checked ? 'bg-gray-800 dark:bg-gray-900' : '')} type="checkbox"
id={checkboxId}
{...inputProps}
Expand Down
3 changes: 2 additions & 1 deletion components/dashboard/src/components/PrebuildLogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function PrebuildLogs(props: PrebuildLogsProps) {

useEffect(() => {
const disposables = new DisposableCollection();
setWorkspaceInstance(undefined);
(async () => {
if (!props.workspaceId) {
return;
Expand Down Expand Up @@ -120,7 +121,7 @@ export default function PrebuildLogs(props: PrebuildLogsProps) {
if (workspaceInstance?.status.conditions.failed) {
setError(new Error(workspaceInstance.status.conditions.failed));
}
}, [ workspaceInstance?.status.phase ]);
}, [ props.workspaceId, workspaceInstance?.status.phase ]);

return <Suspense fallback={<div />}>
<WorkspaceLogs classes="h-full w-full" logsEmitter={logsEmitter} errorMessage={error?.message} />
Expand Down
28 changes: 5 additions & 23 deletions components/dashboard/src/projects/ConfigureProject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import PrebuildLogs from "../components/PrebuildLogs";
import TabMenuItem from "../components/TabMenuItem";
import { getGitpodService } from "../service/service";
import { getCurrentTeam, TeamsContext } from "../teams/teams-context";
import Header from "../components/Header";
import Spinner from "../icons/Spinner.svg";
import NoAccess from "../icons/NoAccess.svg";
import PrebuildLogsEmpty from "../images/prebuild-logs-empty.svg";
Expand All @@ -20,28 +19,12 @@ import { ThemeContext } from "../theme-context";
import { PrebuildInstanceStatus } from "./Prebuilds";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { openAuthorizeWindow } from "../provider-utils";
import { PageWithSubMenu } from "../components/PageWithSubMenu";
import { getProjectSettingsMenu } from "./ProjectSettings";

const MonacoEditor = React.lazy(() => import('../components/MonacoEditor'));

const TASKS = {
NPM: `tasks:
- init: npm install
command: npm run start`,
Yarn: `tasks:
- init: yarn install
command: yarn run start`,
Go: `tasks:
- init: go get && go build ./... && go test ./...
command: go run`,
Rails: `tasks:
- init: bin/setup
command: bin/rails server`,
Rust: `tasks:
- init: cargo build
command: cargo watch -x run`,
Python: `tasks:
- init: pip install -r requirements.txt
command: python main.py`,
Other: `tasks:
- init: |
echo 'TODO: build project'
Expand Down Expand Up @@ -244,9 +227,8 @@ export default function () {
redirectToNewWorkspace();
}

return <>
<Header title="Configuration" subtitle="View and edit project configuration." />
<div className="app-container mt-8 flex space-x-4">
return <PageWithSubMenu subMenu={getProjectSettingsMenu(project, team)} title="Configuration" subtitle="View and edit project configuration.">
<div className="flex space-x-4">
<div className="flex-1 h-96 rounded-xl overflow-hidden relative flex flex-col">
<div className="flex bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-600 px-6 pt-3">
<TabMenuItem name=".gitpod.yml" selected={selectedEditor === '.gitpod.yml'} onClick={() => setSelectedEditor('.gitpod.yml')} />
Copy link
Contributor

@gtsiolis gtsiolis Dec 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: Eventually we will most probably need to separate project addition and project configuration steps so that these can be done separastely. 💭

question: I could be missing something but do you think it makes sense to remove the latest prebuild block here and only keep the configuration n now that we've removed this from the onboarding flow? Later on we could also handle cases where no configuration is added yet. Feel free to also leave this out of the scope of this PR. 🏓

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'm happy to remove the "Prebuild Runner", i.e. the right part of the split screen, and keep only the editor.

However, this feels like a follow-up issue (because I think we also need to fix the copy).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, this feels like a follow-up issue

Agree! Let's keep it as is for now. ➿

Expand Down Expand Up @@ -301,7 +283,7 @@ export default function () {
</div>
</div>
</div>
</>;
</PageWithSubMenu>;
}

function EditorMessage(props: { heading: string, message: string, type: 'success' | 'warning' }) {
Expand Down
16 changes: 11 additions & 5 deletions components/dashboard/src/projects/Prebuild.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@ export default function () {
? await getGitpodService().server.getTeamProjects(team.id)
: await getGitpodService().server.getUserProjects());

const project = projectSlug && projects.find(
p => p.slug ? p.slug === projectSlug :
p.name === projectSlug);

const project = projectSlug && projects.find(p => !!p.slug
? p.slug === projectSlug
: p.name === projectSlug);
if (!project) {
console.error(new Error(`Project not found! (teamId: ${team?.id}, projectName: ${projectSlug})`));
return;
}

const prebuilds = await getGitpodService().server.findPrebuilds({
projectId: project.id,
prebuildId
});
setPrebuild(prebuilds[0]);
})();
}, [ teams ]);
}, [prebuildId, projectSlug, team, teams]);

const renderTitle = () => {
if (!prebuild) {
Expand All @@ -77,6 +77,12 @@ export default function () {
<div className="my-auto">
<p className="text-gray-500 dark:text-gray-50">{shortCommitMessage(prebuild.info.changeTitle)}</p>
</div>
{!!prebuild.info.basedOnPrebuildId && <>
<p className="mx-2 my-auto">·</p>
<div className="my-auto">
<p className="text-gray-500 dark:text-gray-50">Incremental Prebuild (<a className="gp-link" title={prebuild.info.basedOnPrebuildId} href={`./${prebuild.info.basedOnPrebuildId}`}>base</a>)</p>
</div>
</>}
</div>)
};

Expand Down
43 changes: 28 additions & 15 deletions components/dashboard/src/projects/Prebuilds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,21 +242,26 @@ export function prebuildStatusIcon(prebuild?: PrebuildWithStatus) {
switch (prebuild?.status) {
case undefined: // Fall through
case "queued":
return <img className="h-4 w-4" src={StatusPaused} />;
return <img alt="" className="h-4 w-4" src={StatusPaused} />;
case "building":
return <img className="h-4 w-4" src={StatusRunning} />;
return <img alt="" className="h-4 w-4" src={StatusRunning} />;
case "aborted":
return <img className="h-4 w-4" src={StatusCanceled} />;
return <img alt="" className="h-4 w-4" src={StatusCanceled} />;
case "timeout":
return <img className="h-4 w-4" src={StatusFailed} />;
return <img alt="" className="h-4 w-4" src={StatusFailed} />;
case "available":
if (prebuild?.error) {
return <img className="h-4 w-4" src={StatusFailed} />;
return <img alt="" className="h-4 w-4" src={StatusFailed} />;
}
return <img className="h-4 w-4" src={StatusDone} />;
return <img alt="" className="h-4 w-4" src={StatusDone} />;
}
}

function formatDuration(milliseconds: number) {
const hours = Math.floor(milliseconds / (1000 * 60 * 60));
return (hours > 0 ? `${hours}:` : '') + moment(milliseconds).format('mm:ss');
}

export function PrebuildInstanceStatus(props: { prebuildInstance?: WorkspaceInstance }) {
let status = <></>;
let details = <></>;
Expand All @@ -267,11 +272,11 @@ export function PrebuildInstanceStatus(props: { prebuildInstance?: WorkspaceInst
case 'creating': // Fall through
case 'unknown':
status = <div className="flex space-x-1 items-center text-yellow-600">
<img className="h-4 w-4" src={StatusPaused} />
<img alt="" className="h-4 w-4" src={StatusPaused} />
<span>PENDING</span>
</div>;
details = <div className="flex space-x-1 items-center text-gray-400">
<img className="h-4 w-4 animate-spin" src={Spinner} />
<img alt="" className="h-4 w-4 animate-spin" src={Spinner} />
<span>Preparing prebuild ...</span>
</div>;
break;
Expand All @@ -280,43 +285,51 @@ export function PrebuildInstanceStatus(props: { prebuildInstance?: WorkspaceInst
case 'interrupted': // Fall through
case 'stopping':
status = <div className="flex space-x-1 items-center text-blue-600">
<img className="h-4 w-4" src={StatusRunning} />
<img alt="" className="h-4 w-4" src={StatusRunning} />
<span>RUNNING</span>
</div>;
details = <div className="flex space-x-1 items-center text-gray-400">
<img className="h-4 w-4 animate-spin" src={Spinner} />
<img alt="" className="h-4 w-4 animate-spin" src={Spinner} />
<span>Prebuild in progress ...</span>
</div>;
break;
case 'stopped':
status = <div className="flex space-x-1 items-center text-green-600">
<img className="h-4 w-4" src={StatusDone} />
<img alt="" className="h-4 w-4" src={StatusDone} />
<span>READY</span>
</div>;
details = <div className="flex space-x-1 items-center text-gray-400">
<img className="h-4 w-4 filter-grayscale" src={StatusRunning} />
<img alt="" className="h-4 w-4 filter-grayscale" src={StatusRunning} />
<span>{!!props.prebuildInstance?.stoppedTime
? `${Math.round(((new Date(props.prebuildInstance.stoppedTime).getTime()) - (new Date(props.prebuildInstance.creationTime).getTime())) / 1000)}s`
? formatDuration((new Date(props.prebuildInstance.stoppedTime).getTime()) - (new Date(props.prebuildInstance.creationTime).getTime()))
: '...'}</span>
</div>;
break;
}
if (props.prebuildInstance?.status.conditions.stoppedByRequest) {
status = <div className="flex space-x-1 items-center text-gray-500">
<img className="h-4 w-4" src={StatusCanceled} />
<img alt="" className="h-4 w-4" src={StatusCanceled} />
<span>CANCELED</span>
</div>;
details = <div className="flex space-x-1 items-center text-gray-400">
<span>Prebuild canceled</span>
</div>;
} else if (props.prebuildInstance?.status.conditions.failed || props.prebuildInstance?.status.conditions.headlessTaskFailed) {
status = <div className="flex space-x-1 items-center text-gitpod-red">
<img className="h-4 w-4" src={StatusFailed} />
<img alt="" className="h-4 w-4" src={StatusFailed} />
<span>FAILED</span>
</div>;
details = <div className="flex space-x-1 items-center text-gray-400">
<span>Prebuild failed</span>
</div>;
} else if (props.prebuildInstance?.status.conditions.timeout) {
status = <div className="flex space-x-1 items-center text-gitpod-red">
<img alt="" className="h-4 w-4" src={StatusFailed} />
<span>FAILED</span>
</div>;
details = <div className="flex space-x-1 items-center text-gray-400">
<span>Prebuild timed out</span>
</div>;
}
return <div className="flex flex-col space-y-1 justify-center text-sm font-semibold">
<div>{status}</div>
Expand Down
Loading