Skip to content

Always allow running new prebuilds, regardless of any previous prebuild state #15147

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 2 commits into from
Dec 6, 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
49 changes: 26 additions & 23 deletions components/dashboard/src/projects/Prebuild.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,39 +156,42 @@ export default function () {
<Header title={renderTitle()} subtitle={renderSubtitle()} />
<div className="app-container mt-8">
<PrebuildLogs workspaceId={prebuild?.info?.buildWorkspaceId}>
{["aborted", "timeout", "failed"].includes(prebuild?.status || "") || !!prebuild?.error ? (
<button
className="flex items-center space-x-2"
disabled={isRerunningPrebuild}
onClick={rerunPrebuild}
>
{isRerunningPrebuild && (
<img className="h-4 w-4 animate-spin filter brightness-150" src={Spinner} />
)}
<span>Rerun Prebuild ({prebuild?.info.branch})</span>
</button>
) : ["building", "queued"].includes(prebuild?.status || "") ? (
{["building", "queued"].includes(prebuild?.status || "") ? (
<button
className="danger flex items-center space-x-2"
disabled={isCancellingPrebuild}
onClick={cancelPrebuild}
>
{isCancellingPrebuild && (
<img className="h-4 w-4 animate-spin filter brightness-150" src={Spinner} />
<img alt="" className="h-4 w-4 animate-spin filter brightness-150" src={Spinner} />
)}
<span>Cancel Prebuild</span>
</button>
) : prebuild?.status === "available" ? (
<a
className="my-auto"
href={gitpodHostUrl
.withContext(`open-prebuild/${prebuild?.info.id}/${prebuild?.info.changeUrl}`)
.toString()}
>
<button>New Workspace (with this prebuild)</button>
</a>
) : (
<button disabled={true}>New Workspace (with this prebuild)</button>
<>
<button
className="secondary flex items-center space-x-2"
disabled={isRerunningPrebuild}
onClick={rerunPrebuild}
>
{isRerunningPrebuild && (
<img alt="" className="h-4 w-4 animate-spin filter brightness-150" src={Spinner} />
)}
<span>Rerun Prebuild ({prebuild?.info.branch})</span>
</button>
{prebuild?.status === "available" ? (
<a
className="my-auto"
href={gitpodHostUrl
.withContext(`open-prebuild/${prebuild?.info.id}/${prebuild?.info.changeUrl}`)
.toString()}
>
<button>New Workspace (with this prebuild)</button>
</a>
) : (
<button disabled={true}>New Workspace (with this prebuild)</button>
)}
</>
)}
</PrebuildLogs>
</div>
Expand Down
25 changes: 20 additions & 5 deletions components/dashboard/src/projects/Prebuilds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default function (props: { project?: Project; isAdminDashboard?: boolean

const [isLoadingPrebuilds, setIsLoadingPrebuilds] = useState<boolean>(true);
const [prebuilds, setPrebuilds] = useState<PrebuildWithStatus[]>([]);
const [isRunningPrebuild, setIsRunningPrebuild] = useState<boolean>(false);

useEffect(() => {
let registration: Disposable;
Expand Down Expand Up @@ -139,11 +140,18 @@ export default function (props: { project?: Project; isAdminDashboard?: boolean
return -1;
};

const triggerPrebuild = (branchName: string | null) => {
const runPrebuild = async (branchName: string | null) => {
if (!project) {
return;
}
getGitpodService().server.triggerPrebuild(project.id, branchName);
setIsRunningPrebuild(true);
try {
await getGitpodService().server.triggerPrebuild(project.id, branchName);
} catch (error) {
console.error("Could not run prebuild", error);
} finally {
setIsRunningPrebuild(false);
}
};

const formatDate = (date: string | undefined) => {
Expand Down Expand Up @@ -182,9 +190,16 @@ export default function (props: { project?: Project; isAdminDashboard?: boolean
<div className="py-3 pl-3">
<DropDown prefix="Prebuild Status: " customClasses="w-32" entries={statusFilterEntries()} />
</div>
{!isLoadingPrebuilds && prebuilds.length === 0 && !props.isAdminDashboard && (
<button onClick={() => triggerPrebuild(null)} className="ml-2">
Run Prebuild
{!props.isAdminDashboard && (
<button
onClick={() => runPrebuild(null)}
disabled={isRunningPrebuild}
className="ml-2 flex items-center space-x-2"
>
{isRunningPrebuild && (
<img alt="" className="h-4 w-4 animate-spin filter brightness-150" src={Spinner} />
)}
<span>Run Prebuild</span>
</button>
)}
</div>
Expand Down
24 changes: 9 additions & 15 deletions components/dashboard/src/projects/Project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -388,20 +388,7 @@ export default function () {
<ItemFieldContextMenu
className="py-0.5"
menuEntries={
!prebuild ||
prebuild.status === "aborted" ||
prebuild.status === "failed" ||
prebuild.status === "timeout" ||
!!prebuild.error
? [
{
title: `${prebuild ? "Rerun" : "Run"} Prebuild (${
branch.name
})`,
onClick: () => triggerPrebuild(branch),
},
]
: prebuild.status === "building"
prebuild?.status === "queued" || prebuild?.status === "building"
? [
{
title: "Cancel Prebuild",
Expand All @@ -411,7 +398,14 @@ export default function () {
prebuild && cancelPrebuild(prebuild.info.id),
},
]
: []
: [
{
title: `${prebuild ? "Rerun" : "Run"} Prebuild (${
branch.name
})`,
onClick: () => triggerPrebuild(branch),
},
]
}
/>
</ItemField>
Expand Down
55 changes: 29 additions & 26 deletions components/server/ee/src/prebuilds/prebuild-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,34 +134,37 @@ export class PrebuildManager {
`Running prebuilds without a project is no longer supported. Please add '${cloneURL}' as a project in a Gitpod team.`,
);
}
const existingPB = await this.findNonFailedPrebuiltWorkspace({ span }, cloneURL, commitSHAIdentifier);

// If the existing prebuild is failed, it will be retriggered in the afterwards
const config = await this.fetchConfig({ span }, user, context);
if (existingPB) {
// If the existing prebuild is based on an outdated project config, we also want to retrigger it.
const existingPBWS = await this.workspaceDB.trace({ span }).findById(existingPB.buildWorkspaceId);
const existingConfig = existingPBWS?.config;
log.debug(
`startPrebuild | commits: ${commitSHAIdentifier}, existingPB: ${
existingPB.id
}, existingConfig: ${JSON.stringify(existingConfig)}, newConfig: ${JSON.stringify(config)}}`,
);
const filterPrebuildTasks = (tasks: TaskConfig[] = []) =>
tasks
.map((task) =>
Object.keys(task)
.filter((key) => ["before", "init", "prebuild"].includes(key))
// @ts-ignore
.reduce((obj, key) => ({ ...obj, [key]: task[key] }), {}),
)
.filter((task) => Object.keys(task).length > 0);
const isSameConfig =
JSON.stringify(filterPrebuildTasks(existingConfig?.tasks)) ===
JSON.stringify(filterPrebuildTasks(config?.tasks));
// 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.
if (isSameConfig) {
return { prebuildId: existingPB.id, wsid: existingPB.buildWorkspaceId, done: true };

if (!forcePrebuild) {
// Check for an existing, successful prebuild, before triggering a new one.
const existingPB = await this.findNonFailedPrebuiltWorkspace({ span }, cloneURL, commitSHAIdentifier);
if (existingPB) {
// But if the existing prebuild is failed, or based on an outdated config, it will still be retriggered below.
const existingPBWS = await this.workspaceDB.trace({ span }).findById(existingPB.buildWorkspaceId);
const existingConfig = existingPBWS?.config;
log.debug(
`startPrebuild | commits: ${commitSHAIdentifier}, existingPB: ${
existingPB.id
}, existingConfig: ${JSON.stringify(existingConfig)}, newConfig: ${JSON.stringify(config)}}`,
);
const filterPrebuildTasks = (tasks: TaskConfig[] = []) =>
tasks
.map((task) =>
Object.keys(task)
.filter((key) => ["before", "init", "prebuild"].includes(key))
// @ts-ignore
.reduce((obj, key) => ({ ...obj, [key]: task[key] }), {}),
)
.filter((task) => Object.keys(task).length > 0);
const isSameConfig =
JSON.stringify(filterPrebuildTasks(existingConfig?.tasks)) ===
JSON.stringify(filterPrebuildTasks(config?.tasks));
// 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.
if (isSameConfig) {
return { prebuildId: existingPB.id, wsid: existingPB.buildWorkspaceId, done: true };
}
}
}
if (project && context.ref && !project.settings?.keepOutdatedPrebuildsRunning) {
Expand Down