Sfoglia il codice sorgente

retrigger ci if build fails (#3693)

Co-authored-by: Feroze Mohideen <feroze@porter.run>
ianedwards 2 anni fa
parent
commit
7b7aeb8255

+ 15 - 9
dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx

@@ -175,7 +175,10 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
         latestProto
       );
 
-      if (buildIsDirty && !data.redeployOnSave) {
+      const needsRebuild =
+        buildIsDirty || latestRevision.status === "BUILD_FAILED";
+
+      if (needsRebuild && !data.redeployOnSave) {
         setConfirmDeployModalOpen(true);
         return;
       }
@@ -216,11 +219,14 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
         })),
       });
 
+      // force_build will create a new 0 revision that will not be deployed
+      // but will be used to hydrate values when the workflow is run
       await api.applyApp(
         "<token>",
         {
           b64_app_proto: btoa(protoWithUpdatedEnv.toJsonString()),
           deployment_target_id: deploymentTarget.id,
+          force_build: needsRebuild,
         },
         {
           project_id: projectId,
@@ -228,7 +234,7 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
         }
       );
 
-      if (latestSource.type === "github" && buildIsDirty) {
+      if (latestSource.type === "github" && needsRebuild) {
         const res = await api.reRunGHWorkflow(
           "<token>",
           {},
@@ -247,7 +253,6 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
           window.open(res.data, "_blank", "noreferrer");
         }
       }
-
       await queryClient.invalidateQueries([
         "getLatestRevision",
         projectId,
@@ -266,7 +271,7 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
 
       // redirect to the default tab after save
       history.push(`/apps/${porterApp.name}/${DEFAULT_TAB}`);
-    } catch (err) { }
+    } catch (err) {}
   });
 
   const cancelRedeploy = useCallback(() => {
@@ -372,11 +377,11 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
             { label: "Environment", value: "environment" },
             ...(latestProto.build
               ? [
-                {
-                  label: "Build Settings",
-                  value: "build-settings",
-                },
-              ]
+                  {
+                    label: "Build Settings",
+                    value: "build-settings",
+                  },
+                ]
               : []),
             { label: "Settings", value: "settings" },
           ]}
@@ -412,6 +417,7 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
           setOpen={setConfirmDeployModalOpen}
           cancelRedeploy={cancelRedeploy}
           finalizeDeploy={finalizeDeploy}
+          buildIsDirty={buildIsDirty}
         />
       ) : null}
     </FormProvider>

+ 14 - 6
dashboard/src/main/home/app-dashboard/app-view/ConfirmRedeployModal.tsx

@@ -3,32 +3,40 @@ import Modal from "components/porter/Modal";
 import Spacer from "components/porter/Spacer";
 import Text from "components/porter/Text";
 import { PorterAppFormData } from "lib/porter-apps";
-import React, { Dispatch, SetStateAction } from "react";
+import React, { Dispatch, SetStateAction, useMemo } from "react";
 import { useFormContext } from "react-hook-form";
 import styled from "styled-components";
