-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add pattern matching support to atlantis plan/apply -d flag #2742
Changes from all commits
ba3d814
d1983e6
aaaaf25
eeb6f3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,6 +112,10 @@ func (c CommentCommand) IsForSpecificProject() bool { | |
return c.RepoRelDir != "" || c.Workspace != "" || c.ProjectName != "" | ||
} | ||
|
||
func (c CommentCommand) HasDirPatternMatching() bool { | ||
return strings.Contains(c.RepoRelDir, "*") || strings.Contains(c.RepoRelDir, "?") || strings.Contains(c.RepoRelDir, "[") || strings.Contains(c.RepoRelDir, "]") | ||
} | ||
|
||
// CommandName returns the name of this command. | ||
func (c CommentCommand) CommandName() command.Name { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add tests for the new functionality |
||
return c.Name | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,9 @@ package events | |
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"sort" | ||
"strings" | ||
|
||
"github.com/uber-go/tally" | ||
|
||
|
@@ -14,6 +16,7 @@ import ( | |
|
||
"github.com/runatlantis/atlantis/server/core/config" | ||
"github.com/runatlantis/atlantis/server/events/command" | ||
"github.com/runatlantis/atlantis/server/events/models" | ||
"github.com/runatlantis/atlantis/server/events/vcs" | ||
) | ||
|
||
|
@@ -170,7 +173,7 @@ type DefaultProjectCommandBuilder struct { | |
|
||
// See ProjectCommandBuilder.BuildAutoplanCommands. | ||
func (p *DefaultProjectCommandBuilder) BuildAutoplanCommands(ctx *command.Context) ([]command.ProjectContext, error) { | ||
projCtxs, err := p.buildPlanAllCommands(ctx, nil, false) | ||
projCtxs, err := p.buildPlanAllCommands(ctx, &CommentCommand{}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -187,16 +190,16 @@ func (p *DefaultProjectCommandBuilder) BuildAutoplanCommands(ctx *command.Contex | |
|
||
// See ProjectCommandBuilder.BuildPlanCommands. | ||
func (p *DefaultProjectCommandBuilder) BuildPlanCommands(ctx *command.Context, cmd *CommentCommand) ([]command.ProjectContext, error) { | ||
if !cmd.IsForSpecificProject() { | ||
return p.buildPlanAllCommands(ctx, cmd.Flags, cmd.Verbose) | ||
if !cmd.IsForSpecificProject() || (p.EnableRegExpCmd && cmd.HasDirPatternMatching()) { | ||
return p.buildPlanAllCommands(ctx, cmd) | ||
} | ||
pcc, err := p.buildProjectPlanCommand(ctx, cmd) | ||
return pcc, err | ||
} | ||
|
||
// See ProjectCommandBuilder.BuildApplyCommands. | ||
func (p *DefaultProjectCommandBuilder) BuildApplyCommands(ctx *command.Context, cmd *CommentCommand) ([]command.ProjectContext, error) { | ||
if !cmd.IsForSpecificProject() { | ||
if !cmd.IsForSpecificProject() || (p.EnableRegExpCmd && cmd.HasDirPatternMatching()) { | ||
return p.buildAllProjectCommands(ctx, cmd) | ||
} | ||
pac, err := p.buildProjectApplyCommand(ctx, cmd) | ||
|
@@ -217,7 +220,7 @@ func (p *DefaultProjectCommandBuilder) BuildVersionCommands(ctx *command.Context | |
|
||
// buildPlanAllCommands builds plan contexts for all projects we determine were | ||
// modified in this ctx. | ||
func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context, commentFlags []string, verbose bool) ([]command.ProjectContext, error) { | ||
func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context, cmd *CommentCommand) ([]command.ProjectContext, error) { | ||
// We'll need the list of modified files. | ||
modifiedFiles, err := p.VCSClient.GetModifiedFiles(ctx.Pull.BaseRepo, ctx.Pull) | ||
if err != nil { | ||
|
@@ -296,7 +299,14 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context | |
if err != nil { | ||
return nil, err | ||
} | ||
ctx.Log.Info("%d projects are to be planned based on their when_modified config", len(matchingProjects)) | ||
|
||
if p.EnableRegExpCmd && cmd.RepoRelDir != "" { | ||
filteredProjects, err := filterValidProjects(matchingProjects, cmd.RepoRelDir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
matchingProjects = filteredProjects | ||
} | ||
|
||
for _, mp := range matchingProjects { | ||
ctx.Log.Debug("determining config for project at dir: %q workspace: %q", mp.Dir, mp.Workspace) | ||
|
@@ -307,13 +317,13 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context | |
ctx, | ||
command.Plan, | ||
mergedCfg, | ||
commentFlags, | ||
cmd.Flags, | ||
repoDir, | ||
repoCfg.Automerge, | ||
mergedCfg.DeleteSourceBranchOnMerge, | ||
repoCfg.ParallelApply, | ||
repoCfg.ParallelPlan, | ||
verbose, | ||
cmd.IsVerbose(), | ||
)...) | ||
} | ||
} else { | ||
|
@@ -332,6 +342,15 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context | |
ctx.Log.Debug("moduleInfo for %s (matching %q) = %v", repoDir, p.AutoDetectModuleFiles, moduleInfo) | ||
modifiedProjects := p.ProjectFinder.DetermineProjects(ctx.Log, modifiedFiles, ctx.Pull.BaseRepo.FullName, repoDir, p.AutoplanFileList, moduleInfo) | ||
ctx.Log.Info("automatically determined that there were %d projects modified in this pull request: %s", len(modifiedProjects), modifiedProjects) | ||
|
||
if p.EnableRegExpCmd && cmd.RepoRelDir != "" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code looks to be the same as lines 303 to 309. Can we encapsulate this into a function to keep the code as DRY as possible? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or is there a better way to structure this so we can run this block for both if and else blocks? |
||
filteredProjects, err := filterProjects(modifiedProjects, cmd.RepoRelDir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
modifiedProjects = filteredProjects | ||
} | ||
|
||
for _, mp := range modifiedProjects { | ||
ctx.Log.Debug("determining config for project at dir: %q", mp.Path) | ||
pWorkspace, err := p.ProjectFinder.DetermineWorkspaceFromHCL(ctx.Log, repoDir) | ||
|
@@ -345,13 +364,13 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context | |
ctx, | ||
command.Plan, | ||
pCfg, | ||
commentFlags, | ||
cmd.Flags, | ||
repoDir, | ||
DefaultAutomergeEnabled, | ||
pCfg.DeleteSourceBranchOnMerge, | ||
DefaultParallelApplyEnabled, | ||
DefaultParallelPlanEnabled, | ||
verbose, | ||
cmd.IsVerbose(), | ||
)...) | ||
} | ||
} | ||
|
@@ -482,6 +501,14 @@ func (p *DefaultProjectCommandBuilder) buildAllProjectCommands(ctx *command.Cont | |
return nil, err | ||
} | ||
|
||
if p.EnableRegExpCmd && commentCmd.RepoRelDir != "" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another duplicated block here |
||
filteredPlans, err := filterPlans(plans, commentCmd.RepoRelDir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
plans = filteredPlans | ||
} | ||
|
||
// use the default repository workspace because it is the only one guaranteed to have an atlantis.yaml, | ||
// other workspaces will not have the file if they are using pre_workflow_hooks to generate it dynamically | ||
defaultRepoDir, err := p.WorkingDir.GetWorkingDir(ctx.Pull.BaseRepo, ctx.Pull, DefaultWorkspace) | ||
|
@@ -673,3 +700,48 @@ func (p *DefaultProjectCommandBuilder) validateWorkspaceAllowed(repoCfg *valid.R | |
|
||
return repoCfg.ValidateWorkspaceAllowed(repoRelDir, workspace) | ||
} | ||
|
||
func filterProjects(projects []models.Project, filter string) ([]models.Project, error) { | ||
trimmedFilter := strings.TrimPrefix(filter, "./") | ||
filteredProjects := make([]models.Project, 0, len(projects)) | ||
for _, proj := range projects { | ||
match, err := filepath.Match(trimmedFilter, proj.Path) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if match { | ||
filteredProjects = append(filteredProjects, proj) | ||
} | ||
} | ||
return filteredProjects, nil | ||
} | ||
|
||
func filterValidProjects(projects []valid.Project, filter string) ([]valid.Project, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we combine both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only difference I see is |
||
trimmedFilter := strings.TrimPrefix(filter, "./") | ||
filteredProjects := make([]valid.Project, 0, len(projects)) | ||
for _, proj := range projects { | ||
match, err := filepath.Match(trimmedFilter, proj.Dir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if match { | ||
filteredProjects = append(filteredProjects, proj) | ||
} | ||
} | ||
return filteredProjects, nil | ||
} | ||
|
||
func filterPlans(plans []PendingPlan, filter string) ([]PendingPlan, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could also be combined with the above 2 functions. Only thing I see here that's different is using |
||
trimmedFilter := strings.TrimPrefix(filter, "./") | ||
filteredPlans := make([]PendingPlan, 0, len(plans)) | ||
for _, plan := range plans { | ||
match, err := filepath.Match(trimmedFilter, plan.RepoRelDir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if match { | ||
filteredPlans = append(filteredPlans, plan) | ||
} | ||
} | ||
return filteredPlans, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please re-word the PR summary and title to reflect that this is no longer using the
-f
flag and instead will add wildcard filtering using the existing-d
flag