Skip to content

Commit a8bb384

Browse files
committed
[prebuild] Support opening a specfic prebuild
1 parent 6fb1ac0 commit a8bb384

File tree

6 files changed

+82
-7
lines changed

6 files changed

+82
-7
lines changed

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,14 @@ export default function () {
181181
) : prebuild?.status === "available" ? (
182182
<a
183183
className="my-auto"
184-
href={gitpodHostUrl.withContext(`${prebuild?.info.changeUrl}`).toString()}
184+
href={gitpodHostUrl
185+
.withContext(`open-prebuild/${prebuild?.info.id}/${prebuild?.info.changeUrl}`)
186+
.toString()}
185187
>
186-
<button>New Workspace ({prebuild?.info.branch})</button>
188+
<button>New Workspace (with this prebuild)</button>
187189
</a>
188190
) : (
189-
<button disabled={true}>New Workspace ({prebuild?.info.branch})</button>
191+
<button disabled={true}>New Workspace (with this prebuild)</button>
190192
)}
191193
</PrebuildLogs>
192194
</div>

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

+10
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,16 @@ export namespace AdditionalContentContext {
12001200
}
12011201
}
12021202

1203+
export interface OpenPrebuildContext extends WorkspaceContext {
1204+
openPrebuildID: string;
1205+
}
1206+
1207+
export namespace OpenPrebuildContext {
1208+
export function is(ctx: any): ctx is OpenPrebuildContext {
1209+
return "openPrebuildID" in ctx;
1210+
}
1211+
}
1212+
12031213
export interface CommitContext extends WorkspaceContext, GitCheckoutInfo {
12041214
/** @deprecated Moved to .repository.cloneUrl, left here for backwards-compatibility for old workspace contextes in the DB */
12051215
cloneUrl?: string;

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

+15-4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
TeamMemberRole,
4848
WORKSPACE_TIMEOUT_DEFAULT_SHORT,
4949
PrebuildEvent,
50+
OpenPrebuildContext,
5051
} from "@gitpod/gitpod-protocol";
5152
import { ResponseError } from "vscode-jsonrpc";
5253
import {
@@ -963,9 +964,19 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
963964

964965
const logCtx: LogContext = { userId: user.id };
965966
const cloneUrl = context.repository.cloneUrl;
966-
const prebuiltWorkspace = await this.workspaceDb
967-
.trace(ctx)
968-
.findPrebuiltWorkspaceByCommit(cloneUrl, commitSHAs);
967+
let prebuiltWorkspace: PrebuiltWorkspace | undefined;
968+
if (OpenPrebuildContext.is(context)) {
969+
prebuiltWorkspace = await this.workspaceDb.trace(ctx).findPrebuildByID(context.openPrebuildID);
970+
if (prebuiltWorkspace?.cloneURL !== cloneUrl) {
971+
// prevent users from opening arbitrary prebuilds this way - they must match the clone URL so that the resource guards are correct.
972+
return;
973+
}
974+
} else {
975+
prebuiltWorkspace = await this.workspaceDb
976+
.trace(ctx)
977+
.findPrebuiltWorkspaceByCommit(cloneUrl, commitSHAs);
978+
}
979+
969980
const logPayload = { mode, cloneUrl, commit: commitSHAs, prebuiltWorkspace };
970981
log.debug(logCtx, "Looking for prebuilt workspace: ", logPayload);
971982
if (!prebuiltWorkspace) {
@@ -994,7 +1005,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
9941005
const makeResult = (instanceID: string): WorkspaceCreationResult => {
9951006
return <WorkspaceCreationResult>{
9961007
runningWorkspacePrebuild: {
997-
prebuildID: prebuiltWorkspace.id,
1008+
prebuildID: prebuiltWorkspace!.id,
9981009
workspaceID,
9991010
instanceID,
10001011
starting: "queued",

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

+13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
PrebuiltWorkspace,
2222
WorkspaceConfig,
2323
WorkspaceImageSource,
24+
OpenPrebuildContext,
2425
} from "@gitpod/gitpod-protocol";
2526
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
2627
import { LicenseEvaluator } from "@gitpod/licensor/lib";
@@ -373,6 +374,18 @@ export class WorkspaceFactoryEE extends WorkspaceFactory {
373374
config._featureFlags = (config._featureFlags || []).concat(["persistent_volume_claim"]);
374375
}
375376

377+
if (OpenPrebuildContext.is(context.originalContext)) {
378+
// Because of incremental prebuilds, createForContext will take over the original context.
379+
// To ensure we get the right commit when forcing a prebuild, we force the context here.
380+
context.originalContext = buildWorkspace.context;
381+
382+
if (CommitContext.is(context.originalContext)) {
383+
// We force the checkout of the revision rather than the ref/branch.
384+
// Otherwise we'd the correct prebuild with the "wrong" Git status.
385+
delete context.originalContext.ref;
386+
}
387+
}
388+
376389
const id = await this.generateWorkspaceID(context);
377390
const newWs: Workspace = {
378391
id,

Diff for: components/server/src/container-module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ import { LivenessController } from "./liveness/liveness-controller";
113113
import { IDEServiceClient, IDEServiceDefinition } from "@gitpod/ide-service-api/lib/ide.pb";
114114
import { prometheusClientMiddleware } from "@gitpod/gitpod-protocol/lib/util/nice-grpc";
115115
import { UsageService } from "./user/usage-service";
116+
import { OpenPrebuildPrefixContextParser } from "./workspace/open-prebuild-prefix-context-parser";
116117

117118
export const productionContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => {
118119
bind(Config).toConstantValue(ConfigFile.fromFile());
@@ -189,6 +190,7 @@ export const productionContainerModule = new ContainerModule((bind, unbind, isBo
189190
bind(IPrefixContextParser).to(EnvvarPrefixParser).inSingletonScope();
190191
bind(IPrefixContextParser).to(ImageBuildPrefixContextParser).inSingletonScope();
191192
bind(IPrefixContextParser).to(AdditionalContentPrefixContextParser).inSingletonScope();
193+
bind(IPrefixContextParser).to(OpenPrebuildPrefixContextParser).inSingletonScope();
192194

193195
bind(GitTokenScopeGuesser).toSelf().inSingletonScope();
194196
bind(GitTokenValidator).toSelf().inSingletonScope();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { User, WorkspaceContext } from "@gitpod/gitpod-protocol";
8+
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
9+
import { OpenPrebuildContext } from "@gitpod/gitpod-protocol/src/protocol";
10+
import { inject, injectable } from "inversify";
11+
import { Config } from "../config";
12+
import { IPrefixContextParser } from "./context-parser";
13+
14+
@injectable()
15+
export class OpenPrebuildPrefixContextParser implements IPrefixContextParser {
16+
@inject(Config) protected readonly config: Config;
17+
static PREFIX = /^\/?open-prebuild\/([^\/]*)\//;
18+
19+
findPrefix(user: User, context: string): string | undefined {
20+
const result = OpenPrebuildPrefixContextParser.PREFIX.exec(context);
21+
if (!result) {
22+
return undefined;
23+
}
24+
return result[0];
25+
}
26+
27+
public async handle(user: User, prefix: string, context: WorkspaceContext): Promise<WorkspaceContext> {
28+
const match = OpenPrebuildPrefixContextParser.PREFIX.exec(prefix);
29+
if (!match) {
30+
log.error("Could not parse prefix " + prefix);
31+
return context;
32+
}
33+
34+
(context as OpenPrebuildContext).openPrebuildID = match[1];
35+
return context;
36+
}
37+
}

0 commit comments

Comments
 (0)