+import { useLatestRevision } from "./LatestRevisionContext";
 
 type Props = {
   cancelRedeploy: () => void;
   setOpen: Dispatch<SetStateAction<boolean>>;
   finalizeDeploy: () => void;
+  buildIsDirty: boolean;
 };
 
 const ConfirmRedeployModal: React.FC<Props> = ({
   cancelRedeploy,
   setOpen,
   finalizeDeploy,
+  buildIsDirty,
 }) => {
   const { setValue } = useFormContext<PorterAppFormData>();
+  const { latestRevision } = useLatestRevision();
+  const message = useMemo(() => {
+    if (buildIsDirty) {
+      return "A change to your application's build settings has been detected. Confirming this change will trigger a rerun of your application's CI pipeline.";
+    }
+    if (latestRevision.status === "BUILD_FAILED") {
+      return "Your application's build previously failed. Confirming this change will trigger a rerun of your application's CI pipeline.";
+    }
+  }, [latestRevision, buildIsDirty]);
 
   return (
     <Modal closeModal={() => setOpen(false)}>
       <Text size={16}>Confirm deploy</Text>
       <Spacer y={0.5} />
-      <Text color="helper">
-        A change to your application's build settings has been detected.
-        Confirming this change will trigger a rerun of your application's CI
-        pipeline.
-      </Text>
+      <Text color="helper">{message}</Text>
       <Spacer y={0.5} />
 
       <ButtonContainer>

+ 23 - 17
dashboard/src/main/home/app-dashboard/create-app/RepoSettings.tsx

@@ -226,23 +226,29 @@ const RepoSettings: React.FC<Props> = ({
 
               <AnimateHeight height={showSettings ? "auto" : 0} duration={1000}>
                 <StyledSourceBox>
-                  <Select
-                    value={build.method}
-                    width="300px"
-                    options={[
-                      { value: "docker", label: "Docker" },
-                      { value: "pack", label: "Buildpacks" },
-                    ]}
-                    setValue={(option: string) => {
-                      if (option == "docker") {
-                        setValue("app.build.method", "docker");
-                      } else if (option == "pack") {
-                        // if toggling from docker to pack, initialize buildpacks to empty array
-                        setValue("app.build.method", "pack");
-                        setValue("app.build.buildpacks", []);
-                      }
-                    }}
-                    label="Build method"
+                  <Controller
+                    name="app.build.method"
+                    control={control}
+                    render={({ field: { value, onChange } }) => (
+                      <Select
+                        value={value}
+                        width="300px"
+                        options={[
+                          { value: "docker", label: "Docker" },
+                          { value: "pack", label: "Buildpacks" },
+                        ]}
+                        setValue={(option: string) => {
+                          if (option == "docker") {
+                            onChange("docker");
+                          } else if (option == "pack") {
+                            // if toggling from docker to pack, initialize buildpacks to empty array
+                            onChange("pack");
+                            setValue("app.build.buildpacks", []);
+                          }
+                        }}
+                        label="Build method"
+                      />
+                    )}
                   />
                   {match(build)
                     .with({ method: "docker" }, () => (

+ 3 - 8
dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/RevisionTableContents.tsx

@@ -39,12 +39,7 @@ const RevisionTableContents: React.FC<RevisionTableContentsProps> = ({
   setExpandRevisions,
   setRevertData,
 }) => {
-  const { reset } = useFormContext<PorterAppFormData>();
-  const {
-    previewRevision,
-    setPreviewRevision,
-    servicesFromYaml,
-  } = useLatestRevision();
+  const { previewRevision, setPreviewRevision } = useLatestRevision();
 
   const revisionsWithProto = revisions.map((revision) => {
     return {
@@ -127,7 +122,7 @@ const RevisionTableContents: React.FC<RevisionTableContentsProps> = ({
             <Revision>
               No.{" "}
               {getSelectedRevisionNumber({
-                numDeployed: deployedRevisions.length,
+                numDeployed: deployedRevisions[0]?.revision_number || 0,
                 latestRevision: revisions[0],
               })}
             </Revision>
@@ -152,7 +147,7 @@ const RevisionTableContents: React.FC<RevisionTableContentsProps> = ({
               {pendingRevisions.length > 0 &&
                 pendingRevisions.map((revision) => (
                   <Tr key={new Date(revision.updated_at).toUTCString()}>
-                    <Td>{deployedRevisions.length + 1}</Td>
+                    <Td>{(deployedRevisions[0]?.revision_number || 0) + 1}</Td>
                     <Td>
                       {revision.app_proto.build
                         ? revision.app_proto.build.commitSha.substring(0, 7)

+ 49 - 35
dashboard/src/shared/api.tsx

@@ -352,8 +352,9 @@ const getFeedEvents = baseApi<
   }
 >("GET", (pathParams) => {
   let { project_id, cluster_id, stack_name, page } = pathParams;
-  return `/api/projects/${project_id}/clusters/${cluster_id}/applications/${stack_name}/events?page=${page || 1
-    }`;
+  return `/api/projects/${project_id}/clusters/${cluster_id}/applications/${stack_name}/events?page=${
+    page || 1
+  }`;
 });
 
 const createEnvironment = baseApi<
@@ -778,9 +779,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<
@@ -811,9 +814,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<
@@ -829,9 +834,11 @@ 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<
@@ -847,9 +854,11 @@ const getPorterYamlContents = 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)}/porteryaml`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${
+    pathParams.git_repo_id
+  }/repos/${pathParams.kind}/${pathParams.owner}/${
+    pathParams.name
+  }/${encodeURIComponent(pathParams.branch)}/porteryaml`;
 });
 
 const parsePorterYaml = baseApi<
@@ -886,9 +895,11 @@ const getBranchHead = 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)}/head`;
+  return `/api/projects/${pathParams.project_id}/gitrepos/${
+    pathParams.git_repo_id
+  }/repos/${pathParams.kind}/${pathParams.owner}/${
+    pathParams.name
+  }/${encodeURIComponent(pathParams.branch)}/head`;
 });
 
 const validatePorterApp = baseApi<
@@ -913,21 +924,21 @@ const validatePorterApp = baseApi<
 
 const createApp = baseApi<
   | {
-    name: string;
-    type: "github";
-    git_repo_id: number;
-    git_branch: string;
-    git_repo_name: string;
-    porter_yaml_path: string;
-  }
+      name: string;
+      type: "github";
+      git_repo_id: number;
+      git_branch: string;
+      git_repo_name: string;
+      porter_yaml_path: string;
+    }
   | {
-    name: string;
-    type: "docker-registry";
-    image: {
-      repository: string;
-      tag: string;
-    };
-  },
+      name: string;
+      type: "docker-registry";
+      image: {
+        repository: string;
+        tag: string;
+      };
+    },
   {
     project_id: number;
     cluster_id: number;
@@ -941,6 +952,7 @@ const applyApp = baseApi<
     deployment_target_id: string;
     b64_app_proto?: string;
     app_revision_id?: string;
+    force_build?: boolean;
   },
   {
     project_id: number;
@@ -1928,9 +1940,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<
@@ -2987,7 +3001,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<
   {