Bläddra i källkod

Merge branch 'master' into nafees/preview-env-improvements

Mohammed Nafees 3 år sedan
förälder
incheckning
dc015f5bf5

+ 9 - 8
api/server/handlers/environment/create.go

@@ -64,14 +64,15 @@ func (c *CreateEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 	}
 
 	env := &models.Environment{
-		ProjectID:         project.ID,
-		ClusterID:         cluster.ID,
-		GitInstallationID: uint(ga.InstallationID),
-		Name:              request.Name,
-		GitRepoOwner:      owner,
-		GitRepoName:       name,
-		Mode:              request.Mode,
-		WebhookID:         string(webhookUID),
+		ProjectID:           project.ID,
+		ClusterID:           cluster.ID,
+		GitInstallationID:   uint(ga.InstallationID),
+		Name:                request.Name,
+		GitRepoOwner:        owner,
+		GitRepoName:         name,
+		Mode:                request.Mode,
+		WebhookID:           string(webhookUID),
+		NewCommentsDisabled: false,
 	}
 
 	// write Github actions files to the repo

+ 108 - 7
api/server/handlers/environment/finalize_deployment.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"net/http"
 	"net/url"
+	"strings"
 
 	"github.com/google/go-github/v41/github"
 	"github.com/porter-dev/porter/api/server/handlers"
@@ -15,6 +16,7 @@ import (
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/models/integrations"
+	"github.com/porter-dev/porter/internal/repository"
 )
 
 type FinalizeDeploymentHandler struct {
@@ -164,22 +166,121 @@ func (c *FinalizeDeploymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
 		}
 	}
 
-	_, _, err = client.Issues.CreateComment(
+	err = createOrUpdateComment(client, c.Repo(), env.NewCommentsDisabled, depl, github.String(commentBody))
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	c.WriteResult(w, r, depl.ToDeploymentType())
+}
+
+func createOrUpdateComment(
+	client *github.Client,
+	repo repository.Repository,
+	newCommentsDisabled bool,
+	depl *models.Deployment,
+	commentBody *string,
+) error {
+	// when updating a PR comment, we have to handle several cases:
+	//   1. when a Porter environment has deployment status repeat-comments enabled
+	//      - nothing special here, simply create a new comment in the PR
+	//   2. when a Porter environment has deployment status repeat-comments disabled
+	//      - when a Porter deployment has Github comment ID saved in the DB
+	//        - try to update the comment using the Github comment ID
+	//        - if the above fails, try creating a new comment and save the new comment ID in the DB
+	//      - when a Porter deployment does not have a Github comment ID saved in the DB
+	//        - create a new comment and save the Github comment ID in the DB
+
+	if newCommentsDisabled {
+		if depl.GHPRCommentID == 0 {
+			// create a new comment
+			err := createGithubComment(client, repo, depl, commentBody)
+
+			if err != nil {
+				return err
+			}
+		} else {
+			err := updateGithubComment(
+				client, depl.RepoOwner, depl.RepoName, depl.GHPRCommentID, commentBody,
+			)
+
+			if err != nil {
+				if strings.Contains(err.Error(), "404") {
+					// perhaps a deleted comment?
+					// create a new comment
+					err := createGithubComment(client, repo, depl, commentBody)
+
+					if err != nil {
+						return fmt.Errorf("invalid github comment ID for deployment with ID: %d. Error creating "+
+							"new comment: %w", depl.ID, err)
+					}
+				}
+
+				return err
+			}
+		}
+	} else {
+		err := createGithubComment(client, repo, depl, commentBody)
+
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func createGithubComment(
+	client *github.Client,
+	repo repository.Repository,
+	depl *models.Deployment,
+	body *string,
+) error {
+	ghResp, _, err := client.Issues.CreateComment(
 		context.Background(),
-		env.GitRepoOwner,
-		env.GitRepoName,
+		depl.RepoOwner,
+		depl.RepoName,
 		int(depl.PullRequestID),
 		&github.IssueComment{
-			Body: github.String(commentBody),
+			Body: body,
 		},
 	)
 
 	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
+		return fmt.Errorf("error creating new github comment for owner: %s repo %s prNumber: %d. Error: %w",
+			depl.RepoOwner, depl.RepoName, depl.PullRequestID, err)
 	}
 
-	c.WriteResult(w, r, depl.ToDeploymentType())
+	depl.GHPRCommentID = ghResp.GetID()
+
+	_, err = repo.Environment().UpdateDeployment(depl)
+
+	if err != nil {
+		return fmt.Errorf("error updating deployment with ID: %d. Error: %w", depl.ID, err)
+	}
+
+	return nil
+}
+
+func updateGithubComment(
+	client *github.Client,
+	owner, repo string,
+	commentID int64,
+	body *string,
+) error {
+	_, _, err := client.Issues.EditComment(
+		context.Background(),
+		owner,
+		repo,
+		commentID,
+		&github.IssueComment{
+			Body: body,
+		},
+	)
+
+	return err
 }
 
 func isGithubPRClosed(

+ 2 - 12
api/server/handlers/environment/finalize_deployment_with_errors.go

@@ -162,20 +162,10 @@ func (c *FinalizeDeploymentWithErrorsHandler) ServeHTTP(w http.ResponseWriter, r
 		commentBody += fmt.Sprintf("<details>\n  <summary><code>%s</code></summary>\n\n  **Error:** %s\n</details>\n", res, err)
 	}
 
-	_, _, err = client.Issues.CreateComment(
-		context.Background(),
-		env.GitRepoOwner,
-		env.GitRepoName,
-		int(depl.PullRequestID),
-		&github.IssueComment{
-			Body: github.String(commentBody),
-		},
-	)
+	err = createOrUpdateComment(client, c.Repo(), env.NewCommentsDisabled, depl, github.String(commentBody))
 
 	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
-			fmt.Errorf("error creating github comment: %w", err), http.StatusConflict,
-		))
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 		return
 	}
 

