diff --git a/CHANGELOG.md b/CHANGELOG.md index fd89163cb..a5989c627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,11 @@ repos can do in their `atlantis.yaml` files. Read [docs](https://www.runatlantis If you are using `run` steps, check our [upgrade guide](https://www.runatlantis.io/docs/upgrading-atlantis-yaml.html#upgrading-from-v2-to-v3) to see if you need to make any changes before upgrading. +* Flags `--require-approval`, `--require-mergeable` and `--allow-repo-config` are + deprecated in favour of creating a server-side repo config file that applies + the same configuration. If you run `atlantis server` with those flags, a + deprecation warning will be printed telling you what server-side config is + recommended instead. * If you have projects configured with the same directory and workspace (which means you're probably using the `-backend-config` flag) **and** their names contain `/`'s, then you'll have to re-run `atlantis plan` after upgrading if you had any unapplied plans. diff --git a/server/events/project_command_builder.go b/server/events/project_command_builder.go index 4cf68f395..629317ffa 100644 --- a/server/events/project_command_builder.go +++ b/server/events/project_command_builder.go @@ -135,6 +135,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *CommandContext, } ctx.Log.Info("%d projects are to be planned based on their when_modified config", len(matchingProjects)) for _, mp := range matchingProjects { + ctx.Log.Debug("determining config for project at dir: %q workspace: %q", mp.Dir, mp.Workspace) mergedCfg := p.GlobalCfg.MergeProjectCfg(ctx.Log, ctx.BaseRepo.ID(), mp, repoCfg) projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, mergedCfg, commentFlags, repoCfg.Automerge, verbose)) } @@ -145,6 +146,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *CommandContext, modifiedProjects := p.ProjectFinder.DetermineProjects(ctx.Log, modifiedFiles, ctx.BaseRepo.FullName, repoDir) ctx.Log.Info("automatically determined that there were %d projects modified in this pull request: %s", len(modifiedProjects), modifiedProjects) for _, mp := range modifiedProjects { + ctx.Log.Debug("determining config for project at dir: %q", mp.Path) pCfg := p.GlobalCfg.DefaultProjCfg(ctx.Log, ctx.BaseRepo.ID(), mp.Path, DefaultWorkspace) projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, pCfg, commentFlags, DefaultAutomergeEnabled, verbose)) } diff --git a/server/events/yaml/parser_validator.go b/server/events/yaml/parser_validator.go index f5aeb0c52..4d879934a 100644 --- a/server/events/yaml/parser_validator.go +++ b/server/events/yaml/parser_validator.go @@ -118,16 +118,7 @@ func (p *ParserValidator) validateRawGlobalCfg(rawCfg raw.GlobalCfg, defaultCfg return valid.GlobalCfg{}, err } - validCfg := rawCfg.ToValid() - - // Add defaults to the parsed config. - validCfg.Repos = append(defaultCfg.Repos, validCfg.Repos...) - for k, v := range defaultCfg.Workflows { - // We won't override existing workflows. - if _, ok := validCfg.Workflows[k]; !ok { - validCfg.Workflows[k] = v - } - } + validCfg := rawCfg.ToValid(defaultCfg) return validCfg, nil } diff --git a/server/events/yaml/parser_validator_test.go b/server/events/yaml/parser_validator_test.go index c2ceca7bb..9d3c1ace9 100644 --- a/server/events/yaml/parser_validator_test.go +++ b/server/events/yaml/parser_validator_test.go @@ -990,6 +990,25 @@ repos: }, }, }, + "referencing default workflow": { + input: ` +repos: +- id: github.com/owner/repo + workflow: default +`, + exp: valid.GlobalCfg{ + Repos: []valid.Repo{ + defaultCfg.Repos[0], + { + ID: "github.com/owner/repo", + Workflow: defaultCfg.Repos[0].Workflow, + }, + }, + Workflows: map[string]valid.Workflow{ + "default": defaultCfg.Workflows["default"], + }, + }, + }, } for name, c := range cases { t.Run(name, func(t *testing.T) { diff --git a/server/events/yaml/raw/global_cfg.go b/server/events/yaml/raw/global_cfg.go index 1e5b03b4a..47a4d1f2a 100644 --- a/server/events/yaml/raw/global_cfg.go +++ b/server/events/yaml/raw/global_cfg.go @@ -39,6 +39,10 @@ func (g GlobalCfg) Validate() error { continue } name := *repo.Workflow + if name == valid.DefaultWorkflowName { + // The 'default' workflow will always be defined. + continue + } found := false for w := range g.Workflows { if w == name { @@ -53,16 +57,23 @@ func (g GlobalCfg) Validate() error { return nil } -func (g GlobalCfg) ToValid() valid.GlobalCfg { +func (g GlobalCfg) ToValid(defaultCfg valid.GlobalCfg) valid.GlobalCfg { workflows := make(map[string]valid.Workflow) for k, v := range g.Workflows { workflows[k] = v.ToValid(k) } + // Merge in defaults without overriding. + for k, v := range defaultCfg.Workflows { + if _, ok := workflows[k]; !ok { + workflows[k] = v + } + } var repos []valid.Repo for _, r := range g.Repos { repos = append(repos, r.ToValid(workflows)) } + repos = append(defaultCfg.Repos, repos...) return valid.GlobalCfg{ Repos: repos, Workflows: workflows, diff --git a/server/events/yaml/valid/global_cfg.go b/server/events/yaml/valid/global_cfg.go index f843f131f..f14d032b0 100644 --- a/server/events/yaml/valid/global_cfg.go +++ b/server/events/yaml/valid/global_cfg.go @@ -133,7 +133,6 @@ func (r Repo) IDString() string { // MergeProjectCfg merges proj and rCfg with the global config to return a // final config. It assumes that all configs have been validated. func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, proj Project, rCfg RepoCfg) MergedProjectCfg { - log.Debug("merging repo and server-side configs") applyReqs, workflow, allowedOverrides, allowCustomWorkflows := g.getMatchingCfg(log, repoID) // If repos are allowed to override certain keys then override them. @@ -141,7 +140,7 @@ func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, pro switch key { case ApplyRequirementsKey: if proj.ApplyRequirements != nil { - log.Debug("overriding global %s with repo settings: [%s]", ApplyRequirementsKey, strings.Join(proj.ApplyRequirements, ",")) + log.Debug("overriding server-defined %s with repo settings: [%s]", ApplyRequirementsKey, strings.Join(proj.ApplyRequirements, ",")) applyReqs = proj.ApplyRequirements } case WorkflowKey: @@ -164,13 +163,13 @@ func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, pro } } } - log.Debug("overriding global %s with repo-specified workflow %q", WorkflowKey, workflow.Name) + log.Debug("overriding server-defined %s with repo-specified workflow: %q", WorkflowKey, workflow.Name) } } } - log.Debug("final settings for repo %s: %s: [%s], %s: %s", - repoID, ApplyRequirementsKey, strings.Join(applyReqs, ","), WorkflowKey, workflow.Name) + log.Debug("final settings: %s: [%s], %s: %s", + ApplyRequirementsKey, strings.Join(applyReqs, ","), WorkflowKey, workflow.Name) return MergedProjectCfg{ ApplyRequirements: applyReqs, @@ -267,33 +266,57 @@ func (g GlobalCfg) ValidateRepoCfg(rCfg RepoCfg, repoID string) error { // getMatchingCfg returns the key settings for repoID. func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (applyReqs []string, workflow Workflow, allowedOverrides []string, allowCustomWorkflows bool) { + toLog := make(map[string]string) + traceF := func(repoIdx int, repoID string, key string, val interface{}) string { + from := "default server config" + if repoIdx > 0 { + from = fmt.Sprintf("repos[%d], id: %s", repoIdx, repoID) + } + var valStr string + switch v := val.(type) { + case string: + valStr = fmt.Sprintf("%q", v) + case []string: + valStr = fmt.Sprintf("[%s]", strings.Join(v, ",")) + case bool: + valStr = fmt.Sprintf("%t", v) + default: + valStr = "this is a bug" + } + + return fmt.Sprintf("setting %s: %s from %s", key, valStr, from) + } + for _, key := range []string{ApplyRequirementsKey, WorkflowKey, AllowedOverridesKey, AllowCustomWorkflowsKey} { - for _, repo := range g.Repos { + for i, repo := range g.Repos { if repo.IDMatches(repoID) { switch key { case ApplyRequirementsKey: if repo.ApplyRequirements != nil { - log.Debug("setting %s: [%s] from repo config %q", ApplyRequirementsKey, strings.Join(repo.ApplyRequirements, ","), repo.IDString()) + toLog[ApplyRequirementsKey] = traceF(i, repo.IDString(), ApplyRequirementsKey, repo.ApplyRequirements) applyReqs = repo.ApplyRequirements } case WorkflowKey: if repo.Workflow != nil { - log.Debug("setting %s: %q from repo config %q", WorkflowKey, repo.Workflow.Name, repo.IDString()) + toLog[WorkflowKey] = traceF(i, repo.IDString(), WorkflowKey, repo.Workflow.Name) workflow = *repo.Workflow } case AllowedOverridesKey: if repo.AllowedOverrides != nil { - log.Debug("setting %s: [%s] from repo config %q", AllowedOverridesKey, strings.Join(repo.AllowedOverrides, ","), repo.IDString()) + toLog[AllowedOverridesKey] = traceF(i, repo.IDString(), AllowedOverridesKey, repo.AllowedOverrides) allowedOverrides = repo.AllowedOverrides } case AllowCustomWorkflowsKey: if repo.AllowCustomWorkflows != nil { - log.Debug("setting %s: %t from repo config %q", AllowCustomWorkflowsKey, *repo.AllowCustomWorkflows, repo.IDString()) + toLog[AllowCustomWorkflowsKey] = traceF(i, repo.IDString(), AllowCustomWorkflowsKey, *repo.AllowCustomWorkflows) allowCustomWorkflows = *repo.AllowCustomWorkflows } } } } } + for _, l := range toLog { + log.Debug(l) + } return }