Quellcode durchsuchen

Fix issue with predeploy job rendering in the UI (#3521)

Feroze Mohideen vor 2 Jahren
Ursprung
Commit
d4580c7fd9

+ 23 - 11
dashboard/src/lib/porter-apps/index.ts

@@ -68,6 +68,7 @@ export const deletionValidator = z.object({
 export const clientAppValidator = z.object({
   name: z.string().min(1),
   services: serviceValidator.array(),
+  predeploy: serviceValidator.array().optional(),
   env: z.record(z.string(), z.string()).default({}),
   build: buildValidator,
 });
@@ -164,13 +165,12 @@ export function clientAppToProto(data: PorterAppFormData): PorterApp {
   const { app, source } = data;
 
   const services = app.services
-    .filter((s) => !isPredeployService(s))
     .reduce((acc: Record<string, Service>, svc) => {
       acc[svc.name.value] = serviceProto(serializeService(svc));
       return acc;
     }, {});
 
-  const predeploy = app.services.find((s) => isPredeployService(s));
+  const predeploy = app.predeploy?.[0]
 
   const proto = match(source)
     .with(
@@ -273,10 +273,21 @@ export function clientAppFromProto(
       return deserializeService({ service: svc });
     });
 
+  const predeployList = [];
+  if (proto.predeploy) {
+    predeployList.push(deserializeService({
+      service: serializedServiceFromProto({
+        name: "pre-deploy",
+        service: proto.predeploy,
+        isPredeploy: true,
+      })
+    }))
+  }
   if (!overrides?.predeploy) {
     return {
       name: proto.name,
       services,
+      predeploy: predeployList,
       env: proto.env,
       build: clientBuildFromProto(proto.build) ?? {
         method: "pack",
@@ -289,19 +300,20 @@ export function clientAppFromProto(
 
   const predeployOverrides = serializeService(overrides.predeploy);
   const predeploy = proto.predeploy
-    ? deserializeService({
-        service: serializedServiceFromProto({
-          name: "pre-deploy",
-          service: proto.predeploy,
-          isPredeploy: true,
-        }),
-        override: predeployOverrides,
-      })
+    ? [deserializeService({
+      service: serializedServiceFromProto({
+        name: "pre-deploy",
+        service: proto.predeploy,
+        isPredeploy: true,
+      }),
+      override: predeployOverrides,
+    })]
     : undefined;
 
   return {
     name: proto.name,
-    services: [...services, predeploy].filter(valueExists),
+    services,
+    predeploy,
     env: proto.env,
     build: clientBuildFromProto(proto.build) ?? {
       method: "pack",

+ 1 - 0
dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx

@@ -109,6 +109,7 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
       },
     },
   });
+
   const {
     reset,
     handleSubmit,

+ 2 - 1
dashboard/src/main/home/app-dashboard/app-view/tabs/Overview.tsx

@@ -45,13 +45,14 @@ const Overview: React.FC = () => {
             })}
             existingServiceNames={Object.keys(latestProto.services)}
             isPredeploy
+            fieldArrayName={"app.predeploy"}
           />
           <Spacer y={0.5} />
         </>
       )}
       <Text size={16}>Application services</Text>
       <Spacer y={0.5} />
-      <ServiceList addNewText={"Add a new service"} />
+      <ServiceList addNewText={"Add a new service"} fieldArrayName={"app.services"} existingServiceNames={Object.keys(latestProto.services)} />
       <Spacer y={0.75} />
       <Button
         type="submit"

+ 10 - 3
dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx

@@ -121,6 +121,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
     setValue,
     handleSubmit,
     setError,
+    clearErrors,
     formState: { isSubmitting: isValidating, errors },
   } = porterAppFormMethods;
 
@@ -302,6 +303,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
   // reset services when source changes
   useEffect(() => {
     setValue("app.services", []);
+    setValue("app.predeploy", []);
     setDetectedServices({
       detected: false,
       count: 0,
@@ -325,7 +327,8 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
   useEffect(() => {
     if (servicesFromYaml && !detectedServices.detected) {
       const { services, predeploy } = servicesFromYaml;
-      setValue("app.services", [...services, predeploy].filter(valueExists));
+      setValue("app.services", services);
+      setValue("app.predeploy", [predeploy].filter(valueExists));
       setDetectedServices({
         detected: true,
         count: services.length,
@@ -334,6 +337,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
 
     if (!servicesFromYaml && detectedServices.detected) {
       setValue("app.services", []);
+      setValue("app.predeploy", []);
       setDetectedServices({
         detected: false,
         count: 0,
@@ -346,7 +350,8 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
       setError("app.name", {
         message: "An app with this name already exists",
       });
-      return;
+    } else {
+      clearErrors("app.name");
     }
   }, [porterApps, name]);
 
@@ -455,7 +460,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                       )}
                     </Container>
                     <Spacer y={0.5} />
-                    <ServiceList addNewText={"Add a new service"} />
+                    <ServiceList addNewText={"Add a new service"} fieldArrayName={"app.services"} />
                   </>,
                   <>
                     <Text size={16}>Environment variables (optional)</Text>
@@ -482,8 +487,10 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                             name: "pre-deploy",
                             type: "predeploy",
                           }),
+                          expanded: true,
                         })}
                         isPredeploy
+                        fieldArrayName={"app.predeploy"}
                       />
                     </>
                   ),

+ 5 - 5
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceContainer.tsx

@@ -26,7 +26,7 @@ interface ServiceProps {
   index: number;
   service: ClientService;
   chart?: any;
-  update: UseFieldArrayUpdate<PorterAppFormData, "app.services">;
+  update: UseFieldArrayUpdate<PorterAppFormData, "app.services" | "app.predeploy">;
   remove: (index: number) => void;
 }
 
@@ -47,7 +47,7 @@ const ServiceContainer: React.FC<ServiceProps> = ({
   const [maxRAM, setMaxRAM] = useState(
     Math.round(
       convert(AWS_INSTANCE_LIMITS["t3"]["medium"]["RAM"], "GiB").to("MB") *
-        UPPER_BOUND
+      UPPER_BOUND
     )
   ); //default is set to a t3 medium
   const context = useContext(Context);
@@ -140,7 +140,7 @@ const ServiceContainer: React.FC<ServiceProps> = ({
             );
           }
         })
-        .catch((error) => {});
+        .catch((error) => { });
     }
   }, []);
 
