sunguroku 4 лет назад
Родитель
Сommit
414b032b1c

+ 5 - 9
api/server/handlers/environment/create_deployment.go

@@ -66,21 +66,17 @@ func (c *CreateDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		return
 	}
 
-	ghMetadata := types.GitHubMetadata{
-		DeploymentID: ghDeployment.GetID(),
-		RepoOwner: request.GitHubMetadata.RepoOwner,
-		RepoName: request.GitHubMetadata.RepoName,
-		PRName: request.GitHubMetadata.PRName,
-		CommitSHA: request.GitHubMetadata.CommitSHA,
-	}
-
 	// create the deployment
 	depl, err := c.Repo().Environment().CreateDeployment(&models.Deployment{
 		EnvironmentID:      env.ID,
 		Namespace:          request.Namespace,
 		Status:             "creating",
 		PullRequestID:      request.PullRequestID,
-		GitHubMetadata:		ghMetadata,
+		GHDeploymentID: ghDeployment.GetID(),
+		RepoOwner: request.GitHubMetadata.RepoOwner,
+		RepoName: request.GitHubMetadata.RepoName,
+		PRName: request.GitHubMetadata.PRName,
+		CommitSHA: request.GitHubMetadata.CommitSHA,
 	})
 
 	if err != nil {

+ 1 - 1
api/server/handlers/environment/delete_deployment.go

@@ -95,7 +95,7 @@ func (c *DeleteDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		context.Background(),
 		env.GitRepoOwner,
 		env.GitRepoName,
-		depl.GitHubMetadata.DeploymentID,
+		depl.GHDeploymentID,
 		&deploymentStatusRequest,
 	)
 

+ 1 - 1
api/server/handlers/environment/finalize_deployment.go

@@ -88,7 +88,7 @@ func (c *FinalizeDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
 		context.Background(),
 		env.GitRepoOwner,
 		env.GitRepoName,
-		depl.GitHubMetadata.DeploymentID,
+		depl.GHDeploymentID,
 		&deploymentStatusRequest,
 	)
 

+ 2 - 2
api/server/handlers/environment/update_deployment.go

@@ -71,8 +71,8 @@ func (c *UpdateDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
 		return
 	}
 
-	depl.GitHubMetadata.DeploymentID = ghDeployment.GetID()
-	depl.GitHubMetadata.CommitSHA = request.CommitSHA
+	depl.GHDeploymentID = ghDeployment.GetID()
+	depl.CommitSHA = request.CommitSHA
 
 	if err != nil {
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))

+ 1 - 1
dashboard/package.json

@@ -31,7 +31,7 @@
     "ini": ">=1.3.6",
     "js-base64": "^3.6.0",
     "js-yaml": "^4.1.0",
-    "lodash": "^4.17.20",
+    "lodash": "^4.17.21",
     "markdown-to-jsx": "^7.0.1",
     "qs": "^6.9.4",
     "random-words": "^1.1.1",

+ 74 - 20
dashboard/src/main/home/cluster-dashboard/dashboard/preview-environments/EnvironmentDetail.tsx

@@ -11,23 +11,7 @@ import { Context } from "shared/Context";
 import api from "shared/api";
 import ChartList from "../../chart/ChartList";
 import github from "assets/github-white.png";
