mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-08-02 03:48:56 +00:00
This change makes things cleaner because there will only ever be one plan generated in the comment flow. It's okay to make this change because typing the plan command a couple times isn't too bad AND we're going to be implementing autoplanning which should handle most of the times you needed to type plan from before.
221 lines
7.1 KiB
Go
221 lines
7.1 KiB
Go
// Copyright 2017 HootSuite Media Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the License);
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an AS IS BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// Modified hereafter by contributors to runatlantis/atlantis.
|
|
//
|
|
package events
|
|
|
|
import (
|
|
"path"
|
|
"regexp"
|
|
|
|
"github.com/google/go-github/github"
|
|
"github.com/lkysow/go-gitlab"
|
|
"github.com/pkg/errors"
|
|
"github.com/runatlantis/atlantis/server/events/models"
|
|
)
|
|
|
|
const gitlabPullOpened = "opened"
|
|
const usagesCols = 90
|
|
|
|
// multiLineRegex is used to ignore multi-line comments since those aren't valid
|
|
// Atlantis commands.
|
|
var multiLineRegex = regexp.MustCompile(`.*\r?\n.+`)
|
|
|
|
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_event_parsing.go EventParsing
|
|
|
|
type Command struct {
|
|
// Dir is the path relative to the repo root to run the command in.
|
|
// Will never be an empty string and will never end in "/".
|
|
Dir string
|
|
// Flags are the extra arguments appended to comment,
|
|
// ex. atlantis plan -- -target=resource
|
|
Flags []string
|
|
Name CommandName
|
|
Verbose bool
|
|
Workspace string
|
|
}
|
|
|
|
// NewCommand constructs a Command, setting all missing fields to defaults.
|
|
func NewCommand(dir string, flags []string, name CommandName, verbose bool, workspace string) *Command {
|
|
// If dir was an empty string, this will return '.'.
|
|
validDir := path.Clean(dir)
|
|
if validDir == "/" {
|
|
validDir = "."
|
|
}
|
|
if workspace == "" {
|
|
workspace = DefaultWorkspace
|
|
}
|
|
return &Command{
|
|
Dir: validDir,
|
|
Flags: flags,
|
|
Name: name,
|
|
Verbose: verbose,
|
|
Workspace: workspace,
|
|
}
|
|
}
|
|
|
|
type EventParsing interface {
|
|
ParseGithubIssueCommentEvent(comment *github.IssueCommentEvent) (baseRepo models.Repo, user models.User, pullNum int, err error)
|
|
ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, error)
|
|
ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error)
|
|
ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.PullRequest, models.Repo, error)
|
|
ParseGitlabMergeCommentEvent(event gitlab.MergeCommentEvent) (baseRepo models.Repo, headRepo models.Repo, user models.User, err error)
|
|
ParseGitlabMergeRequest(mr *gitlab.MergeRequest, baseRepo models.Repo) models.PullRequest
|
|
}
|
|
|
|
type EventParser struct {
|
|
GithubUser string
|
|
GithubToken string
|
|
GitlabUser string
|
|
GitlabToken string
|
|
}
|
|
|
|
func (e *EventParser) ParseGithubIssueCommentEvent(comment *github.IssueCommentEvent) (baseRepo models.Repo, user models.User, pullNum int, err error) {
|
|
baseRepo, err = e.ParseGithubRepo(comment.Repo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if comment.Comment == nil || comment.Comment.User.GetLogin() == "" {
|
|
err = errors.New("comment.user.login is null")
|
|
return
|
|
}
|
|
commentorUsername := comment.Comment.User.GetLogin()
|
|
user = models.User{
|
|
Username: commentorUsername,
|
|
}
|
|
pullNum = comment.Issue.GetNumber()
|
|
if pullNum == 0 {
|
|
err = errors.New("issue.number is null")
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (e *EventParser) ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, error) {
|
|
var pullModel models.PullRequest
|
|
var headRepoModel models.Repo
|
|
|
|
commit := pull.Head.GetSHA()
|
|
if commit == "" {
|
|
return pullModel, headRepoModel, errors.New("head.sha is null")
|
|
}
|
|
url := pull.GetHTMLURL()
|
|
if url == "" {
|
|
return pullModel, headRepoModel, errors.New("html_url is null")
|
|
}
|
|
branch := pull.Head.GetRef()
|
|
if branch == "" {
|
|
return pullModel, headRepoModel, errors.New("head.ref is null")
|
|
}
|
|
authorUsername := pull.User.GetLogin()
|
|
if authorUsername == "" {
|
|
return pullModel, headRepoModel, errors.New("user.login is null")
|
|
}
|
|
num := pull.GetNumber()
|
|
if num == 0 {
|
|
return pullModel, headRepoModel, errors.New("number is null")
|
|
}
|
|
|
|
baseRepoModel, err := e.ParseGithubRepo(pull.Base.Repo)
|
|
if err != nil {
|
|
return pullModel, headRepoModel, err
|
|
}
|
|
headRepoModel, err = e.ParseGithubRepo(pull.Head.Repo)
|
|
if err != nil {
|
|
return pullModel, headRepoModel, err
|
|
}
|
|
|
|
pullState := models.Closed
|
|
if pull.GetState() == "open" {
|
|
pullState = models.Open
|
|
}
|
|
|
|
return models.PullRequest{
|
|
Author: authorUsername,
|
|
Branch: branch,
|
|
HeadCommit: commit,
|
|
URL: url,
|
|
Num: num,
|
|
State: pullState,
|
|
BaseRepo: baseRepoModel,
|
|
}, headRepoModel, nil
|
|
}
|
|
|
|
func (e *EventParser) ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error) {
|
|
return models.NewRepo(models.Github, ghRepo.GetFullName(), ghRepo.GetCloneURL(), e.GithubUser, e.GithubToken)
|
|
}
|
|
|
|
func (e *EventParser) ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.PullRequest, models.Repo, error) {
|
|
modelState := models.Closed
|
|
if event.ObjectAttributes.State == gitlabPullOpened {
|
|
modelState = models.Open
|
|
}
|
|
// GitLab also has a "merged" state, but we map that to Closed so we don't
|
|
// need to check for it.
|
|
|
|
repo, err := models.NewRepo(models.Gitlab, event.Project.PathWithNamespace, event.Project.GitHTTPURL, e.GitlabUser, e.GitlabToken)
|
|
pull := models.PullRequest{
|
|
URL: event.ObjectAttributes.URL,
|
|
Author: event.User.Username,
|
|
Num: event.ObjectAttributes.IID,
|
|
HeadCommit: event.ObjectAttributes.LastCommit.ID,
|
|
Branch: event.ObjectAttributes.SourceBranch,
|
|
State: modelState,
|
|
BaseRepo: repo,
|
|
}
|
|
|
|
return pull, repo, err
|
|
}
|
|
|
|
// ParseGitlabMergeCommentEvent creates Atlantis models out of a GitLab event.
|
|
func (e *EventParser) ParseGitlabMergeCommentEvent(event gitlab.MergeCommentEvent) (baseRepo models.Repo, headRepo models.Repo, user models.User, err error) {
|
|
// Parse the base repo first.
|
|
repoFullName := event.Project.PathWithNamespace
|
|
cloneURL := event.Project.GitHTTPURL
|
|
baseRepo, err = models.NewRepo(models.Gitlab, repoFullName, cloneURL, e.GitlabUser, e.GitlabToken)
|
|
if err != nil {
|
|
return
|
|
}
|
|
user = models.User{
|
|
Username: event.User.Username,
|
|
}
|
|
|
|
// Now parse the head repo.
|
|
headRepoFullName := event.MergeRequest.Source.PathWithNamespace
|
|
headCloneURL := event.MergeRequest.Source.GitHTTPURL
|
|
headRepo, err = models.NewRepo(models.Gitlab, headRepoFullName, headCloneURL, e.GitlabUser, e.GitlabToken)
|
|
return
|
|
}
|
|
|
|
// ParseGitlabMergeRequest parses the merge requests and returns a pull request
|
|
// model. We require passing in baseRepo because although can't get this information
|
|
// from the merge request, the only caller of this function already has that
|
|
// data. This means we can construct the pull request object correctly.
|
|
func (e *EventParser) ParseGitlabMergeRequest(mr *gitlab.MergeRequest, baseRepo models.Repo) models.PullRequest {
|
|
pullState := models.Closed
|
|
if mr.State == gitlabPullOpened {
|
|
pullState = models.Open
|
|
}
|
|
// GitLab also has a "merged" state, but we map that to Closed so we don't
|
|
// need to check for it.
|
|
|
|
return models.PullRequest{
|
|
URL: mr.WebURL,
|
|
Author: mr.Author.Username,
|
|
Num: mr.IID,
|
|
HeadCommit: mr.SHA,
|
|
Branch: mr.SourceBranch,
|
|
State: pullState,
|
|
BaseRepo: baseRepo,
|
|
}
|
|
}
|