Skip to content

Commit 71a03a8

Browse files
fix(site): fix template schedule update overriding other settings (#13114)
1 parent f2dd0a8 commit 71a03a8

File tree

7 files changed

+123
-111
lines changed

7 files changed

+123
-111
lines changed

codersdk/templates.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ type UpdateTemplateMeta struct {
232232
// RequireActiveVersion mandates workspaces built using this template
233233
// use the active version of the template. This option has no
234234
// effect on template admins.
235-
RequireActiveVersion bool `json:"require_active_version"`
235+
RequireActiveVersion bool `json:"require_active_version,omitempty"`
236236
// DeprecationMessage if set, will mark the template as deprecated and block
237237
// any new workspaces from using this template.
238238
// If passed an empty string, will remove the deprecated message, making

site/e2e/tests/listTemplates.spec.ts renamed to site/e2e/tests/templates/listTemplates.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { test, expect } from "@playwright/test";
2-
import { beforeCoderTest } from "../hooks";
2+
import { beforeCoderTest } from "../../hooks";
33

44
test.beforeEach(({ page }) => beforeCoderTest(page));
55

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { expect, test } from "@playwright/test";
2+
import { createTemplate, createTemplateVersion, getTemplate } from "api/api";
3+
import { getCurrentOrgId, setupApiCalls } from "../../api";
4+
import { beforeCoderTest } from "../../hooks";
5+
6+
test.beforeEach(({ page }) => beforeCoderTest(page));
7+
8+
test("update template schedule settings without override other settings", async ({
9+
page,
10+
baseURL,
11+
}) => {
12+
await setupApiCalls(page);
13+
const orgId = await getCurrentOrgId();
14+
const templateVersion = await createTemplateVersion(orgId, {
15+
storage_method: "file" as const,
16+
provisioner: "echo",
17+
user_variable_values: [],
18+
example_id: "docker",
19+
tags: {},
20+
});
21+
const template = await createTemplate(orgId, {
22+
name: "test-template",
23+
display_name: "Test Template",
24+
template_version_id: templateVersion.id,
25+
disable_everyone_group_access: false,
26+
require_active_version: true,
27+
});
28+
29+
await page.goto(`${baseURL}/templates/${template.name}/settings/schedule`, {
30+
waitUntil: "domcontentloaded",
31+
});
32+
await page.getByLabel("Default autostop (hours)").fill("48");
33+
await page.getByRole("button", { name: "Submit" }).click();
34+
await expect(page.getByText("Template updated successfully")).toBeVisible();
35+
36+
const updatedTemplate = await getTemplate(template.id);
37+
// Validate that the template data remains consistent, with the exception of
38+
// the 'default_ttl_ms' field (updated during the test) and the 'updated at'
39+
// field (automatically updated by the backend).
40+
expect({
41+
...template,
42+
default_ttl_ms: 48 * 60 * 60 * 1000,
43+
updated_at: updatedTemplate.updated_at,
44+
}).toStrictEqual(updatedTemplate);
45+
});

site/src/api/typesGenerated.ts

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/src/components/StackLabel/StackLabel.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ export const StackLabel: FC<ComponentProps<typeof Stack>> = (props) => {
2222
export const StackLabelHelperText: FC<FormHelperTextProps> = (props) => {
2323
return (
2424
<FormHelperText
25-
css={{
25+
css={(theme) => ({
2626
marginTop: 0,
27-
}}
27+
28+
"& strong": {
29+
color: theme.palette.text.primary,
30+
},
31+
})}
2832
{...props}
2933
/>
3034
);

site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx

Lines changed: 67 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { Interpolation, Theme } from "@emotion/react";
21
import Checkbox from "@mui/material/Checkbox";
2+
import FormControlLabel from "@mui/material/FormControlLabel";
3+
import FormHelperText from "@mui/material/FormHelperText";
34
import MenuItem from "@mui/material/MenuItem";
45
import TextField from "@mui/material/TextField";
56
import { type FormikContextType, type FormikTouched, useFormik } from "formik";
@@ -17,14 +18,12 @@ import {
1718
HorizontalForm,
1819
FormFooter,
1920
} from "components/Form/Form";
20-
import {
21-
HelpTooltip,
22-
HelpTooltipContent,
23-
HelpTooltipText,
24-
HelpTooltipTrigger,
25-
} from "components/HelpTooltip/HelpTooltip";
2621
import { IconField } from "components/IconField/IconField";
2722
import { Stack } from "components/Stack/Stack";
23+
import {
24+
StackLabel,
25+
StackLabelHelperText,
26+
} from "components/StackLabel/StackLabel";
2827
import {
2928
getFormHelpers,
3029
nameValidator,
@@ -160,92 +159,74 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
160159
title="Operations"
161160
description="Regulate actions allowed on workspaces created from this template."
162161
>
163-
<Stack direction="column" spacing={5}>
164-
<label htmlFor="allow_user_cancel_workspace_jobs">
165-
<Stack direction="row" spacing={1}>
162+
<FormFields spacing={6}>
163+
<FormControlLabel
164+
control={
166165
<Checkbox
166+
size="small"
167167
id="allow_user_cancel_workspace_jobs"
168168
name="allow_user_cancel_workspace_jobs"
169169
disabled={isSubmitting}
170170
checked={form.values.allow_user_cancel_workspace_jobs}
171171
onChange={form.handleChange}
172172
/>
173-
174-
<Stack direction="column" spacing={0.5}>
175-
<Stack
176-
direction="row"
177-
alignItems="center"
178-
spacing={0.5}
179-
css={styles.optionText}
180-
>
181-
Allow users to cancel in-progress workspace jobs.
182-
<HelpTooltip>
183-
<HelpTooltipTrigger />
184-
<HelpTooltipContent>
185-
<HelpTooltipText>
186-
If checked, users may be able to corrupt their
187-
workspace.
188-
</HelpTooltipText>
189-
</HelpTooltipContent>
190-
</HelpTooltip>
191-
</Stack>
192-
<span css={styles.optionHelperText}>
173+
}
174+
label={
175+
<StackLabel>
176+
Allow users to cancel in-progress workspace jobs.
177+
<StackLabelHelperText>
193178
Depending on your template, canceling builds may leave
194179
workspaces in an unhealthy state. This option isn&apos;t
195-
recommended for most use cases.
196-
</span>
197-
</Stack>
198-
</Stack>
199-
</label>
200-
<Stack spacing={2}>
201-
<label htmlFor="require_active_version">
202-
<Stack direction="row" spacing={1}>
203-
<Checkbox
204-
id="require_active_version"
205-
name="require_active_version"
206-
checked={form.values.require_active_version}
207-
onChange={form.handleChange}
208-
disabled={
209-
!template.require_active_version &&
210-
!advancedSchedulingEnabled
211-
}
212-
/>
180+
recommended for most use cases.{" "}
181+
<strong>
182+
If checked, users may be able to corrupt their workspace.
183+
</strong>
184+
</StackLabelHelperText>
185+
</StackLabel>
186+
}
187+
/>
213188

214-
<Stack direction="column" spacing={0.5}>
215-
<Stack
216-
direction="row"
217-
alignItems="center"
218-
spacing={0.5}
219-
css={styles.optionText}
220-
>
221-
Require workspaces automatically update when started.
222-
<HelpTooltip>
223-
<HelpTooltipTrigger />
224-
<HelpTooltipContent>
225-
<HelpTooltipText>
226-
This setting is not enforced for template admins.
227-
</HelpTooltipText>
228-
</HelpTooltipContent>
229-
</HelpTooltip>
230-
</Stack>
231-
<span css={styles.optionHelperText}>
189+
<FormControlLabel
190+
control={
191+
<Checkbox
192+
size="small"
193+
id="require_active_version"
194+
name="require_active_version"
195+
checked={form.values.require_active_version}
196+
onChange={form.handleChange}
197+
disabled={
198+
!template.require_active_version && !advancedSchedulingEnabled
199+
}
200+
/>
201+
}
202+
label={
203+
<StackLabel>
204+
Require workspaces automatically update when started.
205+
<StackLabelHelperText>
206+
<span>
232207
Workspaces that are manually started or auto-started will
233-
use the active template version.
208+
use the active template version.{" "}
209+
<strong>
210+
This setting is not enforced for template admins.
211+
</strong>
234212
</span>
235-
</Stack>
236-
</Stack>
237-
</label>
238213

239-
{!advancedSchedulingEnabled && (
240-
<Stack direction="row">
241-
<EnterpriseBadge />
242-
<span css={styles.optionHelperText}>
243-
Enterprise license required to enabled.
244-
</span>
245-
</Stack>
246-
)}
247-
</Stack>
248-
</Stack>
214+
{!advancedSchedulingEnabled && (
215+
<Stack
216+
direction="row"
217+
spacing={2}
218+
alignItems="center"
219+
css={{ marginTop: 16 }}
220+
>
221+
<EnterpriseBadge />
222+
<span>Enterprise license required to enabled.</span>
223+
</Stack>
224+
)}
225+
</StackLabelHelperText>
226+
</StackLabel>
227+
}
228+
/>
229+
</FormFields>
249230
</FormSection>
250231

251232
<FormSection
@@ -265,13 +246,13 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
265246
label="Deprecation Message"
266247
/>
267248
{!accessControlEnabled && (
268-
<Stack direction="row">
249+
<Stack direction="row" spacing={2} alignItems="center">
269250
<EnterpriseBadge />
270-
<span css={styles.optionHelperText}>
251+
<FormHelperText>
271252
Enterprise license required to deprecate templates.
272253
{template.deprecated &&
273254
" You cannot change the message, but you may remove it to mark this template as no longer deprecated."}
274-
</span>
255+
</FormHelperText>
275256
</Stack>
276257
)}
277258
</FormFields>
@@ -306,11 +287,11 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
306287
<MenuItem value="public">Public</MenuItem>
307288
</TextField>
308289
{!portSharingControlsEnabled && (
309-
<Stack direction="row">
290+
<Stack direction="row" spacing={2} alignItems="center">
310291
<EnterpriseBadge />
311-
<span css={styles.optionHelperText}>
292+
<FormHelperText>
312293
Enterprise license required to control max port sharing level.
313-
</span>
294+
</FormHelperText>
314295
</Stack>
315296
)}
316297
</FormFields>
@@ -321,15 +302,3 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
321302
</HorizontalForm>
322303
);
323304
};
324-
325-
const styles = {
326-
optionText: (theme) => ({
327-
fontSize: 16,
328-
color: theme.palette.text.primary,
329-
}),
330-
331-
optionHelperText: (theme) => ({
332-
fontSize: 12,
333-
color: theme.palette.text.secondary,
334-
}),
335-
} satisfies Record<string, Interpolation<Theme>>;

site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
234234
allow_user_autostop: form.values.allow_user_autostop,
235235
update_workspace_last_used_at: form.values.update_workspace_last_used_at,
236236
update_workspace_dormant_at: form.values.update_workspace_dormant_at,
237-
require_active_version: false,
238237
disable_everyone_group_access: false,
239238
});
240239
};
@@ -533,14 +532,9 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
533532
<StackLabelHelperText>
534533
When enabled, Coder will permanently delete dormant
535534
workspaces after a period of time.{" "}
536-
<span
537-
css={(theme) => ({
538-
fontWeight: 500,
539-
color: theme.palette.text.primary,
540-
})}
541-
>
535+
<strong>
542536
Once a workspace is deleted it cannot be recovered.
543-
</span>
537+
</strong>
544538
</StackLabelHelperText>
545539
</StackLabel>
546540
}

0 commit comments

Comments
 (0)