Skip to content

Commit 276caa7

Browse files
Add option to force a rebuild in Configuration tab (#23)
* Add option to force a rebuild in Configuration tab * Only show rebuild option when source is set to Git * Put easier-to-evaluate condition first * Only set forceRebuild to true when source is Git
1 parent 7474150 commit 276caa7

File tree

4 files changed

+104
-15
lines changed

4 files changed

+104
-15
lines changed

backend/src/service/updateApp.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,9 @@ export async function updateApp(
157157
commitMessage,
158158
config: updatedConfig,
159159
git: {
160-
skipBuild: !shouldBuildOnUpdate(
161-
currentConfig,
162-
updatedConfig,
163-
currentDeployment,
164-
),
160+
skipBuild:
161+
!appData.forceRebuild &&
162+
!shouldBuildOnUpdate(currentConfig, updatedConfig, currentDeployment),
165163
},
166164
});
167165
// When the new image is built and deployed successfully, it will become the imageTag of the app's template deployment config so that future redeploys use it.
@@ -178,6 +176,7 @@ export async function updateApp(
178176
logger.info({ orgId: organization.id, appId: app.id }, "App updated");
179177
}
180178

179+
// Keep in sync with the isRebuildRequired function in frontend/src/pages/app/ConfigTab.tsx
181180
function shouldBuildOnUpdate(
182181
oldConfig: DeploymentConfig,
183182
newConfig: WorkloadConfigCreate | HelmConfigCreate,

frontend/package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/pages/app/ConfigTab.tsx

Lines changed: 91 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { UserContext } from "@/components/UserProvider";
33
import { AppConfigFormFields } from "@/components/config/AppConfigFormFields";
44
import { GroupConfigFields } from "@/components/config/GroupConfigFields";
55
import { Button } from "@/components/ui/button";
6+
import { Checkbox } from "@/components/ui/checkbox";
67
import { Input } from "@/components/ui/input";
78
import { Label } from "@/components/ui/label";
89
import { api } from "@/lib/api";
@@ -37,6 +38,10 @@ export const ConfigTab = ({
3738
getGroupStateFromApp(app),
3839
);
3940

41+
const [forceRebuild, setForceRebuild] = useState(false);
42+
43+
const rebuildRequired = isRebuildRequired(app, state);
44+
4045
const { mutateAsync: updateApp, isPending: updatePending } = api.useMutation(
4146
"put",
4247
"/app/{appId}",
@@ -68,6 +73,10 @@ export const ConfigTab = ({
6873
appGroup: groupState.groupOption,
6974
projectId: state.projectId ?? undefined,
7075
config: createDeploymentConfig(finalAppState),
76+
forceRebuild:
77+
state.appType === "workload" &&
78+
state.source === "git" &&
79+
forceRebuild,
7180
},
7281
});
7382
if (tab === "configuration") {
@@ -142,18 +151,90 @@ export const ConfigTab = ({
142151
/>
143152
</FormContext>
144153
{enableSaveButton && (
145-
<Button className="mt-8 max-w-max" disabled={updatePending}>
146-
{updatePending ? (
147-
<>
148-
<Loader className="animate-spin" /> Saving...
149-
</>
150-
) : (
151-
<>
152-
<Save /> Save
153-
</>
154+
<>
155+
{state.appType === "workload" && state.source === "git" && (
156+
<Label className="mt-8 flex items-start gap-2">
157+
<Checkbox
158+
disabled={rebuildRequired}
159+
checked={forceRebuild || rebuildRequired}
160+
onCheckedChange={(checked) => {
161+
setForceRebuild(!!checked);
162+
}}
163+
/>
164+
<div className="flex flex-col gap-2">
165+
Rebuild my application
166+
<span className="text-sm text-gray-500">
167+
{rebuildRequired ? (
168+
<>
169+
A new build is required due to the settings you&apos;ve
170+
changed.
171+
</>
172+
) : (
173+
<>
174+
Build a new version of your application using this updated
175+
configuration.
176+
</>
177+
)}
178+
</span>
179+
</div>
180+
</Label>
154181
)}
155-
</Button>
182+
<Button type="submit" className="max-w-max" disabled={updatePending}>
183+
{updatePending ? (
184+
<>
185+
<Loader className="animate-spin" /> Saving...
186+
</>
187+
) : (
188+
<>
189+
<Save /> Save
190+
</>
191+
)}
192+
</Button>
193+
</>
156194
)}
157195
</form>
158196
);
159197
};
198+
199+
// Keep in sync with the shouldBuildOnUpdate function in backend/src/service/updateApp.ts
200+
function isRebuildRequired(app: App, state: CommonFormFields) {
201+
const oldConfig = app.config;
202+
const newConfig = state.workload.git;
203+
204+
const { data: currentDeployment } = api.useQuery(
205+
"get",
206+
"/app/{appId}/deployments/{deploymentId}",
207+
{ params: { path: { appId: app.id, deploymentId: app.activeDeployment } } },
208+
{ enabled: !!app.activeDeployment },
209+
);
210+
211+
// Only Git apps need to be built
212+
if (state.appType !== "workload" || state.source !== "git") {
213+
return false;
214+
}
215+
216+
// Either this app has not been built in the past, or it has not been built successfully
217+
if (oldConfig.source !== "git" || currentDeployment?.status === "ERROR") {
218+
return true;
219+
}
220+
221+
// The code has changed
222+
if (
223+
newConfig.branch !== oldConfig.branch ||
224+
newConfig.repositoryId != oldConfig.repositoryId
225+
) {
226+
return true;
227+
}
228+
229+
// Build options have changed
230+
if (
231+
newConfig.builder != oldConfig.builder ||
232+
newConfig.rootDir != oldConfig.rootDir ||
233+
(newConfig.builder === "dockerfile" &&
234+
newConfig.dockerfilePath != oldConfig.dockerfilePath)
235+
) {
236+
return true;
237+
}
238+
239+
return false;
240+
}

openapi/openapi.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,6 +2236,8 @@ components:
22362236
type: boolean
22372237
config:
22382238
$ref: "#/components/schemas/DeploymentConfig"
2239+
forceRebuild:
2240+
type: boolean
22392241
required:
22402242
- config
22412243
DeploymentConfig:

0 commit comments

Comments
 (0)