diff --git a/dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx b/dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx index f7049b0eba..821471d441 100644 --- a/dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx +++ b/dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx @@ -175,7 +175,10 @@ const AppDataContainer: React.FC = ({ 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 = ({ 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( "", { 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 = ({ tabParam }) => { } ); - if (latestSource.type === "github" && buildIsDirty) { + if (latestSource.type === "github" && needsRebuild) { const res = await api.reRunGHWorkflow( "", {}, @@ -247,7 +253,6 @@ const AppDataContainer: React.FC = ({ tabParam }) => { window.open(res.data, "_blank", "noreferrer"); } } - await queryClient.invalidateQueries([ "getLatestRevision", projectId, @@ -266,7 +271,7 @@ const AppDataContainer: React.FC = ({ 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 = ({ 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 = ({ tabParam }) => { setOpen={setConfirmDeployModalOpen} cancelRedeploy={cancelRedeploy} finalizeDeploy={finalizeDeploy} + buildIsDirty={buildIsDirty} /> ) : null} diff --git a/dashboard/src/main/home/app-dashboard/app-view/ConfirmRedeployModal.tsx b/dashboard/src/main/home/app-dashboard/app-view/ConfirmRedeployModal.tsx index 225c1e39c7..040c253d6a 100644 --- a/dashboard/src/main/home/app-dashboard/app-view/ConfirmRedeployModal.tsx +++ b/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>; finalizeDeploy: () => void; + buildIsDirty: boolean; }; const ConfirmRedeployModal: React.FC = ({ cancelRedeploy, setOpen, finalizeDeploy, + buildIsDirty, }) => { const { setValue } = useFormContext(); + 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 ( setOpen(false)}> Confirm deploy - - A change to your application's build settings has been detected. - Confirming this change will trigger a rerun of your application's CI - pipeline. - + {message} diff --git a/dashboard/src/main/home/app-dashboard/create-app/RepoSettings.tsx b/dashboard/src/main/home/app-dashboard/create-app/RepoSettings.tsx index 3231a771a3..5401a914bb 100644 --- a/dashboard/src/main/home/app-dashboard/create-app/RepoSettings.tsx +++ b/dashboard/src/main/home/app-dashboard/create-app/RepoSettings.tsx @@ -226,23 +226,29 @@ const RepoSettings: React.FC = ({ - { + 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" }, () => ( diff --git a/dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/RevisionTableContents.tsx b/dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/RevisionTableContents.tsx index 25e22e29c3..e69a98c980 100644 --- a/dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/RevisionTableContents.tsx +++ b/dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/RevisionTableContents.tsx @@ -39,12 +39,7 @@ const RevisionTableContents: React.FC = ({ setExpandRevisions, setRevertData, }) => { - const { reset } = useFormContext(); - const { - previewRevision, - setPreviewRevision, - servicesFromYaml, - } = useLatestRevision(); + const { previewRevision, setPreviewRevision } = useLatestRevision(); const revisionsWithProto = revisions.map((revision) => { return { @@ -127,7 +122,7 @@ const RevisionTableContents: React.FC = ({ No.{" "} {getSelectedRevisionNumber({ - numDeployed: deployedRevisions.length, + numDeployed: deployedRevisions[0]?.revision_number || 0, latestRevision: revisions[0], })} @@ -152,7 +147,7 @@ const RevisionTableContents: React.FC = ({ {pendingRevisions.length > 0 && pendingRevisions.map((revision) => ( - {deployedRevisions.length + 1} + {(deployedRevisions[0]?.revision_number || 0) + 1} {revision.app_proto.build ? revision.app_proto.build.commitSha.substring(0, 7) diff --git a/dashboard/src/shared/api.tsx b/dashboard/src/shared/api.tsx index 11391053d9..667f597ef1 100644 --- a/dashboard/src/shared/api.tsx +++ b/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< {