+ 55 - 0
api/server/handlers/environment/get_environment.go

@@ -0,0 +1,55 @@
+package environment
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+
+	"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/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"gorm.io/gorm"
+)
+
+type GetEnvironmentHandler struct {
+	handlers.PorterHandlerWriter
+}
+
+func NewGetEnvironmentHandler(
+	config *config.Config,
+	writer shared.ResultWriter,
+) *GetEnvironmentHandler {
+	return &GetEnvironmentHandler{
+		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
+	}
+}
+
+func (c *GetEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	project, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	envID, reqErr := requestutils.GetURLParamUint(r, "environment_id")
+
+	if reqErr != nil {
+		c.HandleAPIError(w, r, reqErr)
+		return
+	}
+
+	env, err := c.Repo().Environment().ReadEnvironmentByID(project.ID, cluster.ID, envID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			c.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("no such environment with ID: %d", envID)))
+			return
+		}
+
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error reading environment with ID: %d. Error: %w", envID, err)))
+		return
+	}
+
+	c.WriteResult(w, r, env.ToEnvironmentType())
+}

+ 74 - 0
api/server/handlers/environment/toggle_new_comment.go

@@ -0,0 +1,74 @@
+package environment
+
+import (
+	"errors"
+	"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/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"gorm.io/gorm"
+)
+
+type ToggleNewCommentHandler struct {
+	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewToggleNewCommentHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *ToggleNewCommentHandler {
+	return &ToggleNewCommentHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *ToggleNewCommentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	project, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+
+	environmentID, reqErr := requestutils.GetURLParamUint(r, "environment_id")
+
+	if reqErr != nil {
+		c.HandleAPIError(w, r, reqErr)
+		return
+	}
+
+	request := &types.ToggleNewCommentRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	env, err := c.Repo().Environment().ReadEnvironmentByID(project.ID, cluster.ID, environmentID)
+
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			c.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("no such environment with ID: %d", environmentID)))
+			return
+		}
+
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if env.NewCommentsDisabled != request.Disable {
+		env.NewCommentsDisabled = request.Disable
+
+		_, err = c.Repo().Environment().UpdateEnvironment(env)
+
+		if err != nil {
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+	}
+}

+ 25 - 13
api/server/handlers/status/github.go

