Skip to content

Commit

Permalink
Image Settings for Porter Apps V1 (#3731)
Browse files Browse the repository at this point in the history
  • Loading branch information
Feroze Mohideen authored Oct 4, 2023
1 parent b5aee30 commit 4af543e
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import { useLatestRevision } from "../LatestRevisionContext";
import Spacer from "components/porter/Spacer";
import Button from "components/porter/Button";
import Error from "components/porter/Error";
import { match } from "ts-pattern";
import styled from "styled-components";
import copy from "assets/copy-left.svg"
import CopyToClipboard from "components/CopyToClipboard";
import Link from "components/porter/Link";
import Text from "components/porter/Text";
import ImageSettings from "../../image-settings/ImageSettings";
import { match } from "ts-pattern";

const ImageSettingsTab: React.FC = () => {
const {
watch,
formState: { isSubmitting, errors },
setValue,
} = useFormContext<PorterAppFormData>();
const { projectId, latestRevision, latestProto } = useLatestRevision();

Expand All @@ -34,46 +35,47 @@ const ImageSettingsTab: React.FC = () => {
return "";
}, [isSubmitting, errors]);

return (
<>
{match(source)
.with({ type: "docker-registry" }, (source) => (
<>
<ImageSettings
projectId={projectId}
source={source}
/>
<Spacer y={1} />
<Button
type="submit"
status={buttonStatus}
disabled={
isSubmitting ||
latestRevision.status === "CREATED" ||
latestRevision.status === "AWAITING_BUILD_ARTIFACT"
}
>
Save image settings
</Button>
<Spacer y={1} />
<Text size={16}>Update command</Text>
<Spacer y={0.5} />
<Text color="helper">If you have the <Link to="https://docs.porter.run/standard/cli/command-reference/porter-update" target="_blank"><Text>Porter CLI</Text></Link> installed, you can update your application image tag by running the following command: </Text>
<Spacer y={0.5} />
<IdContainer>
<Code>{`$ porter app update-tag ${latestProto.name} --tag latest`}</Code>
<CopyContainer>
<CopyToClipboard text={`porter app update-tag ${latestProto.name} --tag latest`}>
<CopyIcon src={copy} alt="copy" />
</CopyToClipboard>
</CopyContainer>
</IdContainer>
</>
))
.otherwise(() => null)
}
</>
);
return match(source)
.with({ type: "docker-registry" }, (source) => (
<>
<ImageSettings
projectId={projectId}
imageUri={source.image?.repository ?? ""}
setImageUri={(uri: string) => setValue("source.image", { ...(source?.image ?? {}), repository: uri })}
imageTag={source.image?.tag ?? ""}
setImageTag={(tag: string) => setValue("source.image", { ...(source?.image ?? {}), tag })}
resetImageInfo={() => setValue("source.image", { repository: "", tag: "" })}
/>
<Spacer y={1} />
<Button
type="submit"
status={buttonStatus}
disabled={
isSubmitting
|| latestRevision.status === "CREATED"
|| latestRevision.status === "AWAITING_BUILD_ARTIFACT"
|| !source.image?.repository
|| !source.image?.tag
}
>
Save image settings
</Button>
<Spacer y={1} />
<Text size={16}>Update command</Text>
<Spacer y={0.5} />
<Text color="helper">If you have the <Link to="https://docs.porter.run/standard/cli/command-reference/porter-update" target="_blank"><Text>Porter CLI</Text></Link> installed, you can update your application image tag by running the following command: </Text>
<Spacer y={0.5} />
<IdContainer>
<Code>{`$ porter app update-tag ${latestProto.name} --tag latest`}</Code>
<CopyContainer>
<CopyToClipboard text={`porter app update-tag ${latestProto.name} --tag latest`}>
<CopyIcon src={copy} alt="copy" />
</CopyToClipboard>
</CopyContainer>
</IdContainer>
</>
))
.otherwise(() => null);
};

