Просмотр исходного кода

add input for porter yaml path (#4323)

Feroze Mohideen 2 лет назад
Родитель
Сommit
eed95a5c8a

+ 6 - 38
dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx

@@ -164,7 +164,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
       source: {
         git_repo_name: "",
         git_branch: "",
-        porter_yaml_path: "./porter.yaml",
+        porter_yaml_path: "",
       },
       deletions: {
         serviceNames: [],
@@ -617,43 +617,11 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                       <Spacer y={1} />
                       {source?.type ? (
                         source.type === "github" ? (
-                          <>
-                            <RepoSettings
-                              build={build}
-                              source={source}
-                              projectId={currentProject.id}
-                            />
-                            {/* {!userHasSeenNoPorterYamlFoundModal &&
-                              !porterYamlFound &&
-                              !isLoadingPorterYaml && (
-                                <Controller
-                                  name="source.porter_yaml_path"
-                                  control={control}
-                                  render={({ field: { onChange, value } }) => (
-                                    <PorterYamlModal
-                                      close={() => {
-                                        setUserHasSeenNoPorterYamlFoundModal(
-                                          true
-                                        );
-                                      }}
-                                      setPorterYamlPath={(porterYamlPath) => {
-                                        onChange(porterYamlPath);
-                                      }}
-                                      porterYamlPath={value}
-                                      projectId={currentProject.id}
-                                      repoId={source.git_repo_id}
-                                      repoOwner={
-                                        source.git_repo_name.split("/")[0]
-                                      }
-                                      repoName={
-                                        source.git_repo_name.split("/")[1]
-                                      }
-                                      branch={source.git_branch}
-                                    />
-                                  )}
-                                />
-                              )} */}
-                          </>
+                          <RepoSettings
+                            build={build}
+                            source={source}
+                            projectId={currentProject.id}
+                          />
                         ) : (
                           <ImageSettings
                             projectId={currentProject.id}

+ 98 - 0
dashboard/src/main/home/app-dashboard/create-app/PorterYamlInput.tsx

@@ -0,0 +1,98 @@
+import React, { useEffect, useRef, useState } from "react";
+import { Controller, useFormContext } from "react-hook-form";
+import styled from "styled-components";
+
+import { type PorterAppFormData } from "lib/porter-apps";
+
+import FileSelector from "../validate-apply/build-settings/FileSelector";
+
+type Props = {
+  projectId: number;
+  repoId: number;
+  repoOwner: string;
+  repoName: string;
+  branch: string;
+};
+
+const PorterYamlInput: React.FC<Props> = ({
+  projectId,
+  repoId,
+  repoOwner,
+  repoName,
+  branch,
+}) => {
+  const [showFileSelector, setShowFileSelector] = useState<boolean>(false);
+  const { control, watch } = useFormContext<PorterAppFormData>();
+  const porterYamlPath = watch("source.porter_yaml_path");
+
+  const fileSelectorRef = useRef(null);
+
+  useEffect(() => {
+    const handleClickOutside = (event: { target: unknown }): void => {
+      if (
+        fileSelectorRef.current &&
+        !fileSelectorRef.current?.contains(event.target)
+      ) {
+        setShowFileSelector(false);
+      }
+    };
+    document.addEventListener("mousedown", handleClickOutside);
+    return () => {
+      document.removeEventListener("mousedown", handleClickOutside);
+    };
+  }, [fileSelectorRef]);
+
+  return (
+    <div>
+      <div
+        onClick={(e) => {
+          e.stopPropagation();
+          if (!showFileSelector) {
+            setShowFileSelector(true);
+          }
+        }}
+      >
+        <StyledInput value={porterYamlPath} placeholder={"ex ./porter.yaml"} />
+      </div>
+      {showFileSelector && (
+        <div ref={fileSelectorRef}>
+          <Controller
+            name="source.porter_yaml_path"
+            control={control}
+            render={({ field: { onChange } }) => (
+              <FileSelector
+                projectId={projectId}
+                repoId={repoId}
+                repoOwner={repoOwner}
+                repoName={repoName}
+                branch={branch}
+                onFileSelect={(path: string) => {
+                  onChange(`./${path}`);
+                  setShowFileSelector(false);
+                }}
+                isFileSelectable={(path: string) => path.endsWith(".yaml")}
+                headerText={"Select your porter.yaml:"}
+                widthPercent={100}
+              />
+            )}
+          />
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default PorterYamlInput;
+
+const StyledInput = styled.input`
+  height: 35px;
+  padding: 5px 10px;
+  width: 100%;
+  color: #fefefe;
+  font-size: 13px;
+  outline: none;
+  transition: all 0.2s;
+  border-radius: 5px;
+  background: ${(props) => props.theme.fg};
+  border: 1px solid #494b4f;
+`;

+ 2 - 2
dashboard/src/main/home/app-dashboard/create-app/PorterYamlModal.tsx

@@ -35,7 +35,7 @@ const PorterYamlModal: React.FC<Props> = ({
     const [showFileSelector, setShowFileSelector] = useState<boolean>(false);
 
     return showModal ? (
-        <Modal closeModal={() => setShowModal(false)}>
+        <Modal closeModal={() => { setShowModal(false); }}>
             <div>
                 <Text size={16}>No <Code>porter.yaml</Code> detected at <Code>{porterYamlPath}</Code></Text>
                 <Spacer y={0.5} />
@@ -87,7 +87,7 @@ const PorterYamlModal: React.FC<Props> = ({
                         repoOwner={repoOwner} 
                         repoName={repoName} 
                         branch={branch}
-                        onFileSelect={(path: string) => setPossiblePorterYamlPath(`./${path}`)} 
+                        onFileSelect={(path: string) => { setPossiblePorterYamlPath(`./${path}`); }} 
                         isFileSelectable={(path: string) => path.endsWith(".yaml")}
                         headerText={"Select your porter.yaml:"}
                     />

+ 29 - 3
dashboard/src/main/home/app-dashboard/create-app/RepoSettings.tsx

@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from "react";
+import React, { useContext, useEffect, useMemo, useState } from "react";
 import { useQuery } from "@tanstack/react-query";
 import AnimateHeight from "react-animate-height";
 import { Controller, useFormContext } from "react-hook-form";
@@ -16,6 +16,7 @@ import { type PorterAppFormData, type SourceOptions } from "lib/porter-apps";
 import { type BuildOptions } from "lib/porter-apps/build";
 
 import api from "shared/api";
+import { Context } from "shared/Context";
 
 import BranchSelector from "../build-settings/BranchSelector";
 import RepositorySelector from "../build-settings/RepositorySelector";
@@ -23,6 +24,7 @@ import BuildpackSettings, {
   DEFAULT_BUILDERS,
 } from "../validate-apply/build-settings/buildpacks/BuildpackSettings";
 import DockerfileSettings from "../validate-apply/build-settings/docker/DockerfileSettings";
+import PorterYamlInput from "./PorterYamlInput";
 
 type Props = {
   projectId: number;
@@ -46,6 +48,7 @@ const RepoSettings: React.FC<Props> = ({
   build,
   appExists,
 }) => {
+  const { currentProject } = useContext(Context);
   const { control, register, setValue } = useFormContext<PorterAppFormData>();
   const [showSettings, setShowSettings] = useState<boolean>(false);
 
@@ -164,7 +167,7 @@ const RepoSettings: React.FC<Props> = ({
                     git_repo_name: "",
                     git_branch: "",
                     git_repo_id: 0,
-                    porter_yaml_path: "./porter.yaml",
+                    porter_yaml_path: "",
                   });
 
                   setValue("app.build.context", "./");
@@ -219,7 +222,7 @@ const RepoSettings: React.FC<Props> = ({
                       setValue("source", {
                         ...source,
                         git_branch: "",
-                        porter_yaml_path: "./porter.yaml",
+                        porter_yaml_path: "",
                       });
 
                       setValue("app.build.context", "./");
@@ -248,6 +251,29 @@ const RepoSettings: React.FC<Props> = ({
                 label={"Application root path:"}
               />
               <Spacer y={1} />
+              {!appExists && currentProject?.id && (
+                <>
+                  <Text color="helper">
+                    (Optional) Specify your porter.yaml path.{" "}
+                    <a
+                      href="https://docs.porter.run/deploy/configuration-as-code/overview"
+                      target="_blank"
+                      rel="noreferrer"
+                    >
+                      &nbsp;(?)
+                    </a>
+                  </Text>
+                  <Spacer y={0.5} />
+                  <PorterYamlInput
+                    projectId={currentProject.id}
+                    repoId={source.git_repo_id}
+                    repoOwner={source.git_repo_name.split("/")[0]}
+                    repoName={source.git_repo_name.split("/")[1]}
+                    branch={source.git_branch}
+                  />
+                  <Spacer y={1} />
+                </>
+              )}
               {isLoading && !appExists ? (
                 <AdvancedBuildTitle>
                   <Loading />

+ 1 - 1
dashboard/src/main/home/app-dashboard/new-app-flow/utils.ts

@@ -43,7 +43,7 @@ jobs:
       uses: porter-dev/setup-porter@v0.1.0
     - name: Deploy stack
       timeout-minutes: 30
-      run: exec porter apply -f ${porterYamlPath}
+      run: exec porter apply ${porterYamlPath ? `-f ${porterYamlPath}` : ""}
       env:
         PORTER_CLUSTER: ${clusterId}
         PORTER_HOST: https://dashboard.getporter.dev

+ 127 - 116
dashboard/src/main/home/app-dashboard/validate-apply/build-settings/FileSelector.tsx

@@ -1,135 +1,146 @@
-import { useGithubContents } from "lib/hooks/useGithubContents";
 import React, { useState } from "react";
 import styled from "styled-components";
-import file from "assets/file_v2.svg";
-import folder from "assets/folder_v2.svg";
-import file_branch from "assets/file-branch.svg";
 import { match } from "ts-pattern";
+
 import Loading from "components/Loading";
+import { useGithubContents } from "lib/hooks/useGithubContents";
+
+import file from "assets/file_v2.svg";
+import file_branch from "assets/file-branch.svg";
+import folder from "assets/folder_v2.svg";
 
 type Props = {
-    repoId: number;
-    repoOwner: string;
-    repoName: string;
-    branch: string;
-    projectId: number;
-    onFileSelect: (path: string) => void;
-    isFileSelectable?: (path: string) => boolean;
-    widthPixels?: number;
-    heightPixels?: number;
-    headerText: string;
-}
+  repoId: number;
+  repoOwner: string;
+  repoName: string;
+  branch: string;
+  projectId: number;
+  onFileSelect: (path: string) => void;
+  isFileSelectable?: (path: string) => boolean;
+  widthPixels?: number;
+  widthPercent?: number;
+  heightPixels?: number;
+  headerText: string;
+};
 const FileSelector: React.FC<Props> = ({
+  repoId,
+  repoOwner,
+  repoName,
+  branch,
+  projectId,
+  onFileSelect,
+  isFileSelectable = () => true,
+  widthPixels = 500,
+  heightPixels = 275,
+  headerText,
+  widthPercent,
+}) => {
+  const [path, setPath] = useState<string>("./");
+  const { contents, isLoading } = useGithubContents({
     repoId,
     repoOwner,
     repoName,
     branch,
+    path,
     projectId,
-    onFileSelect,
-    isFileSelectable = () => true,
-    widthPixels = 500,
-    heightPixels = 275,
-    headerText,
-}) => {
-    const [ path, setPath ] = useState<string>("./");
-    const { contents, isLoading } = useGithubContents({
-        repoId,
-        repoOwner,
-        repoName,
-        branch,
-        path,
-        projectId,
-    })
+  });
 
-    return (
-        <div>
-            <StyledFileSelector widthPixels={widthPixels} heightPixels={heightPixels}>
-                {isLoading ? (
-                    <Loading />
-                ) : (
-                    <>
-                    {path !== "./" && path !== "" ? (
-                    <Item
-                        onClick={() => {
-                            const parentPath = path.split("/").slice(0, -2).join("/") + "/";
-                            setPath(parentPath);
-                        }}
-                        isHeaderItem={false}
-                    >
-                        <img src={folder} />
-                        ..
-                    </Item>
-                ) : (
-                    <Item
-                        onClick={() => {}}
-                        isHeaderItem
-                    >
-                        <img src={file_branch} />
-                        {headerText}
-                    </Item>
-                )}
-                {contents.map((content, i) => 
-                    {
-                        // this is the path in the scope of the current directory
-                        // e.g. if the path is ./foo/bar, then the relative path is bar
-                        const relativePath = content.path.split("/").slice(-1)[0];
-                        const isSelectable = isFileSelectable(relativePath);
-                        return match(content)
-                            .with({ type: "file" }, (content) => (
-                                <FileItem 
-                                    key={i} 
-                                    onClick={() => {
-                                        if (isSelectable) {
-                                            onFileSelect(content.path);
-                                        }
-                                    }}
-                                    isFileSelectable={isSelectable}
-                                    isHeaderItem={false}
-                                >
-                                    <img src={file} />
-                                    {relativePath}
-                                </FileItem>
-                            ))
-                            .with({ type: "dir" }, (content) => (
-                                <Item 
-                                    key={i} 
-                                    onClick={() => setPath(`${path}${relativePath}/`)}
-                                    isHeaderItem={false}
-                                >
-                                    <img src={folder} />
-                                    {relativePath}
-                                </Item>
-                            ))
-                            .with({ type: "symlink" }, (content) => (
-                                <FileItem 
-                                    key={i} 
-                                    onClick={() => ({})}
-                                    isHeaderItem={false}
-                                    isFileSelectable={false}
-                                >
-                                    <img src={folder} />
-                                    {relativePath}
-                                </FileItem>
-                            ))
-                        .exhaustive();
-                    }
-                )}
-                    </>
-                )}
-            </StyledFileSelector>
-        </div>
-    );
+  return (
+    <div>
+      <StyledFileSelector
+        widthPixels={widthPixels}
+        widthPercent={widthPercent}
+        heightPixels={heightPixels}
+      >
+        {isLoading ? (
+          <Loading />
+        ) : (
+          <>
+            {path !== "./" && path !== "" ? (
+              <Item
+                onClick={() => {
+                  const parentPath =
+                    path.split("/").slice(0, -2).join("/") + "/";
+                  setPath(parentPath);
+                }}
+                isHeaderItem={false}
+              >
+                <img src={folder} />
+                ..
+              </Item>
+            ) : (
+              <Item onClick={() => {}} isHeaderItem>
+                <img src={file_branch} />
+                {headerText}
+              </Item>
+            )}
+            {contents.map((content, i) => {
+              // this is the path in the scope of the current directory
+              // e.g. if the path is ./foo/bar, then the relative path is bar
+              const relativePath = content.path.split("/").slice(-1)[0];
+              const isSelectable = isFileSelectable(relativePath);
+              return match(content)
+                .with({ type: "file" }, (content) => (
+                  <FileItem
+                    key={i}
+                    onClick={() => {
+                      if (isSelectable) {
+                        onFileSelect(content.path);
+                      }
+                    }}
+                    isFileSelectable={isSelectable}
+                    isHeaderItem={false}
+                  >
+                    <img src={file} />
+                    {relativePath}
+                  </FileItem>
+                ))
+                .with({ type: "dir" }, (content) => (
+                  <Item
+                    key={i}
+                    onClick={() => {
+                      setPath(`${path}${relativePath}/`);
+                    }}
+                    isHeaderItem={false}
+                  >
+                    <img src={folder} />
+                    {relativePath}
+                  </Item>
+                ))
+                .with({ type: "symlink" }, (content) => (
+                  <FileItem
+                    key={i}
+                    onClick={() => ({})}
+                    isHeaderItem={false}
+                    isFileSelectable={false}
+                  >
+                    <img src={folder} />
+                    {relativePath}
+                  </FileItem>
+                ))
+                .exhaustive();
+            })}
+          </>
+        )}
+      </StyledFileSelector>
+    </div>
+  );
 };
 
 export default FileSelector;
 
-const StyledFileSelector = styled.div<{ widthPixels: number, heightPixels: number }>`
+const StyledFileSelector = styled.div<{
+  widthPixels: number;
+  widthPercent?: number;
+  heightPixels: number;
+}>`
   margin-top: 10px;
   border-radius: 3px;
   border: 1px solid #ffffff44;
   max-height: 275px;
   overflow-y: auto;
-  width: ${({ widthPixels }) => widthPixels}px;
+  width: ${({ widthPixels, widthPercent }) =>
+    widthPercent ? `${widthPercent}%` : `${widthPixels}px`};
   height: ${({ heightPixels }) => heightPixels}px;
 `;
 
@@ -144,11 +155,11 @@ const Item = styled.div`
   padding: 10px 0px;
   cursor: ${(props: { isHeaderItem: boolean }) =>
     props.isHeaderItem ? "default" : "pointer"};
-  background:  ${(props) =>
-    props.isHeaderItem ?  `${props.theme.fg2}` : `${props.theme.clickable.bg}`};
+  background: ${(props) =>
+    props.isHeaderItem ? `${props.theme.fg2}` : `${props.theme.clickable.bg}`};
   :hover {
     border: ${(props) =>
-        props.isHeaderItem ? `1px solid #494b4f` : `1px solid #7a7b80`};
+      props.isHeaderItem ? `1px solid #494b4f` : `1px solid #7a7b80`};
   }
 
   > img {
@@ -181,11 +192,11 @@ const FileItem = styled(Item)`
     props.isFileSelectable ? "#ffffff" : "#ffffff55"};
   :hover {
     border: ${(props) =>
-        props.isFileSelectable ?  `1px solid #7a7b80`: `1px solid #494b4f`};
+      props.isFileSelectable ? `1px solid #7a7b80` : `1px solid #494b4f`};
   }
 
   img {
     opacity: ${(props: { isFileSelectable: boolean }) =>
-        props.isFileSelectable ? "1" : "0.5"} !important;
+      props.isFileSelectable ? "1" : "0.5"} !important;
   }
-`;
+`;