mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-29 05:58:18 +00:00
194 lines
5.8 KiB
Go
194 lines
5.8 KiB
Go
package events
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/hashicorp/go-version"
|
|
"github.com/runatlantis/atlantis/server/events/models"
|
|
"github.com/runatlantis/atlantis/server/events/vcs"
|
|
"github.com/runatlantis/atlantis/server/events/yaml"
|
|
"github.com/runatlantis/atlantis/server/events/yaml/valid"
|
|
"github.com/runatlantis/atlantis/server/logging"
|
|
)
|
|
|
|
type PullRequestOperator struct {
|
|
TerraformExecutor TerraformExec
|
|
DefaultTFVersion *version.Version
|
|
ParserValidator *yaml.ParserValidator
|
|
ProjectFinder ProjectFinder
|
|
VCSClient vcs.ClientProxy
|
|
Workspace AtlantisWorkspace
|
|
ProjectOperator ProjectOperator
|
|
}
|
|
|
|
type TerraformExec interface {
|
|
RunCommandWithVersion(log *logging.SimpleLogger, path string, args []string, v *version.Version, workspace string) (string, error)
|
|
}
|
|
|
|
func (p *PullRequestOperator) Autoplan(ctx *CommandContext) CommandResponse {
|
|
// check out repo to parse atlantis.yaml
|
|
// this will check out the repo to a * dir
|
|
repoDir, err := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Command.Workspace)
|
|
if err != nil {
|
|
return CommandResponse{Error: err}
|
|
}
|
|
|
|
// Parse config file if it exists.
|
|
config, err := p.ParserValidator.ReadConfig(repoDir)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return CommandResponse{Error: err}
|
|
}
|
|
noAtlantisYAML := os.IsNotExist(err)
|
|
|
|
// We'll need the list of modified files.
|
|
modifiedFiles, err := p.VCSClient.GetModifiedFiles(ctx.BaseRepo, ctx.Pull)
|
|
if err != nil {
|
|
return CommandResponse{Error: err}
|
|
}
|
|
|
|
// Prepare the project contexts so the ProjectOperator can execute.
|
|
var projCtxs []models.ProjectCommandContext
|
|
|
|
// If there is no config file, then we try to plan for each project that
|
|
// was modified in the pull request.
|
|
if noAtlantisYAML {
|
|
modifiedProjects := p.ProjectFinder.DetermineProjects(ctx.Log, modifiedFiles, ctx.BaseRepo.FullName, repoDir)
|
|
for _, mp := range modifiedProjects {
|
|
projCtxs = append(projCtxs, models.ProjectCommandContext{
|
|
BaseRepo: ctx.BaseRepo,
|
|
HeadRepo: ctx.HeadRepo,
|
|
Pull: ctx.Pull,
|
|
User: ctx.User,
|
|
Log: ctx.Log,
|
|
RepoRelPath: mp.Path,
|
|
ProjectConfig: nil,
|
|
GlobalConfig: nil,
|
|
CommentArgs: nil,
|
|
Workspace: DefaultWorkspace,
|
|
})
|
|
}
|
|
} else {
|
|
// Otherwise, we use the projects that match the WhenModified fields
|
|
// in the config file.
|
|
matchingProjects := p.matchingProjects(modifiedFiles, config)
|
|
for _, mp := range matchingProjects {
|
|
projCtxs = append(projCtxs, models.ProjectCommandContext{
|
|
BaseRepo: ctx.BaseRepo,
|
|
HeadRepo: ctx.HeadRepo,
|
|
Pull: ctx.Pull,
|
|
User: ctx.User,
|
|
Log: ctx.Log,
|
|
CommentArgs: nil,
|
|
Workspace: mp.Workspace,
|
|
RepoRelPath: mp.Dir,
|
|
ProjectConfig: &mp,
|
|
GlobalConfig: &config,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Execute the operations.
|
|
var results []ProjectResult
|
|
for _, pCtx := range projCtxs {
|
|
res := p.ProjectOperator.Plan(pCtx, nil)
|
|
res.Path = pCtx.RepoRelPath
|
|
res.Workspace = pCtx.Workspace
|
|
results = append(results, res)
|
|
}
|
|
return CommandResponse{ProjectResults: results}
|
|
}
|
|
|
|
func (p *PullRequestOperator) PlanViaComment(ctx *CommandContext) CommandResponse {
|
|
repoDir, err := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Command.Workspace)
|
|
if err != nil {
|
|
return CommandResponse{Error: err}
|
|
}
|
|
|
|
var projCfg *valid.Project
|
|
var globalCfg *valid.Spec
|
|
|
|
// Parse config file if it exists.
|
|
config, err := p.ParserValidator.ReadConfig(repoDir)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return CommandResponse{Error: err}
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
projCfg = config.FindProject(ctx.Command.Dir, ctx.Command.Workspace)
|
|
globalCfg = &config
|
|
}
|
|
|
|
projCtx := models.ProjectCommandContext{
|
|
BaseRepo: ctx.BaseRepo,
|
|
HeadRepo: ctx.HeadRepo,
|
|
Pull: ctx.Pull,
|
|
User: ctx.User,
|
|
Log: ctx.Log,
|
|
CommentArgs: ctx.Command.Flags,
|
|
Workspace: ctx.Command.Workspace,
|
|
RepoRelPath: ctx.Command.Dir,
|
|
ProjectConfig: projCfg,
|
|
GlobalConfig: globalCfg,
|
|
}
|
|
projAbsPath := filepath.Join(repoDir, ctx.Command.Dir)
|
|
res := p.ProjectOperator.Plan(projCtx, &projAbsPath)
|
|
res.Workspace = projCtx.Workspace
|
|
res.Path = projCtx.RepoRelPath
|
|
return CommandResponse{
|
|
ProjectResults: []ProjectResult{
|
|
res,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (p *PullRequestOperator) ApplyViaComment(ctx *CommandContext) CommandResponse {
|
|
repoDir, err := p.Workspace.GetWorkspace(ctx.BaseRepo, ctx.Pull, ctx.Command.Workspace)
|
|
if err != nil {
|
|
return CommandResponse{Failure: "No workspace found. Did you run plan?"}
|
|
}
|
|
|
|
// todo: can deduplicate this between PlanViaComment
|
|
var projCfg *valid.Project
|
|
var globalCfg *valid.Spec
|
|
|
|
// Parse config file if it exists.
|
|
config, err := p.ParserValidator.ReadConfig(repoDir)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return CommandResponse{Error: err}
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
projCfg = config.FindProject(ctx.Command.Dir, ctx.Command.Workspace)
|
|
globalCfg = &config
|
|
}
|
|
|
|
projCtx := models.ProjectCommandContext{
|
|
BaseRepo: ctx.BaseRepo,
|
|
HeadRepo: ctx.HeadRepo,
|
|
Pull: ctx.Pull,
|
|
User: ctx.User,
|
|
Log: ctx.Log,
|
|
CommentArgs: ctx.Command.Flags,
|
|
Workspace: ctx.Command.Workspace,
|
|
RepoRelPath: ctx.Command.Dir,
|
|
ProjectConfig: projCfg,
|
|
GlobalConfig: globalCfg,
|
|
}
|
|
res := p.ProjectOperator.Apply(projCtx, filepath.Join(repoDir, ctx.Command.Dir))
|
|
res.Workspace = projCtx.Workspace
|
|
res.Path = projCtx.RepoRelPath
|
|
return CommandResponse{
|
|
ProjectResults: []ProjectResult{
|
|
res,
|
|
},
|
|
}
|
|
}
|
|
|
|
// matchingProjects returns the list of projects whose WhenModified fields match
|
|
// any of the modifiedFiles.
|
|
func (p *PullRequestOperator) matchingProjects(modifiedFiles []string, config valid.Spec) []valid.Project {
|
|
//todo
|
|
// match the modified files against the config
|
|
// remember the modified_files paths are relative to the project paths
|
|
return nil
|
|
}
|