-
-const mockEnvironment = {
-  id: 1,
-  environment_id: 1,
-  namespace: "pr-30",
-  pull_request_id: 30,
-  subdomain: "https://porter.run",
-  status: "deployed",
-};
-
-const getMockData = () =>
-  new Promise<{ data: PRDeployment }>((resolve) => {
-    setTimeout(() => {
-      resolve({ data: mockEnvironment });
-      // resolve({ data: [] });
-    }, 2000);
-  });
+import { integrationList } from "shared/common";
 
 const EnvironmentDetail = () => {
   const { params } = useRouteMatch<{ namespace: string }>();
@@ -35,6 +19,7 @@ const EnvironmentDetail = () => {
   const [environment, setEnvironment] = useState<PRDeployment>(null);
   const [hasError, setHasError] = useState(false);
   const [isLoading, setIsLoading] = useState(false);
+  const [showRepoTooltip, setShowRepoTooltip] = useState(false);
 
   const { currentProject, currentCluster, setCurrentError } = useContext(
     Context
@@ -58,7 +43,6 @@ const EnvironmentDetail = () => {
           return;
         }
 
-        console.log('retrieved', data)
         setEnvironment(data);
       })
       .catch((err) => {
@@ -83,6 +67,8 @@ const EnvironmentDetail = () => {
     return <Loading />;
   }
 
+  let repository = `${environment.gh_repo_owner}/${environment.gh_repo_name}`
+
   return (
     <StyledExpandedChart>
       <HeaderWrapper>
@@ -90,7 +76,21 @@ const EnvironmentDetail = () => {
           <BackButtonImg src={backArrow} />
         </BackButton>
         <Title icon={pr_icon} iconWidth="25px">
-          {environment.subdomain}
+          {environment.gh_pr_name}
+          <DeploymentImageContainer>
+              <DeploymentTypeIcon src={integrationList.repo.icon} />
+              <RepositoryName
+                onMouseOver={() => {
+                  setShowRepoTooltip(true);
+                }}
+                onMouseOut={() => {
+                  setShowRepoTooltip(false);
+                }}
+              >
+                {repository}
+              </RepositoryName>
+              {showRepoTooltip && <Tooltip>{repository}</Tooltip>}
+            </DeploymentImageContainer>
           <TagWrapper>
             Namespace <NamespaceTag>{environment.namespace}</NamespaceTag>
           </TagWrapper>
@@ -110,7 +110,7 @@ const EnvironmentDetail = () => {
             {capitalize(environment.status)}
           </Status>
           <Dot>•</Dot>
-          <GHALink to={'https://github.com/actions'} target="_blank">
+          <GHALink to={`https://github.com/${repository}/pull/${environment.pull_request_id}`} target="_blank">
             <img src={github} /> GitHub
             <i className="material-icons">open_in_new</i>
           </GHALink>
@@ -333,3 +333,57 @@ const LinkToActionsWrapper = styled.div`
   align-items: center;
   justify-content: center;
 `;
+
+const DeploymentImageContainer = styled.div`
+  height: 20px;
+  font-size: 13px;
+  position: relative;
+  display: flex;
+  margin-left: 15px;
+  margin-bottom: -3px;
+  align-items: center;
+  font-weight: 400;
+  justify-content: center;
+  color: #ffffff66;
+  padding-left: 5px;
+`;
+
+const DeploymentTypeIcon = styled(Icon)`
+  width: 20px;
+  margin-right: 10px;
+`;
+
+const RepositoryName = styled.div`
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  max-width: 390px;
+  position: relative;
+  margin-right: 3px;
+`;
+
+const Tooltip = styled.div`
+  position: absolute;
+  left: -40px;
+  top: 28px;
+  min-height: 18px;
+  max-width: calc(700px);
+  padding: 5px 7px;
+  background: #272731;
+  z-index: 999;
+  color: white;
+  font-size: 12px;
+  font-family: "Work Sans", sans-serif;
+  outline: 1px solid #ffffff55;
+  opacity: 0;
+  animation: faded-in 0.2s 0.15s;
+  animation-fill-mode: forwards;
+  @keyframes faded-in {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+`;

+ 206 - 53
dashboard/src/main/home/cluster-dashboard/dashboard/preview-environments/EnvironmentList.tsx

@@ -5,21 +5,12 @@ import api from "shared/api";
 import { useHistory, useLocation, useRouteMatch } from "react-router";
 import { getQueryParam } from "shared/routing";
 import styled from "styled-components";
-import SortSelector from "../../SortSelector";
+import { integrationList } from "shared/common";
 import ButtonEnablePREnvironments from "./components/ButtonEnablePREnvironments";
 import ConnectNewRepo from "./components/ConnectNewRepo";
 import Loading from "components/Loading";
 import pr_icon from "assets/pull_request_icon.svg";
 
-export type Environment = {
-  id: number;
-  url: string;
-  pr_link: string;
-  status: string;
-  namespace: string;
-  actions_link: string;
-};
-
 export type PRDeployment = {
   id: number,
   subdomain: string,
@@ -27,6 +18,20 @@ export type PRDeployment = {
   environment_id: number,
   pull_request_id: number,
   namespace: string,
+  gh_pr_name: string,
+  gh_repo_owner: string,
+  gh_repo_name: string,
+  gh_commit_sha: string,
+}
+
+export type Environment = {
+  id: Number,
+  project_id: number,
+  cluster_id: number,
+  git_installation_id: number,
+  name: string,
+  git_repo_owner: string,
+  git_repo_name: string,
 }
 
 export const capitalize = (s: string) => {
@@ -36,7 +41,9 @@ export const capitalize = (s: string) => {
 const EnvironmentList = () => {
   const [isLoading, setIsLoading] = useState(true);
   const [hasError, setHasError] = useState(false);
-  const [environmentList, setEnvironmentList] = useState<PRDeployment[]>([]);
+  const [environmentList, setEnvironmentList] = useState<Environment[]>([]);
+  const [deploymentList, setDeploymentList] = useState<PRDeployment[]>([]);
+  const [showRepoTooltip, setShowRepoTooltip] = useState(false);
   const [showConnectRepoFlow, setShowConnectRepoFlow] = useState(false);
   const { currentProject, currentCluster, setCurrentError } = useContext(
     Context
@@ -48,6 +55,39 @@ const EnvironmentList = () => {
   const location = useLocation();
   const history = useHistory();
 
+    useEffect(() => {
+      let isSubscribed = true;
+      api
+      .listEnvironments(
+        "<token>",
+        {},
+        {
+          project_id: currentProject.id,
+          cluster_id: currentCluster.id,
+        }
+      ).then(({ data }) => {
+          if (!isSubscribed) {
+            return;
+          }
+  
+          if (!Array.isArray(data)) {
+            throw Error("Data is not an array");
+          }
+  
+          setEnvironmentList(data);
+        })
+        .catch((err) => {
+          console.error(err);
+          if (isSubscribed) {
+            setHasError(true);
+          }
+        })
+  
+        return () => {
+          isSubscribed = false;
+        };  
+    }, []);
+
   useEffect(() => {
     let isSubscribed = true;
     // TODO: Replace get mock data by endpoint
@@ -68,15 +108,13 @@ const EnvironmentList = () => {
           throw Error("Data is not an array");
         }
 
-        console.log('retrieved', data)
-
-        setEnvironmentList(data);
+        setDeploymentList(data);
       })
       .catch((err) => {
         console.error(err);
         if (isSubscribed) {
           setHasError(true);
-          setEnvironmentList([]);
+          setDeploymentList([]);
         }
       })
       .finally(() => {
@@ -116,7 +154,11 @@ const EnvironmentList = () => {
   }
 
   if (hasError) {
-    return <>Unexpected error occured, please try again later</>;
+    return(
+      <Placeholder>
+       Error
+      </Placeholder>
+    )
   }
 
   if (!environmentList.length) {
@@ -127,6 +169,74 @@ const EnvironmentList = () => {
     );
   }
 
+
+
+  let renderDeploymentList = () => {
+    if (!deploymentList.length) {
+      return (
+        <Placeholder>
+          No preview apps have been found. Open a PR to create a new preview app.
+        </Placeholder>
+      );
+    }
+
+    return deploymentList.map((d) => {
+      let repository = `${d.gh_repo_owner}/${d.gh_repo_name}`
+      return (
+        <EnvironmentCard key={d.id}>
+          <DataContainer>
+            <PRName>
+              <PRIcon src={pr_icon} alt="pull request icon" />
+              {d.gh_pr_name}
+            </PRName>
+
+            <PRWrapper>
+            <StatusContainer>
+              <Status>
+                <StatusDot status={d.status} />
+                {capitalize(d.status)}
+              </Status>
+            </StatusContainer>
+            <DeploymentImageContainer>
+              <DeploymentTypeIcon src={integrationList.repo.icon} />
+              <RepositoryName
+                onMouseOver={() => {
+                  setShowRepoTooltip(true);
+                }}
+                onMouseOut={() => {
+                  setShowRepoTooltip(false);
+                }}
+              >
+                {repository}
+              </RepositoryName>
+              {showRepoTooltip && <Tooltip>{repository}</Tooltip>}
+            </DeploymentImageContainer>
+            </PRWrapper>
+
+
+          </DataContainer>
+          <Flex>
+            <RowButton
+              to={`${currentUrl}/pr-env-detail/${d.namespace}`}
+              key={d.id}
+            >
+              <i className="material-icons-outlined">info</i>
+              Details
+            </RowButton>
+            <RowButton 
+              to={d.subdomain} 
+              key={d.subdomain}
+              target="_blank"
+            >
+              <i className="material-icons">open_in_new</i>
+              View Live
+            </RowButton>
+          </Flex>
+        </EnvironmentCard>
+      );
+    }) 
+  }
+
   return (
     <Container>
       <ControlRow>
@@ -144,52 +254,38 @@ const EnvironmentList = () => {
         </SortFilterWrapper>
       </ControlRow>
       <EventsGrid>
-        {environmentList.map((env) => {
-          return (
-            <EnvironmentCard key={env.id}>
-              <DataContainer>
-                <PRName>
-                  <PRIcon src={pr_icon} alt="pull request icon" />
-                  {env.subdomain}
-                </PRName>
-                <StatusContainer>
-                  <Status>
-                    <StatusDot status={env.status} />
-                    {capitalize(env.status)}
-                  </Status>
-                </StatusContainer>
-              </DataContainer>
-              <Flex>
-                <RowButton
-                  to={`${currentUrl}/pr-env-detail/${env.namespace}`}
-                  key={env.id}
-                >
-                  <i className="material-icons-outlined">info</i>
-                  Details
-                </RowButton>
-                <RowButton 
-                  to={env.subdomain} 
-                  key={env.subdomain}
-                  target="_blank"
-                >
-                  <i className="material-icons">open_in_new</i>
-                  View Live
-                </RowButton>
-              </Flex>
-            </EnvironmentCard>
-          );
-        })}
+        {renderDeploymentList()}
       </EventsGrid>
     </Container>
   );
 };
 
 export default EnvironmentList;
+const PRWrapper = styled.div`
+  display: flex;
+  align-items: center;
+`
 
 const Placeholder = styled.div`
-  height: 300px;
-`;
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: #ffffff44;
+  background: #26282f;
+  border-radius: 5px;
+  height: 370px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #ffffff44;
+  font-size: 13px;
 
+  > i {
+    font-size: 16px;
+    margin-right: 12px;
+  }
+`;
 const Flex = styled.div`
   display: flex;
   align-items: center;
@@ -376,3 +472,60 @@ const StatusDot = styled.div`
   border-radius: 20px;
   margin-left: 3px;
 `;
+
+const DeploymentImageContainer = styled.div`
+  height: 20px;
+  font-size: 13px;
+  position: relative;
+  display: flex;
+  margin-left: 15px;
+  align-items: center;
+  font-weight: 400;
+  justify-content: center;
+  color: #ffffff66;
+  padding-left: 5px;
+`;
+
+const Icon = styled.img`
+  width: 100%;
+`;
+
+const DeploymentTypeIcon = styled(Icon)`
+  width: 20px;
+  margin-right: 10px;
+`;
+
+const RepositoryName = styled.div`
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  max-width: 390px;
+  position: relative;
+  margin-right: 3px;
+`;
+
+const Tooltip = styled.div`
+  position: absolute;
+  left: -20px;
+  top: 10px;
+  min-height: 18px;
+  max-width: calc(700px);
+  padding: 5px 7px;
+  background: #272731;
+  z-index: 999;
+  color: white;
+  font-size: 12px;
+  font-family: "Work Sans", sans-serif;
+  outline: 1px solid #ffffff55;
+  opacity: 0;
+  animation: faded-in 0.2s 0.15s;
+  animation-fill-mode: forwards;
+  @keyframes faded-in {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+`;

+ 2 - 4
dashboard/src/main/home/cluster-dashboard/dashboard/preview-environments/components/ConnectNewRepo.tsx

@@ -84,13 +84,11 @@ const ConnectNewRepo: React.FC = () => {
         readOnly={false}
       />
 
-      <Heading>Disclaimer</Heading>
       <Helper>
-        You will need to add a porter.yaml file to let porter know how to create
-        the preview environment
+        You will need to add a porter.yaml file to create a preview environment.
       </Helper>
       <PorterYamlLink to={porterYamlDocsLink} target="_blank">
-        Know more about porter.yaml
+        Learn more about porter.yaml
       </PorterYamlLink>
       <ActionContainer>
         <SaveButton

+ 29 - 0
dashboard/src/shared/api.tsx

@@ -127,6 +127,33 @@ const createEnvironment = baseApi<
   return `/api/projects/${project_id}/gitrepos/${git_installation_id}/clusters/${cluster_id}/environment`;
 });
 
+const deleteEnvironment = baseApi<
+{
+  name: string;
+  git_repo_owner: string;
+  git_repo_name: string;
+},
+{
+  project_id: number;
+  cluster_id: number;
+  git_installation_id: number;
+}
+>("DELETE", (pathParams) => {
+  let { project_id, cluster_id, git_installation_id } = pathParams;
+  return `/api/projects/${project_id}/gitrepos/${git_installation_id}/clusters/${cluster_id}/environment`;
+});
+
+const listEnvironments = baseApi<
+{},
+{
+  project_id: number;
+  cluster_id: number;
+}
+>("GET", (pathParams) => {
+  let { project_id, cluster_id } = pathParams;
+  return `/api/projects/${project_id}/clusters/${cluster_id}/environments`;
+});
+
 const createGCPIntegration = baseApi<
   {
     gcp_key_data: string;
@@ -1259,6 +1286,8 @@ export default {
   createDOKS,
   createEmailVerification,
   createEnvironment,
+  deleteEnvironment,
+  listEnvironments,
   createGCPIntegration,
   createGCR,
   createGKE,

+ 27 - 1
internal/integrations/ci/actions/preview.go

@@ -128,7 +128,20 @@ func DeleteEnv(opts *EnvOpts) error {
 
 	defaultBranch := repo.GetDefaultBranch()
 
-	return deleteGithubFile(
+	// delete GitHub Environment
+
+	_, err = opts.Client.Repositories.DeleteEnvironment(
+		context.Background(),
+		opts.GitRepoOwner,
+		opts.GitRepoName,
+		opts.EnvironmentName,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	err = deleteGithubFile(
 		opts.Client,
 		fmt.Sprintf("porter_%s_env.yml", strings.ToLower(opts.EnvironmentName)),
 		opts.GitRepoOwner,
@@ -136,6 +149,19 @@ func DeleteEnv(opts *EnvOpts) error {
 		defaultBranch,
 		false,
 	)
+
+	if err != nil {
+		return err
+	}
+
+	return deleteGithubFile(
+		opts.Client,
+		fmt.Sprintf("porter_%s_delete_env.yml", strings.ToLower(opts.EnvironmentName)),
+		opts.GitRepoOwner,
+		opts.GitRepoName,
+		defaultBranch,
+		false,
+	)
 }
 
 func getPreviewApplyActionYAML(opts *EnvOpts) ([]byte, error) {

+ 1 - 1
internal/integrations/ci/actions/steps.go

@@ -54,7 +54,7 @@ func getCreatePreviewEnvStep(serverURL, porterTokenSecretName string, projectID,
 			"pr_name":		   "${{ github.event.pull_request.title }}",
 			"installation_id": fmt.Sprintf("%d", gitInstallationID),
 			"branch":          "${{ github.head_ref }}",
-			"action_id":       "${{ github.job }}",
+			"action_id":       "${{ github.run_id }}",
 			"repo_owner":      "${{ github.repository_owner }}",
 			"repo_name":       fmt.Sprintf("%s", repoName),
 		},

+ 10 - 6
internal/models/environment.go

@@ -37,17 +37,21 @@ type Deployment struct {
 	Status             string
 	Subdomain          string
 	PullRequestID      uint
-	GitHubMetadata	   types.GitHubMetadata
+	GHDeploymentID	   int64
+	PRName			   string
+	RepoName     	   string
+	RepoOwner		   string
+	CommitSHA		   string
 }
 
 func (d *Deployment) ToDeploymentType() *types.Deployment {
 
 	ghMetadata := &types.GitHubMetadata{
-		DeploymentID: d.GitHubMetadata.DeploymentID,
-		PRName:		  d.GitHubMetadata.PRName,
-		RepoName:	 d.GitHubMetadata.RepoName,
-		RepoOwner:	 d.GitHubMetadata.RepoOwner,
-		CommitSHA:	 d.GitHubMetadata.CommitSHA,
+		DeploymentID: d.GHDeploymentID,
+		PRName:		  d.PRName,
+		RepoName:	 d.RepoName,
+		RepoOwner:	 d.RepoOwner,
+		CommitSHA:	 d.CommitSHA,
 	}
 
 	return &types.Deployment{