export default ImageSettingsTab;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,14 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
)} */}
</>
) : (
<ImageSettings projectId={currentProject.id} source={source} />
<ImageSettings
projectId={currentProject.id}
imageUri={image?.repository ?? ""}
setImageUri={(uri: string) => setValue("source.image", { ...image, repository: uri })}
imageTag={image?.tag ?? ""}
setImageTag={(tag: string) => setValue("source.image", { ...image, tag })}
resetImageInfo={() => setValue("source.image", { ...image, repository: "", tag: "" })}
/>
)
) : null}
</AnimateHeight>
Expand Down
34 changes: 27 additions & 7 deletions dashboard/src/main/home/app-dashboard/expanded-app/ExpandedApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import EventFocusView from "./activity-feed/events/focus-views/EventFocusView";
import HelmValuesTab from "./HelmValuesTab";
import SettingsTab from "./SettingsTab";
import PorterAppRevisionSection from "./PorterAppRevisionSection";
import ImageSettingsTab from "./ImageSettingsTab";

type Props = RouteComponentProps & {};

Expand All @@ -69,6 +70,7 @@ const validTabs = [
"debug",
"environment",
"build-settings",
"image-settings",
"settings",
"helm-values",
"job-history",
Expand Down Expand Up @@ -230,8 +232,17 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
setSyncedEnvGroups(filteredEnvGroups || []);
setPorterJson(porterJson);
setAppData(newAppData);
const globalImage = resChartData.data.config?.global?.image
const hasBuiltImage = globalImage != null &&
globalImage.repository != null &&
globalImage.tag != null &&
!(globalImage.repository === ImageInfo.BASE_IMAGE.repository &&
globalImage.tag === ImageInfo.BASE_IMAGE.tag)
// annoying that we have to parse buildpacks like this but alas
const parsedPorterApp = { ...resPorterApp?.data, buildpacks: newAppData.app.buildpacks?.split(",") ?? [] };
if (parsedPorterApp.image_repo_uri && hasBuiltImage) {
parsedPorterApp.image_info = { repository: globalImage.repository, tag: globalImage.tag };
}
setPorterApp(parsedPorterApp);
setTempPorterApp(parsedPorterApp);
setBuildView(!_.isEmpty(parsedPorterApp.dockerfile) ? "docker" : "buildpacks")
Expand All @@ -249,12 +260,6 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
);
setPorterYaml(finalPorterYaml);
// Only check GHA status if no built image is set
const globalImage = resChartData.data.config?.global?.image
const hasBuiltImage = globalImage != null &&
globalImage.repository != null &&
globalImage.tag != null &&
globalImage.repository !== ImageInfo.BASE_IMAGE.repository &&
globalImage.tag !== ImageInfo.BASE_IMAGE.tag
if (hasBuiltImage || !resPorterApp.data.repo_name) {
setWorkflowCheckPassed(true);
setHasBuiltImage(true);
Expand Down Expand Up @@ -402,7 +407,7 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
);
const yamlString = yaml.dump(finalPorterYaml);
const base64Encoded = btoa(yamlString);
const updatedPorterApp = {
let updatedPorterApp = {
porter_yaml: base64Encoded,
override_release: true,
...PorterApp.empty(),
Expand All @@ -423,6 +428,9 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
updatedPorterApp.buildpacks = tempPorterApp.buildpacks.join(",");
updatedPorterApp.dockerfile = "null";
}
if (tempPorterApp.image_info?.repository && tempPorterApp.image_info?.tag) {
updatedPorterApp = { ...updatedPorterApp, image_info: tempPorterApp.image_info, image_repo_uri: tempPorterApp.image_info.repository }
}

await api.createPorterApp(
"<token>",
Expand Down Expand Up @@ -678,6 +686,14 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
setBuildView={setBuildView}
/>
);
case "image-settings":
return (
<ImageSettingsTab
porterApp={tempPorterApp}
setTempPorterApp={(attrs: Partial<PorterApp>) => setTempPorterApp(PorterApp.setAttributes(tempPorterApp, attrs))}
updatePorterApp={updatePorterApp}
/>
)
case "settings":
return <SettingsTab
appName={appData.app.name}
Expand Down Expand Up @@ -935,6 +951,10 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
label: "Build settings",
value: "build-settings",
},
hasBuiltImage && !appData.app.git_repo_id && {
label: "Image settings",
value: "image-settings",
},
{ label: "Settings", value: "settings" },
(user.email.endsWith("porter.run") || currentProject.helm_values_enabled) && { label: "Helm values", value: "helm-values" },
].filter((x) => x)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { useContext, useState } from "react";
import Spacer from "components/porter/Spacer";
import Button from "components/porter/Button";
import Error from "components/porter/Error";
import styled from "styled-components";
import copy from "assets/copy-left.svg"
import CopyToClipboard from "components/CopyToClipboard";
import Link from "components/porter/Link";
import Text from "components/porter/Text";
import ImageSettings from "../image-settings/ImageSettings";
import { Context } from "shared/Context";
import { CreateUpdatePorterAppOptions } from "shared/types";
import { PorterApp } from "../types/porterApp";

