Skip to content

Commit 09152d3

Browse files
committed
[inc ws] skip prebuilding
1 parent ea82c11 commit 09152d3

File tree

9 files changed

+120
-48
lines changed

9 files changed

+120
-48
lines changed

components/dashboard/src/components/CheckBox.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ function CheckBox(props: {
3737
<div className="flex flex-col ml-2">
3838
<label
3939
htmlFor={checkboxId}
40-
className="text-gray-800 dark:text-gray-100 text-md font-semibold cursor-pointer tracking-wide"
40+
className={
41+
"text-md font-semibold cursor-pointer tracking-wide " +
42+
(inputProps.disabled ? "text-gray-500 dark:text-gray-400" : "text-gray-800 dark:text-gray-100")
43+
}
4144
>
4245
{props.title}
4346
</label>

components/dashboard/src/projects/ProjectSettings.tsx

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,7 @@ export default function () {
6363
<ProjectSettingsPage project={project}>
6464
<h3>Prebuilds</h3>
6565
<CheckBox
66-
title={
67-
<span>
68-
Enable Incremental Prebuilds{" "}
69-
<PillLabel type="warn" className="font-semibold mt-2 ml-2 py-0.5 px-2 self-center">
70-
Beta
71-
</PillLabel>
72-
</span>
73-
}
66+
title={<span>Enable Incremental Prebuilds </span>}
7467
desc={
7568
<span>
7669
When possible, use an earlier successful prebuild as a base to create new prebuilds. This can
@@ -90,28 +83,73 @@ export default function () {
9083
checked={!project.settings?.keepOutdatedPrebuildsRunning}
9184
onChange={({ target }) => updateProjectSettings({ keepOutdatedPrebuildsRunning: !target.checked })}
9285
/>
93-
<h3 className="mt-12">Workspace Starts</h3>
9486
<CheckBox
95-
title={<span>Incrementally update from old prebuilds</span>}
87+
title={
88+
<span>
89+
Use Last Successful Prebuild{" "}
90+
<PillLabel type="warn" className="font-semibold mt-2 ml-2 py-0.5 px-2 self-center">
91+
Alpha
92+
</PillLabel>
93+
</span>
94+
}
9695
desc={
9796
<span>
98-
Whether new workspaces can be started based on prebuilds that ran on older Git commits and get
99-
incrementally updated.
97+
Skip waiting for prebuilds in progress and use the last successful prebuild from previous
98+
commits on the same branch.
10099
</span>
101100
}
102101
checked={!!project.settings?.allowUsingPreviousPrebuilds}
103-
onChange={({ target }) => updateProjectSettings({ allowUsingPreviousPrebuilds: target.checked })}
102+
onChange={({ target }) =>
103+
updateProjectSettings({
104+
allowUsingPreviousPrebuilds: target.checked,
105+
// we are disabling prebuild cancellation when incremental workspaces are enabled
106+
keepOutdatedPrebuildsRunning: target.checked || project?.settings?.keepOutdatedPrebuildsRunning,
107+
})
108+
}
104109
/>
110+
<div className="flex mt-4 max-w-2xl">
111+
<div className="flex flex-col ml-6">
112+
<label
113+
htmlFor="prebuildNthCommit"
114+
className="text-gray-800 dark:text-gray-100 text-md font-semibold cursor-pointer tracking-wide"
115+
>
116+
Skip Prebuilds
117+
</label>
118+
<input
119+
type="number"
120+
id="prebuildNthCommit"
121+
min="0"
122+
max="100"
123+
step="5"
124+
className="mt-2"
125+
disabled={!project.settings?.allowUsingPreviousPrebuilds}
126+
value={
127+
project.settings?.prebuildEveryNthCommit === undefined
128+
? 0
129+
: project.settings?.prebuildEveryNthCommit
130+
}
131+
onChange={({ target }) =>
132+
updateProjectSettings({
133+
prebuildEveryNthCommit: Math.abs(Math.min(Number.parseInt(target.value), 100)) || 0,
134+
})
135+
}
136+
/>
137+
<div className="text-gray-500 dark:text-gray-400 text-sm mt-2">
138+
The number of commits that are skipped between prebuilds.
139+
</div>
140+
</div>
141+
</div>
142+
105143
{showPersistentVolumeClaimUI && (
106144
<>
107145
<br></br>
108-
<h3 className="mt-12">Workspace Persistence</h3>
146+
<h3 className="mt-12">Workspaces</h3>
109147
<CheckBox
110148
title={
111149
<span>
112150
Enable Persistent Volume Claim{" "}
113151
<PillLabel type="warn" className="font-semibold mt-2 ml-2 py-0.5 px-2 self-center">
114-
Experimental
152+
Alpha
115153
</PillLabel>
116154
</span>
117155
}

components/gitpod-protocol/src/teams-projects-protocol.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export interface ProjectSettings {
1919
keepOutdatedPrebuildsRunning?: boolean;
2020
// whether new workspaces can start on older prebuilds and incrementally update
2121
allowUsingPreviousPrebuilds?: boolean;
22+
// how many commits in the commit history a prebuild is good (undefined and 0 means every commit is prebuilt)
23+
prebuildEveryNthCommit?: number;
2224
}
2325

2426
export interface Project {
@@ -122,7 +124,6 @@ export interface StartPrebuildResult {
122124
wsid: string;
123125
done: boolean;
124126
}
125-
126127
export interface Team {
127128
id: string;
128129
name: string;

components/server/ee/src/prebuilds/bitbucket-app.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,14 @@ export class BitbucketApp {
156156
{ span },
157157
{ user, project: projectAndOwner.project, context, commitInfo },
158158
);
159-
await this.webhookEvents.updateEvent(event.id, {
160-
prebuildStatus: "prebuild_triggered",
161-
status: "processed",
162-
prebuildId: ws.prebuildId,
163-
});
164-
return ws;
159+
if (!ws.done) {
160+
await this.webhookEvents.updateEvent(event.id, {
161+
prebuildStatus: "prebuild_triggered",
162+
status: "processed",
163+
prebuildId: ws.prebuildId,
164+
});
165+
return ws;
166+
}
165167
} catch (e) {
166168
console.error("Error processing Bitbucket webhook event", e);
167169
await this.webhookEvents.updateEvent(event.id, {

components/server/ee/src/prebuilds/bitbucket-server-app.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,14 @@ export class BitbucketServerApp {
154154
commitInfo,
155155
},
156156
);
157-
await this.webhookEvents.updateEvent(event.id, {
158-
prebuildStatus: "prebuild_triggered",
159-
status: "processed",
160-
prebuildId: ws.prebuildId,
161-
});
162-
return ws;
157+
if (!ws.done) {
158+
await this.webhookEvents.updateEvent(event.id, {
159+
prebuildStatus: "prebuild_triggered",
160+
status: "processed",
161+
prebuildId: ws.prebuildId,
162+
});
163+
return ws;
164+
}
163165
} catch (e) {
164166
console.error("Error processing Bitbucket Server webhook event", e);
165167
await this.webhookEvents.updateEvent(event.id, {

components/server/ee/src/prebuilds/github-app.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,13 @@ export class GithubApp {
323323
this.prebuildManager
324324
.startPrebuild({ span }, { user, context, project, commitInfo })
325325
.then(async (result) => {
326-
await this.webhookEvents.updateEvent(event.id, {
327-
prebuildStatus: "prebuild_triggered",
328-
status: "processed",
329-
prebuildId: result.prebuildId,
330-
});
326+
if (!result.done) {
327+
await this.webhookEvents.updateEvent(event.id, {
328+
prebuildStatus: "prebuild_triggered",
329+
status: "processed",
330+
prebuildId: result.prebuildId,
331+
});
332+
}
331333
})
332334
.catch(async (err) => {
333335
log.error(logCtx, "Error while starting prebuild", err, { contextURL });
@@ -533,7 +535,7 @@ export class GithubApp {
533535
const isFork = pr.head.repo.id !== pr.base.repo.id;
534536
const runPrebuild =
535537
this.prebuildManager.shouldPrebuild(config) && this.appRules.shouldRunPrebuild(config, false, true, isFork);
536-
let prebuildStartPromise: Promise<StartPrebuildResult> | undefined;
538+
let prebuildStartPromise: Promise<StartPrebuildResult | undefined> | undefined;
537539
if (runPrebuild) {
538540
const commitInfo = await this.getCommitInfo(user, ctx.payload.repository.html_url, pr.head.sha);
539541
prebuildStartPromise = this.prebuildManager.startPrebuild(tracecContext, {
@@ -542,6 +544,7 @@ export class GithubApp {
542544
project,
543545
commitInfo,
544546
});
547+
prebuildStartPromise = prebuildStartPromise.then((result) => (result?.done ? undefined : result));
545548
prebuildStartPromise.catch((err) => log.error(err, "Error while starting prebuild", { contextURL }));
546549
return prebuildStartPromise;
547550
} else {

components/server/ee/src/prebuilds/github-enterprise-app.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,14 @@ export class GitHubEnterpriseApp {
184184
commitInfo,
185185
},
186186
);
187-
await this.webhookEvents.updateEvent(event.id, {
188-
prebuildStatus: "prebuild_triggered",
189-
status: "processed",
190-
prebuildId: ws.prebuildId,
191-
});
192-
return ws;
187+
if (!ws.done) {
188+
await this.webhookEvents.updateEvent(event.id, {
189+
prebuildStatus: "prebuild_triggered",
190+
status: "processed",
191+
prebuildId: ws.prebuildId,
192+
});
193+
return ws;
194+
}
193195
} catch (e) {
194196
log.error("Error processing GitHub Enterprise webhook event.", e);
195197
await this.webhookEvents.updateEvent(event.id, {

components/server/ee/src/prebuilds/prebuild-manager.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export interface StartPrebuildParams {
4646
context: CommitContext;
4747
project?: Project;
4848
commitInfo?: CommitInfo;
49+
forcePrebuild?: boolean;
4950
}
5051

5152
const PREBUILD_LIMITER_WINDOW_SECONDS = 60;
@@ -115,7 +116,7 @@ export class PrebuildManager {
115116

116117
async startPrebuild(
117118
ctx: TraceContext,
118-
{ context, project, user, commitInfo }: StartPrebuildParams,
119+
{ context, project, user, commitInfo, forcePrebuild }: StartPrebuildParams,
119120
): Promise<StartPrebuildResult> {
120121
const span = TraceContext.startSpan("startPrebuild", ctx);
121122
const cloneURL = context.repository.cloneUrl;
@@ -130,15 +131,15 @@ export class PrebuildManager {
130131
const existingPB = await this.findNonFailedPrebuiltWorkspace({ span }, cloneURL, commitSHAIdentifier);
131132

132133
// If the existing prebuild is failed, it will be retriggered in the afterwards
134+
const config = await this.fetchConfig({ span }, user, context);
133135
if (existingPB) {
134136
// If the existing prebuild is based on an outdated project config, we also want to retrigger it.
135137
const existingPBWS = await this.workspaceDB.trace({ span }).findById(existingPB.buildWorkspaceId);
136138
const existingConfig = existingPBWS?.config;
137-
const newConfig = await this.fetchConfig({ span }, user, context);
138139
log.debug(
139140
`startPrebuild | commits: ${commitSHAIdentifier}, existingPB: ${
140141
existingPB.id
141-
}, existingConfig: ${JSON.stringify(existingConfig)}, newConfig: ${JSON.stringify(newConfig)}}`,
142+
}, existingConfig: ${JSON.stringify(existingConfig)}, newConfig: ${JSON.stringify(config)}}`,
142143
);
143144
const filterPrebuildTasks = (tasks: TaskConfig[] = []) =>
144145
tasks
@@ -151,7 +152,7 @@ export class PrebuildManager {
151152
.filter((task) => Object.keys(task).length > 0);
152153
const isSameConfig =
153154
JSON.stringify(filterPrebuildTasks(existingConfig?.tasks)) ===
154-
JSON.stringify(filterPrebuildTasks(newConfig?.tasks));
155+
JSON.stringify(filterPrebuildTasks(config?.tasks));
155156
// If there is an existing prebuild that isn't failed and it's based on the current config, we return it here instead of triggering a new prebuild.
156157
if (isSameConfig) {
157158
return { prebuildId: existingPB.id, wsid: existingPB.buildWorkspaceId, done: true };
@@ -173,11 +174,30 @@ export class PrebuildManager {
173174
normalizedContextURL: context.normalizedContextURL,
174175
};
175176

176-
if (this.shouldPrebuildIncrementally(context.repository.cloneUrl, project)) {
177+
const { commitHistory, additionalRepositoryCommitHistories } =
178+
await this.incrementalPrebuildsService.getCommitHistoryForContext(context, user);
179+
180+
const prebuildEveryNthCommit = project?.settings?.prebuildEveryNthCommit || 0;
181+
if (!forcePrebuild && prebuildEveryNthCommit > 0) {
182+
const history = {
183+
commitHistory: commitHistory?.slice(0, prebuildEveryNthCommit),
184+
additionalRepositoryCommitHistories: additionalRepositoryCommitHistories?.map((repoHist) => ({
185+
cloneUrl: repoHist.cloneUrl,
186+
commitHistory: repoHist.commitHistory.slice(0, prebuildEveryNthCommit),
187+
})),
188+
};
189+
const prebuild = await this.incrementalPrebuildsService.findGoodBaseForIncrementalBuild(
190+
context,
191+
config,
192+
history,
193+
user,
194+
);
195+
if (prebuild) {
196+
return { prebuildId: prebuild.id, wsid: prebuild.buildWorkspaceId, done: true };
197+
}
198+
} else if (this.shouldPrebuildIncrementally(context.repository.cloneUrl, project)) {
177199
// We store the commit histories in the `StartPrebuildContext` in order to pass them down to
178200
// `WorkspaceFactoryEE.createForStartPrebuild`.
179-
const { commitHistory, additionalRepositoryCommitHistories } =
180-
await this.incrementalPrebuildsService.getCommitHistoryForContext(context, user);
181201
if (commitHistory) {
182202
prebuildContext.commitHistory = commitHistory;
183203
}

components/server/ee/src/workspace/gitpod-server-impl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,6 +2662,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
26622662
context,
26632663
user,
26642664
project,
2665+
forcePrebuild: true,
26652666
});
26662667

26672668
this.analytics.track({

0 commit comments

Comments
 (0)