diff --git a/runatlantis.io/docs/repo-level-atlantis-yaml.md b/runatlantis.io/docs/repo-level-atlantis-yaml.md index 23afdac9ed..c596855bad 100644 --- a/runatlantis.io/docs/repo-level-atlantis-yaml.md +++ b/runatlantis.io/docs/repo-level-atlantis-yaml.md @@ -20,8 +20,8 @@ keys by setting the `allowed_overrides` key there. See the [Server Side Repo Con more details. **Notes** -* `atlantis.yaml` files must be placed at the root of the repo -* The only supported name is `atlantis.yaml`. Not `atlantis.yml` or `.atlantis.yaml`. +* By default, repo root `atlantis.yaml` file is used. +* You can change this behaviour by setting [Server Side Repo Config](server-side-repo-config.html) ::: danger DANGER Atlantis uses the `atlantis.yaml` version from the pull request, similar to other diff --git a/runatlantis.io/docs/server-side-repo-config.md b/runatlantis.io/docs/server-side-repo-config.md index bc3ee64594..025df2c9ea 100644 --- a/runatlantis.io/docs/server-side-repo-config.md +++ b/runatlantis.io/docs/server-side-repo-config.md @@ -35,6 +35,10 @@ repos: # By default, all branches are matched branch: /.*/ + # repo_config_file specifies which repo config file to use for this repo. + # By default, atlantis.yaml is used. + repo_config_file: path/to/atlantis.yaml + # apply_requirements sets the Apply Requirements for all repos that match. apply_requirements: [approved, mergeable] @@ -168,7 +172,7 @@ repos: Then each allowed repo can have an `atlantis.yaml` file that sets `apply_requirements` to an empty array (disabling the requirement). ```yaml -# atlantis.yaml in the repo root +# atlantis.yaml in the repo root or set repo_config_file in repos.yaml version: 3 projects: - dir: . @@ -357,6 +361,54 @@ workflows: See [Custom Workflows](custom-workflows.html) for more details on writing custom workflows. +### Multiple Atlantis Servers Handle The Same Repository +Running multiple Atlantis servers to handle the same repository can be done to separate permissions for each Atlantis server. +In this case, a different [atlantis.yaml](repo-level-atlantis-yaml.html) repository config file can be used by using different `repos.yaml` files. + +For example, consider a situation where a separate `production-server` atlantis uses repo config `atlantis-production.yaml` and `staging-server` atlantis uses repo config `atlantis-staging.yaml`. + +Firstly, deploy 2 Atlantis servers, `production-server` and `staging-server`. +Each server has different permissions and a different `repos.yaml` file. +The `repos.yaml` contains `repo_config_file` key to specify the repository atlantis config file path. + +```yaml +# repos.yaml +repos: +- id: /.*/ + # for production-server + repo_config_file: atlantis-production.yaml + # for staging-server + # repo_config_file: atlantis-staging.yaml +``` + +Then, create `atlantis-production.yaml` and `atlantis-staging.yaml` files in the repository. +See the configuration examples in [atlantis.yaml](repo-level-atlantis-yaml.html). + +```yaml +# atlantis-production.yaml +version: 3 +projects: +- name: project + branch: /production/ + dir: infrastructure/production +--- +# atlantis-staging.yaml +version: 3 +projects: + - name: project + branch: /staging/ + dir: infrastructure/staging +``` + +Now, 2 webhook URLs can be setup for the repository, which send events to `production-server` and `staging-server` respectively. +Each servers handle different repository config files. + +:::tip Notes +* If `no projects` comments are annoying, set [--silence-no-projects](server-configuration.html#silence-no-projects). +* The command trigger executable name can be reconfigured from `atlantis` to something else by setting [Executable Name](server-configuration.html#executable-name). +* When using different atlantis server vcs users such as `@atlantis-staging`, the comment `@atlantis-staging plan` can be used instead `atlantis plan` to call `staging-server` only. +::: + ## Reference ### Top-Level Keys @@ -400,6 +452,7 @@ If you set a workflow with the key `default`, it will override this. |-------------------------------|----------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | id | string | none | yes | Value can be a regular expression when specified as /<regex>/ or an exact string match. Repo IDs are of the form `{vcs hostname}/{org}/{name}`, ex. `github.com/owner/repo`. Hostname is specified without scheme or port. For Bitbucket Server, {org} is the **name** of the project, not the key. | | branch | string | none | no | An regex matching pull requests by base branch (the branch the pull request is getting merged into). By default, all branches are matched | +| repo_config_file | string | none | no | Repo config file path in this repo. By default, use `atlantis.yaml` which is located on repository root. When multiple atlantis servers work with the same repo, please set different file names. | | workflow | string | none | no | A custom workflow. | | apply_requirements | []string | none | no | Requirements that must be satisfied before `atlantis apply` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Apply Requirements](apply-requirements.html) for more details. | | allowed_overrides | []string | none | no | A list of restricted keys that `atlantis.yaml` files can override. The only supported keys are `apply_requirements`, `workflow`, `delete_source_branch_on_merge` and `repo_locking` | diff --git a/server/controllers/events/events_controller_e2e_test.go b/server/controllers/events/events_controller_e2e_test.go index e00afc6dfa..7fc1abe2ed 100644 --- a/server/controllers/events/events_controller_e2e_test.go +++ b/server/controllers/events/events_controller_e2e_test.go @@ -80,6 +80,8 @@ func TestGitHubWorkflow(t *testing.T) { Description string // RepoDir is relative to testfixtures/test-repos. RepoDir string + // RepoConfigFile is path for atlantis.yaml + RepoConfigFile string // ModifiedFiles are the list of files that have been modified in this // pull request. ModifiedFiles []string @@ -218,6 +220,24 @@ func TestGitHubWorkflow(t *testing.T) { {"exp-output-merge.txt"}, }, }, + { + Description: "custom repo config file", + RepoDir: "repo-config-file", + RepoConfigFile: "infrastructure/custom-name-atlantis.yaml", + ModifiedFiles: []string{ + "infrastructure/staging/main.tf", + "infrastructure/production/main.tf", + }, + ExpAutoplan: true, + Comments: []string{ + "atlantis apply", + }, + ExpReplies: [][]string{ + {"exp-output-autoplan.txt"}, + {"exp-output-apply.txt"}, + {"exp-output-merge.txt"}, + }, + }, { Description: "modules staging only", RepoDir: "modules", @@ -393,7 +413,7 @@ func TestGitHubWorkflow(t *testing.T) { userConfig = server.UserConfig{} userConfig.DisableApply = c.DisableApply - ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir) + ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir, c.RepoConfigFile) // Set the repo to be cloned through the testing backdoor. repoDir, headSHA := initializeRepo(t, c.RepoDir) atlantisWorkspace.TestingOverrideHeadCloneURL = fmt.Sprintf("file://%s", repoDir) @@ -542,7 +562,7 @@ func TestSimlpleWorkflow_terraformLockFile(t *testing.T) { userConfig = server.UserConfig{} userConfig.DisableApply = true - ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir) + ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir, "") // Set the repo to be cloned through the testing backdoor. repoDir, headSHA := initializeRepo(t, c.RepoDir) @@ -785,7 +805,7 @@ func TestGitHubWorkflowWithPolicyCheck(t *testing.T) { userConfig.EnablePolicyChecksFlag = true userConfig.QuietPolicyChecks = c.ExpQuietPolicyChecks - ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir) + ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir, "") // Set the repo to be cloned through the testing backdoor. repoDir, headSHA := initializeRepo(t, c.RepoDir) @@ -793,7 +813,7 @@ func TestGitHubWorkflowWithPolicyCheck(t *testing.T) { // Setup test dependencies. w := httptest.NewRecorder() - When(vcsClient.PullIsMergeable(AnyRepo(), matchers.AnyModelsPullRequest(), "atlantis-test")).ThenReturn(true, nil) + When(vcsClient.PullIsMergeable(AnyRepo(), matchers.AnyModelsPullRequest(), EqString("atlantis-test"))).ThenReturn(true, nil) When(vcsClient.PullIsApproved(AnyRepo(), matchers.AnyModelsPullRequest())).ThenReturn(models.ApprovalStatus{ IsApproved: true, }, nil) @@ -862,7 +882,7 @@ func TestGitHubWorkflowWithPolicyCheck(t *testing.T) { } } -func setupE2E(t *testing.T, repoDir string) (events_controllers.VCSEventsController, *vcsmocks.MockClient, *mocks.MockGithubPullGetter, *events.FileWorkspace) { +func setupE2E(t *testing.T, repoDir, repoConfigFile string) (events_controllers.VCSEventsController, *vcsmocks.MockClient, *mocks.MockGithubPullGetter, *events.FileWorkspace) { allowForkPRs := false dataDir, binDir, cacheDir := mkSubDirs(t) @@ -917,9 +937,10 @@ func setupE2E(t *testing.T, repoDir string) (events_controllers.VCSEventsControl parser := &config.ParserValidator{} globalCfgArgs := valid.GlobalCfgArgs{ - AllowRepoCfg: true, - MergeableReq: false, - ApprovedReq: false, + RepoConfigFile: repoConfigFile, + AllowRepoCfg: true, + MergeableReq: false, + ApprovedReq: false, PreWorkflowHooks: []*valid.WorkflowHook{ { StepName: "global_hook", diff --git a/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-apply.txt b/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-apply.txt new file mode 100644 index 0000000000..50bd0ecd86 --- /dev/null +++ b/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-apply.txt @@ -0,0 +1,28 @@ +Ran Apply for 2 projects: + +1. dir: `infrastructure/production` workspace: `default` +1. dir: `infrastructure/staging` workspace: `default` + +### 1. dir: `infrastructure/production` workspace: `default` +```diff +null_resource.production[0]: Creating... +null_resource.production[0]: Creation complete after *s [id=*******************] + +Apply complete! Resources: 1 added, 0 changed, 0 destroyed. + + +``` + +--- +### 2. dir: `infrastructure/staging` workspace: `default` +```diff +null_resource.staging[0]: Creating... +null_resource.staging[0]: Creation complete after *s [id=*******************] + +Apply complete! Resources: 1 added, 0 changed, 0 destroyed. + + +``` + +--- + diff --git a/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-autoplan.txt b/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-autoplan.txt new file mode 100644 index 0000000000..064ce1766c --- /dev/null +++ b/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-autoplan.txt @@ -0,0 +1,69 @@ +Ran Plan for 2 projects: + +1. dir: `infrastructure/staging` workspace: `default` +1. dir: `infrastructure/production` workspace: `default` + +### 1. dir: `infrastructure/staging` workspace: `default` +
Show Output + +```diff + +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: ++ create + +Terraform will perform the following actions: + + # null_resource.staging[0] will be created ++ resource "null_resource" "staging" { + + id = (known after apply) + } + +Plan: 1 to add, 0 to change, 0 to destroy. + + +``` + +* :arrow_forward: To **apply** this plan, comment: + * `atlantis apply -d infrastructure/staging` +* :put_litter_in_its_place: To **delete** this plan click [here](lock-url) +* :repeat: To **plan** this project again, comment: + * `atlantis plan -d infrastructure/staging` +
+Plan: 1 to add, 0 to change, 0 to destroy. + +--- +### 2. dir: `infrastructure/production` workspace: `default` +
Show Output + +```diff + +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: ++ create + +Terraform will perform the following actions: + + # null_resource.production[0] will be created ++ resource "null_resource" "production" { + + id = (known after apply) + } + +Plan: 1 to add, 0 to change, 0 to destroy. + + +``` + +* :arrow_forward: To **apply** this plan, comment: + * `atlantis apply -d infrastructure/production` +* :put_litter_in_its_place: To **delete** this plan click [here](lock-url) +* :repeat: To **plan** this project again, comment: + * `atlantis plan -d infrastructure/production` +
+Plan: 1 to add, 0 to change, 0 to destroy. + +--- +* :fast_forward: To **apply** all unapplied plans from this pull request, comment: + * `atlantis apply` +* :put_litter_in_its_place: To delete all plans and locks for the PR, comment: + * `atlantis unlock` diff --git a/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-merge.txt b/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-merge.txt new file mode 100644 index 0000000000..2c4580963f --- /dev/null +++ b/server/controllers/events/testfixtures/test-repos/repo-config-file/exp-output-merge.txt @@ -0,0 +1,4 @@ +Locks and plans deleted for the projects and workspaces modified in this pull request: + +- dir: `infrastructure/production` workspace: `default` +- dir: `infrastructure/staging` workspace: `default` \ No newline at end of file diff --git a/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/custom-name-atlantis.yaml b/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/custom-name-atlantis.yaml new file mode 100644 index 0000000000..6d1626307e --- /dev/null +++ b/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/custom-name-atlantis.yaml @@ -0,0 +1,4 @@ +version: 3 +projects: +- dir: infrastructure/staging +- dir: infrastructure/production diff --git a/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/production/main.tf b/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/production/main.tf new file mode 100644 index 0000000000..ef2b2c2903 --- /dev/null +++ b/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/production/main.tf @@ -0,0 +1,3 @@ +resource "null_resource" "production" { + count = "1" +} diff --git a/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/staging/main.tf b/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/staging/main.tf new file mode 100644 index 0000000000..a7920c24c7 --- /dev/null +++ b/server/controllers/events/testfixtures/test-repos/repo-config-file/infrastructure/staging/main.tf @@ -0,0 +1,3 @@ +resource "null_resource" "staging" { + count = "1" +} diff --git a/server/core/config/parser_validator.go b/server/core/config/parser_validator.go index 4e23c1d4f8..96f707e6bf 100644 --- a/server/core/config/parser_validator.go +++ b/server/core/config/parser_validator.go @@ -15,9 +15,6 @@ import ( yaml "gopkg.in/yaml.v2" ) -// AtlantisYAMLFilename is the name of the config file for each repo. -const AtlantisYAMLFilename = "atlantis.yaml" - // ParserValidator parses and validates server-side repo config files and // repo-level atlantis.yaml files. type ParserValidator struct{} @@ -25,15 +22,15 @@ type ParserValidator struct{} // HasRepoCfg returns true if there is a repo config (atlantis.yaml) file // for the repo at absRepoDir. // Returns an error if for some reason it can't read that directory. -func (p *ParserValidator) HasRepoCfg(absRepoDir string) (bool, error) { +func (p *ParserValidator) HasRepoCfg(absRepoDir, repoConfigFile string) (bool, error) { // Checks for a config file with an invalid extension (atlantis.yml) 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", invalidExtensionFilename) } - _, err = os.Stat(p.repoCfgPath(absRepoDir, AtlantisYAMLFilename)) + _, err = os.Stat(p.repoCfgPath(absRepoDir, repoConfigFile)) if os.IsNotExist(err) { return false, nil } @@ -44,12 +41,13 @@ 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, branch string) (valid.RepoCfg, error) { - configFile := p.repoCfgPath(absRepoDir, AtlantisYAMLFilename) + repoConfigFile := globalCfg.RepoConfigFile(repoID) + configFile := p.repoCfgPath(absRepoDir, repoConfigFile) 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", repoConfigFile) } // 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 08e07987fc..c3eb9b28e9 100644 --- a/server/core/config/parser_validator_test.go +++ b/server/core/config/parser_validator_test.go @@ -25,7 +25,7 @@ var globalCfg = valid.NewGlobalCfgFromArgs(globalCfgArgs) func TestHasRepoCfg_DirDoesNotExist(t *testing.T) { r := config.ParserValidator{} - exists, err := r.HasRepoCfg("/not/exist") + exists, err := r.HasRepoCfg("/not/exist", "unused.yaml") Ok(t, err) Equals(t, false, exists) } @@ -33,19 +33,20 @@ func TestHasRepoCfg_DirDoesNotExist(t *testing.T) { func TestHasRepoCfg_FileDoesNotExist(t *testing.T) { tmpDir := t.TempDir() r := config.ParserValidator{} - exists, err := r.HasRepoCfg(tmpDir) + exists, err := r.HasRepoCfg(tmpDir, "not-exist.yaml") Ok(t, err) Equals(t, false, exists) } func TestHasRepoCfg_InvalidFileExtension(t *testing.T) { tmpDir := t.TempDir() - _, err := os.Create(filepath.Join(tmpDir, "atlantis.yml")) + repoConfigFile := "atlantis.yml" + _, err := os.Create(filepath.Join(tmpDir, repoConfigFile)) Ok(t, err) r := config.ParserValidator{} - _, err = r.HasRepoCfg(tmpDir) - ErrContains(t, "found \"atlantis.yml\" as config file; rename using the .yaml extension - \"atlantis.yaml\"", err) + _, err = r.HasRepoCfg(tmpDir, repoConfigFile) + ErrContains(t, "found \"atlantis.yml\" as config file; rename using the .yaml extension", err) } func TestParseRepoCfg_DirDoesNotExist(t *testing.T) { @@ -1212,6 +1213,18 @@ func TestParseGlobalCfg(t *testing.T) { branch: /?/`, expErr: "repos: (0: (branch: parsing: /?/: error parsing regexp: missing argument to repetition operator: `?`.).).", }, + "invalid repo_config_file which starts with a slash": { + input: `repos: +- id: /.*/ + repo_config_file: /etc/passwd`, + expErr: "repos: (0: (repo_config_file: must not starts with a slash '/'.).).", + }, + "invalid repo_config_file which contains parent directory path": { + input: `repos: +- id: /.*/ + repo_config_file: ../../etc/passwd`, + expErr: "repos: (0: (repo_config_file: must not contains parent directory path like '../'.).).", + }, "workflow doesn't exist": { input: `repos: - id: /.*/ @@ -1300,7 +1313,7 @@ workflows: input: ` repos: - id: github.com/owner/repo - + repo_config_file: "path/to/atlantis.yaml" apply_requirements: [approved, mergeable] pre_workflow_hooks: - run: custom workflow command @@ -1345,6 +1358,7 @@ policies: defaultCfg.Repos[0], { ID: "github.com/owner/repo", + RepoConfigFile: "path/to/atlantis.yaml", ApplyRequirements: []string{"approved", "mergeable"}, PreWorkflowHooks: preWorkflowHooks, Workflow: &customWorkflow1, diff --git a/server/core/config/raw/global_cfg.go b/server/core/config/raw/global_cfg.go index b998342133..a6c4b90f60 100644 --- a/server/core/config/raw/global_cfg.go +++ b/server/core/config/raw/global_cfg.go @@ -22,6 +22,7 @@ type GlobalCfg struct { type Repo struct { ID string `yaml:"id" json:"id"` Branch string `yaml:"branch" json:"branch"` + RepoConfigFile string `yaml:"repo_config_file" json:"repo_config_file"` ApplyRequirements []string `yaml:"apply_requirements" json:"apply_requirements"` PreWorkflowHooks []WorkflowHook `yaml:"pre_workflow_hooks" json:"pre_workflow_hooks"` Workflow *string `yaml:"workflow,omitempty" json:"workflow,omitempty"` @@ -171,6 +172,20 @@ func (r Repo) Validate() error { return errors.Wrapf(err, "parsing: %s", branch) } + repoConfigFileValid := func(value interface{}) error { + repoConfigFile := value.(string) + if repoConfigFile == "" { + return nil + } + if strings.HasPrefix(repoConfigFile, "/") { + return errors.New("must not starts with a slash '/'") + } + if strings.Contains(repoConfigFile, "../") || strings.Contains(repoConfigFile, "..\\") { + return errors.New("must not contains parent directory path like '../'") + } + return nil + } + overridesValid := func(value interface{}) error { overrides := value.([]string) for _, o := range overrides { @@ -195,6 +210,7 @@ func (r Repo) Validate() error { return validation.ValidateStruct(&r, validation.Field(&r.ID, validation.Required, validation.By(idValid)), validation.Field(&r.Branch, validation.By(branchValid)), + validation.Field(&r.RepoConfigFile, validation.By(repoConfigFileValid)), validation.Field(&r.AllowedOverrides, validation.By(overridesValid)), validation.Field(&r.ApplyRequirements, validation.By(validApplyReq)), validation.Field(&r.Workflow, validation.By(workflowExists)), @@ -261,6 +277,7 @@ OUTER: ID: id, IDRegex: idRegex, BranchRegex: branchRegex, + RepoConfigFile: r.RepoConfigFile, ApplyRequirements: mergedApplyReqs, PreWorkflowHooks: preWorkflowHooks, Workflow: workflow, diff --git a/server/core/config/valid/global_cfg.go b/server/core/config/valid/global_cfg.go index ecf4ec5cf8..ebdd7546dc 100644 --- a/server/core/config/valid/global_cfg.go +++ b/server/core/config/valid/global_cfg.go @@ -24,6 +24,9 @@ const DefaultWorkflowName = "default" const DeleteSourceBranchOnMergeKey = "delete_source_branch_on_merge" const RepoLockingKey = "repo_locking" +// DefaultAtlantisFile is the default name of the config file for each repo. +const DefaultAtlantisFile = "atlantis.yaml" + // NonOverrideableApplyReqs will get applied across all "repos" in the server side config. // If repo config is allowed overrides, they can override this. // TODO: Make this more customizable, not everyone wants this rigid workflow @@ -62,6 +65,7 @@ type Repo struct { // If ID is set then this will be nil. IDRegex *regexp.Regexp BranchRegex *regexp.Regexp + RepoConfigFile string ApplyRequirements []string PreWorkflowHooks []*WorkflowHook Workflow *Workflow @@ -157,6 +161,7 @@ func NewGlobalCfg(allowRepoCfg bool, mergeableReq bool, approvedReq bool) Global } type GlobalCfgArgs struct { + RepoConfigFile string AllowRepoCfg bool MergeableReq bool ApprovedReq bool @@ -205,6 +210,7 @@ func NewGlobalCfgFromArgs(args GlobalCfgArgs) GlobalCfg { { IDRegex: regexp.MustCompile(".*"), BranchRegex: regexp.MustCompile(".*"), + RepoConfigFile: args.RepoConfigFile, ApplyRequirements: applyReqs, PreWorkflowHooks: args.PreWorkflowHooks, Workflow: &defaultWorkflow, @@ -521,3 +527,13 @@ func (g GlobalCfg) MatchingRepo(repoID string) *Repo { } return nil } + +// RepoConfigFile returns a repository specific file path +// If not defined, return atlantis.yaml as default +func (g GlobalCfg) RepoConfigFile(repoID string) string { + repo := g.MatchingRepo(repoID) + if repo != nil && repo.RepoConfigFile != "" { + return repo.RepoConfigFile + } + return DefaultAtlantisFile +} diff --git a/server/events/command/project_context.go b/server/events/command/project_context.go index 4a772b7158..139731d96d 100644 --- a/server/events/command/project_context.go +++ b/server/events/command/project_context.go @@ -90,6 +90,8 @@ type ProjectContext struct { DeleteSourceBranchOnMerge bool // RepoLocking will get a lock when plan RepoLocking bool + // RepoConfigFile + RepoConfigFile string // UUID for atlantis logs JobID string // The index of order group. Before planning/applying it will use to sort projects. Default is 0. diff --git a/server/events/comment_parser.go b/server/events/comment_parser.go index 0c8928a35b..3dcdcc6f9f 100644 --- a/server/events/comment_parser.go +++ b/server/events/comment_parser.go @@ -24,7 +24,6 @@ import ( "text/template" "github.com/flynn-archive/go-shlex" - "github.com/runatlantis/atlantis/server/core/config" "github.com/runatlantis/atlantis/server/events/command" "github.com/runatlantis/atlantis/server/events/models" "github.com/spf13/pflag" @@ -191,7 +190,7 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com 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, "", "Which project to run plan for. Refers to the name of the project configured in a repo config file. Cannot be used at same time as workspace or dir flags.") flagSet.BoolVarP(&verbose, verboseFlagLong, verboseFlagShort, false, "Append Atlantis log to comment.") case command.Apply.String(): name = command.Apply @@ -199,7 +198,7 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com 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, "", "Apply the plan for this project. Refers to the name of the project configured in a repo config file. Cannot be used at same time as workspace or dir flags.") 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(): @@ -216,7 +215,7 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com 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, "", "Print the version for this project. Refers to the name of the project configured in a repo config file.") 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)} diff --git a/server/events/comment_parser_test.go b/server/events/comment_parser_test.go index aec505a1da..22d8ea3e38 100644 --- a/server/events/comment_parser_test.go +++ b/server/events/comment_parser_test.go @@ -854,8 +854,8 @@ var PlanUsage = `Usage of plan: -d, --dir string Which directory to run plan in relative to root of repo, ex. 'child/dir'. -p, --project string Which project to run plan for. Refers to the name of the - project configured in atlantis.yaml. Cannot be used at - same time as workspace or dir flags. + project configured in a repo config file. Cannot be used + at same time as workspace or dir flags. --verbose Append Atlantis log to comment. -w, --workspace string Switch to this Terraform workspace before planning. ` @@ -865,8 +865,8 @@ var ApplyUsage = `Usage of apply: -d, --dir string Apply the plan for this directory, relative to root of repo, ex. 'child/dir'. -p, --project string Apply the plan for this project. Refers to the name of - the project configured in atlantis.yaml. Cannot be - used at same time as workspace or dir flags. + the project configured in a repo config file. Cannot + be used at same time as workspace or dir flags. --verbose Append Atlantis log to comment. -w, --workspace string Apply the plan for this Terraform workspace. ` diff --git a/server/events/project_command_builder.go b/server/events/project_command_builder.go index 7eabadcb7c..6d58a79b96 100644 --- a/server/events/project_command_builder.go +++ b/server/events/project_command_builder.go @@ -241,17 +241,18 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context ctx.Log.Debug("%d files were modified in this pull request", len(modifiedFiles)) if p.SkipCloneNoChanges && p.VCSClient.SupportsSingleFileDownload(ctx.Pull.BaseRepo) { - hasRepoCfg, repoCfgData, err := p.VCSClient.DownloadRepoConfigFile(ctx.Pull) + repoCfgFile := p.GlobalCfg.RepoConfigFile(ctx.Pull.BaseRepo.ID()) + hasRepoCfg, repoCfgData, err := p.VCSClient.GetFileContent(ctx.Pull, repoCfgFile) if err != nil { - return nil, errors.Wrapf(err, "downloading %s", config.AtlantisYAMLFilename) + return nil, errors.Wrapf(err, "downloading %s", repoCfgFile) } if hasRepoCfg { repoCfg, err := p.ParserValidator.ParseRepoCfgData(repoCfgData, p.GlobalCfg, ctx.Pull.BaseRepo.ID(), ctx.Pull.BaseBranch) if err != nil { - return nil, errors.Wrapf(err, "parsing %s", config.AtlantisYAMLFilename) + return nil, errors.Wrapf(err, "parsing %s", repoCfgFile) } - ctx.Log.Info("successfully parsed remote %s file", config.AtlantisYAMLFilename) + ctx.Log.Info("successfully parsed remote %s file", repoCfgFile) if len(repoCfg.Projects) > 0 { matchingProjects, err := p.ProjectFinder.DetermineProjectsViaConfig(ctx.Log, modifiedFiles, repoCfg, "") if err != nil { @@ -263,7 +264,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context return []command.ProjectContext{}, nil } } else { - ctx.Log.Info("No projects are defined in %s. Will resume automatic detection", config.AtlantisYAMLFilename) + ctx.Log.Info("No projects are defined in %s. Will resume automatic detection", repoCfgFile) } // NOTE: We discard this work here and end up doing it again after // cloning to ensure all the return values are set properly with @@ -288,9 +289,10 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context } // Parse config file if it exists. - hasRepoCfg, err := p.ParserValidator.HasRepoCfg(repoDir) + repoCfgFile := p.GlobalCfg.RepoConfigFile(ctx.Pull.BaseRepo.ID()) + hasRepoCfg, err := p.ParserValidator.HasRepoCfg(repoDir, repoCfgFile) 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", repoCfgFile, repoDir) } var projCtxs []command.ProjectContext @@ -301,9 +303,9 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context // should be planed. repoCfg, err = p.ParserValidator.ParseRepoCfg(repoDir, p.GlobalCfg, ctx.Pull.BaseRepo.ID(), ctx.Pull.BaseBranch) if err != nil { - return nil, errors.Wrapf(err, "parsing %s", config.AtlantisYAMLFilename) + return nil, errors.Wrapf(err, "parsing %s", repoCfgFile) } - ctx.Log.Info("successfully parsed %s file", config.AtlantisYAMLFilename) + ctx.Log.Info("successfully parsed %s file", repoCfgFile) } if len(repoCfg.Projects) > 0 { @@ -334,9 +336,9 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *command.Context // If there is no config file or it specified no projects, then we'll plan each project that // our algorithm determines was modified. if hasRepoCfg { - ctx.Log.Info("No projects are defined in %s. Will resume automatic detection", config.AtlantisYAMLFilename) + ctx.Log.Info("No projects are defined in %s. Will resume automatic detection", repoCfgFile) } else { - ctx.Log.Info("found no %s file", config.AtlantisYAMLFilename) + ctx.Log.Info("found no %s file", repoCfgFile) } // build a module index for projects that are explicitly included moduleInfo, err := FindModuleProjects(repoDir, p.AutoDetectModuleFiles) @@ -476,14 +478,15 @@ func (p *DefaultProjectCommandBuilder) buildProjectPlanCommand(ctx *command.Cont // getCfg returns the atlantis.yaml config (if it exists) for this project. If // there is no config, then projectCfg and repoCfg will be nil. 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) + repoCfgFile := p.GlobalCfg.RepoConfigFile(ctx.Pull.BaseRepo.ID()) + hasRepoCfg, err := p.ParserValidator.HasRepoCfg(repoDir, repoCfgFile) 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", repoCfgFile, repoDir) return } - if !hasConfigFile { + if !hasRepoCfg { 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", repoCfgFile) return } return @@ -507,7 +510,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, repoCfgFile) return } return @@ -518,7 +521,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", repoCfgFile, dir, workspace) return } projectsCfg = projCfgs diff --git a/server/events/project_command_builder_test.go b/server/events/project_command_builder_test.go index 2e9438fabd..9c3f8b8349 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, valid.DefaultAtlantisFile), []byte(c.AtlantisYAML), 0600) Ok(t, err) } @@ -404,7 +404,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, valid.DefaultAtlantisFile), []byte(c.AtlantisYAML), 0600) Ok(t, err) } @@ -577,7 +577,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, valid.DefaultAtlantisFile), []byte(c.AtlantisYAML), 0600) Ok(t, err) } @@ -747,7 +747,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, valid.DefaultAtlantisFile), []byte(c.AtlantisYAML), 0600) Ok(t, err) } @@ -914,7 +914,7 @@ projects: - dir: . workspace: staging ` - err := os.WriteFile(filepath.Join(repoDir, config.AtlantisYAMLFilename), []byte(yamlCfg), 0600) + err := os.WriteFile(filepath.Join(repoDir, valid.DefaultAtlantisFile), []byte(yamlCfg), 0600) Ok(t, err) When(workingDir.Clone( @@ -1141,7 +1141,7 @@ projects: "project1": map[string]interface{}{ "main.tf": fmt.Sprintf(baseVersionConfig, exactSymbols[0]), }, - config.AtlantisYAMLFilename: atlantisYamlContent, + valid.DefaultAtlantisFile: atlantisYamlContent, }, ModifiedFiles: []string{"project1/main.tf", "project2/main.tf"}, Exp: map[string][]int{ @@ -1154,7 +1154,7 @@ projects: "project1": map[string]interface{}{ "main.tf": nil, }, - config.AtlantisYAMLFilename: atlantisYamlContent, + valid.DefaultAtlantisFile: atlantisYamlContent, }, ModifiedFiles: []string{"project1/main.tf"}, Exp: map[string][]int{ @@ -1298,7 +1298,7 @@ parallel_plan: true`, vcsClient := vcsmocks.NewMockClient() When(vcsClient.GetModifiedFiles(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(c.ModifiedFiles, nil) When(vcsClient.SupportsSingleFileDownload(matchers.AnyModelsRepo())).ThenReturn(true) - When(vcsClient.DownloadRepoConfigFile(matchers.AnyModelsPullRequest())).ThenReturn(true, []byte(c.AtlantisYAML), nil) + When(vcsClient.GetFileContent(matchers.AnyModelsPullRequest(), AnyString())).ThenReturn(true, []byte(c.AtlantisYAML), nil) workingDir := mocks.NewMockWorkingDir() logger := logging.NewNoopLogger(t) diff --git a/server/events/vcs/azuredevops_client.go b/server/events/vcs/azuredevops_client.go index dbb50b8f13..e9622e5aaa 100644 --- a/server/events/vcs/azuredevops_client.go +++ b/server/events/vcs/azuredevops_client.go @@ -394,7 +394,7 @@ func (g *AzureDevopsClient) SupportsSingleFileDownload(repo models.Repo) bool { return false } -func (g *AzureDevopsClient) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { +func (g *AzureDevopsClient) GetFileContent(pull models.PullRequest, fileName string) (bool, []byte, error) { return false, []byte{}, fmt.Errorf("Not Implemented") } diff --git a/server/events/vcs/bitbucketcloud/client.go b/server/events/vcs/bitbucketcloud/client.go index c526fdb018..3a603b7a96 100644 --- a/server/events/vcs/bitbucketcloud/client.go +++ b/server/events/vcs/bitbucketcloud/client.go @@ -265,10 +265,10 @@ func (b *Client) SupportsSingleFileDownload(models.Repo) bool { return false } -// DownloadRepoConfigFile return `atlantis.yaml` content from VCS (which support fetch a single file from repository) -// The first return value indicate that repo contain atlantis.yaml or not -// if BaseRepo had one repo config file, its content will placed on the second return value -func (b *Client) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { +// GetFileContent a repository file content from VCS (which support fetch a single file from repository) +// The first return value indicates whether the repo contains a file or not +// if BaseRepo had a file, its content will placed on the second return value +func (b *Client) GetFileContent(pull models.PullRequest, fileName string) (bool, []byte, error) { return false, []byte{}, fmt.Errorf("Not Implemented") } diff --git a/server/events/vcs/bitbucketserver/client.go b/server/events/vcs/bitbucketserver/client.go index 495d190668..82a58e70f2 100644 --- a/server/events/vcs/bitbucketserver/client.go +++ b/server/events/vcs/bitbucketserver/client.go @@ -351,10 +351,10 @@ func (b *Client) SupportsSingleFileDownload(repo models.Repo) bool { return false } -// DownloadRepoConfigFile return `atlantis.yaml` content from VCS (which support fetch a single file from repository) -// The first return value indicate that repo contain atlantis.yaml or not -// if BaseRepo had one repo config file, its content will placed on the second return value -func (b *Client) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { +// GetFileContent a repository file content from VCS (which support fetch a single file from repository) +// The first return value indicates whether the repo contains a file or not +// if BaseRepo had a file, its content will placed on the second return value +func (b *Client) GetFileContent(pull models.PullRequest, fileName string) (bool, []byte, error) { return false, []byte{}, fmt.Errorf("not implemented") } diff --git a/server/events/vcs/client.go b/server/events/vcs/client.go index 1d83726472..c335c78dac 100644 --- a/server/events/vcs/client.go +++ b/server/events/vcs/client.go @@ -41,10 +41,10 @@ type Client interface { MarkdownPullLink(pull models.PullRequest) (string, error) GetTeamNamesForUser(repo models.Repo, user models.User) ([]string, error) - // DownloadRepoConfigFile return `atlantis.yaml` content from VCS (which support fetch a single file from repository) - // The first return value indicate that repo contain atlantis.yaml or not - // if BaseRepo had one repo config file, its content will placed on the second return value - DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) + // GetFileContent a repository file content from VCS (which support fetch a single file from repository) + // The first return value indicates whether the repo contains a file or not + // if BaseRepo had a file, its content will placed on the second return value + GetFileContent(pull models.PullRequest, fileName string) (bool, []byte, error) SupportsSingleFileDownload(repo models.Repo) bool GetCloneURL(VCSHostType models.VCSHostType, repo string) (string, error) } diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index 24ba5705db..dc6e8eca58 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -23,7 +23,6 @@ import ( "github.com/google/go-github/v31/github" "github.com/pkg/errors" - "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/common" @@ -680,12 +679,12 @@ func (g *GithubClient) ExchangeCode(code string) (*GithubAppTemporarySecrets, er return data, err } -// DownloadRepoConfigFile return `atlantis.yaml` content from VCS (which support fetch a single file from repository) -// The first return value indicate that repo contain atlantis.yaml or not -// 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) { +// GetFileContent a repository file content from VCS (which support fetch a single file from repository) +// The first return value indicates whether the repo contains a file or not +// if BaseRepo had a file, its content will placed on the second return value +func (g *GithubClient) GetFileContent(pull models.PullRequest, fileName string) (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, fileName, &opt) if resp.StatusCode == http.StatusNotFound { return false, []byte{}, nil diff --git a/server/events/vcs/gitlab_client.go b/server/events/vcs/gitlab_client.go index a29d09277d..f690012612 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" @@ -382,13 +380,13 @@ func (g *GitlabClient) GetTeamNamesForUser(repo models.Repo, user models.User) ( return nil, nil } -// DownloadRepoConfigFile return `atlantis.yaml` content from VCS (which support fetch a single file from repository) -// The first return value indicate that repo contain atlantis.yaml or not -// if BaseRepo had one repo config file, its content will placed on the second return value -func (g *GitlabClient) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { +// GetFileContent a repository file content from VCS (which support fetch a single file from repository) +// The first return value indicates whether the repo contains a file or not +// if BaseRepo had a file, its content will placed on the second return value +func (g *GitlabClient) GetFileContent(pull models.PullRequest, fileName string) (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, fileName, &opt) if resp.StatusCode == http.StatusNotFound { return false, []byte{}, nil } diff --git a/server/events/vcs/mocks/matchers/models_user.go b/server/events/vcs/mocks/matchers/models_user.go new file mode 100644 index 0000000000..0aa92b5d88 --- /dev/null +++ b/server/events/vcs/mocks/matchers/models_user.go @@ -0,0 +1,33 @@ +// Code generated by pegomock. DO NOT EDIT. +package matchers + +import ( + "github.com/petergtz/pegomock" + "reflect" + + models "github.com/runatlantis/atlantis/server/events/models" +) + +func AnyModelsUser() models.User { + pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(models.User))(nil)).Elem())) + var nullValue models.User + return nullValue +} + +func EqModelsUser(value models.User) models.User { + pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value}) + var nullValue models.User + return nullValue +} + +func NotEqModelsUser(value models.User) models.User { + pegomock.RegisterMatcher(&pegomock.NotEqMatcher{Value: value}) + var nullValue models.User + return nullValue +} + +func ModelsUserThat(matcher pegomock.ArgumentMatcher) models.User { + pegomock.RegisterMatcher(matcher) + var nullValue models.User + return nullValue +} diff --git a/server/events/vcs/mocks/matchers/models_vcshosttype.go b/server/events/vcs/mocks/matchers/models_vcshosttype.go new file mode 100644 index 0000000000..a54447f7de --- /dev/null +++ b/server/events/vcs/mocks/matchers/models_vcshosttype.go @@ -0,0 +1,33 @@ +// Code generated by pegomock. DO NOT EDIT. +package matchers + +import ( + "github.com/petergtz/pegomock" + "reflect" + + models "github.com/runatlantis/atlantis/server/events/models" +) + +func AnyModelsVCSHostType() models.VCSHostType { + pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(models.VCSHostType))(nil)).Elem())) + var nullValue models.VCSHostType + return nullValue +} + +func EqModelsVCSHostType(value models.VCSHostType) models.VCSHostType { + pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value}) + var nullValue models.VCSHostType + return nullValue +} + +func NotEqModelsVCSHostType(value models.VCSHostType) models.VCSHostType { + pegomock.RegisterMatcher(&pegomock.NotEqMatcher{Value: value}) + var nullValue models.VCSHostType + return nullValue +} + +func ModelsVCSHostTypeThat(matcher pegomock.ArgumentMatcher) models.VCSHostType { + pegomock.RegisterMatcher(matcher) + var nullValue models.VCSHostType + return nullValue +} diff --git a/server/events/vcs/mocks/mock_client.go b/server/events/vcs/mocks/mock_client.go index 6d581214a4..21cc1c7622 100644 --- a/server/events/vcs/mocks/mock_client.go +++ b/server/events/vcs/mocks/mock_client.go @@ -4,11 +4,10 @@ package mocks import ( - "reflect" - "time" - pegomock "github.com/petergtz/pegomock" models "github.com/runatlantis/atlantis/server/events/models" + "reflect" + "time" ) type MockClient struct { @@ -41,11 +40,30 @@ func NewMockClient(options ...pegomock.Option) *MockClient { func (mock *MockClient) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh } func (mock *MockClient) FailHandler() pegomock.FailHandler { return mock.fail } -func (mock *MockClient) CreateComment(_param0 models.Repo, _param1 int, _param2 string, _param3 string) error { +func (mock *MockClient) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0, _param1, _param2, _param3} + params := []pegomock.Param{repo, pull} + result := pegomock.GetGenericMockFrom(mock).Invoke("GetModifiedFiles", params, []reflect.Type{reflect.TypeOf((*[]string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 []string + var ret1 error + if len(result) != 0 { + if result[0] != nil { + ret0 = result[0].([]string) + } + if result[1] != nil { + ret1 = result[1].(error) + } + } + return ret0, ret1 +} + +func (mock *MockClient) CreateComment(repo models.Repo, pullNum int, comment string, command string) error { + if mock == nil { + panic("mock must not be nil. Use myMock := NewMockClient().") + } + params := []pegomock.Param{repo, pullNum, comment, command} result := pegomock.GetGenericMockFrom(mock).Invoke("CreateComment", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) var ret0 error if len(result) != 0 { @@ -56,40 +74,32 @@ func (mock *MockClient) CreateComment(_param0 models.Repo, _param1 int, _param2 return ret0 } -func (mock *MockClient) DownloadRepoConfigFile(_param0 models.PullRequest) (bool, []byte, error) { +func (mock *MockClient) HidePrevCommandComments(repo models.Repo, pullNum int, command string) error { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0} - result := pegomock.GetGenericMockFrom(mock).Invoke("DownloadRepoConfigFile", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*[]byte)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) - var ret0 bool - var ret1 []byte - var ret2 error + params := []pegomock.Param{repo, pullNum, command} + result := pegomock.GetGenericMockFrom(mock).Invoke("HidePrevCommandComments", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 error if len(result) != 0 { if result[0] != nil { - ret0 = result[0].(bool) - } - if result[1] != nil { - ret1 = result[1].([]byte) - } - if result[2] != nil { - ret2 = result[2].(error) + ret0 = result[0].(error) } } - return ret0, ret1, ret2 + return ret0 } -func (mock *MockClient) GetModifiedFiles(_param0 models.Repo, _param1 models.PullRequest) ([]string, error) { +func (mock *MockClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (models.ApprovalStatus, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0, _param1} - result := pegomock.GetGenericMockFrom(mock).Invoke("GetModifiedFiles", params, []reflect.Type{reflect.TypeOf((*[]string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) - var ret0 []string + params := []pegomock.Param{repo, pull} + result := pegomock.GetGenericMockFrom(mock).Invoke("PullIsApproved", params, []reflect.Type{reflect.TypeOf((*models.ApprovalStatus)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 models.ApprovalStatus var ret1 error if len(result) != 0 { if result[0] != nil { - ret0 = result[0].([]string) + ret0 = result[0].(models.ApprovalStatus) } if result[1] != nil { ret1 = result[1].(error) @@ -98,45 +108,45 @@ func (mock *MockClient) GetModifiedFiles(_param0 models.Repo, _param1 models.Pul return ret0, ret1 } -func (mock *MockClient) HidePrevCommandComments(_param0 models.Repo, _param1 int, _param2 string) error { +func (mock *MockClient) PullIsMergeable(repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0, _param1, _param2} - result := pegomock.GetGenericMockFrom(mock).Invoke("HidePrevCommandComments", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) - var ret0 error + params := []pegomock.Param{repo, pull, vcsstatusname} + result := pegomock.GetGenericMockFrom(mock).Invoke("PullIsMergeable", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 bool + var ret1 error if len(result) != 0 { if result[0] != nil { - ret0 = result[0].(error) + ret0 = result[0].(bool) + } + if result[1] != nil { + ret1 = result[1].(error) } } - return ret0 + return ret0, ret1 } -func (mock *MockClient) MarkdownPullLink(_param0 models.PullRequest) (string, error) { +func (mock *MockClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state models.CommitStatus, src string, description string, url string) error { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0} - result := pegomock.GetGenericMockFrom(mock).Invoke("MarkdownPullLink", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) - var ret0 string - var ret1 error + params := []pegomock.Param{repo, pull, state, src, description, url} + result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateStatus", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 error if len(result) != 0 { if result[0] != nil { - ret0 = result[0].(string) - } - if result[1] != nil { - ret1 = result[1].(error) + ret0 = result[0].(error) } } - return ret0, ret1 + return ret0 } -func (mock *MockClient) MergePull(_param0 models.PullRequest, _param1 models.PullRequestOptions) error { +func (mock *MockClient) MergePull(pull models.PullRequest, pullOptions models.PullRequestOptions) error { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0, _param1} + params := []pegomock.Param{pull, pullOptions} result := pegomock.GetGenericMockFrom(mock).Invoke("MergePull", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) var ret0 error if len(result) != 0 { @@ -147,17 +157,17 @@ func (mock *MockClient) MergePull(_param0 models.PullRequest, _param1 models.Pul return ret0 } -func (mock *MockClient) PullIsApproved(_param0 models.Repo, _param1 models.PullRequest) (models.ApprovalStatus, error) { +func (mock *MockClient) MarkdownPullLink(pull models.PullRequest) (string, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0, _param1} - result := pegomock.GetGenericMockFrom(mock).Invoke("PullIsApproved", params, []reflect.Type{reflect.TypeOf((*models.ApprovalStatus)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) - var ret0 models.ApprovalStatus + params := []pegomock.Param{pull} + result := pegomock.GetGenericMockFrom(mock).Invoke("MarkdownPullLink", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + var ret0 string var ret1 error if len(result) != 0 { if result[0] != nil { - ret0 = result[0].(models.ApprovalStatus) + ret0 = result[0].(string) } if result[1] != nil { ret1 = result[1].(error) @@ -185,30 +195,34 @@ func (mock *MockClient) GetTeamNamesForUser(repo models.Repo, user models.User) return ret0, ret1 } -func (mock *MockClient) PullIsMergeable(_param0 models.Repo, _param1 models.PullRequest, vcsstatusname string) (bool, error) { +func (mock *MockClient) GetFileContent(pull models.PullRequest, fileName string) (bool, []byte, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0, _param1} - result := pegomock.GetGenericMockFrom(mock).Invoke("PullIsMergeable", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) + params := []pegomock.Param{pull, fileName} + result := pegomock.GetGenericMockFrom(mock).Invoke("GetFileContent", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*[]byte)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) var ret0 bool - var ret1 error + var ret1 []byte + var ret2 error if len(result) != 0 { if result[0] != nil { ret0 = result[0].(bool) } if result[1] != nil { - ret1 = result[1].(error) + ret1 = result[1].([]byte) + } + if result[2] != nil { + ret2 = result[2].(error) } } - return ret0, ret1 + return ret0, ret1, ret2 } -func (mock *MockClient) SupportsSingleFileDownload(_param0 models.Repo) bool { +func (mock *MockClient) SupportsSingleFileDownload(repo models.Repo) bool { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0} + params := []pegomock.Param{repo} result := pegomock.GetGenericMockFrom(mock).Invoke("SupportsSingleFileDownload", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem()}) var ret0 bool if len(result) != 0 { @@ -219,11 +233,11 @@ func (mock *MockClient) SupportsSingleFileDownload(_param0 models.Repo) bool { return ret0 } -func (mock *MockClient) GetCloneURL(_param0 models.VCSHostType, _param1 string) (string, error) { +func (mock *MockClient) GetCloneURL(VCSHostType models.VCSHostType, repo string) (string, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{_param0, _param1} + params := []pegomock.Param{VCSHostType, repo} result := pegomock.GetGenericMockFrom(mock).Invoke("GetCloneURL", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) var ret0 string var ret1 error @@ -238,21 +252,6 @@ func (mock *MockClient) GetCloneURL(_param0 models.VCSHostType, _param1 string) return ret0, ret1 } -func (mock *MockClient) UpdateStatus(_param0 models.Repo, _param1 models.PullRequest, _param2 models.CommitStatus, _param3 string, _param4 string, _param5 string) error { - if mock == nil { - panic("mock must not be nil. Use myMock := NewMockClient().") - } - params := []pegomock.Param{_param0, _param1, _param2, _param3, _param4, _param5} - result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateStatus", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) - var ret0 error - if len(result) != 0 { - if result[0] != nil { - ret0 = result[0].(error) - } - } - return ret0 -} - func (mock *MockClient) VerifyWasCalledOnce() *VerifierMockClient { return &VerifierMockClient{ mock: mock, @@ -290,8 +289,39 @@ type VerifierMockClient struct { timeout time.Duration } -func (verifier *VerifierMockClient) CreateComment(_param0 models.Repo, _param1 int, _param2 string, _param3 string) *MockClient_CreateComment_OngoingVerification { - params := []pegomock.Param{_param0, _param1, _param2, _param3} +func (verifier *VerifierMockClient) GetModifiedFiles(repo models.Repo, pull models.PullRequest) *MockClient_GetModifiedFiles_OngoingVerification { + params := []pegomock.Param{repo, pull} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetModifiedFiles", params, verifier.timeout) + return &MockClient_GetModifiedFiles_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type MockClient_GetModifiedFiles_OngoingVerification struct { + mock *MockClient + methodInvocations []pegomock.MethodInvocation +} + +func (c *MockClient_GetModifiedFiles_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest) { + repo, pull := c.GetAllCapturedArguments() + return repo[len(repo)-1], pull[len(pull)-1] +} + +func (c *MockClient_GetModifiedFiles_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest) { + params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) + if len(params) > 0 { + _param0 = make([]models.Repo, len(c.methodInvocations)) + for u, param := range params[0] { + _param0[u] = param.(models.Repo) + } + _param1 = make([]models.PullRequest, len(c.methodInvocations)) + for u, param := range params[1] { + _param1[u] = param.(models.PullRequest) + } + } + return +} + +func (verifier *VerifierMockClient) CreateComment(repo models.Repo, pullNum int, comment string, command string) *MockClient_CreateComment_OngoingVerification { + params := []pegomock.Param{repo, pullNum, comment, command} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "CreateComment", params, verifier.timeout) return &MockClient_CreateComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } @@ -302,8 +332,8 @@ type MockClient_CreateComment_OngoingVerification struct { } func (c *MockClient_CreateComment_OngoingVerification) GetCapturedArguments() (models.Repo, int, string, string) { - _param0, _param1, _param2, _param3 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1], _param1[len(_param1)-1], _param2[len(_param2)-1], _param3[len(_param3)-1] + repo, pullNum, comment, command := c.GetAllCapturedArguments() + return repo[len(repo)-1], pullNum[len(pullNum)-1], comment[len(comment)-1], command[len(command)-1] } func (c *MockClient_CreateComment_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []int, _param2 []string, _param3 []string) { @@ -329,50 +359,58 @@ func (c *MockClient_CreateComment_OngoingVerification) GetAllCapturedArguments() return } -func (verifier *VerifierMockClient) DownloadRepoConfigFile(_param0 models.PullRequest) *MockClient_DownloadRepoConfigFile_OngoingVerification { - params := []pegomock.Param{_param0} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "DownloadRepoConfigFile", params, verifier.timeout) - return &MockClient_DownloadRepoConfigFile_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +func (verifier *VerifierMockClient) HidePrevCommandComments(repo models.Repo, pullNum int, command string) *MockClient_HidePrevCommandComments_OngoingVerification { + params := []pegomock.Param{repo, pullNum, command} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "HidePrevCommandComments", params, verifier.timeout) + return &MockClient_HidePrevCommandComments_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } -type MockClient_DownloadRepoConfigFile_OngoingVerification struct { +type MockClient_HidePrevCommandComments_OngoingVerification struct { mock *MockClient methodInvocations []pegomock.MethodInvocation } -func (c *MockClient_DownloadRepoConfigFile_OngoingVerification) GetCapturedArguments() models.PullRequest { - _param0 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1] +func (c *MockClient_HidePrevCommandComments_OngoingVerification) GetCapturedArguments() (models.Repo, int, string) { + repo, pullNum, command := c.GetAllCapturedArguments() + return repo[len(repo)-1], pullNum[len(pullNum)-1], command[len(command)-1] } -func (c *MockClient_DownloadRepoConfigFile_OngoingVerification) GetAllCapturedArguments() (_param0 []models.PullRequest) { +func (c *MockClient_HidePrevCommandComments_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []int, _param2 []string) { params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) if len(params) > 0 { - _param0 = make([]models.PullRequest, len(c.methodInvocations)) + _param0 = make([]models.Repo, len(c.methodInvocations)) for u, param := range params[0] { - _param0[u] = param.(models.PullRequest) + _param0[u] = param.(models.Repo) + } + _param1 = make([]int, len(c.methodInvocations)) + for u, param := range params[1] { + _param1[u] = param.(int) + } + _param2 = make([]string, len(c.methodInvocations)) + for u, param := range params[2] { + _param2[u] = param.(string) } } return } -func (verifier *VerifierMockClient) GetModifiedFiles(_param0 models.Repo, _param1 models.PullRequest) *MockClient_GetModifiedFiles_OngoingVerification { - params := []pegomock.Param{_param0, _param1} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetModifiedFiles", params, verifier.timeout) - return &MockClient_GetModifiedFiles_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +func (verifier *VerifierMockClient) PullIsApproved(repo models.Repo, pull models.PullRequest) *MockClient_PullIsApproved_OngoingVerification { + params := []pegomock.Param{repo, pull} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PullIsApproved", params, verifier.timeout) + return &MockClient_PullIsApproved_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } -type MockClient_GetModifiedFiles_OngoingVerification struct { +type MockClient_PullIsApproved_OngoingVerification struct { mock *MockClient methodInvocations []pegomock.MethodInvocation } -func (c *MockClient_GetModifiedFiles_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest) { - _param0, _param1 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1], _param1[len(_param1)-1] +func (c *MockClient_PullIsApproved_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest) { + repo, pull := c.GetAllCapturedArguments() + return repo[len(repo)-1], pull[len(pull)-1] } -func (c *MockClient_GetModifiedFiles_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest) { +func (c *MockClient_PullIsApproved_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest) { params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) if len(params) > 0 { _param0 = make([]models.Repo, len(c.methodInvocations)) @@ -387,32 +425,32 @@ func (c *MockClient_GetModifiedFiles_OngoingVerification) GetAllCapturedArgument return } -func (verifier *VerifierMockClient) HidePrevCommandComments(_param0 models.Repo, _param1 int, _param2 string) *MockClient_HidePrevCommandComments_OngoingVerification { - params := []pegomock.Param{_param0, _param1, _param2} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "HidePrevCommandComments", params, verifier.timeout) - return &MockClient_HidePrevCommandComments_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +func (verifier *VerifierMockClient) PullIsMergeable(repo models.Repo, pull models.PullRequest, vcsstatusname string) *MockClient_PullIsMergeable_OngoingVerification { + params := []pegomock.Param{repo, pull, vcsstatusname} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PullIsMergeable", params, verifier.timeout) + return &MockClient_PullIsMergeable_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } -type MockClient_HidePrevCommandComments_OngoingVerification struct { +type MockClient_PullIsMergeable_OngoingVerification struct { mock *MockClient methodInvocations []pegomock.MethodInvocation } -func (c *MockClient_HidePrevCommandComments_OngoingVerification) GetCapturedArguments() (models.Repo, int, string) { - _param0, _param1, _param2 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1], _param1[len(_param1)-1], _param2[len(_param2)-1] +func (c *MockClient_PullIsMergeable_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, string) { + repo, pull, vcsstatusname := c.GetAllCapturedArguments() + return repo[len(repo)-1], pull[len(pull)-1], vcsstatusname[len(vcsstatusname)-1] } -func (c *MockClient_HidePrevCommandComments_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []int, _param2 []string) { +func (c *MockClient_PullIsMergeable_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []string) { params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) if len(params) > 0 { _param0 = make([]models.Repo, len(c.methodInvocations)) for u, param := range params[0] { _param0[u] = param.(models.Repo) } - _param1 = make([]int, len(c.methodInvocations)) + _param1 = make([]models.PullRequest, len(c.methodInvocations)) for u, param := range params[1] { - _param1[u] = param.(int) + _param1[u] = param.(models.PullRequest) } _param2 = make([]string, len(c.methodInvocations)) for u, param := range params[2] { @@ -422,35 +460,55 @@ func (c *MockClient_HidePrevCommandComments_OngoingVerification) GetAllCapturedA return } -func (verifier *VerifierMockClient) MarkdownPullLink(_param0 models.PullRequest) *MockClient_MarkdownPullLink_OngoingVerification { - params := []pegomock.Param{_param0} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "MarkdownPullLink", params, verifier.timeout) - return &MockClient_MarkdownPullLink_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +func (verifier *VerifierMockClient) UpdateStatus(repo models.Repo, pull models.PullRequest, state models.CommitStatus, src string, description string, url string) *MockClient_UpdateStatus_OngoingVerification { + params := []pegomock.Param{repo, pull, state, src, description, url} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateStatus", params, verifier.timeout) + return &MockClient_UpdateStatus_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } -type MockClient_MarkdownPullLink_OngoingVerification struct { +type MockClient_UpdateStatus_OngoingVerification struct { mock *MockClient methodInvocations []pegomock.MethodInvocation } -func (c *MockClient_MarkdownPullLink_OngoingVerification) GetCapturedArguments() models.PullRequest { - _param0 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1] +func (c *MockClient_UpdateStatus_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, models.CommitStatus, string, string, string) { + repo, pull, state, src, description, url := c.GetAllCapturedArguments() + return repo[len(repo)-1], pull[len(pull)-1], state[len(state)-1], src[len(src)-1], description[len(description)-1], url[len(url)-1] } -func (c *MockClient_MarkdownPullLink_OngoingVerification) GetAllCapturedArguments() (_param0 []models.PullRequest) { +func (c *MockClient_UpdateStatus_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []models.CommitStatus, _param3 []string, _param4 []string, _param5 []string) { params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) if len(params) > 0 { - _param0 = make([]models.PullRequest, len(c.methodInvocations)) + _param0 = make([]models.Repo, len(c.methodInvocations)) for u, param := range params[0] { - _param0[u] = param.(models.PullRequest) + _param0[u] = param.(models.Repo) + } + _param1 = make([]models.PullRequest, len(c.methodInvocations)) + for u, param := range params[1] { + _param1[u] = param.(models.PullRequest) + } + _param2 = make([]models.CommitStatus, len(c.methodInvocations)) + for u, param := range params[2] { + _param2[u] = param.(models.CommitStatus) + } + _param3 = make([]string, len(c.methodInvocations)) + for u, param := range params[3] { + _param3[u] = param.(string) + } + _param4 = make([]string, len(c.methodInvocations)) + for u, param := range params[4] { + _param4[u] = param.(string) + } + _param5 = make([]string, len(c.methodInvocations)) + for u, param := range params[5] { + _param5[u] = param.(string) } } return } -func (verifier *VerifierMockClient) MergePull(_param0 models.PullRequest, _param1 models.PullRequestOptions) *MockClient_MergePull_OngoingVerification { - params := []pegomock.Param{_param0, _param1} +func (verifier *VerifierMockClient) MergePull(pull models.PullRequest, pullOptions models.PullRequestOptions) *MockClient_MergePull_OngoingVerification { + params := []pegomock.Param{pull, pullOptions} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "MergePull", params, verifier.timeout) return &MockClient_MergePull_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } @@ -461,8 +519,8 @@ type MockClient_MergePull_OngoingVerification struct { } func (c *MockClient_MergePull_OngoingVerification) GetCapturedArguments() (models.PullRequest, models.PullRequestOptions) { - _param0, _param1 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1], _param1[len(_param1)-1] + pull, pullOptions := c.GetAllCapturedArguments() + return pull[len(pull)-1], pullOptions[len(pullOptions)-1] } func (c *MockClient_MergePull_OngoingVerification) GetAllCapturedArguments() (_param0 []models.PullRequest, _param1 []models.PullRequestOptions) { @@ -480,43 +538,33 @@ func (c *MockClient_MergePull_OngoingVerification) GetAllCapturedArguments() (_p return } -func (verifier *VerifierMockClient) PullIsApproved(_param0 models.Repo, _param1 models.PullRequest) *MockClient_PullIsApproved_OngoingVerification { - params := []pegomock.Param{_param0, _param1} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PullIsApproved", params, verifier.timeout) - return &MockClient_PullIsApproved_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +func (verifier *VerifierMockClient) MarkdownPullLink(pull models.PullRequest) *MockClient_MarkdownPullLink_OngoingVerification { + params := []pegomock.Param{pull} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "MarkdownPullLink", params, verifier.timeout) + return &MockClient_MarkdownPullLink_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } -type MockClient_PullIsApproved_OngoingVerification struct { +type MockClient_MarkdownPullLink_OngoingVerification struct { mock *MockClient methodInvocations []pegomock.MethodInvocation } -func (c *MockClient_PullIsApproved_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest) { - _param0, _param1 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1], _param1[len(_param1)-1] +func (c *MockClient_MarkdownPullLink_OngoingVerification) GetCapturedArguments() models.PullRequest { + pull := c.GetAllCapturedArguments() + return pull[len(pull)-1] } -func (c *MockClient_PullIsApproved_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest) { +func (c *MockClient_MarkdownPullLink_OngoingVerification) GetAllCapturedArguments() (_param0 []models.PullRequest) { params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) if len(params) > 0 { - _param0 = make([]models.Repo, len(c.methodInvocations)) + _param0 = make([]models.PullRequest, len(c.methodInvocations)) for u, param := range params[0] { - _param0[u] = param.(models.Repo) - } - _param1 = make([]models.PullRequest, len(c.methodInvocations)) - for u, param := range params[1] { - _param1[u] = param.(models.PullRequest) + _param0[u] = param.(models.PullRequest) } } return } -func (verifier *VerifierMockClient) PullIsMergeable(_param0 models.Repo, _param1 models.PullRequest) *MockClient_PullIsMergeable_OngoingVerification { - params := []pegomock.Param{_param0, _param1} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PullIsMergeable", params, verifier.timeout) - return &MockClient_PullIsMergeable_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} -} - func (verifier *VerifierMockClient) GetTeamNamesForUser(repo models.Repo, user models.User) *MockClient_GetTeamNamesForUser_OngoingVerification { params := []pegomock.Param{repo, user} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetTeamNamesForUser", params, verifier.timeout) @@ -528,33 +576,59 @@ type MockClient_GetTeamNamesForUser_OngoingVerification struct { methodInvocations []pegomock.MethodInvocation } -type MockClient_PullIsMergeable_OngoingVerification struct { +func (c *MockClient_GetTeamNamesForUser_OngoingVerification) GetCapturedArguments() (models.Repo, models.User) { + repo, user := c.GetAllCapturedArguments() + return repo[len(repo)-1], user[len(user)-1] +} + +func (c *MockClient_GetTeamNamesForUser_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.User) { + params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) + if len(params) > 0 { + _param0 = make([]models.Repo, len(c.methodInvocations)) + for u, param := range params[0] { + _param0[u] = param.(models.Repo) + } + _param1 = make([]models.User, len(c.methodInvocations)) + for u, param := range params[1] { + _param1[u] = param.(models.User) + } + } + return +} + +func (verifier *VerifierMockClient) GetFileContent(pull models.PullRequest, fileName string) *MockClient_GetFileContent_OngoingVerification { + params := []pegomock.Param{pull, fileName} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetFileContent", params, verifier.timeout) + return &MockClient_GetFileContent_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +} + +type MockClient_GetFileContent_OngoingVerification struct { mock *MockClient methodInvocations []pegomock.MethodInvocation } -func (c *MockClient_PullIsMergeable_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest) { - _param0, _param1 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1], _param1[len(_param1)-1] +func (c *MockClient_GetFileContent_OngoingVerification) GetCapturedArguments() (models.PullRequest, string) { + pull, fileName := c.GetAllCapturedArguments() + return pull[len(pull)-1], fileName[len(fileName)-1] } -func (c *MockClient_PullIsMergeable_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest) { +func (c *MockClient_GetFileContent_OngoingVerification) GetAllCapturedArguments() (_param0 []models.PullRequest, _param1 []string) { params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) if len(params) > 0 { - _param0 = make([]models.Repo, len(c.methodInvocations)) + _param0 = make([]models.PullRequest, len(c.methodInvocations)) for u, param := range params[0] { - _param0[u] = param.(models.Repo) + _param0[u] = param.(models.PullRequest) } - _param1 = make([]models.PullRequest, len(c.methodInvocations)) + _param1 = make([]string, len(c.methodInvocations)) for u, param := range params[1] { - _param1[u] = param.(models.PullRequest) + _param1[u] = param.(string) } } return } -func (verifier *VerifierMockClient) SupportsSingleFileDownload(_param0 models.Repo) *MockClient_SupportsSingleFileDownload_OngoingVerification { - params := []pegomock.Param{_param0} +func (verifier *VerifierMockClient) SupportsSingleFileDownload(repo models.Repo) *MockClient_SupportsSingleFileDownload_OngoingVerification { + params := []pegomock.Param{repo} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "SupportsSingleFileDownload", params, verifier.timeout) return &MockClient_SupportsSingleFileDownload_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } @@ -565,8 +639,8 @@ type MockClient_SupportsSingleFileDownload_OngoingVerification struct { } func (c *MockClient_SupportsSingleFileDownload_OngoingVerification) GetCapturedArguments() models.Repo { - _param0 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1] + repo := c.GetAllCapturedArguments() + return repo[len(repo)-1] } func (c *MockClient_SupportsSingleFileDownload_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo) { @@ -580,48 +654,32 @@ func (c *MockClient_SupportsSingleFileDownload_OngoingVerification) GetAllCaptur return } -func (verifier *VerifierMockClient) UpdateStatus(_param0 models.Repo, _param1 models.PullRequest, _param2 models.CommitStatus, _param3 string, _param4 string, _param5 string) *MockClient_UpdateStatus_OngoingVerification { - params := []pegomock.Param{_param0, _param1, _param2, _param3, _param4, _param5} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateStatus", params, verifier.timeout) - return &MockClient_UpdateStatus_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} +func (verifier *VerifierMockClient) GetCloneURL(VCSHostType models.VCSHostType, repo string) *MockClient_GetCloneURL_OngoingVerification { + params := []pegomock.Param{VCSHostType, repo} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "GetCloneURL", params, verifier.timeout) + return &MockClient_GetCloneURL_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } -type MockClient_UpdateStatus_OngoingVerification struct { +type MockClient_GetCloneURL_OngoingVerification struct { mock *MockClient methodInvocations []pegomock.MethodInvocation } -func (c *MockClient_UpdateStatus_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, models.CommitStatus, string, string, string) { - _param0, _param1, _param2, _param3, _param4, _param5 := c.GetAllCapturedArguments() - return _param0[len(_param0)-1], _param1[len(_param1)-1], _param2[len(_param2)-1], _param3[len(_param3)-1], _param4[len(_param4)-1], _param5[len(_param5)-1] +func (c *MockClient_GetCloneURL_OngoingVerification) GetCapturedArguments() (models.VCSHostType, string) { + VCSHostType, repo := c.GetAllCapturedArguments() + return VCSHostType[len(VCSHostType)-1], repo[len(repo)-1] } -func (c *MockClient_UpdateStatus_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []models.CommitStatus, _param3 []string, _param4 []string, _param5 []string) { +func (c *MockClient_GetCloneURL_OngoingVerification) GetAllCapturedArguments() (_param0 []models.VCSHostType, _param1 []string) { params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) if len(params) > 0 { - _param0 = make([]models.Repo, len(c.methodInvocations)) + _param0 = make([]models.VCSHostType, len(c.methodInvocations)) for u, param := range params[0] { - _param0[u] = param.(models.Repo) + _param0[u] = param.(models.VCSHostType) } - _param1 = make([]models.PullRequest, len(c.methodInvocations)) + _param1 = make([]string, len(c.methodInvocations)) for u, param := range params[1] { - _param1[u] = param.(models.PullRequest) - } - _param2 = make([]models.CommitStatus, len(c.methodInvocations)) - for u, param := range params[2] { - _param2[u] = param.(models.CommitStatus) - } - _param3 = make([]string, len(c.methodInvocations)) - for u, param := range params[3] { - _param3[u] = param.(string) - } - _param4 = make([]string, len(c.methodInvocations)) - for u, param := range params[4] { - _param4[u] = param.(string) - } - _param5 = make([]string, len(c.methodInvocations)) - for u, param := range params[5] { - _param5[u] = param.(string) + _param1[u] = param.(string) } } return diff --git a/server/events/vcs/not_configured_vcs_client.go b/server/events/vcs/not_configured_vcs_client.go index e98772974f..e59d1d5f90 100644 --- a/server/events/vcs/not_configured_vcs_client.go +++ b/server/events/vcs/not_configured_vcs_client.go @@ -64,7 +64,7 @@ func (a *NotConfiguredVCSClient) SupportsSingleFileDownload(repo models.Repo) bo return false } -func (a *NotConfiguredVCSClient) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { +func (a *NotConfiguredVCSClient) GetFileContent(pull models.PullRequest, fileName string) (bool, []byte, error) { return true, []byte{}, a.err() } func (a *NotConfiguredVCSClient) GetCloneURL(VCSHostType models.VCSHostType, repo string) (string, error) { diff --git a/server/events/vcs/proxy.go b/server/events/vcs/proxy.go index 9703d11372..0a17123bfe 100644 --- a/server/events/vcs/proxy.go +++ b/server/events/vcs/proxy.go @@ -92,8 +92,8 @@ func (d *ClientProxy) GetTeamNamesForUser(repo models.Repo, user models.User) ([ return d.clients[repo.VCSHost.Type].GetTeamNamesForUser(repo, user) } -func (d *ClientProxy) DownloadRepoConfigFile(pull models.PullRequest) (bool, []byte, error) { - return d.clients[pull.BaseRepo.VCSHost.Type].DownloadRepoConfigFile(pull) +func (d *ClientProxy) GetFileContent(pull models.PullRequest, fileName string) (bool, []byte, error) { + return d.clients[pull.BaseRepo.VCSHost.Type].GetFileContent(pull, fileName) } func (d *ClientProxy) SupportsSingleFileDownload(repo models.Repo) bool {