|
|
@@ -1,20 +1,13 @@
|
|
|
import Helper from "components/form-components/Helper";
|
|
|
-import Select from "components/porter/Select";
|
|
|
-import Loading from "components/Loading";
|
|
|
import React, { useContext, useEffect, useMemo, useState } from "react";
|
|
|
import api from "shared/api";
|
|
|
import { Context } from "shared/Context";
|
|
|
import styled, { keyframes } from "styled-components";
|
|
|
import Button from "components/porter/Button";
|
|
|
-import Modal from "components/porter/Modal";
|
|
|
import Spacer from "components/porter/Spacer";
|
|
|
-import Text from "components/porter/Text";
|
|
|
import Error from "components/porter/Error";
|
|
|
import { PorterApp } from "../../types/porterApp";
|
|
|
-import AddCustomBuildpackComponent from "./AddCustomBuildpackComponent";
|
|
|
import BuildpackList from "./BuildpackList";
|
|
|
-import Icon from "components/porter/Icon";
|
|
|
-import stars from "assets/stars-white.svg";
|
|
|
import {
|
|
|
BUILDPACK_TO_NAME,
|
|
|
BuildConfig,
|
|
|
@@ -23,6 +16,7 @@ import {
|
|
|
DEFAULT_HEROKU_STACK,
|
|
|
DetectedBuildpack
|
|
|
} from "../../types/buildpack";
|
|
|
+import BuildpackConfigurationModal from "./BuildpackConfigurationModal";
|
|
|
|
|
|
const BuildpackSettings: React.FC<{
|
|
|
porterApp: PorterApp;
|
|
|
@@ -35,66 +29,15 @@ const BuildpackSettings: React.FC<{
|
|
|
}) => {
|
|
|
const { currentProject } = useContext(Context);
|
|
|
|
|
|
- const [builders, setBuilders] = useState<DetectedBuildpack[]>([]);
|
|
|
const [selectedStack, setSelectedStack] = useState<string>("");
|
|
|
+ const [stackOptions, setStackOptions] = useState<{ label: string; value: string }[]>([]);
|
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
|
const [isDetectingBuildpacks, setIsDetectingBuildpacks] = useState(false);
|
|
|
const [error, setError] = useState<string>("");
|
|
|
|
|
|
const [selectedBuildpacks, setSelectedBuildpacks] = useState<Buildpack[]>([]);
|
|
|
const [availableBuildpacks, setAvailableBuildpacks] = useState<Buildpack[]>([]);
|
|
|
- const renderModalContent = () => {
|
|
|
- return (
|
|
|
- <>
|
|
|
- <Text size={16}>Buildpack Configuration</Text>
|
|
|
- <Spacer y={1} />
|
|
|
- <Scrollable>
|
|
|
- <Select
|
|
|
- value={selectedStack}
|
|
|
- width="300px"
|
|
|
- options={sortedStackOptions}
|
|
|
- setValue={(option) => {
|
|
|
- setSelectedStack(option);
|
|
|
- updatePorterApp({ builder: option });
|
|
|
- }}
|
|
|
- label="Builder and stack"
|
|
|
- />
|
|
|
- <Spacer y={0.5} />
|
|
|
- <BuildpackList
|
|
|
- selectedBuildpacks={selectedBuildpacks}
|
|
|
- setSelectedBuildpacks={setSelectedBuildpacks}
|
|
|
- availableBuildpacks={availableBuildpacks}
|
|
|
- setAvailableBuildpacks={setAvailableBuildpacks}
|
|
|
- porterApp={porterApp}
|
|
|
- updatePorterApp={updatePorterApp}
|
|
|
- showAvailableBuildpacks={true}
|
|
|
- isDetectingBuildpacks={isDetectingBuildpacks}
|
|
|
- detectBuildpacksError={error}
|
|
|
- droppableId={"modal"}
|
|
|
- />
|
|
|
- <Spacer y={0.5} />
|
|
|
- <Text color="helper">
|
|
|
- You may also add buildpacks by directly providing their GitHub links
|
|
|
- or links to ZIP files that contain the buildpack source code.
|
|
|
- </Text>
|
|
|
- <Spacer y={1} />
|
|
|
- <AddCustomBuildpackComponent onAdd={handleAddCustomBuildpack} />
|
|
|
- <Spacer y={2} />
|
|
|
- </Scrollable>
|
|
|
- <Footer>
|
|
|
- <Shade />
|
|
|
- <FooterButtons>
|
|
|
- <Button onClick={() => detectAndSetBuildPacks(true)}>
|
|
|
- <Icon src={stars} height="15px" />
|
|
|
- <Spacer inline x={0.5} />
|
|
|
- Detect buildpacks
|
|
|
- </Button>
|
|
|
- <Button onClick={() => setIsModalOpen(false)} width={"75px"}>Save</Button>
|
|
|
- </FooterButtons>
|
|
|
- </Footer>
|
|
|
- </>
|
|
|
- );
|
|
|
- };
|
|
|
+
|
|
|
const detectAndSetBuildPacks = async (detect: boolean) => {
|
|
|
try {
|
|
|
if (currentProject == null) {
|
|
|
@@ -103,20 +46,16 @@ const BuildpackSettings: React.FC<{
|
|
|
|
|
|
if (!detect) {
|
|
|
// in this case, we are not detecting buildpacks, so we just populate based on the DB
|
|
|
- setBuilders([{
|
|
|
- name: porterApp.builder.split("/")[0],
|
|
|
- builders: [porterApp.builder],
|
|
|
- detected: [],
|
|
|
- others: [],
|
|
|
- buildConfig: {} as BuildConfig,
|
|
|
- }])
|
|
|
- setSelectedStack(porterApp.builder);
|
|
|
- setSelectedBuildpacks(porterApp.buildpacks?.map(bp => ({
|
|
|
- name: BUILDPACK_TO_NAME[bp] ?? bp,
|
|
|
- buildpack: bp,
|
|
|
- config: {},
|
|
|
- })) ?? []);
|
|
|
- setAvailableBuildpacks([]);
|
|
|
+ if (porterApp.builder != null) {
|
|
|
+ setSelectedStack(porterApp.builder);
|
|
|
+ }
|
|
|
+ if (porterApp.buildpacks != null) {
|
|
|
+ setSelectedBuildpacks(porterApp.buildpacks.map(bp => ({
|
|
|
+ name: BUILDPACK_TO_NAME[bp] ?? bp,
|
|
|
+ buildpack: bp,
|
|
|
+ config: {},
|
|
|
+ })));
|
|
|
+ }
|
|
|
} else {
|
|
|
if (isDetectingBuildpacks) {
|
|
|
return;
|
|
|
@@ -141,7 +80,20 @@ const BuildpackSettings: React.FC<{
|
|
|
if (builders.length === 0) {
|
|
|
return;
|
|
|
}
|
|
|
- setBuilders(builders);
|
|
|
+ setStackOptions(builders.flatMap((builder) => {
|
|
|
+ return builder.builders.map((stack) => ({
|
|
|
+ label: `${builder.name} - ${stack}`,
|
|
|
+ value: stack.toLowerCase(),
|
|
|
+ }));
|
|
|
+ }).sort((a, b) => {
|
|
|
+ if (a.label < b.label) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (a.label > b.label) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }));
|
|
|
|
|
|
const defaultBuilder = builders.find(
|
|
|
(builder) => builder.name.toLowerCase() === DEFAULT_BUILDER_NAME
|
|
|
@@ -169,12 +121,7 @@ const BuildpackSettings: React.FC<{
|
|
|
}
|
|
|
}
|
|
|
} catch (err) {
|
|
|
- if (autoDetectBuildpacks) {
|
|
|
- updatePorterApp({ buildpacks: [] });
|
|
|
- setSelectedBuildpacks([]);
|
|
|
- setAvailableBuildpacks([]);
|
|
|
- setError(`Unable to detect buildpacks at path: ${porterApp.build_context}. Please make sure your repo, branch, and application root path are all set correctly and attempt to detect again.`);
|
|
|
- }
|
|
|
+ setError(`Unable to detect buildpacks at path: ${porterApp.build_context}. Please make sure your repo, branch, and application root path are all set correctly and attempt to detect again.`);
|
|
|
} finally {
|
|
|
setIsDetectingBuildpacks(false);
|
|
|
}
|
|
|
@@ -184,30 +131,6 @@ const BuildpackSettings: React.FC<{
|
|
|
detectAndSetBuildPacks(autoDetectBuildpacks);
|
|
|
}, [currentProject]);
|
|
|
|
|
|
- const builderOptions = useMemo(() => {
|
|
|
- if (!Array.isArray(builders)) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- return builders.map((builder) => ({
|
|
|
- label: builder.name,
|
|
|
- value: builder.name.toLowerCase(),
|
|
|
- }));
|
|
|
- }, [builders]);
|
|
|
-
|
|
|
- const stackOptions = useMemo(() => {
|
|
|
- if (!Array.isArray(builders)) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- return builders.flatMap((builder) => {
|
|
|
- return builder.builders.map((stack) => ({
|
|
|
- label: `${builder.name} - ${stack}`,
|
|
|
- value: stack.toLowerCase(),
|
|
|
- }));
|
|
|
- });
|
|
|
- }, [builders]);
|
|
|
-
|
|
|
const handleAddCustomBuildpack = (buildpack: Buildpack) => {
|
|
|
if (porterApp.buildpacks.find((bp) => bp === buildpack.buildpack) == null) {
|
|
|
updatePorterApp({ buildpacks: [...porterApp.buildpacks, buildpack.buildpack] });
|
|
|
@@ -215,20 +138,6 @@ const BuildpackSettings: React.FC<{
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- if (!stackOptions?.length || !builderOptions?.length) {
|
|
|
- return <Loading />;
|
|
|
- }
|
|
|
-
|
|
|
- const sortedStackOptions = stackOptions.sort((a, b) => {
|
|
|
- if (a.label < b.label) {
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (a.label > b.label) {
|
|
|
- return 1;
|
|
|
- }
|
|
|
- return 0;
|
|
|
- });
|
|
|
-
|
|
|
return (
|
|
|
<BuildpackConfigurationContainer>
|
|
|
{selectedBuildpacks.length > 0 && (
|
|
|
@@ -265,9 +174,25 @@ const BuildpackSettings: React.FC<{
|
|
|
<I className="material-icons">add</I> Add / detect buildpacks
|
|
|
</Button>
|
|
|
{isModalOpen && (
|
|
|
- <Modal closeModal={() => setIsModalOpen(false)}>
|
|
|
- {renderModalContent()}
|
|
|
- </Modal>
|
|
|
+ <BuildpackConfigurationModal
|
|
|
+ closeModal={() => setIsModalOpen(false)}
|
|
|
+ selectedStack={selectedStack}
|
|
|
+ sortedStackOptions={stackOptions}
|
|
|
+ setStackValue={(option) => {
|
|
|
+ setSelectedStack(option);
|
|
|
+ updatePorterApp({ builder: option });
|
|
|
+ }}
|
|
|
+ selectedBuildpacks={selectedBuildpacks}
|
|
|
+ setSelectedBuildpacks={setSelectedBuildpacks}
|
|
|
+ availableBuildpacks={availableBuildpacks}
|
|
|
+ setAvailableBuildpacks={setAvailableBuildpacks}
|
|
|
+ porterApp={porterApp}
|
|
|
+ updatePorterApp={updatePorterApp}
|
|
|
+ isDetectingBuildpacks={isDetectingBuildpacks}
|
|
|
+ detectBuildpacksError={error}
|
|
|
+ handleAddCustomBuildpack={handleAddCustomBuildpack}
|
|
|
+ detectAndSetBuildPacks={detectAndSetBuildPacks}
|
|
|
+ />
|
|
|
)}
|
|
|
</BuildpackConfigurationContainer>
|
|
|
);
|
|
|
@@ -276,32 +201,6 @@ const BuildpackSettings: React.FC<{
|
|
|
export default BuildpackSettings;
|
|
|
|
|
|
|
|
|
-const Shade = styled.div`
|
|
|
- position: absolute;
|
|
|
- top: -50px;
|
|
|
- left: 0;
|
|
|
- height: 50px;
|
|
|
- width: 100%;
|
|
|
- background: linear-gradient(to bottom, #00000000, ${({ theme }) => theme.fg});
|
|
|
-`;
|
|
|
-
|
|
|
-const FooterButtons = styled.div`
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
-`;
|
|
|
-
|
|
|
-const Footer = styled.div`
|
|
|
- position: relative;
|
|
|
- width: calc(100% + 50px);
|
|
|
- margin-left: -25px;
|
|
|
- padding: 0 25px;
|
|
|
- border-bottom-left-radius: 10px;
|
|
|
- border-bottom-right-radius: 10px;
|
|
|
- background: ${({ theme }) => theme.fg};
|
|
|
- margin-bottom: -30px;
|
|
|
- padding-bottom: 30px;
|
|
|
-`;
|
|
|
-
|
|
|
const I = styled.i`
|
|
|
color: white;
|
|
|
font-size: 14px;
|
|
|
@@ -311,14 +210,6 @@ const I = styled.i`
|
|
|
justify-content: center;
|
|
|
`;
|
|
|
|
|
|
-const Scrollable = styled.div`
|
|
|
- overflow-y: auto;
|
|
|
- padding: 0 25px;
|
|
|
- width: calc(100% + 50px);
|
|
|
- margin-left: -25px;
|
|
|
- max-height: calc(100vh - 300px);
|
|
|
-`;
|
|
|
-
|
|
|
const fadeIn = keyframes`
|
|
|
from {
|
|
|
opacity: 0;
|