Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx
2506 views
1
/**
2
* Copyright (c) 2023 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 {
8
BranchMatchingStrategy,
9
Configuration,
10
PrebuildTriggerStrategy,
11
} from "@gitpod/public-api/lib/gitpod/v1/configuration_pb";
12
import { FC, FormEvent, useCallback, useMemo, useState } from "react";
13
import { ConfigurationSettingsField } from "../ConfigurationSettingsField";
14
import { Heading3, Subheading } from "@podkit/typography/Headings";
15
import { Text } from "@podkit/typography/Text";
16
import { InputField } from "../../../components/forms/InputField";
17
import { PartialConfiguration, useConfigurationMutation } from "../../../data/configurations/configuration-queries";
18
import { useToast } from "../../../components/toasts/Toasts";
19
import { TextInputField } from "../../../components/forms/TextInputField";
20
import { WorkspaceClassOptions } from "../shared/WorkspaceClassOptions";
21
import { LoadingButton } from "@podkit/buttons/LoadingButton";
22
import { InputFieldHint } from "../../../components/forms/InputFieldHint";
23
import { DEFAULT_WS_CLASS } from "../../../data/workspaces/workspace-classes-query";
24
import { Select, SelectItem, SelectTrigger, SelectValue, SelectContent } from "@podkit/select/Select";
25
import Alert from "../../../components/Alert";
26
import { useUserLoader } from "../../../hooks/use-user-loader";
27
import { useUpdateCurrentUserMutation } from "../../../data/current-user/update-mutation";
28
import { trackEvent } from "../../../Analytics";
29
import dayjs from "dayjs";
30
import { SwitchInputField } from "@podkit/switch/Switch";
31
import { useFeatureFlag } from "../../../data/featureflag-query";
32
import { TextMuted } from "@podkit/typography/TextMuted";
33
import { InfoIcon } from "lucide-react";
34
35
const DEFAULT_PREBUILD_COMMIT_INTERVAL = 20;
36
37
type Props = {
38
configuration: Configuration;
39
};
40
41
const COACHMARK_KEY = "new_prebuilds_trigger_notification";
42
43
export const PrebuildSettingsForm: FC<Props> = ({ configuration }) => {
44
const { toast } = useToast();
45
46
const { user } = useUserLoader();
47
const { mutate: updateUser } = useUpdateCurrentUserMutation();
48
const isEnabledPrebuildFullClone = useFeatureFlag("enabled_configuration_prebuild_full_clone");
49
50
const updateConfiguration = useConfigurationMutation();
51
52
const [interval, setInterval] = useState<string>(
53
`${configuration.prebuildSettings?.prebuildInterval ?? DEFAULT_PREBUILD_COMMIT_INTERVAL}`,
54
);
55
const [branchStrategy, setBranchStrategy] = useState<BranchMatchingStrategy>(
56
configuration.prebuildSettings?.branchStrategy ?? BranchMatchingStrategy.DEFAULT_BRANCH,
57
);
58
const [branchMatchingPattern, setBranchMatchingPattern] = useState<string>(
59
configuration.prebuildSettings?.branchMatchingPattern || "**",
60
);
61
const [workspaceClass, setWorkspaceClass] = useState<string>(
62
configuration.prebuildSettings?.workspaceClass || DEFAULT_WS_CLASS,
63
);
64
const [fullClone, setFullClone] = useState<boolean>(
65
configuration.prebuildSettings?.cloneSettings?.fullClone ?? false,
66
);
67
68
const [isTriggerNotificationOpen, setIsTriggerNotificationOpen] = useState(true);
69
70
const handleSubmit = useCallback(
71
(e: FormEvent) => {
72
e.preventDefault();
73
74
const newInterval = Math.abs(Math.min(Number.parseInt(interval), 100)) || 0;
75
76
const updatedConfig: PartialConfiguration = {
77
configurationId: configuration.id,
78
prebuildSettings: {
79
...configuration.prebuildSettings,
80
prebuildInterval: newInterval,
81
branchStrategy,
82
branchMatchingPattern,
83
workspaceClass,
84
cloneSettings: {
85
fullClone,
86
},
87
},
88
};
89
90
updateConfiguration.mutate(updatedConfig, {
91
onSuccess: () => {
92
toast("Your prebuild settings were updated.");
93
},
94
});
95
},
96
[
97
branchMatchingPattern,
98
branchStrategy,
99
configuration.id,
100
configuration.prebuildSettings,
101
interval,
102
toast,
103
updateConfiguration,
104
workspaceClass,
105
fullClone,
106
],
107
);
108
109
// TODO: Figure out if there's a better way to deal with grpc enums in the UI
110
const handleBranchStrategyChange = useCallback((val) => {
111
// This is pretty hacky, trying to coerce value into a number and then cast it to the enum type
112
// Would be better if we treated these as strings instead of special enums
113
setBranchStrategy(parseInt(val, 10) as BranchMatchingStrategy);
114
}, []);
115
116
const dismissNotification = useCallback(() => {
117
updateUser(
118
{
119
additionalData: { profile: { coachmarksDismissals: { [COACHMARK_KEY]: dayjs().toISOString() } } },
120
},
121
{
122
onSettled: (_, error) => {
123
trackEvent("coachmark_dismissed", {
124
name: COACHMARK_KEY,
125
success: !(error instanceof Error),
126
});
127
setIsTriggerNotificationOpen(false);
128
},
129
},
130
);
131
}, [updateUser]);
132
133
const showTriggerNotification = useMemo<boolean>(() => {
134
if (!isTriggerNotificationOpen || !user) {
135
return false;
136
}
137
138
if (configuration.prebuildSettings?.triggerStrategy === PrebuildTriggerStrategy.ACTIVITY_BASED) {
139
return false;
140
}
141
142
// For repositories created after activity-based prebuilds were introduced, don't show it
143
if (configuration.creationTime && configuration.creationTime.toDate() > new Date("7/15/2024")) {
144
return false;
145
}
146
147
return !user.profile?.coachmarksDismissals[COACHMARK_KEY];
148
}, [configuration.creationTime, configuration.prebuildSettings?.triggerStrategy, isTriggerNotificationOpen, user]);
149
150
return (
151
<ConfigurationSettingsField>
152
{showTriggerNotification && (
153
<Alert
154
type={"info"}
155
closable={true}
156
onClose={() => dismissNotification()}
157
showIcon={true}
158
className="flex rounded p-2 mb-2 w-full"
159
>
160
The way prebuilds are triggered is changing.{" "}
161
<a
162
className="gp-link"
163
target="_blank"
164
href="https://www.gitpod.io/changelog/activity-based-prebuilds"
165
rel="noreferrer"
166
>
167
Learn more
168
</a>
169
</Alert>
170
)}
171
<form onSubmit={handleSubmit}>
172
<Heading3>Prebuild settings</Heading3>
173
<Subheading className="max-w-lg">These settings will be applied on every Prebuild.</Subheading>
174
175
<InputField
176
label="Commit interval"
177
hint="The number of commits to be skipped between prebuild runs."
178
id="prebuild-interval"
179
>
180
<input
181
className="w-20"
182
type="number"
183
id="prebuild-interval"
184
min="0"
185
max="100"
186
step="5"
187
value={interval}
188
onChange={({ target }) => setInterval(target.value)}
189
/>
190
</InputField>
191
192
<InputField label="Branch Filter" hint="Run prebuilds on the selected branches only.">
193
<Select value={`${branchStrategy}`} onValueChange={handleBranchStrategyChange}>
194
<SelectTrigger className="w-60">
195
<SelectValue placeholder="Select a branch filter" />
196
</SelectTrigger>
197
<SelectContent>
198
<SelectItem value={`${BranchMatchingStrategy.ALL_BRANCHES}`}>All branches</SelectItem>
199
<SelectItem value={`${BranchMatchingStrategy.DEFAULT_BRANCH}`}>Default branch</SelectItem>
200
<SelectItem value={`${BranchMatchingStrategy.MATCHED_BRANCHES}`}>
201
Match branches by pattern
202
</SelectItem>
203
</SelectContent>
204
</Select>
205
</InputField>
206
207
{branchStrategy === BranchMatchingStrategy.MATCHED_BRANCHES && (
208
<TextInputField
209
label="Branch name pattern"
210
hint="Glob patterns separated by commas are supported."
211
value={branchMatchingPattern}
212
onChange={setBranchMatchingPattern}
213
/>
214
)}
215
216
{isEnabledPrebuildFullClone && (
217
<InputField
218
label="Clone repositories in full"
219
hint="Make prebuilds fully clone the repository, maintaining the entire git history for use in prebuilds and workspaces."
220
>
221
<SwitchInputField
222
id="prebuild-full-clone-enabled"
223
checked={fullClone}
224
onCheckedChange={setFullClone}
225
label=""
226
/>
227
</InputField>
228
)}
229
230
<div className="mt-4">
231
<Text className="font-semibold text-md text-pk-content-secondary">Prebuild trigger strategy</Text>
232
<TextMuted className="text-sm mt-1 flex flex-row gap-1 items-center">
233
{configuration.prebuildSettings?.triggerStrategy === PrebuildTriggerStrategy.ACTIVITY_BASED
234
? "Activity-based"
235
: "Webhook-based"}
236
<a
237
href="https://www.gitpod.io/docs/configure/repositories/prebuilds#triggers"
238
target="_blank"
239
rel="noreferrer"
240
>
241
<InfoIcon size={14} />
242
</a>
243
</TextMuted>
244
</div>
245
246
<Heading3 className="mt-8">Machine type</Heading3>
247
<Subheading>Choose the workspace machine type for your prebuilds.</Subheading>
248
249
<WorkspaceClassOptions value={workspaceClass} onChange={setWorkspaceClass} />
250
<InputFieldHint>Use a smaller machine type for cost optimization.</InputFieldHint>
251
252
<LoadingButton className="mt-8" type="submit" loading={updateConfiguration.isLoading}>
253
Save
254
</LoadingButton>
255
</form>
256
</ConfigurationSettingsField>
257
);
258
};
259
260