Sfoglia il codice sorgente

Add porter yaml url (#2948)

* PorterYaml Changes

* PorterYaml Changes

* FrontEnd Read In

* FrontEnd Read In
sdess09 3 anni fa
parent
commit
34419c8a15

+ 82 - 0
api/server/handlers/gitinstallation/get_porter_yaml.go

@@ -0,0 +1,82 @@
+package gitinstallation
+
+import (
+	"context"
+	b64 "encoding/base64"
+	"net/http"
+
+	"github.com/google/go-github/v41/github"
+	"github.com/porter-dev/porter/api/server/authz"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/commonutils"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+)
+
+type GithubGetPorterYamlHandler struct {
+	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewGithubGetPorterYamlHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *GithubGetPorterYamlHandler {
+	return &GithubGetPorterYamlHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (c *GithubGetPorterYamlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	request := &types.GetPorterYamlRequest{}
+
+	ok := c.DecodeAndValidate(w, r, request)
+
+	if !ok {
+		return
+	}
+
+	owner, name, ok := commonutils.GetOwnerAndNameParams(c, w, r)
+
+	if !ok {
+		return
+	}
+
+	branch, ok := commonutils.GetBranchParam(c, w, r)
+
+	if !ok {
+		return
+	}
+
+	client, err := GetGithubAppClientFromRequest(c.Config(), r)
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	resp, _, _, err := client.Repositories.GetContents(
+		context.Background(),
+		owner,
+		name,
+		request.Path,
+		&github.RepositoryContentGetOptions{
+			Ref: branch,
+		},
+	)
+	if err != nil {
+		http.NotFound(w, r)
+		return
+	}
+
+	fileData, err := resp.GetContent()
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+	data := b64.StdEncoding.EncodeToString([]byte(fileData))
+
+	c.WriteResult(w, r, data)
+}

+ 37 - 0
api/server/router/git_installation.go

@@ -588,6 +588,43 @@ func getGitInstallationRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/porteryaml ->
+	// gitinstallation.NewGithubGetProcfileHandler
+	getPorterYamlEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent: basePath,
+				RelativePath: fmt.Sprintf(
+					"%s/repos/{%s}/{%s}/{%s}/{%s}/porteryaml",
+					relPath,
+					types.URLParamGitKind,
+					types.URLParamGitRepoOwner,
+					types.URLParamGitRepoName,
+					types.URLParamGitBranch,
+				),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.GitInstallationScope,
+			},
+		},
+	)
+
+	getPorterYamlHandler := gitinstallation.NewGithubGetPorterYamlHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: getPorterYamlEndpoint,
+		Handler:  getPorterYamlHandler,
+		Router:   r,
+	})
+
 	// GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/procfile ->
 	// gitinstallation.NewGithubGetProcfileHandler
 	getProcfileEndpoint := factory.NewAPIEndpoint(

+ 4 - 0
api/types/git_installation.go

@@ -51,6 +51,10 @@ type GithubDirectoryItem struct {
 
 type GetContentsResponse []GithubDirectoryItem
 
+type GetPorterYamlRequest struct {
+	Path string `schema:"path" form:"required"`
+}
+
 type GetProcfileRequest struct {
 	Path string `schema:"path" form:"required"`
 }

+ 49 - 190
dashboard/src/components/repo-selector/DetectContentsList.tsx

@@ -1,10 +1,10 @@
-import React, { useState, useEffect, useContext } from "react";
+import React, { useState, useEffect, useContext, useCallback } from "react";
 import styled from "styled-components";
 import file from "assets/file.svg";
 import folder from "assets/folder.svg";
 import info from "assets/info.svg";
 import close from "assets/close.png";
-
+import Button from "components/porter/Button";
 import api from "../../shared/api";
 import { Context } from "../../shared/Context";
 import { ActionConfigType, FileType } from "../../shared/types";
@@ -26,12 +26,14 @@ type PropsType = {
   dockerfilePath?: string;
   folderPath: string;
   procfilePath?: string;
+  porterYaml?: string;
   setActionConfig: (x: ActionConfigType) => void;
   setProcfileProcess?: (x: string) => void;
   setDockerfilePath: (x: string) => void;
   setProcfilePath: (x: string) => void;
   setFolderPath: (x: string) => void;
   setBuildConfig: (x: any) => void;
+  setPorterYaml: (x: any) => void;
 };
 
 const DetectContentsList: React.FC<PropsType> = (props) => {
@@ -46,13 +48,33 @@ const DetectContentsList: React.FC<PropsType> = (props) => {
   const [showingBuildContextPrompt, setShowingBuildContextPrompt] = useState(
     "buildpacks"
   );
+  const [porterYaml, setPorterYaml] = useState("");
 
   const context = useContext(Context);
+  const fetchAndSetPorterYaml = useCallback(async (fileName: string) => {
+    try {
+      const response = await fetchPorterYamlContent(fileName);
+      setPorterYaml(atob(response.data));
+    } catch (error) {
+      console.error("Error fetching porter.yaml content:", error);
+    }
+  }, []);
+
+  useEffect(() => {
+    const porterYamlItem = contents.find((item: FileType) =>
+      item.path.includes("porter.yaml")
+    );
+
+    if (porterYamlItem) {
+      fetchAndSetPorterYaml("porter.yaml");
+    } else {
+      setPorterYaml("");
+    }
+  }, [contents, fetchAndSetPorterYaml]);
 
   useEffect(() => {
     updateContents();
   }, []);
-
   useEffect(() => {
     const dockerFileItem = contents.find((item: FileType) =>
       item.path.includes("Dockerfile")
@@ -84,8 +106,6 @@ const DetectContentsList: React.FC<PropsType> = (props) => {
     return contents.map((item: FileType, i: number) => {
       let splits = item.path.split("/");
       let fileName = splits[splits.length - 1];
-      if (fileName.includes("porter.yaml")) {
-      }
       if (fileName.includes("Dockerfile")) {
         return (
           <AdvancedBuildSettings
@@ -144,6 +164,30 @@ const DetectContentsList: React.FC<PropsType> = (props) => {
     );
   };
 
+  const fetchPorterYamlContent = async (porterYaml: string) => {
+    let { currentProject } = context;
+    let { actionConfig, branch } = props;
+
+    try {
+      const res = await api.getPorterYamlContents(
+        "<token>",
+        {
+          path: porterYaml,
+        },
+        {
+          project_id: currentProject.id,
+          git_repo_id: actionConfig.git_repo_id,
+          kind: "github",
+          owner: actionConfig.git_repo.split("/")[0],
+          name: actionConfig.git_repo.split("/")[1],
+          branch: branch,
+        }
+      );
+      return res;
+    } catch (err) {
+      console.log(err);
+    }
+  };
   const detectBuildpacks = () => {
     let { currentProject } = context;
     let { actionConfig, branch } = props;
@@ -215,191 +259,6 @@ const DetectContentsList: React.FC<PropsType> = (props) => {
       });
     }
   };
-
-  // const renderJumpToParent = () => {
-  //   if (this.state.currentDir !== "") {
-  //     let splits = this.state.currentDir.split("/");
-  //     let subdir = "";
-  //     if (splits.length !== 1) {
-  //       subdir = this.state.currentDir.replace(splits[splits.length - 1], "");
-  //       if (subdir.charAt(subdir.length - 1) === "/") {
-  //         subdir = subdir.slice(0, subdir.length - 1);
-  //       }
-  //     }
-
-  //     return (
-  //       <Item lastItem={false} onClick={() => this.setSubdirectory(subdir)}>
-  //         <BackLabel>..</BackLabel>
-  //       </Item>
-  //     );
-  //   }
-
-  //   return (
-  //     <FileItem lastItem={false}>
-  //       <img src={info} />
-  //       Select{" "}
-  //       {this.props.dockerfilePath
-  //         ? "Docker Build Context"
-  //         : "Application Folder"}
-  //     </FileItem>
-  //   );
-  // };
-
-  // const renderOverlay = () => {
-  //   if (this.props.procfilePath) {
-  //     let processes = this.state.processes
-  //       ? Object.keys(this.state.processes)
-  //       : [];
-  //     if (this.state.processes == null) {
-  //       return (
-  //         <Overlay>
-  //           <BgOverlay>
-  //             <LoadingWrapper>
-  //               <Loading />
-  //             </LoadingWrapper>
-  //           </BgOverlay>
-  //         </Overlay>
-  //       );
-  //     }
-
-  //     if (processes.length == 0) {
-  //       this.props.setProcfilePath("");
-  //     }
-
-  //     return (
-  //       <Overlay>
-  //         <BgOverlay
-  //           onClick={() =>
-  //             this.setState({ dockerfiles: [] }, () => {
-  //               this.props.setFolderPath("");
-  //               this.props.setProcfilePath("");
-  //             })
-  //           }
-  //         />
-  //         <CloseButton
-  //           onClick={() =>
-  //             this.setState({ dockerfiles: [] }, () => {
-  //               this.props.setProcfilePath("");
-  //             })
-  //           }
-  //         >
-  //           <CloseButtonImg src={close} />
-  //         </CloseButton>
-  //         <Label>
-  //           Porter has detected a Procfile in this folder. Which process would
-  //           you like to run?
-  //         </Label>
-  //         <DockerfileList>
-  //           {processes.map((process: string, i: number) => {
-  //             return (
-  //               <Row
-  //                 key={i}
-  //                 onClick={() => {
-  //                   if (
-  //                     !this.props.folderPath ||
-  //                     this.props.folderPath === ""
-  //                   ) {
-  //                     this.props.setFolderPath("./");
-  //                   }
-  //                   this.props.setProcfileProcess(process);
-  //                 }}
-  //                 isLast={processes.length - 1 === i}
-  //               >
-  //                 <Indicator selected={false} />
-  //                 {process}
-  //               </Row>
-  //             );
-  //           })}
-  //         </DockerfileList>
-  //       </Overlay>
-  //     );
-  //   }
-  //   if (this.state.dockerfiles.length > 0 && !this.props.dockerfilePath) {
-  //     return (
-  //       <Overlay>
-  //         <BgOverlay onClick={() => this.setState({ dockerfiles: [] })} />
-  //         <CloseButton onClick={() => this.setState({ dockerfiles: [] })}>
-  //           <CloseButtonImg src={close} />
-  //         </CloseButton>
-  //         <Label>
-  //           Porter has detected at least one Dockerfile in this folder. Would
-  //           you like to use an existing Dockerfile?
-  //         </Label>
-  //         <DockerfileList>
-  //           {this.state.dockerfiles.map((dockerfile: string, i: number) => {
-  //             return (
-  //               <Row
-  //                 key={i}
-  //                 onClick={() =>
-  //                   this.props.setDockerfilePath(
-  //                     `${this.state.currentDir || "."}/${dockerfile}`
-  //                   )
-  //                 }
-  //                 isLast={this.state.dockerfiles.length - 1 === i}
-  //               >
-  //                 <Indicator selected={false}></Indicator>
-  //                 {dockerfile}
-  //               </Row>
-  //             );
-  //           })}
-  //         </DockerfileList>
-  //         <ConfirmButton
-  //           onClick={() => {
-  //             this.props.setFolderPath(this.state.currentDir || "./");
-  //             if (
-  //               this.state.processes &&
-  //               Object.keys(this.state.processes).length > 0
-  //             ) {
-  //               this.props.setProcfilePath("./Procfile");
-  //             }
-  //           }}
-  //         >
-  //           No, I don't want to use a Dockerfile
-  //         </ConfirmButton>
-  //       </Overlay>
-  //     );
-  //   }
-  //   if (
-  //     this.props.dockerfilePath &&
-  //     !this.props.folderPath &&
-  //     this.state.showingBuildContextPrompt
-  //   ) {
-  //     return (
-  //       <Overlay>
-  //         <BgOverlay onClick={() => this.props.setDockerfilePath("")} />
-  //         <CloseButton
-  //           onClick={() =>
-  //             this.props.setFolderPath(this.state.currentDir || "./")
-  //           }
-  //         >
-  //           <CloseButtonImg src={close} />
-  //         </CloseButton>
-  //         <Label>
-  //           Would you like to set the Docker build context to a different
-  //           directory?
-  //         </Label>
-  //         <MultiSelectRow>
-  //           <ConfirmButton
-  //             onClick={() => {
-  //               this.setState({ showingBuildContextPrompt: false });
-  //               this.setSubdirectory("");
-  //             }}
-  //           >
-  //             Yes
-  //           </ConfirmButton>
-  //           <ConfirmButton
-  //             onClick={() =>
-  //               this.props.setFolderPath(this.state.currentDir || "./")
-  //             }
-  //           >
-  //             No
-  //           </ConfirmButton>
-  //         </MultiSelectRow>
-  //       </Overlay>
-  //     );
-  //   }
-  // };
-
   return (
     <>
       {renderContentList()}

+ 3 - 0
dashboard/src/main/home/app-dashboard/new-app-flow/NewAppFlow.tsx

@@ -93,6 +93,7 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
   const [selectedRegistry, setSelectedRegistry] = useState(null);
   const [shouldCreateWorkflow, setShouldCreateWorkflow] = useState(true);
   const [buildConfig, setBuildConfig] = useState();
+  const [porterYaml, setPorterYaml] = useState("");
   const getFullActionConfig = (): FullGithubActionConfigType => {
     let imageRepoURI = `${selectedRegistry?.url}/${templateName}`;
     return {
@@ -207,6 +208,8 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
                   procfilePath={procfilePath}
                   setProcfilePath={setProcfilePath}
                   setBuildConfig={setBuildConfig}
+                  porterYaml={porterYaml}
+                  setPorterYaml={setPorterYaml}
                 />
               </>,
               <>

+ 6 - 0
dashboard/src/main/home/app-dashboard/new-app-flow/SourceSettings.tsx

@@ -34,6 +34,8 @@ type Props = {
   folderPath: string | null;
   setFolderPath: (x: string) => void;
   setBuildConfig: (x: any) => void;
+  porterYaml: string;
+  setPorterYaml: (x: any) => void;
 };
 
 const SourceSettings: React.FC<Props> = ({
@@ -54,6 +56,8 @@ const SourceSettings: React.FC<Props> = ({
   folderPath,
   setFolderPath,
   setBuildConfig,
+  porterYaml,
+  setPorterYaml,
 }) => {
   const renderGithubSettings = () => {
     return (
@@ -124,6 +128,8 @@ const SourceSettings: React.FC<Props> = ({
             setProcfileProcess={setProcfileProcess}
             setFolderPath={setFolderPath}
             setBuildConfig={setBuildConfig}
+            porterYaml={porterYaml}
+            setPorterYaml={setPorterYaml}
           />
         )}
       </>

+ 96 - 62
dashboard/src/shared/api.tsx

@@ -180,7 +180,12 @@ const createPorterApp = baseApi<
 const updatePorterStack = baseApi<
   {
     stack_name: string;
-    dependencies: { name: string; alias: string; version: string; repository: string }[];
+    dependencies: {
+      name: string;
+      alias: string;
+      version: string;
+      repository: string;
+    }[];
     values: any;
   },
   {
@@ -357,12 +362,12 @@ const createInvite = baseApi<
   return `/api/projects/${pathParams.id}/invites`;
 });
 
-const inviteAdmin = baseApi<
-  {},
-  { project_id: number }
->("POST", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/invite_admin`;
-});
+const inviteAdmin = baseApi<{}, { project_id: number }>(
+  "POST",
+  (pathParams) => {
+    return `/api/projects/${pathParams.project_id}/invite_admin`;
+  }
+);
 
 const createPasswordReset = baseApi<
   {
@@ -614,9 +619,11 @@ const detectBuildpack = baseApi<
     branch: string;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id
-    }/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name
-    }/${encodeURIComponent(pathParams.branch)}/buildpack/detect`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${
+    pathParams.git_repo_id
+  }/repos/${pathParams.kind}/${pathParams.owner}/${
+    pathParams.name
+  }/${encodeURIComponent(pathParams.branch)}/buildpack/detect`;
 });
 
 const detectGitlabBuildpack = baseApi<
@@ -647,9 +654,11 @@ const getBranchContents = baseApi<
     branch: string;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id
-    }/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name
-    }/${encodeURIComponent(pathParams.branch)}/contents`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${
+    pathParams.git_repo_id
+  }/repos/${pathParams.kind}/${pathParams.owner}/${
+    pathParams.name
+  }/${encodeURIComponent(pathParams.branch)}/contents`;
 });
 
 const getProcfileContents = baseApi<
@@ -665,9 +674,31 @@ const getProcfileContents = baseApi<
     branch: string;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/gitrepos/${pathParams.git_repo_id
-    }/repos/${pathParams.kind}/${pathParams.owner}/${pathParams.name
-    }/${encodeURIComponent(pathParams.branch)}/procfile`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${
+    pathParams.git_repo_id
+  }/repos/${pathParams.kind}/${pathParams.owner}/${
+    pathParams.name
+  }/${encodeURIComponent(pathParams.branch)}/procfile`;
+});
+
+const getPorterYamlContents = baseApi<
+  {
+    path: string;
+  },
+  {
+    project_id: number;
+    git_repo_id: number;
+    kind: string;
+    owner: string;
+    name: string;
+    branch: string;
+  }
+>("GET", (pathParams) => {
+  return `/api/projects/${pathParams.project_id}/gitrepos/${
+    pathParams.git_repo_id
+  }/repos/${pathParams.kind}/${pathParams.owner}/${
+    pathParams.name
+  }/${encodeURIComponent(pathParams.branch)}/porteryaml`;
 });
 
 const getGitlabProcfileContents = baseApi<
@@ -897,30 +928,30 @@ const getInfraTemplate = baseApi<
 
 const provisionCluster = baseApi<
   {
-    project_id: number,
-    cluster_id?: number,
-    cloud_provider: string,
-    cloud_provider_credentials_id: string,
+    project_id: number;
+    cluster_id?: number;
+    cloud_provider: string;
+    cloud_provider_credentials_id: string;
     cluster_settings: {
-      cluster_name: string,
-      cluster_version: string,
-      cidr_range: string,
-      region: string,
+      cluster_name: string;
+      cluster_version: string;
+      cidr_range: string;
+      region: string;
       node_groups: [
         {
-          instance_type: string,
-          min_instances: number,
-          max_instances: number,
-          node_group_type: number
+          instance_type: string;
+          min_instances: number;
+          max_instances: number;
+          node_group_type: number;
         },
         {
-          instance_type: string,
-          min_instances: number,
-          max_instances: number,
-          node_group_type: number
+          instance_type: string;
+          min_instances: number;
+          max_instances: number;
+          node_group_type: number;
         }
-      ]
-    }
+      ];
+    };
   },
   {
     project_id: number;
@@ -929,33 +960,33 @@ const provisionCluster = baseApi<
   return `/api/projects/${project_id}/provision/cluster`;
 });
 
-const createContract = baseApi<
-  Contract,
-  { project_id: number }
->("POST", ({ project_id }) => {
-  return `/api/projects/${project_id}/contract`;
-});
+const createContract = baseApi<Contract, { project_id: number }>(
+  "POST",
+  ({ project_id }) => {
+    return `/api/projects/${project_id}/contract`;
+  }
+);
 
-const getContracts = baseApi<
-  { cluster_id?: number },
-  { project_id: number }
->("GET", ({ project_id }) => {
-  return `/api/projects/${project_id}/contracts`;
-});
+const getContracts = baseApi<{ cluster_id?: number }, { project_id: number }>(
+  "GET",
+  ({ project_id }) => {
+    return `/api/projects/${project_id}/contracts`;
+  }
+);
 
-const deleteContract = baseApi<
-  {},
-  { project_id: number, revision_id: string }
->("DELETE", ({ project_id, revision_id }) => {
-  return `/api/projects/${project_id}/contracts/${revision_id}`;
-});
+const deleteContract = baseApi<{}, { project_id: number; revision_id: string }>(
+  "DELETE",
+  ({ project_id, revision_id }) => {
+    return `/api/projects/${project_id}/contracts/${revision_id}`;
+  }
+);
 
-const getClusterState = baseApi<
-  {},
-  { project_id: number, cluster_id: number }
->("GET", ({ project_id, cluster_id }) => {
-  return `/api/projects/${project_id}/clusters/${cluster_id}/state`;
-});
+const getClusterState = baseApi<{}, { project_id: number; cluster_id: number }>(
+  "GET",
+  ({ project_id, cluster_id }) => {
+    return `/api/projects/${project_id}/clusters/${cluster_id}/state`;
+  }
+);
 
 const provisionInfra = baseApi<
   {
@@ -1521,9 +1552,11 @@ const getEnvGroup = baseApi<
     version?: number;
   }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.id}/clusters/${pathParams.cluster_id
-    }/namespaces/${pathParams.namespace}/envgroup?name=${pathParams.name}${pathParams.version ? "&version=" + pathParams.version : ""
-    }`;
+  return `/api/projects/${pathParams.id}/clusters/${
+    pathParams.cluster_id
+  }/namespaces/${pathParams.namespace}/envgroup?name=${pathParams.name}${
+    pathParams.version ? "&version=" + pathParams.version : ""
+  }`;
 });
 
 const getConfigMap = baseApi<
@@ -2436,7 +2469,7 @@ const removeStackEnvGroup = baseApi<
     `/api/v1/projects/${project_id}/clusters/${cluster_id}/namespaces/${namespace}/stacks/${stack_id}/remove_env_group/${env_group_name}`
 );
 
-const getGithubStatus = baseApi<{}, {}>("GET", ({ }) => `/api/status/github`);
+const getGithubStatus = baseApi<{}, {}>("GET", ({}) => `/api/status/github`);
 
 const createSecretAndOpenGitHubPullRequest = baseApi<
   {
@@ -2565,6 +2598,7 @@ export default {
   getOAuthIds,
   getPodEvents,
   getProcfileContents,
+  getPorterYamlContents,
   getGitlabProcfileContents,
   getProjectClusters,
   getProjectRegistries,