Skip to content

Add message: Prebuilds have been paused #14081

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 1 commit into from
Oct 28, 2022
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
51 changes: 48 additions & 3 deletions components/dashboard/src/projects/Project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import { getGitpodService, gitpodHostUrl } from "../service/service";
import { TeamsContext, getCurrentTeam } from "../teams/teams-context";
import { prebuildStatusIcon, prebuildStatusLabel } from "./Prebuilds";
import { shortCommitMessage, toRemoteURL } from "./render-utils";
import Spinner from "../icons/Spinner.svg";
import { ReactComponent as Spinner } from "../icons/Spinner.svg";
import NoAccess from "../icons/NoAccess.svg";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { openAuthorizeWindow } from "../provider-utils";
import Alert from "../components/Alert";

export default function () {
const location = useLocation();
Expand All @@ -34,6 +35,8 @@ export default function () {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isLoadingBranches, setIsLoadingBranches] = useState<boolean>(false);
const [branches, setBranches] = useState<Project.BranchDetails[]>([]);
const [isConsideredInactive, setIsConsideredInactive] = useState<boolean>(false);
const [isResuming, setIsResuming] = useState<boolean>(false);
const [prebuilds, setPrebuilds] = useState<Map<string, PrebuildWithStatus | undefined>>(new Map());
const [prebuildLoaders] = useState<Set<string>>(new Set());

Expand Down Expand Up @@ -91,6 +94,7 @@ export default function () {
// default branch on top of the rest
const branches = details.branches.sort((a, b) => (b.isDefault as any) - (a.isDefault as any)) || [];
setBranches(branches);
setIsConsideredInactive(!!details.isConsideredInactive);
}
} finally {
setIsLoadingBranches(false);
Expand Down Expand Up @@ -184,6 +188,22 @@ export default function () {
return date ? dayjs(date).fromNow() : "";
};

const resumePrebuilds = async () => {
if (!project) {
return;
}
try {
setIsResuming(true);
const response = await getGitpodService().server.triggerPrebuild(project.id, null);
setIsConsideredInactive(false);
history.push(`/${!!team ? "t/" + team.slug : "projects"}/${projectSlug}/${response.prebuildId}`);
} catch (error) {
console.error(error);
} finally {
setIsResuming(false);
}
};

return (
<>
<Header
Expand Down Expand Up @@ -243,7 +263,7 @@ export default function () {
<div className="flex-1" />
{isLoading && (
<div className="flex justify-center w-1/12">
<img alt="" className="h-4 w-4 animate-spin" src={Spinner} />
<Spinner className="h-4 w-4 animate-spin" />
</div>
)}
</div>
Expand All @@ -259,9 +279,34 @@ export default function () {
<span>Prebuild</span>
</ItemField>
</Item>
{isConsideredInactive && (
<Alert
type={"warning"}
onClose={() => {}}
showIcon={true}
className="flex rounded mb-2 w-full"
>
To reduce resource usage, prebuilds are automatically paused when not used for a
workspace after 7 days.{" "}
{isResuming && (
<>
Resuming <Spinner className="h-4 w-4 animate-spin" />
</>
)}
{!isResuming && (
<a
href="javascript:void(0)"
className="gp-link hover:text-gray-600"
onClick={() => resumePrebuilds()}
>
Resume prebuilds
</a>
)}
</Alert>
)}
{isLoadingBranches && (
<div className="flex items-center justify-center space-x-2 text-gray-400 text-sm pt-16 pb-40">
<img className="h-4 w-4 animate-spin" src={Spinner} />
<Spinner className="h-4 w-4 animate-spin" />
<span>Fetching repository branches...</span>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export namespace Project {

export interface Overview {
branches: BranchDetails[];
isConsideredInactive?: boolean;
}

export namespace Overview {
Expand Down
9 changes: 1 addition & 8 deletions components/server/ee/src/prebuilds/prebuild-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,14 +394,7 @@ export class PrebuildManager {
}

private async shouldSkipInactiveProject(project: Project): Promise<boolean> {
const usage = await this.projectService.getProjectUsage(project.id);
if (!usage?.lastWorkspaceStart) {
return false;
}
const now = Date.now();
const lastUse = new Date(usage.lastWorkspaceStart).getTime();
const inactiveProjectTime = 1000 * 60 * 60 * 24 * 7 * 1; // 1 week
return now - lastUse > inactiveProjectTime;
return await this.projectService.isProjectConsideredInactive(project.id);
}

private async shouldSkipInactiveRepository(ctx: TraceContext, cloneURL: string): Promise<boolean> {
Expand Down
5 changes: 5 additions & 0 deletions components/server/ee/src/workspace/gitpod-server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2620,6 +2620,11 @@ export class GitpodServerEEImpl extends GitpodServerImpl {

const context = (await this.contextParser.handle(ctx, user, contextURL)) as CommitContext;

// HACK: treat manual triggered prebuild as a reset for the inactivity state
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seem like a hack to me 👍 (especially if we rename lastWorkspaceStart to something more accurate like lastUserActivity)

Suggested change
// HACK: treat manual triggered prebuild as a reset for the inactivity state
// Treat manual triggered prebuild as a reset for the inactivity state

await this.projectDB.updateProjectUsage(project.id, {
lastWorkspaceStart: new Date().toISOString(),
});

const prebuild = await this.prebuildManager.startPrebuild(ctx, {
context,
user,
Expand Down
11 changes: 11 additions & 0 deletions components/server/src/projects/projects-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,17 @@ export class ProjectsService {
return this.projectDB.getProjectUsage(projectId);
}

async isProjectConsideredInactive(projectId: string): Promise<boolean> {
const usage = await this.getProjectUsage(projectId);
if (!usage?.lastWorkspaceStart) {
return false;
}
const now = Date.now();
const lastUse = new Date(usage.lastWorkspaceStart).getTime();
const inactiveProjectTime = 1000 * 60 * 60 * 24 * 7 * 1; // 1 week
return now - lastUse > inactiveProjectTime;
}

async getPrebuildEvents(cloneUrl: string): Promise<PrebuildEvent[]> {
const events = await this.webhookEventDB.findByCloneUrl(cloneUrl, 100);
return events.map((we) => ({
Expand Down
6 changes: 5 additions & 1 deletion components/server/src/workspace/gitpod-server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2345,7 +2345,11 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
}
await this.guardProjectOperation(user, projectId, "get");
try {
return await this.projectsService.getProjectOverviewCached(user, project);
const result = await this.projectsService.getProjectOverviewCached(user, project);
if (result) {
result.isConsideredInactive = await this.projectsService.isProjectConsideredInactive(project.id);
}
return result;
} catch (error) {
if (UnauthorizedError.is(error)) {
throw new ResponseError(ErrorCodes.NOT_AUTHENTICATED, "Unauthorized", error.data);
Expand Down