Skip to content

Commit f9717b1

Browse files
AlexTugarevroboquat
authored andcommitted
[projects] add message: Prebuilds have been paused
1 parent 71dfccd commit f9717b1

File tree

6 files changed

+71
-12
lines changed

6 files changed

+71
-12
lines changed

Diff for: components/dashboard/src/projects/Project.tsx

+48-3
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ import { getGitpodService, gitpodHostUrl } from "../service/service";
1414
import { TeamsContext, getCurrentTeam } from "../teams/teams-context";
1515
import { prebuildStatusIcon, prebuildStatusLabel } from "./Prebuilds";
1616
import { shortCommitMessage, toRemoteURL } from "./render-utils";
17-
import Spinner from "../icons/Spinner.svg";
17+
import { ReactComponent as Spinner } from "../icons/Spinner.svg";
1818
import NoAccess from "../icons/NoAccess.svg";
1919
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
2020
import { openAuthorizeWindow } from "../provider-utils";
21+
import Alert from "../components/Alert";
2122

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

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

191+
const resumePrebuilds = async () => {
192+
if (!project) {
193+
return;
194+
}
195+
try {
196+
setIsResuming(true);
197+
const response = await getGitpodService().server.triggerPrebuild(project.id, null);
198+
setIsConsideredInactive(false);
199+
history.push(`/${!!team ? "t/" + team.slug : "projects"}/${projectSlug}/${response.prebuildId}`);
200+
} catch (error) {
201+
console.error(error);
202+
} finally {
203+
setIsResuming(false);
204+
}
205+
};
206+
187207
return (
188208
<>
189209
<Header
@@ -243,7 +263,7 @@ export default function () {
243263
<div className="flex-1" />
244264
{isLoading && (
245265
<div className="flex justify-center w-1/12">
246-
<img alt="" className="h-4 w-4 animate-spin" src={Spinner} />
266+
<Spinner className="h-4 w-4 animate-spin" />
247267
</div>
248268
)}
249269
</div>
@@ -259,9 +279,34 @@ export default function () {
259279
<span>Prebuild</span>
260280
</ItemField>
261281
</Item>
282+
{isConsideredInactive && (
283+
<Alert
284+
type={"warning"}
285+
onClose={() => {}}
286+
showIcon={true}
287+
className="flex rounded mb-2 w-full"
288+
>
289+
To reduce resource usage, prebuilds are automatically paused when not used for a
290+
workspace after 7 days.{" "}
291+
{isResuming && (
292+
<>
293+
Resuming <Spinner className="h-4 w-4 animate-spin" />
294+
</>
295+
)}
296+
{!isResuming && (
297+
<a
298+
href="javascript:void(0)"
299+
className="gp-link hover:text-gray-600"
300+
onClick={() => resumePrebuilds()}
301+
>
302+
Resume prebuilds
303+
</a>
304+
)}
305+
</Alert>
306+
)}
262307
{isLoadingBranches && (
263308
<div className="flex items-center justify-center space-x-2 text-gray-400 text-sm pt-16 pb-40">
264-
<img className="h-4 w-4 animate-spin" src={Spinner} />
309+
<Spinner className="h-4 w-4 animate-spin" />
265310
<span>Fetching repository branches...</span>
266311
</div>
267312
)}

Diff for: components/gitpod-protocol/src/teams-projects-protocol.ts

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export namespace Project {
4545

4646
export interface Overview {
4747
branches: BranchDetails[];
48+
isConsideredInactive?: boolean;
4849
}
4950

5051
export namespace Overview {

Diff for: components/server/ee/src/prebuilds/prebuild-manager.ts

+1-8
Original file line numberDiff line numberDiff line change
@@ -394,14 +394,7 @@ export class PrebuildManager {
394394
}
395395

396396
private async shouldSkipInactiveProject(project: Project): Promise<boolean> {
397-
const usage = await this.projectService.getProjectUsage(project.id);
398-
if (!usage?.lastWorkspaceStart) {
399-
return false;
400-
}
401-
const now = Date.now();
402-
const lastUse = new Date(usage.lastWorkspaceStart).getTime();
403-
const inactiveProjectTime = 1000 * 60 * 60 * 24 * 7 * 1; // 1 week
404-
return now - lastUse > inactiveProjectTime;
397+
return await this.projectService.isProjectConsideredInactive(project.id);
405398
}
406399

407400
private async shouldSkipInactiveRepository(ctx: TraceContext, cloneURL: string): Promise<boolean> {

Diff for: components/server/ee/src/workspace/gitpod-server-impl.ts

+5
Original file line numberDiff line numberDiff line change
@@ -2635,6 +2635,11 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
26352635

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

2638+
// HACK: treat manual triggered prebuild as a reset for the inactivity state
2639+
await this.projectDB.updateProjectUsage(project.id, {
2640+
lastWorkspaceStart: new Date().toISOString(),
2641+
});
2642+
26382643
const prebuild = await this.prebuildManager.startPrebuild(ctx, {
26392644
context,
26402645
user,

Diff for: components/server/src/projects/projects-service.ts

+11
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,17 @@ export class ProjectsService {
279279
return this.projectDB.getProjectUsage(projectId);
280280
}
281281

282+
async isProjectConsideredInactive(projectId: string): Promise<boolean> {
283+
const usage = await this.getProjectUsage(projectId);
284+
if (!usage?.lastWorkspaceStart) {
285+
return false;
286+
}
287+
const now = Date.now();
288+
const lastUse = new Date(usage.lastWorkspaceStart).getTime();
289+
const inactiveProjectTime = 1000 * 60 * 60 * 24 * 7 * 1; // 1 week
290+
return now - lastUse > inactiveProjectTime;
291+
}
292+
282293
async getPrebuildEvents(cloneUrl: string): Promise<PrebuildEvent[]> {
283294
const events = await this.webhookEventDB.findByCloneUrl(cloneUrl, 100);
284295
return events.map((we) => ({

Diff for: components/server/src/workspace/gitpod-server-impl.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -2357,7 +2357,11 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
23572357
}
23582358
await this.guardProjectOperation(user, projectId, "get");
23592359
try {
2360-
return await this.projectsService.getProjectOverviewCached(user, project);
2360+
const result = await this.projectsService.getProjectOverviewCached(user, project);
2361+
if (result) {
2362+
result.isConsideredInactive = await this.projectsService.isProjectConsideredInactive(project.id);
2363+
}
2364+
return result;
23612365
} catch (error) {
23622366
if (UnauthorizedError.is(error)) {
23632367
throw new ResponseError(ErrorCodes.NOT_AUTHENTICATED, "Unauthorized", error.data);

0 commit comments

Comments
 (0)