Przeglądaj źródła

use name from yaml if it exists (#3545)

ianedwards 2 lat temu
rodzic
commit
41651dba0c

+ 20 - 11
dashboard/src/lib/hooks/usePorterYaml.ts

@@ -1,7 +1,7 @@
 import { PorterApp } from "@porter-dev/api-contracts";
 import { useQuery } from "@tanstack/react-query";
 import { SourceOptions, serviceOverrides } from "lib/porter-apps";
-import { ClientService, DetectedServices } from "lib/porter-apps/services";
+import { DetectedServices } from "lib/porter-apps/services";
 import { useCallback, useContext, useEffect, useState } from "react";
 import { Context } from "shared/Context";
 import api from "shared/api";
@@ -9,15 +9,17 @@ import { z } from "zod";
 
 type PorterYamlStatus =
   | {
-    loading: true;
-    detectedServices: null;
-    porterYamlFound: boolean;
-  }
+      loading: true;
+      detectedName: null;
+      detectedServices: null;
+      porterYamlFound: boolean;
+    }
   | {
-    detectedServices: DetectedServices | null;
-    loading: false;
-    porterYamlFound: boolean;
-  };
+      detectedServices: DetectedServices | null;
+      detectedName: string | null;
+      loading: false;
+      porterYamlFound: boolean;
+    };
 
 /*
  *
@@ -30,7 +32,7 @@ export const usePorterYaml = ({
   source,
   useDefaults = true,
 }: {
-  source: SourceOptions & { type: "github" } | null;
+  source: (SourceOptions & { type: "github" }) | null;
   useDefaults?: boolean;
 }): PorterYamlStatus => {
   const { currentProject, currentCluster } = useContext(Context);
@@ -38,6 +40,7 @@ export const usePorterYaml = ({
     detectedServices,
     setDetectedServices,
   ] = useState<DetectedServices | null>(null);
+  const [detectedName, setDetectedName] = useState<string | null>(null);
   const [porterYamlFound, setPorterYamlFound] = useState(false);
 
   const { data, status } = useQuery(
@@ -55,7 +58,6 @@ export const usePorterYaml = ({
         return;
       }
 
-
       const res = await api.getPorterYamlContents(
         "<token>",
         {
@@ -128,6 +130,10 @@ export const usePorterYaml = ({
             predeploy,
           });
         }
+
+        if (proto.name) {
+          setDetectedName(proto.name);
+        }
       } catch (err) {
         // silent failure for now
       }
@@ -156,6 +162,7 @@ export const usePorterYaml = ({
   if (source?.type !== "github") {
     return {
       loading: false,
+      detectedName: null,
       detectedServices: null,
       porterYamlFound: false,
     };
@@ -164,6 +171,7 @@ export const usePorterYaml = ({
   if (status === "loading") {
     return {
       loading: true,
+      detectedName: null,
       detectedServices: null,
       porterYamlFound: true,
     };
@@ -171,6 +179,7 @@ export const usePorterYaml = ({
 
   return {
     detectedServices,
+    detectedName,
     loading: false,
     porterYamlFound,
   };

+ 37 - 25
dashboard/src/lib/porter-apps/index.ts

@@ -66,7 +66,10 @@ export const deletionValidator = z.object({
 
 // clientAppValidator is the representation of a Porter app on the client, and is used to validate inputs for app setting fields
 export const clientAppValidator = z.object({
-  name: z.string().min(1),
+  name: z.object({
+    readOnly: z.boolean(),
+    value: z.string(),
+  }),
   services: serviceValidator.array(),
   predeploy: serviceValidator.array().optional(),
   env: z.record(z.string(), z.string()).default({}),
@@ -164,20 +167,19 @@ const clientBuildToProto = (build: BuildOptions) => {
 export function clientAppToProto(data: PorterAppFormData): PorterApp {
   const { app, source } = data;
 
-  const services = app.services
-    .reduce((acc: Record<string, Service>, svc) => {
-      acc[svc.name.value] = serviceProto(serializeService(svc));
-      return acc;
-    }, {});
+  const services = app.services.reduce((acc: Record<string, Service>, svc) => {
+    acc[svc.name.value] = serviceProto(serializeService(svc));
+    return acc;
+  }, {});
 
-  const predeploy = app.predeploy?.[0]
+  const predeploy = app.predeploy?.[0];
 
   const proto = match(source)
     .with(
       { type: "github" },
       () =>
         new PorterApp({
-          name: app.name,
+          name: app.name.value,
           services,
           env: app.env,
           build: clientBuildToProto(app.build),
@@ -190,7 +192,7 @@ export function clientAppToProto(data: PorterAppFormData): PorterApp {
       { type: "docker-registry" },
       (src) =>
         new PorterApp({
-          name: app.name,
+          name: app.name.value,
           services,
           env: app.env,
           image: {
@@ -275,17 +277,22 @@ export function clientAppFromProto(
 
   const predeployList = [];
   if (proto.predeploy) {
-    predeployList.push(deserializeService({
-      service: serializedServiceFromProto({
-        name: "pre-deploy",
-        service: proto.predeploy,
-        isPredeploy: true,
+    predeployList.push(
+      deserializeService({
+        service: serializedServiceFromProto({
+          name: "pre-deploy",
+          service: proto.predeploy,
+          isPredeploy: true,
+        }),
       })
-    }))
+    );
   }
   if (!overrides?.predeploy) {
     return {
-      name: proto.name,
+      name: {
+        readOnly: true,
+        value: proto.name,
+      },
       services,
       predeploy: predeployList,
       env: proto.env,
@@ -300,18 +307,23 @@ 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,
+    name: {
+      readOnly: true,
+      value: proto.name,
+    },
     services,
     predeploy,
     env: proto.env,

+ 1 - 10
dashboard/src/main/home/app-dashboard/app-view/AppView.tsx

@@ -27,15 +27,6 @@ export const porterAppValidator = z.object({
 });
 export type PorterAppRecord = z.infer<typeof porterAppValidator>;
 
-// Buildpack icons
-const icons = [
-  "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/ruby/ruby-plain.svg",
-  "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-plain.svg",
-  "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/python/python-plain.svg",
-  "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/go/go-original-wordmark.svg",
-  web,
-];
-
 // commented out tabs are not yet implemented
 // will be included as support is available based on data from app revisions rather than helm releases
 const validTabs = [
@@ -103,4 +94,4 @@ const StyledExpandedApp = styled.div`
       opacity: 1;
     }
   }
-`;
+`;

+ 30 - 15
dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx

@@ -98,7 +98,10 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
     reValidateMode: "onSubmit",
     defaultValues: {
       app: {
-        name: "",
+        name: {
+          value: "",
+          readOnly: false,
+        },
         build: {
           method: "pack",
           context: "./",
@@ -113,7 +116,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
       },
       deletions: {
         serviceNames: [],
-      }
+      },
     },
   });
   const {
@@ -132,9 +135,9 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
   const build = watch("app.build");
   const image = watch("source.image");
   const services = watch("app.services");
-  const { detectedServices: servicesFromYaml, porterYamlFound } = usePorterYaml({ source: source?.type === "github" ? source : null });
+  const { detectedServices: servicesFromYaml, porterYamlFound, detectedName } = usePorterYaml({ source: source?.type === "github" ? source : null });
   const deploymentTarget = useDefaultDeploymentTarget();
-  const { updateAppStep } = useAppAnalytics(name);
+  const { updateAppStep } = useAppAnalytics(name.value);
   const { validateApp } = useAppValidation({
     deploymentTargetID: deploymentTarget?.deployment_target_id,
     creating: true,
@@ -299,7 +302,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
   }, [isValidating, isDeploying, deployError, errors]);
 
   const submitDisabled = useMemo(() => {
-    return !name || !source || services.length === 0;
+    return !name || !source || services?.length === 0;
   }, [name, source, services?.length]);
 
   // reset services when source changes
@@ -345,17 +348,21 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
         count: 0,
       });
     }
-  }, [servicesFromYaml, detectedServices.detected]);
+
+    if (detectedName) {
+      setValue("app.name", { value: detectedName, readOnly: true });
+    }
+  }, [servicesFromYaml, detectedName, detectedServices.detected]);
 
   useEffect(() => {
-    if (porterApps.includes(name)) {
-      setError("app.name", {
+    if (porterApps.includes(name.value)) {
+      setError("app.name.value", {
         message: "An app with this name already exists",
       });
     } else {
-      clearErrors("app.name");
+      clearErrors("app.name.value");
     }
-  }, [porterApps, name]);
+  }, [porterApps, name.value]);
 
   if (!currentProject || !currentCluster) {
     return null;
@@ -389,7 +396,11 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                       placeholder="ex: academic-sophon"
                       type="text"
                       error={errors.app?.name?.message}
-                      {...register("app.name")}
+                      disabled={name.readOnly}
+                      disabledTooltip={
+                        "You may only edit this field in your porter.yaml."
+                      }
+                      {...register("app.name.value")}
                     />
                   </>,
                   <>
@@ -471,15 +482,19 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                             }
                           >
                             {detectedServices.count > 0
-                              ? `Detected ${detectedServices.count} service${detectedServices.count > 1 ? "s" : ""
-                              } from porter.yaml.`
+                              ? `Detected ${detectedServices.count} service${
+                                  detectedServices.count > 1 ? "s" : ""
+                                } from porter.yaml.`
                               : `Could not detect any services from porter.yaml. Make sure it exists in the root of your repo.`}
                           </Text>
                         </AppearingDiv>
                       )}
                     </Container>
                     <Spacer y={0.5} />
-                    <ServiceList addNewText={"Add a new service"} fieldArrayName={"app.services"} />
+                    <ServiceList
+                      addNewText={"Add a new service"}
+                      fieldArrayName={"app.services"}
+                    />
                   </>,
                   <>
                     <Text size={16}>Environment variables (optional)</Text>
@@ -536,7 +551,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
           githubRepoOwner={source.git_repo_name.split("/")[0]}
           githubRepoName={source.git_repo_name.split("/")[1]}
           branch={source.git_branch}
-          stackName={name}
+          stackName={name.value}
           projectId={currentProject.id}
           clusterId={currentCluster.id}
           deployPorterApp={() =>