Path: blob/main/components/dashboard/src/repositories/detail/prebuilds/PrebuildSettingsForm.tsx
2506 views
/**1* Copyright (c) 2023 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import {7BranchMatchingStrategy,8Configuration,9PrebuildTriggerStrategy,10} from "@gitpod/public-api/lib/gitpod/v1/configuration_pb";11import { FC, FormEvent, useCallback, useMemo, useState } from "react";12import { ConfigurationSettingsField } from "../ConfigurationSettingsField";13import { Heading3, Subheading } from "@podkit/typography/Headings";14import { Text } from "@podkit/typography/Text";15import { InputField } from "../../../components/forms/InputField";16import { PartialConfiguration, useConfigurationMutation } from "../../../data/configurations/configuration-queries";17import { useToast } from "../../../components/toasts/Toasts";18import { TextInputField } from "../../../components/forms/TextInputField";19import { WorkspaceClassOptions } from "../shared/WorkspaceClassOptions";20import { LoadingButton } from "@podkit/buttons/LoadingButton";21import { InputFieldHint } from "../../../components/forms/InputFieldHint";22import { DEFAULT_WS_CLASS } from "../../../data/workspaces/workspace-classes-query";23import { Select, SelectItem, SelectTrigger, SelectValue, SelectContent } from "@podkit/select/Select";24import Alert from "../../../components/Alert";25import { useUserLoader } from "../../../hooks/use-user-loader";26import { useUpdateCurrentUserMutation } from "../../../data/current-user/update-mutation";27import { trackEvent } from "../../../Analytics";28import dayjs from "dayjs";29import { SwitchInputField } from "@podkit/switch/Switch";30import { useFeatureFlag } from "../../../data/featureflag-query";31import { TextMuted } from "@podkit/typography/TextMuted";32import { InfoIcon } from "lucide-react";3334const DEFAULT_PREBUILD_COMMIT_INTERVAL = 20;3536type Props = {37configuration: Configuration;38};3940const COACHMARK_KEY = "new_prebuilds_trigger_notification";4142export const PrebuildSettingsForm: FC<Props> = ({ configuration }) => {43const { toast } = useToast();4445const { user } = useUserLoader();46const { mutate: updateUser } = useUpdateCurrentUserMutation();47const isEnabledPrebuildFullClone = useFeatureFlag("enabled_configuration_prebuild_full_clone");4849const updateConfiguration = useConfigurationMutation();5051const [interval, setInterval] = useState<string>(52`${configuration.prebuildSettings?.prebuildInterval ?? DEFAULT_PREBUILD_COMMIT_INTERVAL}`,53);54const [branchStrategy, setBranchStrategy] = useState<BranchMatchingStrategy>(55configuration.prebuildSettings?.branchStrategy ?? BranchMatchingStrategy.DEFAULT_BRANCH,56);57const [branchMatchingPattern, setBranchMatchingPattern] = useState<string>(58configuration.prebuildSettings?.branchMatchingPattern || "**",59);60const [workspaceClass, setWorkspaceClass] = useState<string>(61configuration.prebuildSettings?.workspaceClass || DEFAULT_WS_CLASS,62);63const [fullClone, setFullClone] = useState<boolean>(64configuration.prebuildSettings?.cloneSettings?.fullClone ?? false,65);6667const [isTriggerNotificationOpen, setIsTriggerNotificationOpen] = useState(true);6869const handleSubmit = useCallback(70(e: FormEvent) => {71e.preventDefault();7273const newInterval = Math.abs(Math.min(Number.parseInt(interval), 100)) || 0;7475const updatedConfig: PartialConfiguration = {76configurationId: configuration.id,77prebuildSettings: {78...configuration.prebuildSettings,79prebuildInterval: newInterval,80branchStrategy,81branchMatchingPattern,82workspaceClass,83cloneSettings: {84fullClone,85},86},87};8889updateConfiguration.mutate(updatedConfig, {90onSuccess: () => {91toast("Your prebuild settings were updated.");92},93});94},95[96branchMatchingPattern,97branchStrategy,98configuration.id,99configuration.prebuildSettings,100interval,101toast,102updateConfiguration,103workspaceClass,104fullClone,105],106);107108// TODO: Figure out if there's a better way to deal with grpc enums in the UI109const handleBranchStrategyChange = useCallback((val) => {110// This is pretty hacky, trying to coerce value into a number and then cast it to the enum type111// Would be better if we treated these as strings instead of special enums112setBranchStrategy(parseInt(val, 10) as BranchMatchingStrategy);113}, []);114115const dismissNotification = useCallback(() => {116updateUser(117{118additionalData: { profile: { coachmarksDismissals: { [COACHMARK_KEY]: dayjs().toISOString() } } },119},120{121onSettled: (_, error) => {122trackEvent("coachmark_dismissed", {123name: COACHMARK_KEY,124success: !(error instanceof Error),125});126setIsTriggerNotificationOpen(false);127},128},129);130}, [updateUser]);131132const showTriggerNotification = useMemo<boolean>(() => {133if (!isTriggerNotificationOpen || !user) {134return false;135}136137if (configuration.prebuildSettings?.triggerStrategy === PrebuildTriggerStrategy.ACTIVITY_BASED) {138return false;139}140141// For repositories created after activity-based prebuilds were introduced, don't show it142if (configuration.creationTime && configuration.creationTime.toDate() > new Date("7/15/2024")) {143return false;144}145146return !user.profile?.coachmarksDismissals[COACHMARK_KEY];147}, [configuration.creationTime, configuration.prebuildSettings?.triggerStrategy, isTriggerNotificationOpen, user]);148149return (150<ConfigurationSettingsField>151{showTriggerNotification && (152<Alert153type={"info"}154closable={true}155onClose={() => dismissNotification()}156showIcon={true}157className="flex rounded p-2 mb-2 w-full"158>159The way prebuilds are triggered is changing.{" "}160<a161className="gp-link"162target="_blank"163href="https://www.gitpod.io/changelog/activity-based-prebuilds"164rel="noreferrer"165>166Learn more167</a>168</Alert>169)}170<form onSubmit={handleSubmit}>171<Heading3>Prebuild settings</Heading3>172<Subheading className="max-w-lg">These settings will be applied on every Prebuild.</Subheading>173174<InputField175label="Commit interval"176hint="The number of commits to be skipped between prebuild runs."177id="prebuild-interval"178>179<input180className="w-20"181type="number"182id="prebuild-interval"183min="0"184max="100"185step="5"186value={interval}187onChange={({ target }) => setInterval(target.value)}188/>189</InputField>190191<InputField label="Branch Filter" hint="Run prebuilds on the selected branches only.">192<Select value={`${branchStrategy}`} onValueChange={handleBranchStrategyChange}>193<SelectTrigger className="w-60">194<SelectValue placeholder="Select a branch filter" />195</SelectTrigger>196<SelectContent>197<SelectItem value={`${BranchMatchingStrategy.ALL_BRANCHES}`}>All branches</SelectItem>198<SelectItem value={`${BranchMatchingStrategy.DEFAULT_BRANCH}`}>Default branch</SelectItem>199<SelectItem value={`${BranchMatchingStrategy.MATCHED_BRANCHES}`}>200Match branches by pattern201</SelectItem>202</SelectContent>203</Select>204</InputField>205206{branchStrategy === BranchMatchingStrategy.MATCHED_BRANCHES && (207<TextInputField208label="Branch name pattern"209hint="Glob patterns separated by commas are supported."210value={branchMatchingPattern}211onChange={setBranchMatchingPattern}212/>213)}214215{isEnabledPrebuildFullClone && (216<InputField217label="Clone repositories in full"218hint="Make prebuilds fully clone the repository, maintaining the entire git history for use in prebuilds and workspaces."219>220<SwitchInputField221id="prebuild-full-clone-enabled"222checked={fullClone}223onCheckedChange={setFullClone}224label=""225/>226</InputField>227)}228229<div className="mt-4">230<Text className="font-semibold text-md text-pk-content-secondary">Prebuild trigger strategy</Text>231<TextMuted className="text-sm mt-1 flex flex-row gap-1 items-center">232{configuration.prebuildSettings?.triggerStrategy === PrebuildTriggerStrategy.ACTIVITY_BASED233? "Activity-based"234: "Webhook-based"}235<a236href="https://www.gitpod.io/docs/configure/repositories/prebuilds#triggers"237target="_blank"238rel="noreferrer"239>240<InfoIcon size={14} />241</a>242</TextMuted>243</div>244245<Heading3 className="mt-8">Machine type</Heading3>246<Subheading>Choose the workspace machine type for your prebuilds.</Subheading>247248<WorkspaceClassOptions value={workspaceClass} onChange={setWorkspaceClass} />249<InputFieldHint>Use a smaller machine type for cost optimization.</InputFieldHint>250251<LoadingButton className="mt-8" type="submit" loading={updateConfiguration.isLoading}>252Save253</LoadingButton>254</form>255</ConfigurationSettingsField>256);257};258259260