@@ -1,15 +1,16 @@
 package status
 
 import (
+	"encoding/json"
 	"fmt"
+	"io"
 	"net/http"
-	"strings"
 
-	"github.com/mmcdole/gofeed"
 	"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"
 )
 
 type GetGithubStatusHandler struct {
@@ -26,23 +27,34 @@ func NewGetGithubStatusHandler(
 }
 
 func (c *GetGithubStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	fp := gofeed.NewParser()
-	feed, err := fp.ParseURL("https://www.githubstatus.com/history.rss")
+	resp, err := http.Get("https://www.githubstatus.com/api/v2/incidents/unresolved.json")
 
 	if err != nil {
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error fetching github status RSS: %w", err)))
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error fetching github incidents: %w", err)))
 		return
 	}
 
-	if len(feed.Items) > 0 {
-		description := feed.Items[0].Description
-		link := feed.Items[0].Link
+	defer resp.Body.Close()
 
-		if !strings.Contains(description, "This incident has been resolved") {
-			// ongoing incident
-			c.WriteResult(w, r, link)
-			return
-		}
+	data, err := io.ReadAll(resp.Body)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error reading github incidents: %w", err)))
+		return
+	}
+
+	var incidents types.GithubUnresolvedIncidents
+
+	err = json.Unmarshal(data, &incidents)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error unmarshalling json: %w", err)))
+		return
+	}
+
+	if len(incidents.Incidents) > 0 {
+		c.WriteResult(w, r, fmt.Sprintf("https://www.githubstatus.com/incidents/%s", incidents.Incidents[0].ID))
+		return
 	}
 
 	c.WriteResult(w, r, "no active incidents")

+ 57 - 0
api/server/router/cluster.go

@@ -319,6 +319,63 @@ func getClusterRoutes(
 			Router:   r,
 		})
 