type Props = {
porterApp: PorterApp;
updatePorterApp: (options: Partial<CreateUpdatePorterAppOptions>) => Promise<void>;
setTempPorterApp: (app: PorterApp) => void;
}
const ImageSettingsTab: React.FC<Props> = ({
porterApp,
updatePorterApp,
setTempPorterApp,
}) => {
const { currentProject } = useContext(Context);

const [buttonStatus, setButtonStatus] = useState<
"loading" | "success" | string
>("");

const saveConfig = async () => {
try {
await updatePorterApp({});
} catch (err) {
console.log(err);
}
};

const handleSave = async () => {
setButtonStatus("loading");

try {
await saveConfig();
setButtonStatus("success");
} catch (error) {
setButtonStatus("Something went wrong");
console.log(error);
}
};

return (
<>
<ImageSettings
projectId={currentProject?.id ?? 0}
imageUri={porterApp.image_info?.repository ?? ""}
setImageUri={(uri: string) => setTempPorterApp({ ...porterApp, image_info: { ...porterApp.image_info, repository: uri } })}
imageTag={porterApp.image_info?.tag ?? ""}
setImageTag={(tag: string) => setTempPorterApp({ ...porterApp, image_info: { ...porterApp.image_info, tag: tag } })}
resetImageInfo={() => setTempPorterApp({ ...porterApp, image_info: { ...porterApp.image_info, repository: "", tag: "" } })}
/>
<Spacer y={1} />
<Button
type="button"
status={buttonStatus}
disabled={!porterApp.image_info?.repository || !porterApp.image_info?.tag}
onClick={handleSave}
>
Save image settings
</Button>
<Spacer y={1} />
<Text size={16}>Update command</Text>
<Spacer y={0.5} />
<Text color="helper">If you have the <Link to="https://docs.porter.run/standard/cli/command-reference/porter-update" target="_blank"><Text>Porter CLI</Text></Link> installed, you can update your application image tag by running the following command: </Text>
<Spacer y={0.5} />
<IdContainer>
<Code>{`$ porter app update-tag ${porterApp.name} --tag latest`}</Code>
<CopyContainer>
<CopyToClipboard text={`porter app update-tag ${porterApp.name} --tag latest`}>
<CopyIcon src={copy} alt="copy" />
</CopyToClipboard>
</CopyContainer>
</IdContainer>
</>
);
};

export default ImageSettingsTab;

const Code = styled.span`
font-family: monospace;
`;

const IdContainer = styled.div`
background: #000000;
border-radius: 5px;
padding: 10px;
display: flex;
width: 100%;
border-radius: 5px;
border: 1px solid ${({ theme }) => theme.border};
align-items: center;
`;

const CopyContainer = styled.div`
display: flex;
align-items: center;
margin-left: auto;
`;

const CopyIcon = styled.img`
cursor: pointer;
margin-left: 5px;
margin-right: 5px;
width: 15px;
height: 15px;
:hover {
opacity: 0.8;
}
`;
Loading

0 comments on commit 4af543e

Please sign in to comment.