diff --git a/.goreleaser.yml b/.goreleaser.yml index 248d424d80..363023a45e 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -2,14 +2,8 @@ env: - CGO_ENABLED=0 builds: - targets: - - darwin_amd64 - - darwin_arm64 - - linux_386 - linux_amd64 - - linux_arm - linux_arm64 - - windows_386 - - windows_amd64 archives: - id: zip @@ -26,7 +20,7 @@ changelog: release: github: - owner: runatlantis + owner: edgelaboratories name: atlantis draft: true diff --git a/Dockerfile b/Dockerfile index 6261301d50..499535161f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,10 +13,10 @@ FROM ghcr.io/runatlantis/atlantis-base:2022.03.04 AS base ARG TARGETPLATFORM # install terraform binaries -ENV DEFAULT_TERRAFORM_VERSION=1.1.7 +ENV DEFAULT_TERRAFORM_VERSION=1.1.8 -# In the official Atlantis image we only have the latest of each Terraform version. -RUN AVAILABLE_TERRAFORM_VERSIONS="0.11.15 0.12.31 0.13.7 0.14.11 0.15.5 1.0.11 ${DEFAULT_TERRAFORM_VERSION}" && \ +# In this Atlantis image we only have the latest Terraform version. +RUN AVAILABLE_TERRAFORM_VERSIONS="${DEFAULT_TERRAFORM_VERSION}" && \ case ${TARGETPLATFORM} in \ "linux/amd64") TERRAFORM_ARCH=amd64 ;; \ "linux/arm64") TERRAFORM_ARCH=arm64 ;; \ diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000..831b156bb0 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,4 @@ +#!groovy +@Library('EdgeLabJenkins@master') _ + +dockerPipeline("atlantis", "default", false, true) diff --git a/server/controllers/github_app_controller.go b/server/controllers/github_app_controller.go index d8808cd835..7f5b352ca1 100644 --- a/server/controllers/github_app_controller.go +++ b/server/controllers/github_app_controller.go @@ -13,11 +13,12 @@ import ( // GithubAppController handles the creation and setup of a new GitHub app type GithubAppController struct { - AtlantisURL *url.URL - Logger logging.SimpleLogging - GithubSetupComplete bool - GithubHostname string - GithubOrg string + AtlantisURL *url.URL + Logger logging.SimpleLogging + GithubSetupComplete bool + GithubHostname string + GithubOrg string + AtlantisYAMLFilename string } type githubWebhook struct { @@ -42,7 +43,6 @@ type githubAppRequest struct { // A code query parameter is exchanged for this app's ID, key, and webhook_secret // Implements https://developer.github.com/apps/building-github-apps/creating-github-apps-from-a-manifest/#implementing-the-github-app-manifest-flow func (g *GithubAppController) ExchangeCode(w http.ResponseWriter, r *http.Request) { - if g.GithubSetupComplete { g.respond(w, logging.Error, http.StatusBadRequest, "Atlantis already has GitHub credentials") return @@ -55,7 +55,7 @@ func (g *GithubAppController) ExchangeCode(w http.ResponseWriter, r *http.Reques g.Logger.Debug("Exchanging GitHub app code for app credentials") creds := &vcs.GithubAnonymousCredentials{} - client, err := vcs.NewGithubClient(g.GithubHostname, creds, g.Logger) + client, err := vcs.NewGithubClient(g.GithubHostname, creds, g.Logger, g.AtlantisYAMLFilename) if err != nil { g.respond(w, logging.Error, http.StatusInternalServerError, "Failed to exchange code for github app: %s", err) return @@ -84,7 +84,6 @@ func (g *GithubAppController) ExchangeCode(w http.ResponseWriter, r *http.Reques // New redirects the user to create a new GitHub app func (g *GithubAppController) New(w http.ResponseWriter, r *http.Request) { - if g.GithubSetupComplete { g.respond(w, logging.Error, http.StatusBadRequest, "Atlantis already has GitHub credentials") return diff --git a/server/core/config/parser_validator.go b/server/core/config/parser_validator.go index b5989baa95..39fffe5132 100644 --- a/server/core/config/parser_validator.go +++ b/server/core/config/parser_validator.go @@ -15,12 +15,26 @@ import ( yaml "gopkg.in/yaml.v2" ) -// AtlantisYAMLFilename is the name of the config file for each repo. -const AtlantisYAMLFilename = "atlantis.yaml" +// DefaultAtlantisYAMLFilename is the name of the config file for each repo. +const DefaultAtlantisYAMLFilename = "atlantis.yaml" // ParserValidator parses and validates server-side repo config files and // repo-level atlantis.yaml files. -type ParserValidator struct{} +type ParserValidator struct { + AtlantisYAMLFilename string +} + +// NewParserValidator creates a validator to parse the configuration. +func NewParserValidator(atlantisYAMLFilenames ...string) *ParserValidator { + atlantisYAMLFilename := DefaultAtlantisYAMLFilename + if len(atlantisYAMLFilenames) != 0 && atlantisYAMLFilenames[0] != "" { + atlantisYAMLFilename = atlantisYAMLFilenames[0] + } + + return &ParserValidator{ + AtlantisYAMLFilename: atlantisYAMLFilename, + } +} // HasRepoCfg returns true if there is a repo config (atlantis.yaml) file // for the repo at absRepoDir. @@ -30,10 +44,10 @@ func (p *ParserValidator) HasRepoCfg(absRepoDir string) (bool, error) { const invalidExtensionFilename = "atlantis.yml" _, err := os.Stat(p.repoCfgPath(absRepoDir, invalidExtensionFilename)) if err == nil { - return false, errors.Errorf("found %q as config file; rename using the .yaml extension - %q", invalidExtensionFilename, AtlantisYAMLFilename) + return false, errors.Errorf("found %q as config file; rename using the .yaml extension - %q", invalidExtensionFilename, p.AtlantisYAMLFilename) } - _, err = os.Stat(p.repoCfgPath(absRepoDir, AtlantisYAMLFilename)) + _, err = os.Stat(p.repoCfgPath(absRepoDir, p.AtlantisYAMLFilename)) if os.IsNotExist(err) { return false, nil } @@ -44,12 +58,11 @@ func (p *ParserValidator) HasRepoCfg(absRepoDir string) (bool, error) { // repo at absRepoDir. // If there was no config file, it will return an os.IsNotExist(error). func (p *ParserValidator) ParseRepoCfg(absRepoDir string, globalCfg valid.GlobalCfg, repoID string) (valid.RepoCfg, error) { - configFile := p.repoCfgPath(absRepoDir, AtlantisYAMLFilename) + configFile := p.repoCfgPath(absRepoDir, p.AtlantisYAMLFilename) configData, err := os.ReadFile(configFile) // nolint: gosec - if err != nil { if !os.IsNotExist(err) { - return valid.RepoCfg{}, errors.Wrapf(err, "unable to read %s file", AtlantisYAMLFilename) + return valid.RepoCfg{}, errors.Wrapf(err, "unable to read %s file", p.AtlantisYAMLFilename) } // Don't wrap os.IsNotExist errors because we want our callers to be // able to detect if it's a NotExist err. diff --git a/server/core/config/parser_validator_test.go b/server/core/config/parser_validator_test.go index 9fa0912fb0..0c085dca51 100644 --- a/server/core/config/parser_validator_test.go +++ b/server/core/config/parser_validator_test.go @@ -24,7 +24,7 @@ var globalCfgArgs = valid.GlobalCfgArgs{ var globalCfg = valid.NewGlobalCfgFromArgs(globalCfgArgs) func TestHasRepoCfg_DirDoesNotExist(t *testing.T) { - r := config.ParserValidator{} + r := config.NewParserValidator() exists, err := r.HasRepoCfg("/not/exist") Ok(t, err) Equals(t, false, exists) @@ -33,7 +33,7 @@ func TestHasRepoCfg_DirDoesNotExist(t *testing.T) { func TestHasRepoCfg_FileDoesNotExist(t *testing.T) { tmpDir, cleanup := TempDir(t) defer cleanup() - r := config.ParserValidator{} + r := config.NewParserValidator() exists, err := r.HasRepoCfg(tmpDir) Ok(t, err) Equals(t, false, exists) @@ -45,13 +45,13 @@ func TestHasRepoCfg_InvalidFileExtension(t *testing.T) { _, err := os.Create(filepath.Join(tmpDir, "atlantis.yml")) Ok(t, err) - r := config.ParserValidator{} + r := config.NewParserValidator() _, err = r.HasRepoCfg(tmpDir) ErrContains(t, "found \"atlantis.yml\" as config file; rename using the .yaml extension - \"atlantis.yaml\"", err) } func TestParseRepoCfg_DirDoesNotExist(t *testing.T) { - r := config.ParserValidator{} + r := config.NewParserValidator() _, err := r.ParseRepoCfg("/not/exist", globalCfg, "") Assert(t, os.IsNotExist(err), "exp not exist err") } @@ -59,7 +59,7 @@ func TestParseRepoCfg_DirDoesNotExist(t *testing.T) { func TestParseRepoCfg_FileDoesNotExist(t *testing.T) { tmpDir, cleanup := TempDir(t) defer cleanup() - r := config.ParserValidator{} + r := config.NewParserValidator() _, err := r.ParseRepoCfg(tmpDir, globalCfg, "") Assert(t, os.IsNotExist(err), "exp not exist err") } @@ -67,10 +67,10 @@ func TestParseRepoCfg_FileDoesNotExist(t *testing.T) { func TestParseRepoCfg_BadPermissions(t *testing.T) { tmpDir, cleanup := TempDir(t) defer cleanup() - err := os.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), nil, 0000) + err := os.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), nil, 0o000) Ok(t, err) - r := config.ParserValidator{} + r := config.NewParserValidator() _, err = r.ParseRepoCfg(tmpDir, globalCfg, "") ErrContains(t, "unable to read atlantis.yaml file: ", err) } @@ -102,9 +102,9 @@ func TestParseCfgs_InvalidYAML(t *testing.T) { for _, c := range cases { t.Run(c.description, func(t *testing.T) { confPath := filepath.Join(tmpDir, "atlantis.yaml") - err := os.WriteFile(confPath, []byte(c.input), 0600) + err := os.WriteFile(confPath, []byte(c.input), 0o600) Ok(t, err) - r := config.ParserValidator{} + r := config.NewParserValidator() _, err = r.ParseRepoCfg(tmpDir, globalCfg, "") ErrContains(t, c.expErr, err) globalCfgArgs := valid.GlobalCfgArgs{ @@ -1067,10 +1067,10 @@ workflows: for _, c := range cases { t.Run(c.description, func(t *testing.T) { - err := os.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(c.input), 0600) + err := os.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(c.input), 0o600) Ok(t, err) - r := config.ParserValidator{} + r := config.NewParserValidator() act, err := r.ParseRepoCfg(tmpDir, globalCfg, "") if c.expErr != "" { ErrEquals(t, c.expErr, err) @@ -1095,10 +1095,10 @@ projects: workflow: custom workflows: custom: ~` - err := os.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(repoCfg), 0600) + err := os.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(repoCfg), 0o600) Ok(t, err) - r := config.ParserValidator{} + r := config.NewParserValidator() globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: false, MergeableReq: false, @@ -1111,7 +1111,7 @@ workflows: } func TestParseGlobalCfg_NotExist(t *testing.T) { - r := config.ParserValidator{} + r := config.NewParserValidator() globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: false, MergeableReq: false, @@ -1481,11 +1481,11 @@ workflows: for name, c := range cases { t.Run(name, func(t *testing.T) { - r := config.ParserValidator{} + r := config.NewParserValidator() tmp, cleanup := TempDir(t) defer cleanup() path := filepath.Join(tmp, "conf.yaml") - Ok(t, os.WriteFile(path, []byte(c.input), 0600)) + Ok(t, os.WriteFile(path, []byte(c.input), 0o600)) globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: false, @@ -1682,7 +1682,7 @@ func TestParserValidator_ParseGlobalCfgJSON(t *testing.T) { } for name, c := range cases { t.Run(name, func(t *testing.T) { - pv := &config.ParserValidator{} + pv := config.NewParserValidator() globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: false, MergeableReq: false, @@ -1746,10 +1746,10 @@ func TestParseRepoCfg_V2ShellParsing(t *testing.T) { apply: steps: - run: %s`, c.in, c.in) - Ok(t, os.WriteFile(v2Path, []byte("version: 2\n"+cfg), 0600)) - Ok(t, os.WriteFile(v3Path, []byte("version: 3\n"+cfg), 0600)) + Ok(t, os.WriteFile(v2Path, []byte("version: 2\n"+cfg), 0o600)) + Ok(t, os.WriteFile(v3Path, []byte("version: 3\n"+cfg), 0o600)) - p := &config.ParserValidator{} + p := config.NewParserValidator() globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: true, MergeableReq: false, diff --git a/server/core/config/valid/global_cfg_test.go b/server/core/config/valid/global_cfg_test.go index e6a86f6a3a..6e9da10881 100644 --- a/server/core/config/valid/global_cfg_test.go +++ b/server/core/config/valid/global_cfg_test.go @@ -661,7 +661,7 @@ policies: var global valid.GlobalCfg if c.gCfg != "" { path := filepath.Join(tmp, "config.yaml") - Ok(t, os.WriteFile(path, []byte(c.gCfg), 0600)) + Ok(t, os.WriteFile(path, []byte(c.gCfg), 0o600)) var err error globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: false, @@ -669,7 +669,7 @@ policies: ApprovedReq: false, UnDivergedReq: false, } - global, err = (&config.ParserValidator{}).ParseGlobalCfg(path, valid.NewGlobalCfgFromArgs(globalCfgArgs)) + global, err = config.NewParserValidator().ParseGlobalCfg(path, valid.NewGlobalCfgFromArgs(globalCfgArgs)) Ok(t, err) } else { globalCfgArgs := valid.GlobalCfgArgs{ @@ -832,7 +832,7 @@ repos: var global valid.GlobalCfg if c.gCfg != "" { path := filepath.Join(tmp, "config.yaml") - Ok(t, os.WriteFile(path, []byte(c.gCfg), 0600)) + Ok(t, os.WriteFile(path, []byte(c.gCfg), 0o600)) var err error globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: false, @@ -841,7 +841,7 @@ repos: UnDivergedReq: false, } - global, err = (&config.ParserValidator{}).ParseGlobalCfg(path, valid.NewGlobalCfgFromArgs(globalCfgArgs)) + global, err = config.NewParserValidator().ParseGlobalCfg(path, valid.NewGlobalCfgFromArgs(globalCfgArgs)) Ok(t, err) } else { globalCfgArgs := valid.GlobalCfgArgs{ diff --git a/server/events/comment_parser.go b/server/events/comment_parser.go index a4433e863f..745800416e 100644 --- a/server/events/comment_parser.go +++ b/server/events/comment_parser.go @@ -190,7 +190,7 @@ func (e *CommentParser) Parse(comment string, vcsHost models.VCSHostType) Commen flagSet.SetOutput(io.Discard) flagSet.StringVarP(&workspace, workspaceFlagLong, workspaceFlagShort, "", "Switch to this Terraform workspace before planning.") flagSet.StringVarP(&dir, dirFlagLong, dirFlagShort, "", "Which directory to run plan in relative to root of repo, ex. 'child/dir'.") - flagSet.StringVarP(&project, projectFlagLong, projectFlagShort, "", fmt.Sprintf("Which project to run plan for. Refers to the name of the project configured in %s. Cannot be used at same time as workspace or dir flags.", config.AtlantisYAMLFilename)) + flagSet.StringVarP(&project, projectFlagLong, projectFlagShort, "", fmt.Sprintf("Which project to run plan for. Refers to the name of the project configured in %s. Cannot be used at same time as workspace or dir flags.", config.DefaultAtlantisYAMLFilename)) flagSet.BoolVarP(&verbose, verboseFlagLong, verboseFlagShort, false, "Append Atlantis log to comment.") case command.Apply.String(): name = command.Apply @@ -198,7 +198,7 @@ func (e *CommentParser) Parse(comment string, vcsHost models.VCSHostType) Commen flagSet.SetOutput(io.Discard) flagSet.StringVarP(&workspace, workspaceFlagLong, workspaceFlagShort, "", "Apply the plan for this Terraform workspace.") flagSet.StringVarP(&dir, dirFlagLong, dirFlagShort, "", "Apply the plan for this directory, relative to root of repo, ex. 'child/dir'.") - flagSet.StringVarP(&project, projectFlagLong, projectFlagShort, "", fmt.Sprintf("Apply the plan for this project. Refers to the name of the project configured in %s. Cannot be used at same time as workspace or dir flags.", config.AtlantisYAMLFilename)) + flagSet.StringVarP(&project, projectFlagLong, projectFlagShort, "", fmt.Sprintf("Apply the plan for this project. Refers to the name of the project configured in %s. Cannot be used at same time as workspace or dir flags.", config.DefaultAtlantisYAMLFilename)) flagSet.BoolVarP(&autoMergeDisabled, autoMergeDisabledFlagLong, autoMergeDisabledFlagShort, false, "Disable automerge after apply.") flagSet.BoolVarP(&verbose, verboseFlagLong, verboseFlagShort, false, "Append Atlantis log to comment.") case command.ApprovePolicies.String(): @@ -215,7 +215,7 @@ func (e *CommentParser) Parse(comment string, vcsHost models.VCSHostType) Commen flagSet = pflag.NewFlagSet(command.Version.String(), pflag.ContinueOnError) flagSet.StringVarP(&workspace, workspaceFlagLong, workspaceFlagShort, "", "Switch to this Terraform workspace before running version.") flagSet.StringVarP(&dir, dirFlagLong, dirFlagShort, "", "Which directory to run version in relative to root of repo, ex. 'child/dir'.") - flagSet.StringVarP(&project, projectFlagLong, projectFlagShort, "", fmt.Sprintf("Print the version for this project. Refers to the name of the project configured in %s.", config.AtlantisYAMLFilename)) + flagSet.StringVarP(&project, projectFlagLong, projectFlagShort, "", fmt.Sprintf("Print the version for this project. Refers to the name of the project configured in %s.", config.DefaultAtlantisYAMLFilename)) flagSet.BoolVarP(&verbose, verboseFlagLong, verboseFlagShort, false, "Append Atlantis log to comment.") default: return CommentParseResult{CommentResponse: fmt.Sprintf("Error: unknown command %q – this is a bug", cmd)} @@ -368,7 +368,7 @@ func (e *CommentParser) errMarkdown(errMsg string, cmd string, flagSet *pflag.Fl func (e *CommentParser) HelpComment(applyDisabled bool) string { buf := &bytes.Buffer{} - var tmpl = template.Must(template.New("").Parse(helpCommentTemplate)) + tmpl := template.Must(template.New("").Parse(helpCommentTemplate)) if err := tmpl.Execute(buf, struct { ApplyDisabled bool }{ @@ -377,7 +377,6 @@ func (e *CommentParser) HelpComment(applyDisabled bool) string { return fmt.Sprintf("Failed to render template, this is a bug: %v", err) } return buf.String() - } var helpCommentTemplate = "```cmake\n" + diff --git a/server/events/project_command_builder.go b/server/events/project_command_builder.go index 5ac2c4f723..0d9f0b7b49 100644 --- a/server/events/project_command_builder.go +++ b/server/events/project_command_builder.go @@ -220,15 +220,15 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context if p.SkipCloneNoChanges && p.VCSClient.SupportsSingleFileDownload(ctx.Pull.BaseRepo) { hasRepoCfg, repoCfgData, err := p.VCSClient.DownloadRepoConfigFile(ctx.Pull) if err != nil { - return nil, errors.Wrapf(err, "downloading %s", config.AtlantisYAMLFilename) + return nil, errors.Wrapf(err, "downloading %s", p.ParserValidator.AtlantisYAMLFilename) } if hasRepoCfg { repoCfg, err := p.ParserValidator.ParseRepoCfgData(repoCfgData, p.GlobalCfg, ctx.Pull.BaseRepo.ID()) if err != nil { - return nil, errors.Wrapf(err, "parsing %s", config.AtlantisYAMLFilename) + return nil, errors.Wrapf(err, "parsing %s", p.ParserValidator.AtlantisYAMLFilename) } - ctx.Log.Info("successfully parsed remote %s file", config.AtlantisYAMLFilename) + ctx.Log.Info("successfully parsed remote %s file", p.ParserValidator.AtlantisYAMLFilename) matchingProjects, err := p.ProjectFinder.DetermineProjectsViaConfig(ctx.Log, modifiedFiles, repoCfg, "") if err != nil { return nil, err @@ -263,7 +263,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context // Parse config file if it exists. hasRepoCfg, err := p.ParserValidator.HasRepoCfg(repoDir) if err != nil { - return nil, errors.Wrapf(err, "looking for %s file in %q", config.AtlantisYAMLFilename, repoDir) + return nil, errors.Wrapf(err, "looking for %s file in %q", p.ParserValidator.AtlantisYAMLFilename, repoDir) } var projCtxs []command.ProjectContext @@ -273,9 +273,9 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context // should be planed. repoCfg, err := p.ParserValidator.ParseRepoCfg(repoDir, p.GlobalCfg, ctx.Pull.BaseRepo.ID()) if err != nil { - return nil, errors.Wrapf(err, "parsing %s", config.AtlantisYAMLFilename) + return nil, errors.Wrapf(err, "parsing %s", p.ParserValidator.AtlantisYAMLFilename) } - ctx.Log.Info("successfully parsed %s file", config.AtlantisYAMLFilename) + ctx.Log.Info("successfully parsed %s file", p.ParserValidator.AtlantisYAMLFilename) matchingProjects, err := p.ProjectFinder.DetermineProjectsViaConfig(ctx.Log, modifiedFiles, repoCfg, repoDir) if err != nil { return nil, err @@ -303,7 +303,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context } else { // If there is no config file, then we'll plan each project that // our algorithm determines was modified. - ctx.Log.Info("found no %s file", config.AtlantisYAMLFilename) + ctx.Log.Info("found no %s file", p.ParserValidator.AtlantisYAMLFilename) modifiedProjects := p.ProjectFinder.DetermineProjects(ctx.Log, modifiedFiles, ctx.Pull.BaseRepo.FullName, repoDir, p.AutoplanFileList) if err != nil { return nil, errors.Wrapf(err, "finding modified projects: %s", modifiedFiles) @@ -383,12 +383,12 @@ func (p *DefaultProjectCommandBuilder) buildProjectPlanCommand(ctx *command.Cont func (p *DefaultProjectCommandBuilder) getCfg(ctx *command.Context, projectName string, dir string, workspace string, repoDir string) (projectsCfg []valid.Project, repoCfg *valid.RepoCfg, err error) { hasConfigFile, err := p.ParserValidator.HasRepoCfg(repoDir) if err != nil { - err = errors.Wrapf(err, "looking for %s file in %q", config.AtlantisYAMLFilename, repoDir) + err = errors.Wrapf(err, "looking for %s file in %q", p.ParserValidator.AtlantisYAMLFilename, repoDir) return } if !hasConfigFile { if projectName != "" { - err = fmt.Errorf("cannot specify a project name unless an %s file exists to configure projects", config.AtlantisYAMLFilename) + err = fmt.Errorf("cannot specify a project name unless an %s file exists to configure projects", p.ParserValidator.AtlantisYAMLFilename) return } return @@ -412,7 +412,7 @@ func (p *DefaultProjectCommandBuilder) getCfg(ctx *command.Context, projectName } } if len(projectsCfg) == 0 { - err = fmt.Errorf("no project with name %q is defined in %s", projectName, config.AtlantisYAMLFilename) + err = fmt.Errorf("no project with name %q is defined in %s", projectName, p.ParserValidator.AtlantisYAMLFilename) return } return @@ -423,7 +423,7 @@ func (p *DefaultProjectCommandBuilder) getCfg(ctx *command.Context, projectName return } if len(projCfgs) > 1 { - err = fmt.Errorf("must specify project name: more than one project defined in %s matched dir: %q workspace: %q", config.AtlantisYAMLFilename, dir, workspace) + err = fmt.Errorf("must specify project name: more than one project defined in %s matched dir: %q workspace: %q", p.ParserValidator.AtlantisYAMLFilename, dir, workspace) return } projectsCfg = projCfgs @@ -561,7 +561,6 @@ func (p *DefaultProjectCommandBuilder) buildProjectCommandCtx(ctx *command.Conte repoRelDir string, workspace string, verbose bool) ([]command.ProjectContext, error) { - matchingProjects, repoCfgPtr, err := p.getCfg(ctx, projectName, repoRelDir, workspace, repoDir) if err != nil { return []command.ProjectContext{}, err diff --git a/server/events/project_command_builder_internal_test.go b/server/events/project_command_builder_internal_test.go index 259e9378c2..b4012f220c 100644 --- a/server/events/project_command_builder_internal_test.go +++ b/server/events/project_command_builder_internal_test.go @@ -598,8 +598,8 @@ projects: // Write and parse the global config file. globalCfgPath := filepath.Join(tmp, "global.yaml") - Ok(t, os.WriteFile(globalCfgPath, []byte(c.globalCfg), 0600)) - parser := &config.ParserValidator{} + Ok(t, os.WriteFile(globalCfgPath, []byte(c.globalCfg), 0o600)) + parser := config.NewParserValidator() globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: false, MergeableReq: false, @@ -610,7 +610,7 @@ projects: Ok(t, err) if c.repoCfg != "" { - Ok(t, os.WriteFile(filepath.Join(tmp, "atlantis.yaml"), []byte(c.repoCfg), 0600)) + Ok(t, os.WriteFile(filepath.Join(tmp, "atlantis.yaml"), []byte(c.repoCfg), 0o600)) } builder := NewProjectCommandBuilder( @@ -803,13 +803,13 @@ projects: // Write and parse the global config file. globalCfgPath := filepath.Join(tmp, "global.yaml") - Ok(t, os.WriteFile(globalCfgPath, []byte(c.globalCfg), 0600)) - parser := &config.ParserValidator{} + Ok(t, os.WriteFile(globalCfgPath, []byte(c.globalCfg), 0o600)) + parser := config.NewParserValidator() globalCfg, err := parser.ParseGlobalCfg(globalCfgPath, valid.NewGlobalCfg(false, false, false)) Ok(t, err) if c.repoCfg != "" { - Ok(t, os.WriteFile(filepath.Join(tmp, "atlantis.yaml"), []byte(c.repoCfg), 0600)) + Ok(t, os.WriteFile(filepath.Join(tmp, "atlantis.yaml"), []byte(c.repoCfg), 0o600)) } logger := logging.NewNoopLogger(t) @@ -1028,8 +1028,8 @@ workflows: // Write and parse the global config file. globalCfgPath := filepath.Join(tmp, "global.yaml") - Ok(t, os.WriteFile(globalCfgPath, []byte(c.globalCfg), 0600)) - parser := &config.ParserValidator{} + Ok(t, os.WriteFile(globalCfgPath, []byte(c.globalCfg), 0o600)) + parser := config.NewParserValidator() globalCfgArgs := valid.GlobalCfgArgs{ AllowRepoCfg: false, MergeableReq: false, @@ -1041,7 +1041,7 @@ workflows: Ok(t, err) if c.repoCfg != "" { - Ok(t, os.WriteFile(filepath.Join(tmp, "atlantis.yaml"), []byte(c.repoCfg), 0600)) + Ok(t, os.WriteFile(filepath.Join(tmp, "atlantis.yaml"), []byte(c.repoCfg), 0o600)) } statsScope, _, _ := metrics.NewLoggingScope(logging.NewNoopLogger(t), "atlantis") diff --git a/server/events/project_command_builder_test.go b/server/events/project_command_builder_test.go index 5e1a63875b..9c11af4d96 100644 --- a/server/events/project_command_builder_test.go +++ b/server/events/project_command_builder_test.go @@ -135,7 +135,7 @@ projects: vcsClient := vcsmocks.NewMockClient() When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"main.tf"}, nil) if c.AtlantisYAML != "" { - err := os.WriteFile(filepath.Join(tmpDir, config.AtlantisYAMLFilename), []byte(c.AtlantisYAML), 0600) + err := os.WriteFile(filepath.Join(tmpDir, config.DefaultAtlantisYAMLFilename), []byte(c.AtlantisYAML), 0o600) Ok(t, err) } @@ -148,7 +148,7 @@ projects: builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, vcsClient, workingDir, @@ -403,7 +403,7 @@ projects: vcsClient := vcsmocks.NewMockClient() When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn([]string{"main.tf"}, nil) if c.AtlantisYAML != "" { - err := os.WriteFile(filepath.Join(tmpDir, config.AtlantisYAMLFilename), []byte(c.AtlantisYAML), 0600) + err := os.WriteFile(filepath.Join(tmpDir, config.DefaultAtlantisYAMLFilename), []byte(c.AtlantisYAML), 0o600) Ok(t, err) } @@ -416,7 +416,7 @@ projects: builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, vcsClient, workingDir, @@ -558,7 +558,7 @@ projects: vcsClient := vcsmocks.NewMockClient() When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(c.ModifiedFiles, nil) if c.AtlantisYAML != "" { - err := os.WriteFile(filepath.Join(tmpDir, config.AtlantisYAMLFilename), []byte(c.AtlantisYAML), 0600) + err := os.WriteFile(filepath.Join(tmpDir, config.DefaultAtlantisYAMLFilename), []byte(c.AtlantisYAML), 0o600) Ok(t, err) } @@ -571,7 +571,7 @@ projects: builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, vcsClient, workingDir, @@ -684,7 +684,7 @@ func TestDefaultProjectCommandBuilder_BuildMultiApply(t *testing.T) { builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, nil, workingDir, @@ -745,7 +745,7 @@ projects: - dir: . workspace: staging ` - err := os.WriteFile(filepath.Join(repoDir, config.AtlantisYAMLFilename), []byte(yamlCfg), 0600) + err := os.WriteFile(filepath.Join(repoDir, config.DefaultAtlantisYAMLFilename), []byte(yamlCfg), 0o600) Ok(t, err) When(workingDir.Clone( @@ -773,7 +773,7 @@ projects: builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, nil, workingDir, @@ -852,7 +852,7 @@ func TestDefaultProjectCommandBuilder_EscapeArgs(t *testing.T) { builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, vcsClient, workingDir, @@ -952,7 +952,7 @@ projects: "project1": map[string]interface{}{ "main.tf": fmt.Sprintf(baseVersionConfig, exactSymbols[0]), }, - config.AtlantisYAMLFilename: atlantisYamlContent, + config.DefaultAtlantisYAMLFilename: atlantisYamlContent, }, ModifiedFiles: []string{"project1/main.tf", "project2/main.tf"}, Exp: map[string][]int{ @@ -965,7 +965,7 @@ projects: "project1": map[string]interface{}{ "main.tf": nil, }, - config.AtlantisYAMLFilename: atlantisYamlContent, + config.DefaultAtlantisYAMLFilename: atlantisYamlContent, }, ModifiedFiles: []string{"project1/main.tf"}, Exp: map[string][]int{ @@ -1039,7 +1039,7 @@ projects: builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, vcsClient, workingDir, @@ -1106,7 +1106,7 @@ projects: builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, vcsClient, workingDir, @@ -1164,7 +1164,7 @@ func TestDefaultProjectCommandBuilder_WithPolicyCheckEnabled_BuildAutoplanComman builder := events.NewProjectCommandBuilder( true, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, vcsClient, workingDir, @@ -1268,7 +1268,7 @@ func TestDefaultProjectCommandBuilder_BuildVersionCommand(t *testing.T) { builder := events.NewProjectCommandBuilder( false, - &config.ParserValidator{}, + config.NewParserValidator(), &events.DefaultProjectFinder{}, nil, workingDir, diff --git a/server/events/vcs/azuredevops_client.go b/server/events/vcs/azuredevops_client.go index 6d0b4469dd..229b9b7e18 100644 --- a/server/events/vcs/azuredevops_client.go +++ b/server/events/vcs/azuredevops_client.go @@ -17,20 +17,21 @@ import ( // AzureDevopsClient represents an Azure DevOps VCS client type AzureDevopsClient struct { - Client *azuredevops.Client - ctx context.Context - UserName string + Client *azuredevops.Client + ctx context.Context + UserName string + atlantisYAMLFilename string } // NewAzureDevopsClient returns a valid Azure DevOps client. -func NewAzureDevopsClient(hostname string, userName string, token string) (*AzureDevopsClient, error) { +func NewAzureDevopsClient(hostname, userName, token, atlantisYAMLFilename string) (*AzureDevopsClient, error) { tp := azuredevops.BasicAuthTransport{ Username: "", Password: strings.TrimSpace(token), } httpClient := tp.Client() httpClient.Timeout = time.Second * 10 - var adClient, err = azuredevops.NewClient(httpClient) + adClient, err := azuredevops.NewClient(httpClient) if err != nil { return nil, err } @@ -45,9 +46,10 @@ func NewAzureDevopsClient(hostname string, userName string, token string) (*Azur } client := &AzureDevopsClient{ - Client: adClient, - UserName: userName, - ctx: context.Background(), + Client: adClient, + UserName: userName, + ctx: context.Background(), + atlantisYAMLFilename: atlantisYAMLFilename, } return client, nil @@ -392,6 +394,10 @@ func (g *AzureDevopsClient) DownloadRepoConfigFile(pull models.PullRequest) (boo return false, []byte{}, fmt.Errorf("Not Implemented") } +func (g *AzureDevopsClient) AtlantisYAMLFilename() string { + return g.atlantisYAMLFilename +} + // GitStatusContextFromSrc parses an Atlantis formatted src string into a context suitable // for the status update API. In the AzureDevops branch policy UI there is a single string // field used to drive these contexts where all text preceding the final '/' character is diff --git a/server/events/vcs/azuredevops_client_test.go b/server/events/vcs/azuredevops_client_test.go index a9314fd036..3486534cea 100644 --- a/server/events/vcs/azuredevops_client_test.go +++ b/server/events/vcs/azuredevops_client_test.go @@ -100,7 +100,7 @@ func TestAzureDevopsClient_MergePull(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token") + client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token", "") client.Client.VsaexBaseURL = *testServerURL Ok(t, err) defer disableSSLVerification()() @@ -114,7 +114,6 @@ func TestAzureDevopsClient_MergePull(t *testing.T) { completionOptions, id, ) - if err != nil { fmt.Printf("Merge failed: %+v\n", err) return @@ -214,7 +213,7 @@ func TestAzureDevopsClient_UpdateStatus(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token") + client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token", "") Ok(t, err) defer disableSSLVerification()() @@ -277,7 +276,7 @@ func TestAzureDevopsClient_GetModifiedFiles(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token") + client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token", "") Ok(t, err) defer disableSSLVerification()() @@ -397,7 +396,7 @@ func TestAzureDevopsClient_PullIsMergeable(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token") + client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token", "") Ok(t, err) defer disableSSLVerification()() @@ -491,7 +490,7 @@ func TestAzureDevopsClient_PullIsApproved(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token") + client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token", "") Ok(t, err) defer disableSSLVerification()() @@ -536,7 +535,7 @@ func TestAzureDevopsClient_GetPullRequest(t *testing.T) { })) testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token") + client, err := vcs.NewAzureDevopsClient(testServerURL.Host, "user", "token", "") Ok(t, err) defer disableSSLVerification()() @@ -556,7 +555,7 @@ func TestAzureDevopsClient_GetPullRequest(t *testing.T) { } func TestAzureDevopsClient_MarkdownPullLink(t *testing.T) { - client, err := vcs.NewAzureDevopsClient("hostname", "user", "token") + client, err := vcs.NewAzureDevopsClient("hostname", "user", "token", "") Ok(t, err) pull := models.PullRequest{Num: 1} s, _ := client.MarkdownPullLink(pull) diff --git a/server/events/vcs/bitbucketcloud/client.go b/server/events/vcs/bitbucketcloud/client.go index 598bce5df6..fd9279527c 100644 --- a/server/events/vcs/bitbucketcloud/client.go +++ b/server/events/vcs/bitbucketcloud/client.go @@ -14,27 +14,29 @@ import ( ) type Client struct { - HTTPClient *http.Client - Username string - Password string - BaseURL string - AtlantisURL string + HTTPClient *http.Client + Username string + Password string + BaseURL string + AtlantisURL string + atlantisYAMLFilename string } // NewClient builds a bitbucket cloud client. atlantisURL is the // URL for Atlantis that will be linked to from the build status icons. This // linking is annoying because we don't have anywhere good to link but a URL is // required. -func NewClient(httpClient *http.Client, username string, password string, atlantisURL string) *Client { +func NewClient(httpClient *http.Client, username, password, atlantisURL, atlantisYAMLFilename string) *Client { if httpClient == nil { httpClient = http.DefaultClient } return &Client{ - HTTPClient: httpClient, - Username: username, - Password: password, - BaseURL: BaseURL, - AtlantisURL: atlantisURL, + HTTPClient: httpClient, + Username: username, + Password: password, + BaseURL: BaseURL, + AtlantisURL: atlantisURL, + atlantisYAMLFilename: atlantisYAMLFilename, } } @@ -266,3 +268,7 @@ func (b *Client) SupportsSingleFileDownload(models.Repo) bool { func (b *Client) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { return false, []byte{}, fmt.Errorf("Not Implemented") } + +func (b *Client) AtlantisYAMLFilename() string { + return b.atlantisYAMLFilename +} diff --git a/server/events/vcs/bitbucketcloud/client_test.go b/server/events/vcs/bitbucketcloud/client_test.go index 54e5179be8..61efbe54b0 100644 --- a/server/events/vcs/bitbucketcloud/client_test.go +++ b/server/events/vcs/bitbucketcloud/client_test.go @@ -70,7 +70,7 @@ func TestClient_GetModifiedFilesPagination(t *testing.T) { defer testServer.Close() serverURL = testServer.URL - client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io", "") client.BaseURL = testServer.URL files, err := client.GetModifiedFiles(models.Repo{ @@ -131,7 +131,7 @@ func TestClient_GetModifiedFilesOldNil(t *testing.T) { })) defer testServer.Close() - client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io", "") client.BaseURL = testServer.URL files, err := client.GetModifiedFiles(models.Repo{ @@ -197,7 +197,7 @@ func TestClient_PullIsApproved(t *testing.T) { })) defer testServer.Close() - client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io", "") client.BaseURL = testServer.URL repo, err := models.NewRepo(models.BitbucketServer, "owner/repo", "https://bitbucket.org/owner/repo.git", "user", "token") @@ -322,7 +322,7 @@ func TestClient_PullIsMergeable(t *testing.T) { })) defer testServer.Close() - client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io", "") client.BaseURL = testServer.URL actMergeable, err := client.PullIsMergeable(models.Repo{ @@ -342,11 +342,10 @@ func TestClient_PullIsMergeable(t *testing.T) { Equals(t, c.ExpMergeable, actMergeable) }) } - } func TestClient_MarkdownPullLink(t *testing.T) { - client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io") + client := bitbucketcloud.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io", "") pull := models.PullRequest{Num: 1} s, _ := client.MarkdownPullLink(pull) exp := "#1" diff --git a/server/events/vcs/bitbucketserver/client.go b/server/events/vcs/bitbucketserver/client.go index e0041e1d3e..480c8e034c 100644 --- a/server/events/vcs/bitbucketserver/client.go +++ b/server/events/vcs/bitbucketserver/client.go @@ -22,11 +22,12 @@ import ( const maxCommentLength = 32768 type Client struct { - HTTPClient *http.Client - Username string - Password string - BaseURL string - AtlantisURL string + HTTPClient *http.Client + Username string + Password string + BaseURL string + AtlantisURL string + atlantisYAMLFilename string } type DeleteSourceBranch struct { @@ -42,7 +43,7 @@ type DeleteSourceBranch struct { // URL for Atlantis that will be linked to from the build status icons. This // linking is annoying because we don't have anywhere good to link but a URL is // required. -func NewClient(httpClient *http.Client, username string, password string, baseURL string, atlantisURL string) (*Client, error) { +func NewClient(httpClient *http.Client, username, password, baseURL, atlantisURL, atlantisYAMLFilename string) (*Client, error) { if httpClient == nil { httpClient = http.DefaultClient } @@ -54,11 +55,12 @@ func NewClient(httpClient *http.Client, username string, password string, baseUR return nil, fmt.Errorf("must have 'http://' or 'https://' in base url %q", baseURL) } return &Client{ - HTTPClient: httpClient, - Username: username, - Password: password, - BaseURL: strings.TrimRight(parsedURL.String(), "/"), - AtlantisURL: atlantisURL, + HTTPClient: httpClient, + Username: username, + Password: password, + BaseURL: strings.TrimRight(parsedURL.String(), "/"), + AtlantisURL: atlantisURL, + atlantisYAMLFilename: atlantisYAMLFilename, }, nil } @@ -348,3 +350,7 @@ func (b *Client) SupportsSingleFileDownload(repo models.Repo) bool { func (b *Client) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { return false, []byte{}, fmt.Errorf("not implemented") } + +func (b *Client) AtlantisYAMLFilename() string { + return b.atlantisYAMLFilename +} diff --git a/server/events/vcs/bitbucketserver/client_test.go b/server/events/vcs/bitbucketserver/client_test.go index 73aa8b0962..a21ab68f84 100644 --- a/server/events/vcs/bitbucketserver/client_test.go +++ b/server/events/vcs/bitbucketserver/client_test.go @@ -59,7 +59,7 @@ func TestClient_BasePath(t *testing.T) { for _, c := range cases { t.Run(c.inputURL, func(t *testing.T) { - client, err := bitbucketserver.NewClient(nil, "u", "p", c.inputURL, "atlantis-url") + client, err := bitbucketserver.NewClient(nil, "u", "p", c.inputURL, "atlantis-url", "") if c.expErr != "" { ErrEquals(t, c.expErr, err) } else { @@ -117,7 +117,7 @@ func TestClient_GetModifiedFilesPagination(t *testing.T) { defer testServer.Close() serverURL = testServer.URL - client, err := bitbucketserver.NewClient(http.DefaultClient, "user", "pass", serverURL, "runatlantis.io") + client, err := bitbucketserver.NewClient(http.DefaultClient, "user", "pass", serverURL, "runatlantis.io", "") Ok(t, err) files, err := client.GetModifiedFiles(models.Repo{ @@ -158,7 +158,7 @@ func TestClient_MergePull(t *testing.T) { })) defer testServer.Close() - client, err := bitbucketserver.NewClient(http.DefaultClient, "user", "pass", testServer.URL, "runatlantis.io") + client, err := bitbucketserver.NewClient(http.DefaultClient, "user", "pass", testServer.URL, "runatlantis.io", "") Ok(t, err) err = client.MergePull(models.PullRequest{ @@ -217,7 +217,7 @@ func TestClient_MergePullDeleteSourceBranch(t *testing.T) { })) defer testServer.Close() - client, err := bitbucketserver.NewClient(http.DefaultClient, "user", "pass", testServer.URL, "runatlantis.io") + client, err := bitbucketserver.NewClient(http.DefaultClient, "user", "pass", testServer.URL, "runatlantis.io", "") Ok(t, err) err = client.MergePull(models.PullRequest{ @@ -245,7 +245,7 @@ func TestClient_MergePullDeleteSourceBranch(t *testing.T) { } func TestClient_MarkdownPullLink(t *testing.T) { - client, err := bitbucketserver.NewClient(nil, "u", "p", "https://base-url", "atlantis-url") + client, err := bitbucketserver.NewClient(nil, "u", "p", "https://base-url", "atlantis-url", "") Ok(t, err) pull := models.PullRequest{Num: 1} s, _ := client.MarkdownPullLink(pull) diff --git a/server/events/vcs/client.go b/server/events/vcs/client.go index e63fdcd1f7..151c6ed2f8 100644 --- a/server/events/vcs/client.go +++ b/server/events/vcs/client.go @@ -45,4 +45,6 @@ type Client interface { // if BaseRepo had one repo config file, its content will placed on the second return value DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) SupportsSingleFileDownload(repo models.Repo) bool + + AtlantisYAMLFilename() string } diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index d3615ad172..329b656709 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -24,7 +24,6 @@ import ( "github.com/Laisky/graphql" "github.com/google/go-github/v31/github" "github.com/pkg/errors" - "github.com/runatlantis/atlantis/server/core/config" "github.com/runatlantis/atlantis/server/events/models" "github.com/runatlantis/atlantis/server/events/vcs/common" "github.com/runatlantis/atlantis/server/logging" @@ -38,12 +37,13 @@ const maxCommentLength = 65536 // GithubClient is used to perform GitHub actions. type GithubClient struct { - user string - client *github.Client - v4MutateClient *graphql.Client - v4QueryClient *githubv4.Client - ctx context.Context - logger logging.SimpleLogging + user string + client *github.Client + v4MutateClient *graphql.Client + v4QueryClient *githubv4.Client + ctx context.Context + logger logging.SimpleLogging + atlantisYAMLFilename string } // GithubAppTemporarySecrets holds app credentials obtained from github after creation. @@ -61,7 +61,7 @@ type GithubAppTemporarySecrets struct { } // NewGithubClient returns a valid GitHub client. -func NewGithubClient(hostname string, credentials GithubCredentials, logger logging.SimpleLogging) (*GithubClient, error) { +func NewGithubClient(hostname string, credentials GithubCredentials, logger logging.SimpleLogging, atlantisYAMLFilename string) (*GithubClient, error) { transport, err := credentials.Client() if err != nil { return nil, errors.Wrap(err, "error initializing github authentication transport") @@ -111,12 +111,13 @@ func NewGithubClient(hostname string, credentials GithubCredentials, logger logg return nil, errors.Wrap(err, "getting user") } return &GithubClient{ - user: user, - client: client, - v4MutateClient: v4MutateClient, - v4QueryClient: v4QueryClient, - ctx: context.Background(), - logger: logger, + user: user, + client: client, + v4MutateClient: v4MutateClient, + v4QueryClient: v4QueryClient, + ctx: context.Background(), + logger: logger, + atlantisYAMLFilename: atlantisYAMLFilename, }, nil } @@ -481,7 +482,7 @@ func (g *GithubClient) ExchangeCode(code string) (*GithubAppTemporarySecrets, er // if BaseRepo had one repo config file, its content will placed on the second return value func (g *GithubClient) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { opt := github.RepositoryContentGetOptions{Ref: pull.HeadBranch} - fileContent, _, resp, err := g.client.Repositories.GetContents(g.ctx, pull.BaseRepo.Owner, pull.BaseRepo.Name, config.AtlantisYAMLFilename, &opt) + fileContent, _, resp, err := g.client.Repositories.GetContents(g.ctx, pull.BaseRepo.Owner, pull.BaseRepo.Name, g.atlantisYAMLFilename, &opt) if resp.StatusCode == http.StatusNotFound { return false, []byte{}, nil @@ -501,3 +502,7 @@ func (g *GithubClient) DownloadRepoConfigFile(pull models.PullRequest) (bool, [] func (g *GithubClient) SupportsSingleFileDownload(repo models.Repo) bool { return true } + +func (g *GithubClient) AtlantisYAMLFilename() string { + return g.atlantisYAMLFilename +} diff --git a/server/events/vcs/github_client_internal_test.go b/server/events/vcs/github_client_internal_test.go index ebe039d319..6b571040d3 100644 --- a/server/events/vcs/github_client_internal_test.go +++ b/server/events/vcs/github_client_internal_test.go @@ -22,14 +22,14 @@ import ( // If the hostname is github.com, should use normal BaseURL. func TestNewGithubClient_GithubCom(t *testing.T) { - client, err := NewGithubClient("github.com", &GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := NewGithubClient("github.com", &GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) Equals(t, "https://api.github.com/", client.client.BaseURL.String()) } // If the hostname is a non-github hostname should use the right BaseURL. func TestNewGithubClient_NonGithub(t *testing.T) { - client, err := NewGithubClient("example.com", &GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := NewGithubClient("example.com", &GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) Equals(t, "https://example.com/api/v3/", client.client.BaseURL.String()) // If possible in the future, test the GraphQL client's URL as well. But at the diff --git a/server/events/vcs/github_client_test.go b/server/events/vcs/github_client_test.go index 42583f70fc..70b4631b5d 100644 --- a/server/events/vcs/github_client_test.go +++ b/server/events/vcs/github_client_test.go @@ -63,7 +63,7 @@ func TestGithubClient_GetModifiedFiles(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logger) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logger, "") Ok(t, err) defer disableSSLVerification()() @@ -118,7 +118,7 @@ func TestGithubClient_GetModifiedFilesMovedFile(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() @@ -212,7 +212,7 @@ func TestGithubClient_PaginatesComments(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() @@ -301,7 +301,7 @@ func TestGithubClient_HideOldComments(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() @@ -367,7 +367,7 @@ func TestGithubClient_UpdateStatus(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() @@ -453,7 +453,7 @@ func TestGithubClient_PullIsApproved(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() @@ -544,7 +544,7 @@ func TestGithubClient_PullIsMergeable(t *testing.T) { })) testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() @@ -626,7 +626,7 @@ func TestGithubClient_MergePullHandlesError(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() @@ -700,7 +700,6 @@ func TestGithubClient_MergePullCorrectMethod(t *testing.T) { for name, c := range cases { t.Run(name, func(t *testing.T) { - // Modify response. jsBytes, err := os.ReadFile("fixtures/github-repo.json") Ok(t, err) @@ -749,7 +748,7 @@ func TestGithubClient_MergePullCorrectMethod(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() @@ -777,7 +776,7 @@ func TestGithubClient_MergePullCorrectMethod(t *testing.T) { } func TestGithubClient_MarkdownPullLink(t *testing.T) { - client, err := vcs.NewGithubClient("hostname", &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient("hostname", &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) pull := models.PullRequest{Num: 1} s, _ := client.MarkdownPullLink(pull) @@ -804,7 +803,6 @@ func TestGithubClient_SplitComments(t *testing.T) { testServer := httptest.NewTLSServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.Method + " " + r.RequestURI { case "POST /api/v3/repos/runatlantis/atlantis/issues/1/comments": defer r.Body.Close() // nolint: errcheck @@ -832,7 +830,7 @@ func TestGithubClient_SplitComments(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() pull := models.PullRequest{Num: 1} @@ -866,11 +864,10 @@ func TestGithubClient_SplitComments(t *testing.T) { // Test that we retry the get pull request call if it 404s. func TestGithubClient_Retry404(t *testing.T) { - var numCalls = 0 + numCalls := 0 testServer := httptest.NewTLSServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.Method + " " + r.RequestURI { case "GET /api/v3/repos/runatlantis/atlantis/pulls/1": defer r.Body.Close() // nolint: errcheck @@ -890,7 +887,7 @@ func TestGithubClient_Retry404(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() repo := models.Repo{ @@ -911,11 +908,10 @@ func TestGithubClient_Retry404(t *testing.T) { // Test that we retry the get pull request files call if it 404s. func TestGithubClient_Retry404Files(t *testing.T) { - var numCalls = 0 + numCalls := 0 testServer := httptest.NewTLSServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.Method + " " + r.RequestURI { case "GET /api/v3/repos/runatlantis/atlantis/pulls/1/files?per_page=300": defer r.Body.Close() // nolint: errcheck @@ -935,7 +931,7 @@ func TestGithubClient_Retry404Files(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logging.NewNoopLogger(t), "") Ok(t, err) defer disableSSLVerification()() repo := models.Repo{ @@ -988,7 +984,7 @@ func TestGithubClient_GetTeamNamesForUser(t *testing.T) { })) testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logger) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, logger, "") Ok(t, err) defer disableSSLVerification()() diff --git a/server/events/vcs/github_credentials_test.go b/server/events/vcs/github_credentials_test.go index a85d8d2d95..f372de353b 100644 --- a/server/events/vcs/github_credentials_test.go +++ b/server/events/vcs/github_credentials_test.go @@ -15,7 +15,7 @@ func TestGithubClient_GetUser_AppSlug(t *testing.T) { Ok(t, err) anonCreds := &vcs.GithubAnonymousCredentials{} - anonClient, err := vcs.NewGithubClient(testServer, anonCreds, logging.NewNoopLogger(t)) + anonClient, err := vcs.NewGithubClient(testServer, anonCreds, logging.NewNoopLogger(t), "") Ok(t, err) tempSecrets, err := anonClient.ExchangeCode("good-code") Ok(t, err) @@ -39,7 +39,7 @@ func TestGithubClient_AppAuthentication(t *testing.T) { Ok(t, err) anonCreds := &vcs.GithubAnonymousCredentials{} - anonClient, err := vcs.NewGithubClient(testServer, anonCreds, logging.NewNoopLogger(t)) + anonClient, err := vcs.NewGithubClient(testServer, anonCreds, logging.NewNoopLogger(t), "") Ok(t, err) tempSecrets, err := anonClient.ExchangeCode("good-code") Ok(t, err) @@ -49,7 +49,7 @@ func TestGithubClient_AppAuthentication(t *testing.T) { Key: []byte(fixtures.GithubPrivateKey), Hostname: testServer, } - _, err = vcs.NewGithubClient(testServer, appCreds, logging.NewNoopLogger(t)) + _, err = vcs.NewGithubClient(testServer, appCreds, logging.NewNoopLogger(t), "") Ok(t, err) token, err := appCreds.GetToken() diff --git a/server/events/vcs/gitlab_client.go b/server/events/vcs/gitlab_client.go index 7bf7a10b3a..ac3ec7b499 100644 --- a/server/events/vcs/gitlab_client.go +++ b/server/events/vcs/gitlab_client.go @@ -22,8 +22,6 @@ import ( "strings" "time" - "github.com/runatlantis/atlantis/server/core/config" - "github.com/runatlantis/atlantis/server/events/command" "github.com/runatlantis/atlantis/server/events/vcs/common" @@ -43,6 +41,8 @@ type GitlabClient struct { Client *gitlab.Client // Version is set to the server version. Version *version.Version + + atlantisYAMLFilename string } // commonMarkSupported is a version constraint that is true when this version of @@ -54,8 +54,10 @@ var commonMarkSupported = MustConstraint(">=11.1") var gitlabClientUnderTest = false // NewGitlabClient returns a valid GitLab client. -func NewGitlabClient(hostname string, token string, logger logging.SimpleLogging) (*GitlabClient, error) { - client := &GitlabClient{} +func NewGitlabClient(hostname string, token string, logger logging.SimpleLogging, atlantisYAMLFilename string) (*GitlabClient, error) { + client := &GitlabClient{ + atlantisYAMLFilename: atlantisYAMLFilename, + } // Create the client differently depending on the base URL. if hostname == "gitlab.com" { @@ -265,7 +267,7 @@ func (g *GitlabClient) WaitForSuccessPipeline(ctx context.Context, pull models.P case <-ctx.Done(): // validation check time out cancel() - return //ctx.Err() + return // ctx.Err() default: mr, _ := g.GetMergeRequest(pull.BaseRepo.FullName, pull.Num) @@ -366,7 +368,7 @@ func (g *GitlabClient) GetTeamNamesForUser(repo models.Repo, user models.User) ( func (g *GitlabClient) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { opt := gitlab.GetRawFileOptions{Ref: gitlab.String(pull.HeadBranch)} - bytes, resp, err := g.Client.RepositoryFiles.GetRawFile(pull.BaseRepo.FullName, config.AtlantisYAMLFilename, &opt) + bytes, resp, err := g.Client.RepositoryFiles.GetRawFile(pull.BaseRepo.FullName, g.atlantisYAMLFilename, &opt) if resp.StatusCode == http.StatusNotFound { return false, []byte{}, nil } @@ -381,3 +383,7 @@ func (g *GitlabClient) DownloadRepoConfigFile(pull models.PullRequest) (bool, [] func (g *GitlabClient) SupportsSingleFileDownload(repo models.Repo) bool { return true } + +func (g *GitlabClient) AtlantisYAMLFilename() string { + return g.atlantisYAMLFilename +} diff --git a/server/events/vcs/gitlab_client_test.go b/server/events/vcs/gitlab_client_test.go index d045c19701..fcd3f15df3 100644 --- a/server/events/vcs/gitlab_client_test.go +++ b/server/events/vcs/gitlab_client_test.go @@ -56,7 +56,7 @@ func TestNewGitlabClient_BaseURL(t *testing.T) { for _, c := range cases { t.Run(c.Hostname, func(t *testing.T) { log := logging.NewNoopLogger(t) - client, err := NewGitlabClient(c.Hostname, "token", log) + client, err := NewGitlabClient(c.Hostname, "token", log, "") Ok(t, err) Equals(t, c.ExpBaseURL, client.Client.BaseURL().String()) }) @@ -251,7 +251,7 @@ func TestGitlabClient_UpdateStatus(t *testing.T) { func TestGitlabClient_MarkdownPullLink(t *testing.T) { gitlabClientUnderTest = true defer func() { gitlabClientUnderTest = false }() - client, err := NewGitlabClient("gitlab.com", "token", nil) + client, err := NewGitlabClient("gitlab.com", "token", nil, "") Ok(t, err) pull := models.PullRequest{Num: 1} s, _ := client.MarkdownPullLink(pull) diff --git a/server/events/vcs/mocks/mock_client_hack.go b/server/events/vcs/mocks/mock_client_hack.go new file mode 100644 index 0000000000..b3f4621e1d --- /dev/null +++ b/server/events/vcs/mocks/mock_client_hack.go @@ -0,0 +1,3 @@ +package mocks + +func (mock *MockClient) AtlantisYAMLFilename() string { return "atlantis.yaml" } diff --git a/server/events/vcs/not_configured_vcs_client.go b/server/events/vcs/not_configured_vcs_client.go index 45d4416228..c19d186af5 100644 --- a/server/events/vcs/not_configured_vcs_client.go +++ b/server/events/vcs/not_configured_vcs_client.go @@ -29,30 +29,39 @@ type NotConfiguredVCSClient struct { func (a *NotConfiguredVCSClient) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) { return nil, a.err() } + func (a *NotConfiguredVCSClient) CreateComment(repo models.Repo, pullNum int, comment string, command string) error { return a.err() } + func (a *NotConfiguredVCSClient) HidePrevCommandComments(repo models.Repo, pullNum int, command string) error { return nil } + func (a *NotConfiguredVCSClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (models.ApprovalStatus, error) { return models.ApprovalStatus{}, a.err() } + func (a *NotConfiguredVCSClient) PullIsMergeable(repo models.Repo, pull models.PullRequest) (bool, error) { return false, a.err() } + func (a *NotConfiguredVCSClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state models.CommitStatus, src string, description string, url string) error { return a.err() } + func (a *NotConfiguredVCSClient) MergePull(pull models.PullRequest, pullOptions models.PullRequestOptions) error { return a.err() } + func (a *NotConfiguredVCSClient) MarkdownPullLink(pull models.PullRequest) (string, error) { return "", a.err() } + func (a *NotConfiguredVCSClient) err() error { return fmt.Errorf("atlantis was not configured to support repos from %s", a.Host.String()) } + func (a *NotConfiguredVCSClient) GetTeamNamesForUser(repo models.Repo, user models.User) ([]string, error) { return nil, a.err() } @@ -64,3 +73,7 @@ func (a *NotConfiguredVCSClient) SupportsSingleFileDownload(repo models.Repo) bo func (a *NotConfiguredVCSClient) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { return true, []byte{}, a.err() } + +func (a *NotConfiguredVCSClient) AtlantisYAMLFilename() string { + return "" +} diff --git a/server/events/vcs/proxy.go b/server/events/vcs/proxy.go index 2e937b4cf4..a8e8d8f1e3 100644 --- a/server/events/vcs/proxy.go +++ b/server/events/vcs/proxy.go @@ -95,3 +95,13 @@ func (d *ClientProxy) DownloadRepoConfigFile(pull models.PullRequest) (bool, []b func (d *ClientProxy) SupportsSingleFileDownload(repo models.Repo) bool { return d.clients[repo.VCSHost.Type].SupportsSingleFileDownload(repo) } + +func (d *ClientProxy) AtlantisYAMLFilename() string { + for k := range d.clients { + if d.clients[k].AtlantisYAMLFilename() != "" { + return d.clients[k].AtlantisYAMLFilename() + } + } + + return "" +} diff --git a/server/server.go b/server/server.go index b9f871e770..1b6dee08b8 100644 --- a/server/server.go +++ b/server/server.go @@ -146,7 +146,6 @@ type WebhookConfig struct { // for the server CLI command because it injects all the dependencies. func NewServer(userConfig UserConfig, config Config) (*Server, error) { logger, err := logging.NewStructuredLoggerFromLevel(userConfig.ToLogLevel()) - if err != nil { return nil, err } @@ -166,7 +165,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { policyChecksEnabled = true } - validator := &cfg.ParserValidator{} + validator := cfg.NewParserValidator(userConfig.AtlantisYAMLFilename) globalCfg := valid.NewGlobalCfgFromArgs( valid.GlobalCfgArgs{ @@ -189,7 +188,6 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { } statsScope, closer, err := metrics.NewScope(globalCfg.Metrics, logger, userConfig.StatsNamespace) - if err != nil { return nil, errors.Wrapf(err, "instantiating metrics scope") } @@ -224,7 +222,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { } var err error - rawGithubClient, err := vcs.NewGithubClient(userConfig.GithubHostname, githubCredentials, logger) + rawGithubClient, err := vcs.NewGithubClient(userConfig.GithubHostname, githubCredentials, logger, userConfig.AtlantisYAMLFilename) if err != nil { return nil, err } @@ -234,7 +232,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { if userConfig.GitlabUser != "" { supportedVCSHosts = append(supportedVCSHosts, models.Gitlab) var err error - gitlabClient, err = vcs.NewGitlabClient(userConfig.GitlabHostname, userConfig.GitlabToken, logger) + gitlabClient, err = vcs.NewGitlabClient(userConfig.GitlabHostname, userConfig.GitlabToken, logger, userConfig.AtlantisYAMLFilename) if err != nil { return nil, err } @@ -246,7 +244,9 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { http.DefaultClient, userConfig.BitbucketUser, userConfig.BitbucketToken, - userConfig.AtlantisURL) + userConfig.AtlantisURL, + userConfig.AtlantisYAMLFilename, + ) } else { supportedVCSHosts = append(supportedVCSHosts, models.BitbucketServer) var err error @@ -255,7 +255,9 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { userConfig.BitbucketUser, userConfig.BitbucketToken, userConfig.BitbucketBaseURL, - userConfig.AtlantisURL) + userConfig.AtlantisURL, + userConfig.AtlantisYAMLFilename, + ) if err != nil { return nil, errors.Wrapf(err, "setting up Bitbucket Server client") } @@ -265,7 +267,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { supportedVCSHosts = append(supportedVCSHosts, models.AzureDevops) var err error - azuredevopsClient, err = vcs.NewAzureDevopsClient(userConfig.AzureDevOpsHostname, userConfig.AzureDevopsUser, userConfig.AzureDevopsToken) + azuredevopsClient, err = vcs.NewAzureDevopsClient(userConfig.AzureDevOpsHostname, userConfig.AzureDevopsUser, userConfig.AzureDevopsToken, userConfig.AtlantisYAMLFilename) if err != nil { return nil, err } @@ -322,13 +324,11 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { commitStatusUpdater := &events.DefaultCommitStatusUpdater{Client: vcsClient, StatusName: userConfig.VCSStatusName} binDir, err := mkSubDir(userConfig.DataDir, BinDirName) - if err != nil { return nil, err } cacheDir, err := mkSubDir(userConfig.DataDir, TerraformPluginCacheDirName) - if err != nil { return nil, err } @@ -507,7 +507,6 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { ) showStepRunner, err := runtime.NewShowStepRunner(terraformClient, defaultTfVersion) - if err != nil { return nil, errors.Wrap(err, "initializing show step runner") } @@ -516,7 +515,6 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { defaultTfVersion, policy.NewConfTestExecutorWorkflow(logger, binDir, &terraform.DefaultDownloader{}), ) - if err != nil { return nil, errors.Wrap(err, "initializing policy check runner") } @@ -918,7 +916,7 @@ func (s *Server) Index(w http.ResponseWriter, _ *http.Request) { Locked: applyCmdLock.Locked, TimeFormatted: applyCmdLock.Time.Format("02-01-2006 15:04:05"), } - //Sort by date - newest to oldest. + // Sort by date - newest to oldest. sort.SliceStable(lockResults, func(i, j int) bool { return lockResults[i].Time.After(lockResults[j].Time) }) err = s.IndexTemplate.Execute(w, templates.IndexData{ @@ -934,7 +932,7 @@ func (s *Server) Index(w http.ResponseWriter, _ *http.Request) { func mkSubDir(parentDir string, subDir string) (string, error) { fullDir := filepath.Join(parentDir, subDir) - if err := os.MkdirAll(fullDir, 0700); err != nil { + if err := os.MkdirAll(fullDir, 0o700); err != nil { return "", errors.Wrapf(err, "unable to create dir %q", fullDir) } diff --git a/server/user_config.go b/server/user_config.go index cab3d8d9f5..1a3ec1741c 100644 --- a/server/user_config.go +++ b/server/user_config.go @@ -11,6 +11,7 @@ type UserConfig struct { AllowForkPRs bool `mapstructure:"allow-fork-prs"` AllowRepoConfig bool `mapstructure:"allow-repo-config"` AtlantisURL string `mapstructure:"atlantis-url"` + AtlantisYAMLFilename string `mapstructure:"atlantis-yaml-filename"` Automerge bool `mapstructure:"automerge"` AutoplanFileList string `mapstructure:"autoplan-file-list"` AzureDevopsToken string `mapstructure:"azuredevops-token"`