@@ -246,7 +246,7 @@ const ServiceContainer: React.FC<ServiceProps> = ({
         // Check if has built image
         getHasBuiltImage() && (
           <StatusFooter
-            setExpandedJob={() => {}}
+            setExpandedJob={() => { }}
             chart={chart}
             service={service}
           />
@@ -331,7 +331,7 @@ const ServiceHeader = styled.div<{
     border-radius: 20px;
     margin-left: -10px;
     transform: ${(props: { showExpanded?: boolean; chart: any }) =>
-      props.showExpanded ? "" : "rotate(-90deg)"};
+    props.showExpanded ? "" : "rotate(-90deg)"};
   }
 `;
 

+ 3 - 1
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/ServiceList.tsx

@@ -44,6 +44,7 @@ type ServiceListProps = {
   prePopulateService?: ClientService;
   isPredeploy?: boolean;
   existingServiceNames?: string[];
+  fieldArrayName: "app.services" | "app.predeploy";
 };
 
 const ServiceList: React.FC<ServiceListProps> = ({
@@ -51,6 +52,7 @@ const ServiceList: React.FC<ServiceListProps> = ({
   prePopulateService,
   isPredeploy = false,
   existingServiceNames = [],
+  fieldArrayName,
 }) => {
   // top level app form
   const { control: appControl } = useFormContext<PorterAppFormData>();
@@ -72,7 +74,7 @@ const ServiceList: React.FC<ServiceListProps> = ({
   });
   const { append, remove, update, fields } = useFieldArray({
     control: appControl,
-    name: "app.services",
+    name: fieldArrayName,
   });
   const {
     append: appendDeletion,

+ 9 - 8
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/JobTabs.tsx

@@ -39,14 +39,14 @@ const JobTabs: React.FC<Props> = ({
 
   const tabs = isPredeploy
     ? [
-        { label: "Main", value: "main" as const },
-        { label: "Resources", value: "resources" as const },
-      ]
+      { label: "Main", value: "main" as const },
+      { label: "Resources", value: "resources" as const },
+    ]
     : [
-        { label: "Main", value: "main" as const },
-        { label: "Resources", value: "resources" as const },
-        { label: "Advanced", value: "advanced" as const },
-      ];
+      { label: "Main", value: "main" as const },
+      { label: "Resources", value: "resources" as const },
+      { label: "Advanced", value: "advanced" as const },
+    ];
 
   return (
     <>
@@ -56,13 +56,14 @@ const JobTabs: React.FC<Props> = ({
         setCurrentTab={setCurrentTab}
       />
       {match(currentTab)
-        .with("main", () => <MainTab index={index} service={service} />)
+        .with("main", () => <MainTab index={index} service={service} isPredeploy={isPredeploy} />)
         .with("resources", () => (
           <Resources
             index={index}
             maxCPU={maxCPU}
             maxRAM={maxRAM}
             service={service}
+            isPredeploy={isPredeploy}
           />
         ))
         .with("advanced", () => (

+ 3 - 2
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Main.tsx

@@ -12,9 +12,10 @@ import Link from "components/porter/Link";
 type MainTabProps = {
   index: number;
   service: ClientService;
+  isPredeploy?: boolean;
 };
 
-const MainTab: React.FC<MainTabProps> = ({ index, service }) => {
+const MainTab: React.FC<MainTabProps> = ({ index, service, isPredeploy = false }) => {
   const { register, watch } = useFormContext<PorterAppFormData>();
   const cron = watch(`app.services.${index}.config.cron.value`);
 
@@ -45,7 +46,7 @@ const MainTab: React.FC<MainTabProps> = ({ index, service }) => {
         width="300px"
         disabled={service.run.readOnly}
         disabledTooltip={"You may only edit this field in your porter.yaml."}
-        {...register(`app.services.${index}.run.value`)}
+        {...register(isPredeploy ? `app.predeploy.${index}.run.value` : `app.services.${index}.run.value`)}
       />
       {service.config.type === "job" && (
         <>

+ 4 - 2
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Resources.tsx

@@ -14,6 +14,7 @@ type ResourcesProps = {
   maxCPU: number;
   maxRAM: number;
   service: ClientService;
+  isPredeploy?: boolean;
 };
 
 const Resources: React.FC<ResourcesProps> = ({
@@ -21,6 +22,7 @@ const Resources: React.FC<ResourcesProps> = ({
   maxCPU,
   maxRAM,
   service,
+  isPredeploy = false,
 }) => {
   const { control, register, watch } = useFormContext<PorterAppFormData>();
 
@@ -32,7 +34,7 @@ const Resources: React.FC<ResourcesProps> = ({
     <>
       <Spacer y={1} />
       <Controller
-        name={`app.services.${index}.cpuCores`}
+        name={isPredeploy ? `app.predeploy.${index}.cpuCores` : `app.services.${index}.cpuCores`}
         control={control}
         render={({ field: { value, onChange } }) => (
           <InputSlider
@@ -58,7 +60,7 @@ const Resources: React.FC<ResourcesProps> = ({
       />
       <Spacer y={1} />
       <Controller
-        name={`app.services.${index}.ramMegabytes`}
+        name={isPredeploy ? `app.predeploy.${index}.ramMegabytes` : `app.services.${index}.ramMegabytes`}
         control={control}
         render={({ field: { value, onChange } }) => (
           <InputSlider