Просмотр исходного кода

Merge branch 'stacks-v1' of github.com:porter-dev/porter into stacks-v1

Feroze Mohideen 3 лет назад
Родитель
Сommit
1f2b0c6869

+ 12 - 0
api/server/handlers/stacks/create_porter_app.go

@@ -1,11 +1,13 @@
 package stacks
 
 import (
+	"fmt"
 	"net/http"
 
 	"github.com/porter-dev/porter/api/server/authz"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
@@ -38,6 +40,16 @@ func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 		return
 	}
 
+	existing, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, request.Name)
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	} else if existing.Name != "" {
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+			fmt.Errorf("porter app with name %s already exists in this environment", existing.Name), http.StatusForbidden))
+		return
+	}
+
 	app := &models.PorterApp{
 		Name:      request.Name,
 		ClusterID: cluster.ID,

+ 6 - 1
dashboard/src/components/repo-selector/BuildpackStack.tsx

@@ -53,6 +53,7 @@ export const BuildpackStack: React.FC<{
   hide: boolean;
   onChange: (config: BuildConfig) => void;
   currentBuildConfig?: BuildConfig;
+  setBuildConfig?: (config: BuildConfig) => void;
 }> = ({
   actionConfig,
   folderPath,
@@ -60,6 +61,7 @@ export const BuildpackStack: React.FC<{
   hide,
   onChange,
   currentBuildConfig,
+  setBuildConfig,
 }) => {
   const { currentProject } = useContext(Context);
 
@@ -76,7 +78,6 @@ export const BuildpackStack: React.FC<{
     []
   );
   const renderModalContent = () => {
-    console.log(selectedBuildpacks);
     return (
       <>
         <Text size={16}>Buildpack Configuration</Text>
@@ -118,6 +119,10 @@ export const BuildpackStack: React.FC<{
 
     if (typeof onChange === "function") {
       onChange(buildConfig);
+
+      if (currentBuildConfig) {
+        setBuildConfig(buildConfig);
+      }
     }
   }, [selectedStack, selectedBuildpacks]);
 

+ 3 - 4
dashboard/src/components/repo-selector/DetectContentsList.tsx

@@ -26,7 +26,6 @@ type PropsType = {
   dockerfilePath?: string;
   folderPath: string;
   porterYaml?: string;
-  buildConfig: BuildConfig;
   setActionConfig: (x: ActionConfigType) => void;
   setDockerfilePath: (x: string) => void;
   setFolderPath: (x: string) => void;
@@ -224,7 +223,9 @@ const DetectContentsList: React.FC<PropsType> = (props) => {
   };
   return (
     <>
-      {renderContentList() && props.dockerfilePath != "" ? (
+      {renderContentList() &&
+      props.dockerfilePath != "" &&
+      props.dockerfilePath != null ? (
         <AdvancedBuildSettings
           dockerfilePath={props.dockerfilePath}
           setDockerfilePath={props.setDockerfilePath}
@@ -235,7 +236,6 @@ const DetectContentsList: React.FC<PropsType> = (props) => {
           actionConfig={props.actionConfig}
           branch={props.branch}
           folderPath={props.folderPath}
-          buildConfig={props.buildConfig}
         />
       ) : (
         <AdvancedBuildSettings
@@ -248,7 +248,6 @@ const DetectContentsList: React.FC<PropsType> = (props) => {
           actionConfig={props.actionConfig}
           branch={props.branch}
           folderPath={props.folderPath}
-          buildConfig={props.buildConfig}
         />
       )}
     </>

+ 189 - 27
dashboard/src/main/home/app-dashboard/expanded-app/BuildSettingsTabStack.tsx

@@ -1,5 +1,13 @@
 import AnimateHeight from "react-animate-height";
-import React, { Component, Dispatch, useMemo, useRef, useState } from "react";
+import React, {
+  Component,
+  Dispatch,
+  useContext,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
 import Text from "components/porter/Text";
 import Spacer from "components/porter/Spacer";
 import Input from "components/porter/Input";
@@ -24,12 +32,21 @@ import Loading from "components/Loading";
 import { BuildpackSelection } from "components/repo-selector/BuildpackSelection";
 import BuildpackConfigSection from "main/home/cluster-dashboard/expanded-chart/build-settings/_BuildpackConfigSection";
 import { BuildpackStack } from "components/repo-selector/BuildpackStack";
+import MultiSaveButton from "components/MultiSaveButton";
+import api from "shared/api";
+import { AxiosError } from "axios";
 type Props = {
   appData: any;
   setAppData: Dispatch<any>;
+  onTabSwitch: () => void;
 };
 
-const BuildSettingsTabStack: React.FC<Props> = ({ appData, setAppData }) => {
+const BuildSettingsTabStack: React.FC<Props> = ({
+  appData,
+  setAppData,
+  onTabSwitch,
+}) => {
+  const { setCurrentError } = useContext(Context);
   const [updated, setUpdated] = useState(null);
   const [branch, setBranch] = useState(appData.app.git_branch);
   const [showSettings, setShowSettings] = useState(false);
@@ -52,28 +69,161 @@ const BuildSettingsTabStack: React.FC<Props> = ({ appData, setAppData }) => {
   const [buildConfig, setBuildConfig] = useState<BuildConfig>({
     ...defaultBuildConfig,
   });
+  const [runningWorkflowURL, setRunningWorkflowURL] = useState("");
+
   const [actionConfig, setActionConfig] = useState<ActionConfigType>({
     ...defaultActionConfig,
   });
+  const [buttonStatus, setButtonStatus] = useState<
+    "loading" | "successful" | string
+  >("");
 
   const [imageUrl, setImageUrl] = useState(appData.chart.image_uri);
 
-  const buildpackConfigRef = useRef<{
-    isLoading: boolean;
-    getBuildConfig: () => BuildConfig;
-  }>(null);
+  const clearButtonStatus = (time: number = 800) => {
+    setTimeout(() => {
+      setButtonStatus("");
+    }, time);
+  };
+  const triggerWorkflow = async () => {
+    try {
+      await api.reRunGHWorkflow(
+        "",
+        {},
+        {
+          project_id: appData.app.project_id,
+          cluster_id: appData.app.cluster_id,
+          git_installation_id: appData.app.git_repo_id,
+          owner: appData.app.repo_name?.split("/")[0],
+          name: appData.app.repo_name?.split("/")[1],
+          branch: branch,
+          release_name: "stack_" + appData.chart.name,
+        }
+      );
+    } catch (error) {
+      if (!error?.response) {
+        throw error;
+      }
+
+      let tmpError: AxiosError = error;
+
+      /**
+       * @smell
+       * Currently the expanded chart is clearing all the state when a chart update is triggered (saveEnvVariables).
+       * Temporary usage of setCurrentError until a context is applied to keep the state of the ReRunError during re renders.
+       */
+
+      if (tmpError.response.status === 400) {
+        // setReRunError({
+        //   title: "No previous run found",
+        //   description:
+        //     "There are no previous runs for this workflow, please trigger manually a run before changing the build settings.",
+        // });
+        setCurrentError(
+          "There are no previous runs for this workflow. Please manually trigger a run before changing build settings."
+        );
+        return;
+      }
+
+      if (tmpError.response.status === 409) {
+        // setReRunError({
+        //   title: "The workflow is still running",
+        //   description:
+        //     'If you want to make more changes, please choose the option "Save" until the workflow finishes.',
+        // });
 
-  const currentActionConfig = useMemo(() => {
-    console.log(appData.chart.config);
+        if (typeof tmpError.response.data === "string") {
+          setRunningWorkflowURL(tmpError.response.data);
+        }
+        setCurrentError(
+          'The workflow is still running. You can "Save" the current build settings for the next workflow run and view the current status of the workflow here: ' +
+            tmpError.response.data
+        );
+        return;
+      }
+
+      if (tmpError.response.status === 404) {
+        let description = "No action file matching this deployment was found.";
+        if (typeof tmpError.response.data === "string") {
+          const filename = tmpError.response.data;
+          description = description.concat(
+            `Please check that the file "${filename}" exists in your repository.`
+          );
+        }
+        // setReRunError({
+        //   title: "The action doesn't seem to exist",
+        //   description,
+        // });
+
+        setCurrentError(description);
+        return;
+      }
+      throw error;
+    }
+  };
+  const saveConfig = async () => {
     console.log(appData);
-    const actionConf = appData.chart.config;
+    try {
+      await api.updatePorterApp(
+        "<token>",
+        {
+          repo_name: appData.app.repo_name,
+          git_branch: branch,
+          build_context: appData.app.build_context,
+          builder: buildConfig.builder,
+          buildpacks: buildConfig.buildpacks?.join(","),
+          dockerfile: appData.app.dockerfile,
+          image_repo_uri: appData.chart.image_repo_uri,
+        },
+        {
+          project_id: appData.app.project_id,
+          cluster_id: appData.app.cluster_id,
+          name: appData.app.name,
+        }
+      );
+      onTabSwitch();
+    } catch (err) {
+      throw err;
+    }
+  };
+  const handleSave = async () => {
+    setButtonStatus("loading");
+
+    try {
+      console.log(buildConfig.builder);
 
-    return {
-      kind: "github",
-      ...actionConf,
-    } as FullActionConfigType;
-  }, [appData.chart]);
+      await saveConfig();
+      setAppData(appData);
 
+      onTabSwitch();
+      setButtonStatus("successful");
+    } catch (error) {
+      setButtonStatus("Something went wrong");
+      console.log(error);
+    } finally {
+      clearButtonStatus();
+    }
+  };
+  const handleSaveAndReDeploy = async () => {
+    setButtonStatus("loading");
+
+    try {
+      console.log(buildConfig.builder);
+
+      await saveConfig();
+      setAppData(appData);
+
+      await triggerWorkflow();
+
+      onTabSwitch();
+      setButtonStatus("successful");
+    } catch (error) {
+      setButtonStatus("Something went wrong");
+      console.log(error);
+    } finally {
+      clearButtonStatus();
+    }
+  };
   return (
     <>
       <Text size={16}>Build settings</Text>
@@ -145,10 +295,35 @@ const BuildSettingsTabStack: React.FC<Props> = ({ appData, setAppData }) => {
               }}
               hide={!showSettings}
               currentBuildConfig={buildConfig}
+              setBuildConfig={setBuildConfig}
             />
           )}
         </StyledSourceBox>
       </AnimateHeight>
+
+      <MultiSaveButton
+        options={[
+          {
+            text: "Save",
+            onClick: handleSave,
+            description:
+              "Save the build settings to be used in the next workflow run",
+          },
+          {
+            text: "Save and Redeploy",
+            onClick: handleSaveAndReDeploy,
+            description:
+              "Immediately trigger a workflow run with updated build settings",
+          },
+        ]}
+        disabled={false}
+        makeFlush={true}
+        clearPosition={true}
+        statusPosition="left"
+        expandTo="left"
+        saveText=""
+        status={buttonStatus}
+      ></MultiSaveButton>
     </>
   );
 };
@@ -162,19 +337,6 @@ const DarkMatter = styled.div<{ antiHeight?: string }>`
   margin-top: ${(props) => props.antiHeight || "-15px"};
 `;
 
-const Subtitle = styled.div`
-  padding: 11px 0px 16px;
-  font-family: "Work Sans", sans-serif;
-  font-size: 13px;
-  color: #aaaabb;
-  line-height: 1.6em;
-`;
-
-const Required = styled.div`
-  margin-left: 8px;
-  color: #fc4976;
-  display: inline-block;
-`;
 const AdvancedBuildTitle = styled.div`
   display: flex;
   align-items: center;

+ 41 - 6
dashboard/src/main/home/app-dashboard/expanded-app/ExpandedApp.tsx

@@ -2,6 +2,7 @@ import React, { useEffect, useState, useContext, useCallback } from "react";
 import { RouteComponentProps, withRouter } from "react-router";
 import styled from "styled-components";
 import yaml from "js-yaml";
+import { z } from "zod";
 
 import notFound from "assets/not-found.png";
 import web from "assets/web.png";
@@ -30,6 +31,7 @@ import { Service } from "../new-app-flow/serviceTypes";
 import ConfirmOverlay from "components/porter/ConfirmOverlay";
 import { createFinalPorterYaml } from "../new-app-flow/schema";
 import EnvGroupArray, { KeyValueType } from "main/home/cluster-dashboard/env-groups/EnvGroupArray";
+import { PorterYamlSchema } from "../new-app-flow/schema";
 
 type Props = RouteComponentProps & {};
 
@@ -45,7 +47,7 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
   const { currentCluster, currentProject, setCurrentError } = useContext(
     Context
   );
-
+  const [rawYaml, setRawYaml] = useState<string>("");
   const [isLoading, setIsLoading] = useState(true);
   const [appData, setAppData] = useState(null);
   const [error, setError] = useState(null);
@@ -63,6 +65,9 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
   const [showRevisions, setShowRevisions] = useState<boolean>(false);
   const [newestImage, setNewestImage] = useState<string>(null);
   const [showDeleteOverlay, setShowDeleteOverlay] = useState<boolean>(false);
+  const [porterJson, setPorterJson] = useState<
+    z.infer<typeof PorterYamlSchema> | undefined
+  >(undefined);
 
   const [services, setServices] = useState<Service[]>([]);
   const [envVars, setEnvVars] = useState<KeyValueType[]>([]);
@@ -189,7 +194,35 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
     }
   }
 
-  const renderIcon = (b?: string, size?: string) => {
+  const fetchPorterYamlContent = async (porterYaml: string) => {
+    try {
+      const res = await api.getPorterYamlContents(
+        "<token>",
+        {
+          path: porterYaml,
+        },
+        {
+          project_id: appData.app.project_id,
+          git_repo_id: appData.app.git_repo_id,
+          owner: appData.app.repo_name?.split("/")[0],
+          name: appData.app.repo_name?.split("/")[1],
+          kind: "github",
+          branch: appData.app.git_branch,
+        }
+      );
+      setRawYaml(atob(res.data));
+      let parsedYaml;
+      parsedYaml = yaml.load(rawYaml);
+      const parsedData = PorterYamlSchema.parse(parsedYaml);
+      const porterYamlToJson = parsedData as z.infer<typeof PorterYamlSchema>;
+      setPorterJson(porterYamlToJson);
+      console.log(porterJson);
+    } catch (err) {
+      console.log(err);
+    }
+  };
+
+  const renderIcon = (b: string, size?: string) => {
     var src = box;
     if (b) {
       const bp = b.split(",")[0]?.split("/")[1];
@@ -379,7 +412,11 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
         )
       case "build-settings":
         return (
-          <BuildSettingsTabStack appData={appData} setAppData={setAppData} />
+          <BuildSettingsTabStack
+            appData={appData}
+            setAppData={setAppData}
+            onTabSwitch={getPorterApp}
+          />
         );
       case "settings":
         return (
@@ -517,13 +554,12 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
           <Spacer y={1} />
           <TabSelector
             options={
-              appData.app?.build_packs
+              appData.app.git_repo_id
                 ? [
                   { label: "Events", value: "events" },
                   { label: "Logs", value: "logs" },
                   { label: "Metrics", value: "metrics" },
                   { label: "Overview", value: "overview" },
-                  { label: "Environment variables", value: "environment-variables" },
                   { label: "Build settings", value: "build-settings" },
                   { label: "Settings", value: "settings" },
                 ]
@@ -532,7 +568,6 @@ const ExpandedApp: React.FC<Props> = ({ ...props }) => {
                   { label: "Logs", value: "logs" },
                   { label: "Metrics", value: "metrics" },
                   { label: "Overview", value: "overview" },
-                  { label: "Environment variables", value: "environment-variables" },
                   { label: "Settings", value: "settings" },
                 ]
             }

+ 1 - 4
dashboard/src/main/home/app-dashboard/expanded-app/SharedBuildSettings.tsx

@@ -21,7 +21,6 @@ type Props = {
   folderPath: string;
   setFolderPath: (x: string) => void;
   setBuildConfig: (x: any) => void;
-  buildConfig: BuildConfig;
   porterYaml: string;
   setPorterYaml: (x: any) => void;
   imageUrl: string;
@@ -31,7 +30,6 @@ type Props = {
 const SharedBuildSettings: React.FC<Props> = ({
   actionConfig,
   setActionConfig,
-  buildConfig,
   branch,
   setBranch,
   dockerfilePath,
@@ -101,7 +99,7 @@ const SharedBuildSettings: React.FC<Props> = ({
         width="100%"
         setValue={setFolderPath}
       />
-      {actionConfig.git_repo && branch && buildConfig.builder != "" && (
+      {actionConfig.git_repo && branch && (
         <DetectContentsList
           actionConfig={actionConfig}
           branch={branch}
@@ -110,7 +108,6 @@ const SharedBuildSettings: React.FC<Props> = ({
           setActionConfig={setActionConfig}
           setDockerfilePath={setDockerfilePath}
           setFolderPath={setFolderPath}
-          buildConfig={buildConfig}
           setBuildConfig={setBuildConfig}
           porterYaml={porterYaml}
           setPorterYaml={setPorterYaml}

+ 0 - 2
dashboard/src/main/home/app-dashboard/new-app-flow/AdvancedBuildSettings.tsx

@@ -26,7 +26,6 @@ interface AdvancedBuildSettingsProps {
   dockerfilePath?: string;
   setDockerfilePath: (x: string) => void;
   setBuildConfig: (x: any) => void;
-  buildConfig: BuildConfig;
 }
 
 type Buildpack = {
@@ -66,7 +65,6 @@ const AdvancedBuildSettings: React.FC<AdvancedBuildSettingsProps> = (props) => {
         <BuildpackStack
           actionConfig={props.actionConfig}
           branch={props.branch}
-          defaultBuildConfig={props.buildConfig}
           folderPath={props.folderPath}
           onChange={(config) => {
             props.setBuildConfig(config);

+ 27 - 29
dashboard/src/main/home/app-dashboard/new-app-flow/NewAppFlow.tsx

@@ -1,24 +1,19 @@
-import React, { useEffect, useState, useContext, useMemo } from "react";
+import React, { useState, useContext } from "react";
 import styled from "styled-components";
 import { RouteComponentProps, withRouter } from "react-router";
 import _ from "lodash";
 import yaml from "js-yaml";
 
-import { hardcodedNames, hardcodedIcons } from "shared/hardcodedNameDict";
 import { Context } from "shared/Context";
 import api from "shared/api";
-import { pushFiltered } from "shared/routing";
 import web from "assets/web.png";
 
 import Back from "components/porter/Back";
 import DashboardHeader from "../../cluster-dashboard/DashboardHeader";
-import Link from "components/porter/Link";
 import Text from "components/porter/Text";
 import Spacer from "components/porter/Spacer";
 import Input from "components/porter/Input";
 import VerticalSteps from "components/porter/VerticalSteps";
-import PorterFormWrapper from "components/porter-form/PorterFormWrapper";
-import Placeholder from "components/Placeholder";
 import Button from "components/porter/Button";
 import SourceSelector, { SourceType } from "./SourceSelector";
 import SourceSettings from "./SourceSettings";
@@ -26,14 +21,8 @@ import Services from "./Services";
 import EnvGroupArray, {
   KeyValueType,
 } from "main/home/cluster-dashboard/env-groups/EnvGroupArray";
-import Select from "components/porter/Select";
 import GithubActionModal from "./GithubActionModal";
-import {
-  ActionConfigType,
-  FullActionConfigType,
-  FullGithubActionConfigType,
-  GithubActionConfigType,
-} from "shared/types";
+import { GithubActionConfigType } from "shared/types";
 import Error from "components/porter/Error";
 import { z } from "zod";
 import { PorterYamlSchema, createFinalPorterYaml } from "./schema";
@@ -87,7 +76,9 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
   const [imageTag, setImageTag] = useState("latest");
   const { currentCluster, currentProject } = useContext(Context);
   const [deploying, setDeploying] = useState<boolean>(false);
-  const [deploymentError, setDeploymentError] = useState<string | undefined>(undefined);
+  const [deploymentError, setDeploymentError] = useState<string | undefined>(
+    undefined
+  );
   const [currentStep, setCurrentStep] = useState<number>(0);
   const [existingStep, setExistingStep] = useState<number>(0);
   const [formState, setFormState] = useState<FormState>(INITIAL_STATE);
@@ -151,8 +142,9 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
       ) {
         setDetected({
           detected: true,
-          message: `Detected ${Object.keys(porterYamlToJson.apps).length
-            } apps from porter.yaml`,
+          message: `Detected ${
+            Object.keys(porterYamlToJson.apps).length
+          } apps from porter.yaml`,
         });
       } else {
         setDetected({
@@ -200,7 +192,7 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
         currentProject.id == null ||
         currentCluster.id == null
       ) {
-        throw new Error("Project or cluster not found");
+        throw "Project or cluster not found";
       }
 
       // validate form data
@@ -216,11 +208,11 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
       const base64Encoded = btoa(yamlString);
       const imageInfo = imageUrl
         ? {
-          image_info: {
-            repository: imageUrl,
-            tag: imageTag,
-          },
-        }
+            image_info: {
+              repository: imageUrl,
+              tag: imageTag,
+            },
+          }
         : {};
 
       // write to the db
@@ -254,7 +246,7 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
           cluster_id: currentCluster.id,
           project_id: currentProject.id,
         }
-      )
+      );
       if (!actionConfig?.git_repo) {
         props.history.push(`/apps/${formState.applicationName}`);
       }
@@ -262,7 +254,10 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
     } catch (err) {
       // TODO: better error handling
       console.log(err);
-      const errMessage = err?.response?.data?.error ?? err?.toString() ?? 'An error occurred while deploying your app. Please try again.'
+      const errMessage =
+        err?.response?.data?.error ??
+        err?.toString() ??
+        "An error occurred while deploying your app. Please try again.";
       setDeploymentError(errMessage);
 
       return false;
@@ -346,7 +341,6 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
                   procfilePath={procfilePath}
                   setProcfilePath={setProcfilePath}
                   setBuildConfig={setBuildConfig}
-                  buildConfig={buildConfig}
                   porterYaml={porterYaml}
                   setPorterYaml={(newYaml: string) => {
                     validatePorterYaml(newYaml);
@@ -425,13 +419,17 @@ const NewAppFlow: React.FC<Props> = ({ ...props }) => {
                   if (imageUrl) {
                     deployPorterApp();
                   } else {
-                    setDeploymentError(undefined)
+                    setDeploymentError(undefined);
                     setShowGHAModal(true);
                   }
                 }}
-                status={deploying ? "loading" : deploymentError ? (
-                  <Error message={deploymentError} />
-                ) : undefined}
+                status={
+                  deploying ? (
+                    "loading"
+                  ) : deploymentError ? (
+                    <Error message={deploymentError} />
+                  ) : undefined
+                }
                 loadingText={"Deploying..."}
                 width={"150px"}
               >

+ 0 - 3
dashboard/src/main/home/app-dashboard/new-app-flow/SourceSettings.tsx

@@ -33,7 +33,6 @@ type Props = {
   setProcfilePath: (x: string) => void;
   folderPath: string | null;
   setFolderPath: (x: string) => void;
-  buildConfig: BuildConfig;
   setBuildConfig: (x: any) => void;
   porterYaml: string;
   setPorterYaml: (x: any) => void;
@@ -41,7 +40,6 @@ type Props = {
 
 const SourceSettings: React.FC<Props> = ({
   source,
-  buildConfig,
   imageUrl,
   setImageUrl,
   imageTag,
@@ -118,7 +116,6 @@ const SourceSettings: React.FC<Props> = ({
         <div>
           {source === "github" ? (
             <SharedBuildSettings
-              buildConfig={buildConfig}
               actionConfig={actionConfig}
               branch={branch}
               dockerfilePath={dockerfilePath}

+ 1 - 1
dashboard/src/main/home/integrations/IntegrationList.tsx

@@ -259,7 +259,7 @@ const Placeholder = styled.div`
   justify-content: center;
   color: #aaaabb;
   border-radius: 5px;
-  background: #26292e;
+  background: ${({ theme }) => theme.fg};
   border: 1px solid #494b4f;
 `;
 

+ 1 - 1
dashboard/src/main/home/integrations/Integrations.tsx

@@ -51,7 +51,7 @@ const Integrations: React.FC<PropsType> = (props) => {
                 >
                   {integrationList[integration].label}
                 </TitleSection>
-                <Buffer />
+                <Spacer y={1} />
                 <CreateIntegrationForm
                   integrationName={integration}
                   closeForm={() => {

+ 1 - 1
dashboard/src/main/home/integrations/SlackIntegrationList.tsx

@@ -107,7 +107,7 @@ const Placeholder = styled.div`
   justify-content: center;
   color: #aaaabb;
   border-radius: 5px;
-  background: #26292e;
+  background: ${({ theme }) => theme.fg};
   border: 1px solid #494b4f;
 `;
 

+ 1 - 1
dashboard/src/main/home/integrations/create-integration/ECRForm.tsx

@@ -132,7 +132,7 @@ ECRForm.contextType = Context;
 const CredentialWrapper = styled.div`
   padding: 30px;
   border-radius: 5px;
-  background: #26292e;
+  background: ${({ theme }) => theme.fg}};
   border: 1px solid #494b4f;
   margin-bottom: 30px;
 `;

+ 1 - 1
internal/repository/gorm/porter_app.go

@@ -37,7 +37,7 @@ func (repo *PorterAppRepository) ListPorterAppByClusterID(clusterID uint) ([]*mo
 func (repo *PorterAppRepository) ReadPorterAppByName(clusterID uint, name string) (*models.PorterApp, error) {
 	app := &models.PorterApp{}
 
-	if err := repo.db.Where("cluster_id = ? AND name = ?", clusterID, name).First(&app).Error; err != nil {
+	if err := repo.db.Where("cluster_id = ? AND name = ?", clusterID, name).Limit(1).Find(&app).Error; err != nil {
 		return nil, err
 	}