+		// GET /api/projects/{project_id}/clusters/{cluster_id}/environments/{environment_id} -> environment.NewGetEnvironmentHandler
+		getEnvEndpoint := factory.NewAPIEndpoint(
+			&types.APIRequestMetadata{
+				Verb:   types.APIVerbGet,
+				Method: types.HTTPVerbGet,
+				Path: &types.Path{
+					Parent:       basePath,
+					RelativePath: relPath + "/environments/{environment_id}",
+				},
+				Scopes: []types.PermissionScope{
+					types.UserScope,
+					types.ProjectScope,
+					types.ClusterScope,
+				},
+			},
+		)
+
+		getEnvHandler := environment.NewGetEnvironmentHandler(
+			config,
+			factory.GetResultWriter(),
+		)
+
+		routes = append(routes, &router.Route{
+			Endpoint: getEnvEndpoint,
+			Handler:  getEnvHandler,
+			Router:   r,
+		})
+
+		// PATCH /api/projects/{project_id}/clusters/{cluster_id}/environment/{environment_id}/toggle_new_comment -> environment.NewToggleNewCommentHandler
+		toggleNewCommentEndpoint := factory.NewAPIEndpoint(
+			&types.APIRequestMetadata{
+				Verb:   types.APIVerbUpdate,
+				Method: types.HTTPVerbPatch,
+				Path: &types.Path{
+					Parent:       basePath,
+					RelativePath: relPath + "/environments/{environment_id}/toggle_new_comment",
+				},
+				Scopes: []types.PermissionScope{
+					types.UserScope,
+					types.ProjectScope,
+					types.ClusterScope,
+				},
+			},
+		)
+
+		toggleNewCommentHandler := environment.NewToggleNewCommentHandler(
+			config,
+			factory.GetDecoderValidator(),
+			factory.GetResultWriter(),
+		)
+
+		routes = append(routes, &router.Route{
+			Endpoint: toggleNewCommentEndpoint,
+			Handler:  toggleNewCommentHandler,
+			Router:   r,
+		})
+
 		// GET /api/projects/{project_id}/clusters/{cluster_id}/deployments -> environment.NewListDeploymentsByClusterHandler
 		listDeploymentsEndpoint := factory.NewAPIEndpoint(
 			&types.APIRequestMetadata{

+ 5 - 0
api/types/environment.go

@@ -14,6 +14,7 @@ type Environment struct {
 	Mode                 string `json:"mode"`
 	DeploymentCount      uint   `json:"deployment_count"`
 	LastDeploymentStatus string `json:"last_deployment_status"`
+	NewCommentsDisabled  bool   `json:"new_comments_disabled"`
 }
 
 type CreateEnvironmentRequest struct {
@@ -123,4 +124,8 @@ type PullRequest struct {
 	BranchInto string `json:"branch_into"`
 }
 
+type ToggleNewCommentRequest struct {
+	Disable bool `json:"disable"`
+}
+
 type ListEnvironmentsResponse []*Environment

+ 6 - 0
api/types/status.go

@@ -7,3 +7,9 @@ const (
 type StreamStatusRequest struct {
 	Selectors string `schema:"selectors"`
 }
+
+type GithubUnresolvedIncidents struct {
+	Incidents []*struct {
+		ID string `json:"id"`
+	} `json:"incidents"`
+}

+ 83 - 3
dashboard/src/main/home/cluster-dashboard/preview-environments/deployments/DeploymentList.tsx

@@ -16,6 +16,8 @@ import PullRequestCard from "./PullRequestCard";
 import DynamicLink from "components/DynamicLink";
 import { PreviewEnvironmentsHeader } from "../components/PreviewEnvironmentsHeader";
 import SearchBar from "components/SearchBar";
+import CheckboxRow from "components/form-components/CheckboxRow";
+import DocsHelper from "components/DocsHelper";
 
 const AvailableStatusFilters = [
   "all",
@@ -34,6 +36,7 @@ const DeploymentList = () => {
   const [deploymentList, setDeploymentList] = useState<PRDeployment[]>([]);
   const [pullRequests, setPullRequests] = useState<PullRequest[]>([]);
   const [searchValue, setSearchValue] = useState("");
+  const [newCommentsDisabled, setNewCommentsDisabled] = useState(false);
 
   const [
     statusSelectorVal,
@@ -66,6 +69,18 @@ const DeploymentList = () => {
     // return mockRequest();
   };
 
+  const getEnvironment = () => {
+    return api.getEnvironment(
+      "<token>",
+      {},
+      {
+        project_id: currentProject.id,
+        cluster_id: currentCluster.id,
+        environment_id: Number(environment_id),
+      }
+    );
+  };
+
   useEffect(() => {
     const status_filter = getQueryParam("status_filter");
 
@@ -93,7 +108,6 @@ const DeploymentList = () => {
 
         setDeploymentList(data.deployments || []);
         setPullRequests(data.pull_requests || []);
-        setIsLoading(false);
       })
       .catch((err) => {
         console.error(err);
@@ -101,6 +115,21 @@ const DeploymentList = () => {
           setHasError(true);
         }
       });
+    getEnvironment()
+      .then(({ data }) => {
+        if (!isSubscribed) {
+          return;
+        }
+
+        setNewCommentsDisabled(data.new_comments_disabled || false);
+      })
+      .catch((err) => {
+        console.error(err);
+        if (isSubscribed) {
+          setHasError(true);
+        }
+      });
+    setIsLoading(false);
 
     return () => {
       isSubscribed = false;
@@ -116,9 +145,15 @@ const DeploymentList = () => {
     } catch (error) {
       setHasError(true);
       console.error(error);
-    } finally {
-      setIsLoading(false);
     }
+    try {
+      const { data } = await getEnvironment();
+      setNewCommentsDisabled(data.new_comments_disabled || false);
+    } catch (error) {
+      setHasError(true);
+      console.error(error);
+    }
+    setIsLoading(false);
   };
 
   const handlePreviewEnvironmentManualCreation = (pullRequest: PullRequest) => {
@@ -232,6 +267,24 @@ const DeploymentList = () => {
     setStatusSelectorVal(value);
   };
 
+  const handleToggleCommentStatus = (currentlyDisabled: boolean) => {
+    api
+      .toggleNewCommentForEnvironment(
+        "<token>",
+        {
+          disable: !currentlyDisabled,
+        },
+        {
+          project_id: currentProject.id,
+          cluster_id: currentCluster.id,
+          environment_id: Number(environment_id),
+        }
+      )
+      .then(() => {
+        setNewCommentsDisabled(!currentlyDisabled);
+      });
+  };
+
   return (
     <>
       <PreviewEnvironmentsHeader />
@@ -282,6 +335,24 @@ const DeploymentList = () => {
           </StyledStatusSelector>
         </ActionsWrapper>
       </Flex>
+      <Flex>
+        <ActionsWrapper>
+          <FlexWrap>
+            <CheckboxRow
+              label="Disable new comments for deployments"
+              checked={newCommentsDisabled}
+              toggle={() => handleToggleCommentStatus(newCommentsDisabled)}
+            />
+            <Div>
+              <DocsHelper
+                disableMargin
+                tooltipText="When checked, comments for every new deployment are disabled. Instead, the most recent comment is updated each time."
+                placement="top-end"
+              />
+            </Div>
+          </FlexWrap>
+        </ActionsWrapper>
+      </Flex>
       <Container>
         <EventsGrid>{renderDeploymentList()}</EventsGrid>
       </Container>
@@ -307,6 +378,15 @@ const Flex = styled.div`
   align-items: center;
 `;
 
+const Div = styled.div`
+  margin-bottom: -7px;
+`;
+
+const FlexWrap = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
 const BackButton = styled(DynamicLink)`
   cursor: pointer;
   font-size: 24px;

+ 28 - 0
dashboard/src/shared/api.tsx

@@ -201,6 +201,32 @@ const listEnvironments = baseApi<
   return `/api/projects/${project_id}/clusters/${cluster_id}/environments`;
 });
 
+const getEnvironment = baseApi<
+  {},
+  {
+    project_id: number;
+    cluster_id: number;
+    environment_id: number;
+  }
+>("GET", (pathParams) => {
+  let { project_id, cluster_id, environment_id } = pathParams;
+  return `/api/projects/${project_id}/clusters/${cluster_id}/environments/${environment_id}`;
+});
+
+const toggleNewCommentForEnvironment = baseApi<
+  {
+    disable: boolean;
+  },
+  {
+    project_id: number;
+    cluster_id: number;
+    environment_id: number;
+  }
+>("PATCH", (pathParams) => {
+  let { project_id, cluster_id, environment_id } = pathParams;
+  return `/api/projects/${project_id}/clusters/${cluster_id}/environments/${environment_id}/toggle_new_comment`;
+});
+
 const createGCPIntegration = baseApi<
   {
     gcp_key_data: string;
@@ -2073,6 +2099,8 @@ export default {
   createPreviewEnvironmentDeployment,
   reenablePreviewEnvironmentDeployment,
   listEnvironments,
+  getEnvironment,
+  toggleNewCommentForEnvironment,
   createGCPIntegration,
   createInvite,
   createNamespace,

+ 91 - 69
go.mod

@@ -6,15 +6,15 @@ require (
 	cloud.google.com/go v0.99.0
 	github.com/AlecAivazis/survey/v2 v2.2.9
 	github.com/Masterminds/semver/v3 v3.1.1
-	github.com/aws/aws-sdk-go v1.35.4
+	github.com/aws/aws-sdk-go v1.43.28
 	github.com/bradleyfalzon/ghinstallation/v2 v2.0.3
-	github.com/buildpacks/pack v0.26.0
+	github.com/buildpacks/pack v0.27.0
 	github.com/cli/cli v1.11.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/digitalocean/godo v1.75.0
-	github.com/docker/cli v20.10.14+incompatible
+	github.com/docker/cli v20.10.17+incompatible
 	github.com/docker/distribution v2.8.1+incompatible
-	github.com/docker/docker v20.10.14+incompatible
+	github.com/docker/docker v20.10.17+incompatible
 	github.com/docker/docker-credential-helpers v0.6.4
 	github.com/docker/go-connections v0.4.0
 	github.com/fatih/color v1.13.0
@@ -23,7 +23,7 @@ require (
 	github.com/go-playground/validator/v10 v10.3.0
 	github.com/go-redis/redis/v8 v8.11.0
 	github.com/go-test/deep v1.0.7
-	github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
+	github.com/golang-jwt/jwt/v4 v4.4.1 // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/go-github/v39 v39.2.0 // indirect
 	github.com/google/go-github/v41 v41.0.0
@@ -37,32 +37,32 @@ require (
 	github.com/mitchellh/mapstructure v1.4.3
 	github.com/moby/moby v20.10.6+incompatible
 	github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
-	github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
+	github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198
 	github.com/pkg/errors v0.9.1
 	github.com/porter-dev/switchboard v0.0.0-20220628112428-7665a0121e4f
 	github.com/rs/zerolog v1.26.0
 	github.com/sendgrid/sendgrid-go v3.8.0+incompatible
-	github.com/spf13/cobra v1.4.0
+	github.com/spf13/cobra v1.5.0
 	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.10.0
-	github.com/stretchr/testify v1.7.1
-	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
-	golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
-	golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
+	github.com/stretchr/testify v1.8.0
+	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
+	golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
+	golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26
 	google.golang.org/api v0.62.0
-	google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731
-	google.golang.org/grpc v1.46.0
+	google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03
+	google.golang.org/grpc v1.47.0
 	google.golang.org/protobuf v1.28.0
 	gorm.io/driver/sqlite v1.1.3
 	gorm.io/gorm v1.22.3
-	helm.sh/helm/v3 v3.8.0
-	k8s.io/api v0.23.1
-	k8s.io/apimachinery v0.23.5
-	k8s.io/cli-runtime v0.23.1
-	k8s.io/client-go v0.23.1
+	helm.sh/helm/v3 v3.9.0
+	k8s.io/api v0.24.2
+	k8s.io/apimachinery v0.24.2
+	k8s.io/cli-runtime v0.24.2
+	k8s.io/client-go v0.24.2
 	k8s.io/helm v2.17.0+incompatible
-	k8s.io/kubectl v0.23.1
-	sigs.k8s.io/aws-iam-authenticator v0.5.2
+	k8s.io/kubectl v0.24.1
+	sigs.k8s.io/aws-iam-authenticator v0.5.8
 	sigs.k8s.io/yaml v1.3.0
 )
 
@@ -74,20 +74,42 @@ require (
 )
 
 require (
-	github.com/Azure/azure-sdk-for-go v63.4.0+incompatible // indirect
+	github.com/Azure/azure-sdk-for-go v65.0.0+incompatible // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.1 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.1 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v0.5.0 // indirect
+	github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
+	github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
 	github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 // indirect
 	github.com/PuerkitoBio/goquery v1.5.1 // indirect
 	github.com/andybalholm/cascadia v1.1.0 // indirect
+	github.com/aws/aws-sdk-go-v2 v1.16.4 // indirect
+	github.com/aws/aws-sdk-go-v2/config v1.15.9 // indirect
+	github.com/aws/aws-sdk-go-v2/credentials v1.12.4 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.5 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.11 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.5 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.12 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ecr v1.17.5 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.13.5 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.5 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.11.7 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.16.6 // indirect
+	github.com/aws/smithy-go v1.11.2 // indirect
+	github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04 // indirect
+	github.com/chrismellard/docker-credential-acr-env v0.0.0-20220327082430-c57b701bfc08 // indirect
 	github.com/cosmtrek/air v1.30.0 // indirect
+	github.com/dimchansky/utfbom v1.1.1 // indirect
+	github.com/emicklei/go-restful/v3 v3.8.0 // indirect
+	github.com/go-gorp/gorp/v3 v3.0.2 // indirect
 	github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
+	github.com/google/gnostic v0.6.9 // indirect
 	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 	github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
 	github.com/kylelemons/godebug v1.1.0 // indirect
 	github.com/mmcdole/gofeed v1.1.3 // indirect
 	github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
 	github.com/xanzy/go-gitlab v0.68.0 // indirect
 )
@@ -96,8 +118,8 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.14.0
 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
-	github.com/Azure/go-autorest/autorest v0.11.20 // indirect
-	github.com/Azure/go-autorest/autorest/adal v0.9.15 // indirect
+	github.com/Azure/go-autorest/autorest v0.11.27 // indirect
+	github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
 	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
 	github.com/Azure/go-autorest/logger v0.2.1 // indirect
 	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
@@ -106,16 +128,16 @@ require (
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/semver v1.5.0 // indirect
 	github.com/Masterminds/sprig/v3 v3.2.2 // indirect
-	github.com/Masterminds/squirrel v1.5.2 // indirect
+	github.com/Masterminds/squirrel v1.5.3 // indirect
 	github.com/Microsoft/go-winio v0.5.2 // indirect
-	github.com/Microsoft/hcsshim v0.9.2 // indirect
+	github.com/Microsoft/hcsshim v0.9.3 // indirect
 	github.com/PuerkitoBio/purell v1.1.1 // indirect
 	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
 	github.com/apex/log v1.9.0 // indirect
-	github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
+	github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
-	github.com/buildpacks/imgutil v0.0.0-20220425182719-2edb52457eb0 // indirect
-	github.com/buildpacks/lifecycle v0.14.0 // indirect
+	github.com/buildpacks/imgutil v0.0.0-20220527150729-7a271a852e31 // indirect
+	github.com/buildpacks/lifecycle v0.14.1 // indirect
 	github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
@@ -123,7 +145,7 @@ require (
 	github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
 	github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
 	github.com/containerd/cgroups v1.0.3 // indirect
-	github.com/containerd/containerd v1.6.3 // indirect
+	github.com/containerd/containerd v1.6.6 // indirect
 	github.com/containerd/stargz-snapshotter/estargz v0.11.4 // indirect
 	github.com/cyphar/filepath-securejoin v0.2.3 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
@@ -134,27 +156,27 @@ require (
 	github.com/emirpasic/gods v1.18.1 // indirect
 	github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect
 	github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
-	github.com/evanphx/json-patch v4.12.0+incompatible // indirect
-	github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
+	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
+	github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
 	github.com/fsnotify/fsnotify v1.5.4 // indirect
 	github.com/fvbommel/sortorder v1.0.1 // indirect
 	github.com/gdamore/encoding v1.0.0 // indirect
 	github.com/gdamore/tcell/v2 v2.5.1 // indirect
 	github.com/ghodss/yaml v1.0.0 // indirect
-	github.com/go-errors/errors v1.0.1 // indirect
-	github.com/go-logr/logr v1.2.2 // indirect
+	github.com/go-errors/errors v1.4.2 // indirect
+	github.com/go-logr/logr v1.2.3 // indirect
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
-	github.com/go-openapi/jsonreference v0.19.5 // indirect
-	github.com/go-openapi/swag v0.19.14 // indirect
+	github.com/go-openapi/jsonreference v0.20.0 // indirect
+	github.com/go-openapi/swag v0.21.1 // indirect
 	github.com/go-playground/locales v0.13.0 // indirect
 	github.com/go-playground/universal-translator v0.17.0 // indirect
 	github.com/gobwas/glob v0.2.3 // indirect
 	github.com/gofrs/flock v0.8.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
-	github.com/google/btree v1.0.1 // indirect
+	github.com/google/btree v1.1.2 // indirect
 	github.com/google/go-cmp v0.5.8 // indirect
-	github.com/google/go-containerregistry v0.8.0 // indirect
+	github.com/google/go-containerregistry v0.9.0 // indirect
 	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
@@ -163,7 +185,7 @@ require (
 	github.com/googleapis/gnostic v0.5.5 // indirect
 	github.com/gorilla/mux v1.8.0 // indirect
 	github.com/gosuri/uitable v0.0.4 // indirect
-	github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
+	github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/heroku/color v0.0.6 // indirect
 	github.com/huandu/xstrings v1.3.2 // indirect
@@ -183,21 +205,21 @@ require (
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.2 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
-	github.com/jmoiron/sqlx v1.3.4 // indirect
+	github.com/jmoiron/sqlx v1.3.5 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
-	github.com/klauspost/compress v1.15.2 // indirect
+	github.com/klauspost/compress v1.15.7 // indirect
 	github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b // indirect
 	github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
 	github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
 	github.com/leodido/go-urn v1.2.0 // indirect
-	github.com/lib/pq v1.10.4 // indirect
+	github.com/lib/pq v1.10.6 // indirect
 	github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 	github.com/magiconair/properties v1.8.5 // indirect
-	github.com/mailru/easyjson v0.7.6 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
@@ -206,7 +228,7 @@ require (
 	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
-	github.com/mitchellh/go-wordwrap v1.0.0 // indirect
+	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
 	github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 	github.com/moby/buildkit v0.10.3
@@ -220,45 +242,45 @@ require (
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/onsi/ginkgo v1.16.4 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
-	github.com/opencontainers/runc v1.1.1 // indirect
+	github.com/opencontainers/runc v1.1.2 // indirect
 	github.com/opencontainers/selinux v1.10.1 // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
 	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/prometheus/client_golang v1.12.1 // indirect
+	github.com/prometheus/client_golang v1.12.2 // indirect
 	github.com/prometheus/client_model v0.2.0 // indirect
-	github.com/prometheus/common v0.32.1 // indirect
+	github.com/prometheus/common v0.35.0 // indirect
 	github.com/prometheus/procfs v0.7.3 // indirect
 	github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
-	github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc // indirect
-	github.com/russross/blackfriday v1.5.2 // indirect
+	github.com/rubenv/sql-migrate v1.1.2 // indirect
+	github.com/russross/blackfriday v1.6.0 // indirect
 	github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
 	github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
 	github.com/sendgrid/rest v2.6.3+incompatible // indirect
 	github.com/sergi/go-diff v1.2.0 // indirect
-	github.com/shopspring/decimal v1.2.0 // indirect
+	github.com/shopspring/decimal v1.3.1 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/spf13/afero v1.6.0 // indirect
-	github.com/spf13/cast v1.4.1 // indirect
+	github.com/spf13/cast v1.5.0 // indirect
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 	github.com/src-d/gcfg v1.4.0 // indirect
 	github.com/subosito/gotenv v1.2.0 // indirect
 	github.com/vbatts/tar-split v0.11.2 // indirect
 	github.com/xanzy/ssh-agent v0.3.1 // indirect
-	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
-	github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
+	github.com/xlab/treeprint v1.1.0 // indirect
 	github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
 	go.opencensus.io v0.23.0 // indirect
-	go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
-	golang.org/x/mod v0.5.1 // indirect
-	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
-	golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
-	golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
+	go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect
+	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
+	golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
+	golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect
 	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
+	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	gopkg.in/gorp.v1 v1.7.2 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
@@ -266,16 +288,16 @@ require (
 	gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
 	gopkg.in/src-d/go-git.v4 v4.13.1 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
-	gopkg.in/yaml.v3 v3.0.0 // indirect
-	k8s.io/apiextensions-apiserver v0.23.1 // indirect
-	k8s.io/apiserver v0.23.1 // indirect
-	k8s.io/component-base v0.23.1 // indirect
-	k8s.io/klog/v2 v2.30.0 // indirect
-	k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
-	k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect
-	oras.land/oras-go v1.1.0 // indirect
-	sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
-	sigs.k8s.io/kustomize/api v0.10.1 // indirect
-	sigs.k8s.io/kustomize/kyaml v0.13.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	k8s.io/apiextensions-apiserver v0.24.2 // indirect
+	k8s.io/apiserver v0.24.2 // indirect
+	k8s.io/component-base v0.24.2 // indirect
+	k8s.io/klog/v2 v2.70.0 // indirect
+	k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect
+	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
+	oras.land/oras-go v1.2.0 // indirect
+	sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect
+	sigs.k8s.io/kustomize/api v0.11.5 // indirect
+	sigs.k8s.io/kustomize/kyaml v0.13.7 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
 )

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 308 - 0
go.sum


+ 5 - 0
internal/models/environment.go

@@ -19,6 +19,8 @@ type Environment struct {
 	Name string
 	Mode string
 
+	NewCommentsDisabled bool
+
 	// WebhookID uniquely identifies the environment when other fields (project, cluster)
 	// aren't present
 	WebhookID string `gorm:"unique"`
@@ -35,6 +37,8 @@ func (e *Environment) ToEnvironmentType() *types.Environment {
 		GitRepoOwner:      e.GitRepoOwner,
 		GitRepoName:       e.GitRepoName,
 
+		NewCommentsDisabled: e.NewCommentsDisabled,
+
 		Name: e.Name,
 		Mode: e.Mode,
 	}
@@ -49,6 +53,7 @@ type Deployment struct {
 	Subdomain      string
 	PullRequestID  uint
 	GHDeploymentID int64
+	GHPRCommentID  int64
 	PRName         string
 	RepoName       string
 	RepoOwner      string

+ 1 - 0
internal/repository/environment.go

@@ -9,6 +9,7 @@ type EnvironmentRepository interface {
 	ReadEnvironmentByOwnerRepoName(projectID, clusterID uint, owner, repo string) (*models.Environment, error)
 	ReadEnvironmentByWebhookIDOwnerRepoName(webhookID, owner, repo string) (*models.Environment, error)
 	ListEnvironments(projectID, clusterID uint) ([]*models.Environment, error)
+	UpdateEnvironment(environment *models.Environment) (*models.Environment, error)
 	DeleteEnvironment(env *models.Environment) (*models.Environment, error)
 	CreateDeployment(deployment *models.Deployment) (*models.Deployment, error)
 	ReadDeployment(environmentID uint, namespace string) (*models.Deployment, error)

+ 8 - 0
internal/repository/gorm/environment.go

@@ -119,6 +119,14 @@ func (repo *EnvironmentRepository) ListEnvironments(projectID, clusterID uint) (
 	return envs, nil
 }
 
+func (repo *EnvironmentRepository) UpdateEnvironment(environment *models.Environment) (*models.Environment, error) {
+	if err := repo.db.Save(environment).Error; err != nil {
+		return nil, err
+	}
+
+	return environment, nil
+}
+
 func (repo *EnvironmentRepository) DeleteEnvironment(env *models.Environment) (*models.Environment, error) {
 	if err := repo.db.Delete(&env).Error; err != nil {
 		return nil, err

+ 4 - 0
internal/repository/test/environment.go

@@ -42,6 +42,10 @@ func (repo *EnvironmentRepository) ListEnvironments(projectID, clusterID uint) (
 	panic("unimplemented")
 }
 
+func (repo *EnvironmentRepository) UpdateEnvironment(environment *models.Environment) (*models.Environment, error) {
+	panic("unimplemented")
+}
+
 func (repo *EnvironmentRepository) DeleteEnvironment(env *models.Environment) (*models.Environment, error) {
 	panic("unimplemented")
 }

Vissa filer visades inte eftersom för många filer har ändrats