From 50ace7de5c5ff23ebaffb8356ab02afc47b8f8c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:14:55 +0000 Subject: [PATCH 01/38] fix(deps): update module github.com/google/go-github/v63 to v65 in go.mod (main) (#4968) Signed-off-by: X-Guardian Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: X-Guardian --- e2e/github.go | 2 +- e2e/go.mod | 2 +- e2e/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- server/controllers/events/events_controller.go | 2 +- server/controllers/events/events_controller_e2e_test.go | 2 +- server/controllers/events/events_controller_test.go | 2 +- server/controllers/events/github_request_validator.go | 2 +- server/events/apply_command_runner_test.go | 2 +- server/events/command_runner.go | 2 +- server/events/command_runner_test.go | 2 +- server/events/event_parser.go | 2 +- server/events/event_parser_test.go | 2 +- server/events/mocks/mock_event_parsing.go | 2 +- server/events/mocks/mock_github_pull_getter.go | 2 +- server/events/plan_command_runner_test.go | 2 +- server/events/vcs/github_client.go | 2 +- server/events/vcs/github_credentials.go | 2 +- server/events/vcs/instrumented_client.go | 2 +- server/events/vcs/mocks/mock_github_pull_request_getter.go | 2 +- server/events/vcs/testdata/fixtures.go | 2 +- testdrive/github.go | 2 +- testdrive/testdrive.go | 2 +- 24 files changed, 26 insertions(+), 26 deletions(-) diff --git a/e2e/github.go b/e2e/github.go index 0ffe087dc1..dd70421e84 100644 --- a/e2e/github.go +++ b/e2e/github.go @@ -21,7 +21,7 @@ import ( "os/exec" "strings" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" ) type GithubClient struct { diff --git a/e2e/go.mod b/e2e/go.mod index 6afa1c6d32..cccf3f4471 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -3,7 +3,7 @@ module github.com/runatlantis/atlantis/e2e go 1.23.0 require ( - github.com/google/go-github/v63 v63.0.0 + github.com/google/go-github/v65 v65.0.0 github.com/hashicorp/go-multierror v1.1.1 github.com/xanzy/go-gitlab v0.111.0 ) diff --git a/e2e/go.sum b/e2e/go.sum index 4693aabf12..42fc2c0195 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -10,8 +10,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE= -github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA= +github.com/google/go-github/v65 v65.0.0 h1:pQ7BmO3DZivvFk92geC0jB0q2m3gyn8vnYPgV7GSLhQ= +github.com/google/go-github/v65 v65.0.0/go.mod h1:DvrqWo5hvsdhJvHd4WyVF9ttANN3BniqjP8uTFMNb60= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/go.mod b/go.mod index da4fba5da4..c6f4fd40da 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/go-playground/validator/v10 v10.22.1 github.com/go-test/deep v1.1.1 github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/google/go-github/v63 v63.0.0 + github.com/google/go-github/v65 v65.0.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 diff --git a/go.sum b/go.sum index 4fedfcb557..0d0760b774 100644 --- a/go.sum +++ b/go.sum @@ -216,8 +216,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4= github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4= -github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE= -github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA= +github.com/google/go-github/v65 v65.0.0 h1:pQ7BmO3DZivvFk92geC0jB0q2m3gyn8vnYPgV7GSLhQ= +github.com/google/go-github/v65 v65.0.0/go.mod h1:DvrqWo5hvsdhJvHd4WyVF9ttANN3BniqjP8uTFMNb60= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= diff --git a/server/controllers/events/events_controller.go b/server/controllers/events/events_controller.go index 9cc71e632d..b2687182ff 100644 --- a/server/controllers/events/events_controller.go +++ b/server/controllers/events/events_controller.go @@ -20,7 +20,7 @@ import ( "net/http" "strings" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/mcdafydd/go-azuredevops/azuredevops" "github.com/microcosm-cc/bluemonday" "github.com/pkg/errors" diff --git a/server/controllers/events/events_controller_e2e_test.go b/server/controllers/events/events_controller_e2e_test.go index 68e517709c..7a80b13b07 100644 --- a/server/controllers/events/events_controller_e2e_test.go +++ b/server/controllers/events/events_controller_e2e_test.go @@ -13,7 +13,7 @@ import ( "strings" "testing" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/hashicorp/go-version" . "github.com/petergtz/pegomock/v4" diff --git a/server/controllers/events/events_controller_test.go b/server/controllers/events/events_controller_test.go index 7ba1c7e295..bbf008cafb 100644 --- a/server/controllers/events/events_controller_test.go +++ b/server/controllers/events/events_controller_test.go @@ -25,7 +25,7 @@ import ( "strings" "testing" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/mcdafydd/go-azuredevops/azuredevops" . "github.com/petergtz/pegomock/v4" events_controllers "github.com/runatlantis/atlantis/server/controllers/events" diff --git a/server/controllers/events/github_request_validator.go b/server/controllers/events/github_request_validator.go index d813cc83db..30d9512b12 100644 --- a/server/controllers/events/github_request_validator.go +++ b/server/controllers/events/github_request_validator.go @@ -19,7 +19,7 @@ import ( "io" "net/http" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" ) //go:generate pegomock generate --package mocks -o mocks/mock_github_request_validator.go GithubRequestValidator diff --git a/server/events/apply_command_runner_test.go b/server/events/apply_command_runner_test.go index 2b56902951..2258c3d413 100644 --- a/server/events/apply_command_runner_test.go +++ b/server/events/apply_command_runner_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" . "github.com/petergtz/pegomock/v4" "github.com/runatlantis/atlantis/server/core/db" "github.com/runatlantis/atlantis/server/core/locking" diff --git a/server/events/command_runner.go b/server/events/command_runner.go index 574c3383fa..a56ecf67c2 100644 --- a/server/events/command_runner.go +++ b/server/events/command_runner.go @@ -17,7 +17,7 @@ import ( "fmt" "strconv" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/mcdafydd/go-azuredevops/azuredevops" "github.com/pkg/errors" "github.com/runatlantis/atlantis/server/core/config/valid" diff --git a/server/events/command_runner_test.go b/server/events/command_runner_test.go index 07026b6837..c4d41b441a 100644 --- a/server/events/command_runner_test.go +++ b/server/events/command_runner_test.go @@ -27,7 +27,7 @@ import ( "github.com/runatlantis/atlantis/server/logging" "github.com/runatlantis/atlantis/server/metrics" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" . "github.com/petergtz/pegomock/v4" lockingmocks "github.com/runatlantis/atlantis/server/core/locking/mocks" "github.com/runatlantis/atlantis/server/events" diff --git a/server/events/event_parser.go b/server/events/event_parser.go index e2283d4d3a..62dd634a18 100644 --- a/server/events/event_parser.go +++ b/server/events/event_parser.go @@ -23,7 +23,7 @@ import ( giteasdk "code.gitea.io/sdk/gitea" "github.com/go-playground/validator/v10" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" lru "github.com/hashicorp/golang-lru/v2" "github.com/mcdafydd/go-azuredevops/azuredevops" "github.com/pkg/errors" diff --git a/server/events/event_parser_test.go b/server/events/event_parser_test.go index a136a3ab8b..3b7b206a7d 100644 --- a/server/events/event_parser_test.go +++ b/server/events/event_parser_test.go @@ -21,7 +21,7 @@ import ( "strings" "testing" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/mcdafydd/go-azuredevops/azuredevops" "github.com/mohae/deepcopy" "github.com/runatlantis/atlantis/server/events" diff --git a/server/events/mocks/mock_event_parsing.go b/server/events/mocks/mock_event_parsing.go index e51132c89a..ca816e9a97 100644 --- a/server/events/mocks/mock_event_parsing.go +++ b/server/events/mocks/mock_event_parsing.go @@ -5,7 +5,7 @@ package mocks import ( gitea "code.gitea.io/sdk/gitea" - github "github.com/google/go-github/v63/github" + github "github.com/google/go-github/v65/github" azuredevops "github.com/mcdafydd/go-azuredevops/azuredevops" pegomock "github.com/petergtz/pegomock/v4" models "github.com/runatlantis/atlantis/server/events/models" diff --git a/server/events/mocks/mock_github_pull_getter.go b/server/events/mocks/mock_github_pull_getter.go index 163d66473b..7127f5cd9b 100644 --- a/server/events/mocks/mock_github_pull_getter.go +++ b/server/events/mocks/mock_github_pull_getter.go @@ -4,7 +4,7 @@ package mocks import ( - github "github.com/google/go-github/v63/github" + github "github.com/google/go-github/v65/github" pegomock "github.com/petergtz/pegomock/v4" models "github.com/runatlantis/atlantis/server/events/models" logging "github.com/runatlantis/atlantis/server/logging" diff --git a/server/events/plan_command_runner_test.go b/server/events/plan_command_runner_test.go index 18ddb06ef7..6944c28776 100644 --- a/server/events/plan_command_runner_test.go +++ b/server/events/plan_command_runner_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" . "github.com/petergtz/pegomock/v4" "github.com/runatlantis/atlantis/server/core/db" "github.com/runatlantis/atlantis/server/events" diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index 86a7bfd48f..ddc0ff1e1b 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -22,7 +22,7 @@ import ( "strings" "time" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/pkg/errors" "github.com/runatlantis/atlantis/server/events/command" "github.com/runatlantis/atlantis/server/events/models" diff --git a/server/events/vcs/github_credentials.go b/server/events/vcs/github_credentials.go index becebee086..b00ec146a5 100644 --- a/server/events/vcs/github_credentials.go +++ b/server/events/vcs/github_credentials.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/bradleyfalzon/ghinstallation/v2" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/pkg/errors" ) diff --git a/server/events/vcs/instrumented_client.go b/server/events/vcs/instrumented_client.go index 54e977e595..942a4823e1 100644 --- a/server/events/vcs/instrumented_client.go +++ b/server/events/vcs/instrumented_client.go @@ -3,7 +3,7 @@ package vcs import ( "strconv" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/runatlantis/atlantis/server/events/models" "github.com/runatlantis/atlantis/server/logging" "github.com/runatlantis/atlantis/server/metrics" diff --git a/server/events/vcs/mocks/mock_github_pull_request_getter.go b/server/events/vcs/mocks/mock_github_pull_request_getter.go index d08efee394..995281b4c5 100644 --- a/server/events/vcs/mocks/mock_github_pull_request_getter.go +++ b/server/events/vcs/mocks/mock_github_pull_request_getter.go @@ -4,7 +4,7 @@ package mocks import ( - github "github.com/google/go-github/v63/github" + github "github.com/google/go-github/v65/github" pegomock "github.com/petergtz/pegomock/v4" models "github.com/runatlantis/atlantis/server/events/models" logging "github.com/runatlantis/atlantis/server/logging" diff --git a/server/events/vcs/testdata/fixtures.go b/server/events/vcs/testdata/fixtures.go index 398801de77..d637c3b5dd 100644 --- a/server/events/vcs/testdata/fixtures.go +++ b/server/events/vcs/testdata/fixtures.go @@ -22,7 +22,7 @@ import ( "testing" "github.com/golang-jwt/jwt/v5" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/mcdafydd/go-azuredevops/azuredevops" ) diff --git a/testdrive/github.go b/testdrive/github.go index e00402e18e..91b961d5ae 100644 --- a/testdrive/github.go +++ b/testdrive/github.go @@ -18,7 +18,7 @@ import ( "strings" "time" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" ) var githubUsername string diff --git a/testdrive/testdrive.go b/testdrive/testdrive.go index 951c12cd99..a8bdf7a408 100644 --- a/testdrive/testdrive.go +++ b/testdrive/testdrive.go @@ -31,7 +31,7 @@ import ( "time" "github.com/briandowns/spinner" - "github.com/google/go-github/v63/github" + "github.com/google/go-github/v65/github" "github.com/mitchellh/colorstring" "github.com/pkg/errors" ) From 13fa63552387fbfe96689c7245536c08911a194d Mon Sep 17 00:00:00 2001 From: Alexey Kosenko Date: Sat, 2 Nov 2024 23:09:17 +0300 Subject: [PATCH 02/38] feat(automerge): implement GitHub --auto-merge-method flag for apply command (#4895) Signed-off-by: a1k0u Signed-off-by: X-Guardian --- runatlantis.io/docs/automerging.md | 17 ++ runatlantis.io/docs/using-atlantis.md | 1 + server/core/config/valid/global_cfg.go | 1 + server/events/apply_command_runner.go | 2 +- server/events/automerger.go | 3 +- server/events/comment_parser.go | 36 +++- server/events/comment_parser_test.go | 33 +++- server/events/event_parser.go | 7 +- server/events/event_parser_test.go | 8 +- server/events/mocks/mock_comment_building.go | 172 ++++++++++-------- server/events/models/models.go | 3 + .../events/project_command_context_builder.go | 4 +- .../project_command_context_builder_test.go | 8 +- server/events/vcs/github_client.go | 42 ++++- server/events/vcs/github_client_test.go | 70 ++++++- 15 files changed, 292 insertions(+), 115 deletions(-) diff --git a/runatlantis.io/docs/automerging.md b/runatlantis.io/docs/automerging.md index 2716a572ee..5c2f96d34e 100644 --- a/runatlantis.io/docs/automerging.md +++ b/runatlantis.io/docs/automerging.md @@ -29,6 +29,23 @@ Automerging can be enabled either by: If automerge is enabled, you can disable it for a single `atlantis apply` command with the `--auto-merge-disabled` option. +## How to set the merge method for automerge + +If automerge is enabled, you can use the `--auto-merge-method` option +for the `atlantis apply` command to specify which merge method use. + +```shell +atlantis apply --auto-merge-method +``` + +The `method` must be one of: + +- merge +- rebase +- squash + +This is currently only implemented for the GitHub VCS. + ## Requirements ### All Plans Must Succeed diff --git a/runatlantis.io/docs/using-atlantis.md b/runatlantis.io/docs/using-atlantis.md index 61c06e1a21..aa0a372231 100644 --- a/runatlantis.io/docs/using-atlantis.md +++ b/runatlantis.io/docs/using-atlantis.md @@ -149,6 +149,7 @@ atlantis apply -w staging * `-p project` Apply the plan for this project. Refers to the name of the project configured in the repo's [`atlantis.yaml` file](repo-level-atlantis-yaml.md). Cannot be used at same time as `-d` or `-w`. * `-w workspace` Apply the plan for this [Terraform workspace](https://developer.hashicorp.com/terraform/language/state/workspaces). Ignore this if Terraform workspaces are unused. * `--auto-merge-disabled` Disable [automerge](automerging.md) for this apply command. +* `--auto-merge-method method` Specify which [merge method](automerging.md#how-to-set-merge-method-for-automerge) use for the apply command if [automerge](automerging.md) is enabled. Implemented only for GitHub. * `--verbose` Append Atlantis log to comment. ### Additional Terraform flags diff --git a/server/core/config/valid/global_cfg.go b/server/core/config/valid/global_cfg.go index 11a267c151..a930ef22bc 100644 --- a/server/core/config/valid/global_cfg.go +++ b/server/core/config/valid/global_cfg.go @@ -104,6 +104,7 @@ type MergedProjectCfg struct { Name string AutoplanEnabled bool AutoMergeDisabled bool + AutoMergeMethod string TerraformVersion *version.Version RepoCfgVersion int PolicySets PolicySets diff --git a/server/events/apply_command_runner.go b/server/events/apply_command_runner.go index ee6bf8ab1f..6c69032910 100644 --- a/server/events/apply_command_runner.go +++ b/server/events/apply_command_runner.go @@ -181,7 +181,7 @@ func (a *ApplyCommandRunner) Run(ctx *command.Context, cmd *CommentCommand) { a.updateCommitStatus(ctx, pullStatus) if a.autoMerger.automergeEnabled(projectCmds) && !cmd.AutoMergeDisabled { - a.autoMerger.automerge(ctx, pullStatus, a.autoMerger.deleteSourceBranchOnMergeEnabled(projectCmds)) + a.autoMerger.automerge(ctx, pullStatus, a.autoMerger.deleteSourceBranchOnMergeEnabled(projectCmds), cmd.AutoMergeMethod) } } diff --git a/server/events/automerger.go b/server/events/automerger.go index fa74beac0f..1d19964076 100644 --- a/server/events/automerger.go +++ b/server/events/automerger.go @@ -13,7 +13,7 @@ type AutoMerger struct { GlobalAutomerge bool } -func (c *AutoMerger) automerge(ctx *command.Context, pullStatus models.PullStatus, deleteSourceBranchOnMerge bool) { +func (c *AutoMerger) automerge(ctx *command.Context, pullStatus models.PullStatus, deleteSourceBranchOnMerge bool, mergeMethod string) { // We only automerge if all projects have been successfully applied. for _, p := range pullStatus.Projects { if p.Status != models.AppliedPlanStatus { @@ -32,6 +32,7 @@ func (c *AutoMerger) automerge(ctx *command.Context, pullStatus models.PullStatu ctx.Log.Info("automerging pull request") var pullOptions models.PullRequestOptions pullOptions.DeleteSourceBranchOnMerge = deleteSourceBranchOnMerge + pullOptions.MergeMethod = mergeMethod err := c.VCSClient.MergePull(ctx.Log, ctx.Pull, pullOptions) if err != nil { diff --git a/server/events/comment_parser.go b/server/events/comment_parser.go index 3b3d2d3b0a..829c15ced9 100644 --- a/server/events/comment_parser.go +++ b/server/events/comment_parser.go @@ -41,6 +41,8 @@ const ( policySetFlagShort = "" autoMergeDisabledFlagLong = "auto-merge-disabled" autoMergeDisabledFlagShort = "" + autoMergeMethodFlagLong = "auto-merge-method" + autoMergeMethodFlagShort = "" verboseFlagLong = "verbose" verboseFlagShort = "" clearPolicyApprovalFlagLong = "clear-policy-approval" @@ -70,7 +72,7 @@ type CommentBuilder interface { // BuildPlanComment builds a plan comment for the specified args. BuildPlanComment(repoRelDir string, workspace string, project string, commentArgs []string) string // BuildApplyComment builds an apply comment for the specified args. - BuildApplyComment(repoRelDir string, workspace string, project string, autoMergeDisabled bool) string + BuildApplyComment(repoRelDir string, workspace string, project string, autoMergeDisabled bool, autoMergeMethod string) string // BuildApprovePoliciesComment builds an approve_policies comment for the specified args. BuildApprovePoliciesComment(repoRelDir string, workspace string, project string) string } @@ -226,7 +228,9 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com var project string var policySet string var clearPolicyApproval bool - var verbose, autoMergeDisabled bool + var verbose bool + var autoMergeDisabled bool + var autoMergeMethod string var flagSet *pflag.FlagSet var name command.Name @@ -248,6 +252,7 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com flagSet.StringVarP(&dir, dirFlagLong, dirFlagShort, "", "Apply the plan for this directory, relative to root of repo, ex. 'child/dir'.") 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.StringVarP(&autoMergeMethod, autoMergeMethodFlagLong, autoMergeMethodFlagShort, "", "Specifies the merge method for the VCS if automerge is enabled. (Currently only implemented for GitHub)") flagSet.BoolVarP(&verbose, verboseFlagLong, verboseFlagShort, false, "Append Atlantis log to comment.") case command.ApprovePolicies.String(): name = command.ApprovePolicies @@ -317,8 +322,20 @@ func (e *CommentParser) Parse(rawComment string, vcsHost models.VCSHostType) Com return CommentParseResult{CommentResponse: e.errMarkdown(err, cmd, flagSet)} } + if autoMergeMethod != "" { + if autoMergeDisabled { + err := fmt.Sprintf("cannot use --%s at the same time as --%s", autoMergeMethodFlagLong, autoMergeDisabledFlagLong) + return CommentParseResult{CommentResponse: e.errMarkdown(err, cmd, flagSet)} + } + + if vcsHost != models.Github { + err := fmt.Sprintf("--%s is not currently implemented for %s", autoMergeMethodFlagLong, vcsHost.String()) + return CommentParseResult{CommentResponse: e.errMarkdown(err, cmd, flagSet)} + } + } + return CommentParseResult{ - Command: NewCommentCommand(dir, extraArgs, name, subName, verbose, autoMergeDisabled, workspace, project, policySet, clearPolicyApproval), + Command: NewCommentCommand(dir, extraArgs, name, subName, verbose, autoMergeDisabled, autoMergeMethod, workspace, project, policySet, clearPolicyApproval), } } @@ -387,7 +404,7 @@ func (e *CommentParser) parseArgs(name command.Name, args []string, flagSet *pfl // BuildPlanComment builds a plan comment for the specified args. func (e *CommentParser) BuildPlanComment(repoRelDir string, workspace string, project string, commentArgs []string) string { - flags := e.buildFlags(repoRelDir, workspace, project, false) + flags := e.buildFlags(repoRelDir, workspace, project, false, "") commentFlags := "" if len(commentArgs) > 0 { var flagsWithoutQuotes []string @@ -402,18 +419,18 @@ func (e *CommentParser) BuildPlanComment(repoRelDir string, workspace string, pr } // BuildApplyComment builds an apply comment for the specified args. -func (e *CommentParser) BuildApplyComment(repoRelDir string, workspace string, project string, autoMergeDisabled bool) string { - flags := e.buildFlags(repoRelDir, workspace, project, autoMergeDisabled) +func (e *CommentParser) BuildApplyComment(repoRelDir string, workspace string, project string, autoMergeDisabled bool, autoMergeMethod string) string { + flags := e.buildFlags(repoRelDir, workspace, project, autoMergeDisabled, autoMergeMethod) return fmt.Sprintf("%s %s%s", e.ExecutableName, command.Apply.String(), flags) } // BuildApprovePoliciesComment builds an apply comment for the specified args. func (e *CommentParser) BuildApprovePoliciesComment(repoRelDir string, workspace string, project string) string { - flags := e.buildFlags(repoRelDir, workspace, project, false) + flags := e.buildFlags(repoRelDir, workspace, project, false, "") return fmt.Sprintf("%s %s%s", e.ExecutableName, command.ApprovePolicies.String(), flags) } -func (e *CommentParser) buildFlags(repoRelDir string, workspace string, project string, autoMergeDisabled bool) string { +func (e *CommentParser) buildFlags(repoRelDir string, workspace string, project string, autoMergeDisabled bool, autoMergeMethod string) string { // Add quotes if dir has spaces. if strings.Contains(repoRelDir, " ") { repoRelDir = fmt.Sprintf("%q", repoRelDir) @@ -441,6 +458,9 @@ func (e *CommentParser) buildFlags(repoRelDir string, workspace string, project if autoMergeDisabled { flags = fmt.Sprintf("%s --%s", flags, autoMergeDisabledFlagLong) } + if autoMergeMethod != "" { + flags = fmt.Sprintf("%s --%s %s", flags, autoMergeMethodFlagLong, autoMergeMethod) + } return flags } diff --git a/server/events/comment_parser_test.go b/server/events/comment_parser_test.go index 45c22e7e5f..88ededcfff 100644 --- a/server/events/comment_parser_test.go +++ b/server/events/comment_parser_test.go @@ -729,6 +729,7 @@ func TestBuildPlanApplyVersionComment(t *testing.T) { workspace string project string autoMergeDisabled bool + autoMergeMethod string commentArgs []string expPlanFlags string expApplyFlags string @@ -824,6 +825,16 @@ func TestBuildPlanApplyVersionComment(t *testing.T) { expApplyFlags: "-d dir -w workspace --auto-merge-disabled", expVersionFlags: "-d dir -w workspace", }, + { + repoRelDir: "dir", + workspace: "workspace", + project: "", + autoMergeMethod: "squash", + commentArgs: []string{`"arg1"`, `"arg2"`, `arg3`}, + expPlanFlags: "-d dir -w workspace -- arg1 arg2 arg3", + expApplyFlags: "-d dir -w workspace --auto-merge-method squash", + expVersionFlags: "-d dir -w workspace", + }, } for _, c := range cases { @@ -834,7 +845,7 @@ func TestBuildPlanApplyVersionComment(t *testing.T) { actComment := commentParser.BuildPlanComment(c.repoRelDir, c.workspace, c.project, c.commentArgs) Equals(t, fmt.Sprintf("atlantis plan %s", c.expPlanFlags), actComment) case command.Apply: - actComment := commentParser.BuildApplyComment(c.repoRelDir, c.workspace, c.project, c.autoMergeDisabled) + actComment := commentParser.BuildApplyComment(c.repoRelDir, c.workspace, c.project, c.autoMergeDisabled, c.autoMergeMethod) Equals(t, fmt.Sprintf("atlantis apply %s", c.expApplyFlags), actComment) } } @@ -1020,14 +1031,18 @@ var PlanUsage = `Usage of plan: ` var ApplyUsage = `Usage of apply: - --auto-merge-disabled Disable automerge after 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 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. + --auto-merge-disabled Disable automerge after apply. + --auto-merge-method string Specifies the merge method for the VCS if + automerge is enabled. (Currently only implemented + for GitHub) + -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 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. ` var ApprovePolicyUsage = `Usage of approve_policies: diff --git a/server/events/event_parser.go b/server/events/event_parser.go index 62dd634a18..a6b4b363ac 100644 --- a/server/events/event_parser.go +++ b/server/events/event_parser.go @@ -128,6 +128,8 @@ type CommentCommand struct { SubName string // AutoMergeDisabled is true if the command should not automerge after apply. AutoMergeDisabled bool + // AutoMergeMethod specified the merge method for the VCS if automerge enabled. + AutoMergeMethod string // Verbose is true if the command should output verbosely. Verbose bool // Workspace is the name of the Terraform workspace to run the command in. @@ -177,11 +179,11 @@ func (c CommentCommand) IsAutoplan() bool { // String returns a string representation of the command. func (c CommentCommand) String() string { - return fmt.Sprintf("command=%q verbose=%t dir=%q workspace=%q project=%q policyset=%q, clear-policy-approval=%t, flags=%q", c.Name.String(), c.Verbose, c.RepoRelDir, c.Workspace, c.ProjectName, c.PolicySet, c.ClearPolicyApproval, strings.Join(c.Flags, ",")) + return fmt.Sprintf("command=%q, verbose=%t, dir=%q, workspace=%q, project=%q, policyset=%q, auto-merge-disabled=%t, auto-merge-method=%s, clear-policy-approval=%t, flags=%q", c.Name.String(), c.Verbose, c.RepoRelDir, c.Workspace, c.ProjectName, c.PolicySet, c.AutoMergeDisabled, c.AutoMergeMethod, c.ClearPolicyApproval, strings.Join(c.Flags, ",")) } // NewCommentCommand constructs a CommentCommand, setting all missing fields to defaults. -func NewCommentCommand(repoRelDir string, flags []string, name command.Name, subName string, verbose, autoMergeDisabled bool, workspace string, project string, policySet string, clearPolicyApproval bool) *CommentCommand { +func NewCommentCommand(repoRelDir string, flags []string, name command.Name, subName string, verbose, autoMergeDisabled bool, autoMergeMethod string, workspace string, project string, policySet string, clearPolicyApproval bool) *CommentCommand { // If repoRelDir was empty we want to keep it that way to indicate that it // wasn't specified in the comment. if repoRelDir != "" { @@ -198,6 +200,7 @@ func NewCommentCommand(repoRelDir string, flags []string, name command.Name, sub Verbose: verbose, Workspace: workspace, AutoMergeDisabled: autoMergeDisabled, + AutoMergeMethod: autoMergeMethod, ProjectName: project, PolicySet: policySet, ClearPolicyApproval: clearPolicyApproval, diff --git a/server/events/event_parser_test.go b/server/events/event_parser_test.go index 3b7b206a7d..fffe30e3eb 100644 --- a/server/events/event_parser_test.go +++ b/server/events/event_parser_test.go @@ -750,14 +750,14 @@ func TestNewCommand_CleansDir(t *testing.T) { for _, c := range cases { t.Run(c.RepoRelDir, func(t *testing.T) { - cmd := events.NewCommentCommand(c.RepoRelDir, nil, command.Plan, "", false, false, "workspace", "", "", false) + cmd := events.NewCommentCommand(c.RepoRelDir, nil, command.Plan, "", false, false, "", "workspace", "", "", false) Equals(t, c.ExpDir, cmd.RepoRelDir) }) } } func TestNewCommand_EmptyDirWorkspaceProject(t *testing.T) { - cmd := events.NewCommentCommand("", nil, command.Plan, "", false, false, "", "", "", false) + cmd := events.NewCommentCommand("", nil, command.Plan, "", false, false, "", "", "", "", false) Equals(t, events.CommentCommand{ RepoRelDir: "", Flags: nil, @@ -769,7 +769,7 @@ func TestNewCommand_EmptyDirWorkspaceProject(t *testing.T) { } func TestNewCommand_AllFieldsSet(t *testing.T) { - cmd := events.NewCommentCommand("dir", []string{"a", "b"}, command.Plan, "", true, false, "workspace", "project", "policyset", false) + cmd := events.NewCommentCommand("dir", []string{"a", "b"}, command.Plan, "", true, false, "", "workspace", "project", "policyset", false) Equals(t, events.CommentCommand{ Workspace: "workspace", RepoRelDir: "dir", @@ -816,7 +816,7 @@ func TestCommentCommand_IsAutoplan(t *testing.T) { } func TestCommentCommand_String(t *testing.T) { - exp := `command="plan" verbose=true dir="mydir" workspace="myworkspace" project="myproject" policyset="", clear-policy-approval=false, flags="flag1,flag2"` + exp := `command="plan", verbose=true, dir="mydir", workspace="myworkspace", project="myproject", policyset="", auto-merge-disabled=false, auto-merge-method=, clear-policy-approval=false, flags="flag1,flag2"` Equals(t, exp, (events.CommentCommand{ RepoRelDir: "mydir", Flags: []string{"flag1", "flag2"}, diff --git a/server/events/mocks/mock_comment_building.go b/server/events/mocks/mock_comment_building.go index 1e461a07ee..1d25d4eacd 100644 --- a/server/events/mocks/mock_comment_building.go +++ b/server/events/mocks/mock_comment_building.go @@ -24,49 +24,49 @@ func NewMockCommentBuilder(options ...pegomock.Option) *MockCommentBuilder { func (mock *MockCommentBuilder) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh } func (mock *MockCommentBuilder) FailHandler() pegomock.FailHandler { return mock.fail } -func (mock *MockCommentBuilder) BuildApplyComment(repoRelDir string, workspace string, project string, autoMergeDisabled bool) string { +func (mock *MockCommentBuilder) BuildApplyComment(repoRelDir string, workspace string, project string, autoMergeDisabled bool, mergeMethod string) string { if mock == nil { panic("mock must not be nil. Use myMock := NewMockCommentBuilder().") } - params := []pegomock.Param{repoRelDir, workspace, project, autoMergeDisabled} - result := pegomock.GetGenericMockFrom(mock).Invoke("BuildApplyComment", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) - var ret0 string - if len(result) != 0 { - if result[0] != nil { - ret0 = result[0].(string) + _params := []pegomock.Param{repoRelDir, workspace, project, autoMergeDisabled, mergeMethod} + _result := pegomock.GetGenericMockFrom(mock).Invoke("BuildApplyComment", _params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) + var _ret0 string + if len(_result) != 0 { + if _result[0] != nil { + _ret0 = _result[0].(string) } } - return ret0 + return _ret0 } func (mock *MockCommentBuilder) BuildApprovePoliciesComment(repoRelDir string, workspace string, project string) string { if mock == nil { panic("mock must not be nil. Use myMock := NewMockCommentBuilder().") } - params := []pegomock.Param{repoRelDir, workspace, project} - result := pegomock.GetGenericMockFrom(mock).Invoke("BuildApprovePoliciesComment", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) - var ret0 string - if len(result) != 0 { - if result[0] != nil { - ret0 = result[0].(string) + _params := []pegomock.Param{repoRelDir, workspace, project} + _result := pegomock.GetGenericMockFrom(mock).Invoke("BuildApprovePoliciesComment", _params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) + var _ret0 string + if len(_result) != 0 { + if _result[0] != nil { + _ret0 = _result[0].(string) } } - return ret0 + return _ret0 } func (mock *MockCommentBuilder) BuildPlanComment(repoRelDir string, workspace string, project string, commentArgs []string) string { if mock == nil { panic("mock must not be nil. Use myMock := NewMockCommentBuilder().") } - params := []pegomock.Param{repoRelDir, workspace, project, commentArgs} - result := pegomock.GetGenericMockFrom(mock).Invoke("BuildPlanComment", params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) - var ret0 string - if len(result) != 0 { - if result[0] != nil { - ret0 = result[0].(string) + _params := []pegomock.Param{repoRelDir, workspace, project, commentArgs} + _result := pegomock.GetGenericMockFrom(mock).Invoke("BuildPlanComment", _params, []reflect.Type{reflect.TypeOf((*string)(nil)).Elem()}) + var _ret0 string + if len(_result) != 0 { + if _result[0] != nil { + _ret0 = _result[0].(string) } } - return ret0 + return _ret0 } func (mock *MockCommentBuilder) VerifyWasCalledOnce() *VerifierMockCommentBuilder { @@ -106,9 +106,9 @@ type VerifierMockCommentBuilder struct { timeout time.Duration } -func (verifier *VerifierMockCommentBuilder) BuildApplyComment(repoRelDir string, workspace string, project string, autoMergeDisabled bool) *MockCommentBuilder_BuildApplyComment_OngoingVerification { - params := []pegomock.Param{repoRelDir, workspace, project, autoMergeDisabled} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildApplyComment", params, verifier.timeout) +func (verifier *VerifierMockCommentBuilder) BuildApplyComment(repoRelDir string, workspace string, project string, autoMergeDisabled bool, mergeMethod string) *MockCommentBuilder_BuildApplyComment_OngoingVerification { + _params := []pegomock.Param{repoRelDir, workspace, project, autoMergeDisabled, mergeMethod} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildApplyComment", _params, verifier.timeout) return &MockCommentBuilder_BuildApplyComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } @@ -117,37 +117,51 @@ type MockCommentBuilder_BuildApplyComment_OngoingVerification struct { methodInvocations []pegomock.MethodInvocation } -func (c *MockCommentBuilder_BuildApplyComment_OngoingVerification) GetCapturedArguments() (string, string, string, bool) { - repoRelDir, workspace, project, autoMergeDisabled := c.GetAllCapturedArguments() - return repoRelDir[len(repoRelDir)-1], workspace[len(workspace)-1], project[len(project)-1], autoMergeDisabled[len(autoMergeDisabled)-1] +func (c *MockCommentBuilder_BuildApplyComment_OngoingVerification) GetCapturedArguments() (string, string, string, bool, string) { + repoRelDir, workspace, project, autoMergeDisabled, mergeMethod := c.GetAllCapturedArguments() + return repoRelDir[len(repoRelDir)-1], workspace[len(workspace)-1], project[len(project)-1], autoMergeDisabled[len(autoMergeDisabled)-1], mergeMethod[len(mergeMethod)-1] } -func (c *MockCommentBuilder_BuildApplyComment_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []string, _param2 []string, _param3 []bool) { - params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) - if len(params) > 0 { - _param0 = make([]string, len(c.methodInvocations)) - for u, param := range params[0] { - _param0[u] = param.(string) +func (c *MockCommentBuilder_BuildApplyComment_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []string, _param2 []string, _param3 []bool, _param4 []string) { + _params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) + if len(_params) > 0 { + if len(_params) > 0 { + _param0 = make([]string, len(c.methodInvocations)) + for u, param := range _params[0] { + _param0[u] = param.(string) + } } - _param1 = make([]string, len(c.methodInvocations)) - for u, param := range params[1] { - _param1[u] = param.(string) + if len(_params) > 1 { + _param1 = make([]string, len(c.methodInvocations)) + for u, param := range _params[1] { + _param1[u] = param.(string) + } } - _param2 = make([]string, len(c.methodInvocations)) - for u, param := range params[2] { - _param2[u] = param.(string) + if len(_params) > 2 { + _param2 = make([]string, len(c.methodInvocations)) + for u, param := range _params[2] { + _param2[u] = param.(string) + } } - _param3 = make([]bool, len(c.methodInvocations)) - for u, param := range params[3] { - _param3[u] = param.(bool) + if len(_params) > 3 { + _param3 = make([]bool, len(c.methodInvocations)) + for u, param := range _params[3] { + _param3[u] = param.(bool) + } + } + if len(_params) > 4 { + _param4 = make([]string, len(c.methodInvocations)) + for u, param := range _params[4] { + _param4[u] = param.(string) + } } } return } func (verifier *VerifierMockCommentBuilder) BuildApprovePoliciesComment(repoRelDir string, workspace string, project string) *MockCommentBuilder_BuildApprovePoliciesComment_OngoingVerification { - params := []pegomock.Param{repoRelDir, workspace, project} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildApprovePoliciesComment", params, verifier.timeout) + _params := []pegomock.Param{repoRelDir, workspace, project} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildApprovePoliciesComment", _params, verifier.timeout) return &MockCommentBuilder_BuildApprovePoliciesComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } @@ -162,27 +176,33 @@ func (c *MockCommentBuilder_BuildApprovePoliciesComment_OngoingVerification) Get } func (c *MockCommentBuilder_BuildApprovePoliciesComment_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []string, _param2 []string) { - params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) - if len(params) > 0 { - _param0 = make([]string, len(c.methodInvocations)) - for u, param := range params[0] { - _param0[u] = param.(string) + _params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) + if len(_params) > 0 { + if len(_params) > 0 { + _param0 = make([]string, len(c.methodInvocations)) + for u, param := range _params[0] { + _param0[u] = param.(string) + } } - _param1 = make([]string, len(c.methodInvocations)) - for u, param := range params[1] { - _param1[u] = param.(string) + if len(_params) > 1 { + _param1 = make([]string, len(c.methodInvocations)) + for u, param := range _params[1] { + _param1[u] = param.(string) + } } - _param2 = make([]string, len(c.methodInvocations)) - for u, param := range params[2] { - _param2[u] = param.(string) + if len(_params) > 2 { + _param2 = make([]string, len(c.methodInvocations)) + for u, param := range _params[2] { + _param2[u] = param.(string) + } } } return } func (verifier *VerifierMockCommentBuilder) BuildPlanComment(repoRelDir string, workspace string, project string, commentArgs []string) *MockCommentBuilder_BuildPlanComment_OngoingVerification { - params := []pegomock.Param{repoRelDir, workspace, project, commentArgs} - methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildPlanComment", params, verifier.timeout) + _params := []pegomock.Param{repoRelDir, workspace, project, commentArgs} + methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildPlanComment", _params, verifier.timeout) return &MockCommentBuilder_BuildPlanComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} } @@ -197,23 +217,31 @@ func (c *MockCommentBuilder_BuildPlanComment_OngoingVerification) GetCapturedArg } func (c *MockCommentBuilder_BuildPlanComment_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []string, _param2 []string, _param3 [][]string) { - params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) - if len(params) > 0 { - _param0 = make([]string, len(c.methodInvocations)) - for u, param := range params[0] { - _param0[u] = param.(string) + _params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) + if len(_params) > 0 { + if len(_params) > 0 { + _param0 = make([]string, len(c.methodInvocations)) + for u, param := range _params[0] { + _param0[u] = param.(string) + } } - _param1 = make([]string, len(c.methodInvocations)) - for u, param := range params[1] { - _param1[u] = param.(string) + if len(_params) > 1 { + _param1 = make([]string, len(c.methodInvocations)) + for u, param := range _params[1] { + _param1[u] = param.(string) + } } - _param2 = make([]string, len(c.methodInvocations)) - for u, param := range params[2] { - _param2[u] = param.(string) + if len(_params) > 2 { + _param2 = make([]string, len(c.methodInvocations)) + for u, param := range _params[2] { + _param2[u] = param.(string) + } } - _param3 = make([][]string, len(c.methodInvocations)) - for u, param := range params[3] { - _param3[u] = param.([]string) + if len(_params) > 3 { + _param3 = make([][]string, len(c.methodInvocations)) + for u, param := range _params[3] { + _param3[u] = param.([]string) + } } } return diff --git a/server/events/models/models.go b/server/events/models/models.go index fe5c0ee09d..f7bd4790db 100644 --- a/server/events/models/models.go +++ b/server/events/models/models.go @@ -185,6 +185,9 @@ type PullRequestOptions struct { // When DeleteSourceBranchOnMerge flag is set to true VCS deletes the source branch after the PR is merged // Applied by GitLab & AzureDevops DeleteSourceBranchOnMerge bool + // MergeMethod specifies the merge method for the VCS + // Implemented only for Github + MergeMethod string } type PullRequestState int diff --git a/server/events/project_command_context_builder.go b/server/events/project_command_context_builder.go index 05d61fda6d..19c1c8ff34 100644 --- a/server/events/project_command_context_builder.go +++ b/server/events/project_command_context_builder.go @@ -130,7 +130,7 @@ func (cb *DefaultProjectCommandContextBuilder) BuildProjectContext( projectCmdContext := newProjectCommandContext( ctx, cmdName, - cb.CommentBuilder.BuildApplyComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name, prjCfg.AutoMergeDisabled), + cb.CommentBuilder.BuildApplyComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name, prjCfg.AutoMergeDisabled, prjCfg.AutoMergeMethod), cb.CommentBuilder.BuildApprovePoliciesComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name), cb.CommentBuilder.BuildPlanComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name, commentFlags), prjCfg, @@ -203,7 +203,7 @@ func (cb *PolicyCheckProjectCommandContextBuilder) BuildProjectContext( projectCmds = append(projectCmds, newProjectCommandContext( ctx, command.PolicyCheck, - cb.CommentBuilder.BuildApplyComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name, prjCfg.AutoMergeDisabled), + cb.CommentBuilder.BuildApplyComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name, prjCfg.AutoMergeDisabled, prjCfg.AutoMergeMethod), cb.CommentBuilder.BuildApprovePoliciesComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name), cb.CommentBuilder.BuildPlanComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name, commentFlags), prjCfg, diff --git a/server/events/project_command_context_builder_test.go b/server/events/project_command_context_builder_test.go index c3d75e950c..84ce0ff630 100644 --- a/server/events/project_command_context_builder_test.go +++ b/server/events/project_command_context_builder_test.go @@ -51,7 +51,7 @@ func TestProjectCommandContextBuilder_PullStatus(t *testing.T) { t.Run("with project name defined", func(t *testing.T) { When(mockCommentBuilder.BuildPlanComment(projRepoRelDir, projWorkspace, projName, []string{})).ThenReturn(expectedPlanCmt) - When(mockCommentBuilder.BuildApplyComment(projRepoRelDir, projWorkspace, projName, false)).ThenReturn(expectedApplyCmt) + When(mockCommentBuilder.BuildApplyComment(projRepoRelDir, projWorkspace, projName, false, "")).ThenReturn(expectedApplyCmt) pullStatus.Projects = []models.ProjectStatus{ { @@ -68,7 +68,7 @@ func TestProjectCommandContextBuilder_PullStatus(t *testing.T) { t.Run("with no project name defined", func(t *testing.T) { projCfg.Name = "" When(mockCommentBuilder.BuildPlanComment(projRepoRelDir, projWorkspace, "", []string{})).ThenReturn(expectedPlanCmt) - When(mockCommentBuilder.BuildApplyComment(projRepoRelDir, projWorkspace, "", false)).ThenReturn(expectedApplyCmt) + When(mockCommentBuilder.BuildApplyComment(projRepoRelDir, projWorkspace, "", false, "")).ThenReturn(expectedApplyCmt) pullStatus.Projects = []models.ProjectStatus{ { Status: models.ErroredPlanStatus, @@ -88,7 +88,7 @@ func TestProjectCommandContextBuilder_PullStatus(t *testing.T) { t.Run("when ParallelApply is set to true", func(t *testing.T) { projCfg.Name = "Apply Comment" When(mockCommentBuilder.BuildPlanComment(projRepoRelDir, projWorkspace, "", []string{})).ThenReturn(expectedPlanCmt) - When(mockCommentBuilder.BuildApplyComment(projRepoRelDir, projWorkspace, "", false)).ThenReturn(expectedApplyCmt) + When(mockCommentBuilder.BuildApplyComment(projRepoRelDir, projWorkspace, "", false, "")).ThenReturn(expectedApplyCmt) pullStatus.Projects = []models.ProjectStatus{ { Status: models.ErroredPlanStatus, @@ -109,7 +109,7 @@ func TestProjectCommandContextBuilder_PullStatus(t *testing.T) { t.Run("when AbortOnExcecutionOrderFail is set to true", func(t *testing.T) { projCfg.Name = "Apply Comment" When(mockCommentBuilder.BuildPlanComment(projRepoRelDir, projWorkspace, "", []string{})).ThenReturn(expectedPlanCmt) - When(mockCommentBuilder.BuildApplyComment(projRepoRelDir, projWorkspace, "", false)).ThenReturn(expectedApplyCmt) + When(mockCommentBuilder.BuildApplyComment(projRepoRelDir, projWorkspace, "", false, "")).ThenReturn(expectedApplyCmt) pullStatus.Projects = []models.ProjectStatus{ { Status: models.ErroredPlanStatus, diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index ddc0ff1e1b..826cff8b09 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -17,7 +17,10 @@ import ( "context" "encoding/base64" "fmt" + "maps" "net/http" + "slices" + "sort" "strconv" "strings" "time" @@ -885,7 +888,7 @@ func (g *GithubClient) UpdateStatus(logger logging.SimpleLogging, repo models.Re } // MergePull merges the pull request. -func (g *GithubClient) MergePull(logger logging.SimpleLogging, pull models.PullRequest, _ models.PullRequestOptions) error { +func (g *GithubClient) MergePull(logger logging.SimpleLogging, pull models.PullRequest, pullOptions models.PullRequestOptions) error { logger.Debug("Merging GitHub pull request %d", pull.Num) // Users can set their repo to disallow certain types of merging. // We detect which types aren't allowed and use the type that is. @@ -896,17 +899,42 @@ func (g *GithubClient) MergePull(logger logging.SimpleLogging, pull models.PullR if err != nil { return errors.Wrap(err, "fetching repo info") } + const ( defaultMergeMethod = "merge" rebaseMergeMethod = "rebase" squashMergeMethod = "squash" ) - method := defaultMergeMethod - if !repo.GetAllowMergeCommit() { - if repo.GetAllowRebaseMerge() { - method = rebaseMergeMethod - } else if repo.GetAllowSquashMerge() { - method = squashMergeMethod + + mergeMethodsAllow := map[string]func() bool{ + defaultMergeMethod: repo.GetAllowMergeCommit, + rebaseMergeMethod: repo.GetAllowRebaseMerge, + squashMergeMethod: repo.GetAllowSquashMerge, + } + + mergeMethodsName := slices.Collect(maps.Keys(mergeMethodsAllow)) + sort.Strings(mergeMethodsName) + + var method string + if pullOptions.MergeMethod != "" { + method = pullOptions.MergeMethod + + isMethodAllowed, isMethodExist := mergeMethodsAllow[method] + if !isMethodExist { + return fmt.Errorf("Merge method '%s' is unknown. Specify one of the valid values: '%s'", method, strings.Join(mergeMethodsName, ", ")) + } + + if !isMethodAllowed() { + return fmt.Errorf("Merge method '%s' is not allowed by the repository Pull Request settings", method) + } + } else { + method = defaultMergeMethod + if !repo.GetAllowMergeCommit() { + if repo.GetAllowRebaseMerge() { + method = rebaseMergeMethod + } else if repo.GetAllowSquashMerge() { + method = squashMergeMethod + } } } diff --git a/server/events/vcs/github_client_test.go b/server/events/vcs/github_client_test.go index ef97c64245..632fe6bca6 100644 --- a/server/events/vcs/github_client_test.go +++ b/server/events/vcs/github_client_test.go @@ -977,10 +977,12 @@ func TestGithubClient_MergePullHandlesError(t *testing.T) { func TestGithubClient_MergePullCorrectMethod(t *testing.T) { logger := logging.NewNoopLogger(t) cases := map[string]struct { - allowMerge bool - allowRebase bool - allowSquash bool - expMethod string + allowMerge bool + allowRebase bool + allowSquash bool + mergeMethodOption string + expMethod string + expErr string }{ "all true": { allowMerge: true, @@ -1012,6 +1014,59 @@ func TestGithubClient_MergePullCorrectMethod(t *testing.T) { allowSquash: false, expMethod: "rebase", }, + "all true: merge with merge: overrided by command": { + allowMerge: true, + allowRebase: true, + allowSquash: true, + mergeMethodOption: "merge", + expMethod: "merge", + }, + "all true: merge with rebase: overrided by command": { + allowMerge: true, + allowRebase: true, + allowSquash: true, + mergeMethodOption: "rebase", + expMethod: "rebase", + }, + "all true: merge with squash: overrided by command": { + allowMerge: true, + allowRebase: true, + allowSquash: true, + mergeMethodOption: "squash", + expMethod: "squash", + }, + "merge with merge: overridden by command: merge not allowed": { + allowMerge: false, + allowRebase: true, + allowSquash: true, + mergeMethodOption: "merge", + expMethod: "", + expErr: "Merge method 'merge' is not allowed by the repository Pull Request settings", + }, + "merge with rebase: overridden by command: rebase not allowed": { + allowMerge: true, + allowRebase: false, + allowSquash: true, + mergeMethodOption: "rebase", + expMethod: "", + expErr: "Merge method 'rebase' is not allowed by the repository Pull Request settings", + }, + "merge with squash: overridden by command: squash not allowed": { + allowMerge: true, + allowRebase: true, + allowSquash: false, + mergeMethodOption: "squash", + expMethod: "", + expErr: "Merge method 'squash' is not allowed by the repository Pull Request settings", + }, + "merge with unknown: overridden by command: unknown doesn't exist": { + allowMerge: true, + allowRebase: true, + allowSquash: true, + mergeMethodOption: "unknown", + expMethod: "", + expErr: "Merge method 'unknown' is unknown. Specify one of the valid values: 'merge, rebase, squash'", + }, } for name, c := range cases { @@ -1086,9 +1141,14 @@ func TestGithubClient_MergePullCorrectMethod(t *testing.T) { Num: 1, }, models.PullRequestOptions{ DeleteSourceBranchOnMerge: false, + MergeMethod: c.mergeMethodOption, }) - Ok(t, err) + if c.expErr == "" { + Ok(t, err) + } else { + ErrContains(t, c.expErr, err) + } }) } } From c68502b3abf127faad8fcc73a0debde9c46d002a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Nov 2024 19:00:40 -0700 Subject: [PATCH 03/38] chore(deps): update actions/checkout action to v4.2.2 in .github/workflows/scorecard.yml (main) (#5053) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 9f777b580e..23d3a79af1 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -20,7 +20,7 @@ jobs: steps: - name: 'Checkout code' - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false show-progress: false From e6e7c91c08b584438a55e323871d4a89f168dbe5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 03:55:13 +0000 Subject: [PATCH 04/38] chore(deps): update dependency opentofu/opentofu to v1.8.4 in dockerfile (main) (#5054) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 592e679766..db9e7b4f4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ ARG GOLANG_TAG=1.23.0-alpine@sha256:d0b31558e6b3e4cc59f6011d79905835108c919143eb # renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp ARG DEFAULT_TERRAFORM_VERSION=1.9.8 # renovate: datasource=github-releases depName=opentofu/opentofu versioning=hashicorp -ARG DEFAULT_OPENTOFU_VERSION=1.8.3 +ARG DEFAULT_OPENTOFU_VERSION=1.8.4 # renovate: datasource=github-releases depName=open-policy-agent/conftest ARG DEFAULT_CONFTEST_VERSION=0.56.0 From f23ab63fc12b371b85d7eb3b898c69460430ad18 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 17:21:42 -0800 Subject: [PATCH 05/38] chore(deps): update dependency open-policy-agent/conftest to v0.56.0 in testing/dockerfile (main) (#5055) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- testing/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/Dockerfile b/testing/Dockerfile index 8ea0ceada3..68613b0e3d 100644 --- a/testing/Dockerfile +++ b/testing/Dockerfile @@ -16,7 +16,7 @@ RUN case $(uname -m) in x86_64|amd64) ARCH="amd64" ;; aarch64|arm64|armv7l) ARCH # Install conftest # renovate: datasource=github-releases depName=open-policy-agent/conftest -ENV CONFTEST_VERSION=0.55.0 +ENV CONFTEST_VERSION=0.56.0 SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN case $(uname -m) in x86_64|amd64) ARCH="x86_64" ;; aarch64|arm64|armv7l) ARCH="arm64" ;; esac && \ curl -LOs https://github.com/open-policy-agent/conftest/releases/download/v${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION}_Linux_${ARCH}.tar.gz && \ From 497dcd7467244dc9b826d6e7b94e3aaeec116f8c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 03:54:02 +0000 Subject: [PATCH 06/38] chore(deps): update ghcr.io/runatlantis/testing-env:latest docker digest to 5c56ee1 in .github/workflows/test.yml (main) (#5056) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 283fd75e10..bf8b437926 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,7 +48,7 @@ jobs: if: needs.changes.outputs.should-run-tests == 'true' name: Tests runs-on: ubuntu-24.04 - container: ghcr.io/runatlantis/testing-env:latest@sha256:af0b45be2e53fe0762e51adb9493d049fe947b35c0f8c3ad79f89200d6c303ca + container: ghcr.io/runatlantis/testing-env:latest@sha256:5c56ee1df3dd9ea426bee50df43e2407df054e81f4b4eb183173e90a11f86922 steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 From e4dea0311debf65c2af9bea0ae4daf505b5cef08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Lundstr=C3=B6m?= Date: Mon, 4 Nov 2024 18:59:23 +0100 Subject: [PATCH 07/38] fix: Ignore apply check(s) even if missing from rollup when determining mergeability for GitHub (#4974) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Henrik Lundström --- server/events/vcs/github_client.go | 7 ++- server/events/vcs/github_client_test.go | 6 +++ .../ruleset-atlantis-apply-expected.json | 52 +++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 server/events/vcs/testdata/github-pull-request-mergeability/ruleset-atlantis-apply-expected.json diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index 826cff8b09..698d4dc999 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -714,8 +714,7 @@ func CheckRunPassed(checkRun CheckRun) bool { } func StatusContextPassed(statusContext StatusContext, vcsstatusname string) bool { - return strings.HasPrefix(string(statusContext.Context), fmt.Sprintf("%s/%s", vcsstatusname, command.Apply.String())) || - statusContext.State == "SUCCESS" + return statusContext.State == "SUCCESS" } func ExpectedCheckPassed(expectedContext githubv4.String, checkRuns []CheckRun, statusContexts []StatusContext, vcsstatusname string) bool { @@ -775,6 +774,10 @@ func (g *GithubClient) IsMergeableMinusApply(logger logging.SimpleLogging, repo // Go through all checks and workflows required by branch protection or rulesets // Make sure that they can all be found in the statusCheckRollup and that they all pass for _, requiredCheck := range requiredChecks { + if strings.HasPrefix(string(requiredCheck), fmt.Sprintf("%s/%s", vcsstatusname, command.Apply.String())) { + // Ignore atlantis apply check(s) + continue + } if !ExpectedCheckPassed(requiredCheck, checkRuns, statusContexts, vcsstatusname) { logger.Debug("%s: Expected Required Check: %s", notMergeablePrefix, requiredCheck) return false, nil diff --git a/server/events/vcs/github_client_test.go b/server/events/vcs/github_client_test.go index 632fe6bca6..b668f9e919 100644 --- a/server/events/vcs/github_client_test.go +++ b/server/events/vcs/github_client_test.go @@ -691,6 +691,12 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApply(t *testing.T) `"APPROVED"`, false, }, + { + "blocked", + "ruleset-atlantis-apply-expected.json", + `"APPROVED"`, + true, + }, { "blocked", "ruleset-optional-check-failed.json", diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-atlantis-apply-expected.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-atlantis-apply-expected.json new file mode 100644 index 0000000000..f83b126d4e --- /dev/null +++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-atlantis-apply-expected.json @@ -0,0 +1,52 @@ +{ + "data": { + "repository": { + "pullRequest": { + "reviewDecision": null, + "baseRef": { + "branchProtectionRule": { + "requiredStatusChecks": [] + }, + "rules": { + "pageInfo": { + "endCursor": "QWERTY", + "hasNextPage": false + }, + "nodes": [ + { + "type": "REQUIRED_STATUS_CHECKS", + "repositoryRuleset": { + "enforcement": "ACTIVE" + }, + "parameters": { + "requiredStatusChecks": [ + { + "context": "atlantis/apply" + } + ] + } + } + ] + } + }, + "commits": { + "nodes": [ + { + "commit": { + "statusCheckRollup": { + "contexts": { + "pageInfo": { + "endCursor": "QWERTY", + "hasNextPage": false + }, + "nodes": [] + } + } + } + } + ] + } + } + } + } +} From 8285a0faf21cf9a294fa0297dfed3c4c84ea6a5c Mon Sep 17 00:00:00 2001 From: Andrej Svenke Date: Mon, 4 Nov 2024 19:29:47 +0100 Subject: [PATCH 08/38] feat: set the shell for workflow command steps (#5024) Signed-off-by: anryko --- runatlantis.io/docs/custom-workflows.md | 31 ++- server/core/config/raw/step.go | 261 ++++++++++++------ server/core/config/raw/step_test.go | 109 +++++++- server/core/config/valid/repo_cfg.go | 12 + server/core/runtime/env_step_runner.go | 11 +- server/core/runtime/env_step_runner_test.go | 2 +- .../runtime/models/shell_command_runner.go | 33 ++- .../models/shell_command_runner_test.go | 4 +- server/core/runtime/multienv_step_runner.go | 11 +- .../core/runtime/multienv_step_runner_test.go | 2 +- server/core/runtime/run_step_runner.go | 12 +- server/core/runtime/run_step_runner_test.go | 2 +- server/core/terraform/terraform_client.go | 2 +- .../terraform_client_internal_test.go | 2 +- .../events/mocks/mock_custom_step_runner.go | 4 +- server/events/mocks/mock_env_step_runner.go | 10 +- server/events/project_command_runner.go | 34 ++- server/events/project_command_runner_test.go | 12 +- 18 files changed, 414 insertions(+), 140 deletions(-) diff --git a/runatlantis.io/docs/custom-workflows.md b/runatlantis.io/docs/custom-workflows.md index f2edd827ae..af655abf26 100644 --- a/runatlantis.io/docs/custom-workflows.md +++ b/runatlantis.io/docs/custom-workflows.md @@ -599,6 +599,10 @@ Full ```yaml - run: command: custom-command arg1 arg2 + shell: sh + shellArgs: + - "--debug" + - "-c" output: show ``` @@ -606,6 +610,8 @@ Full |-----|--------------------------------------------------------------|---------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | run | map\[string -> string\] | none | no | Run a custom command | | run.command | string | none | yes | Shell command to run | +| run.shell | string | "sh" | no | Name of the shell to use for command execution | +| run.shellArgs | string or []string | "-c" | no | Command line arguments to be passed to the shell. Cannot be set without `shell` | | run.output | string | "show" | no | How to post-process the output of this command when posted in the PR comment. The options are
*`show` - preserve the full output
* `hide` - hide output from comment (still visible in the real-time streaming output)
* `strip_refreshing` - hide all output up until and including the last line containing "Refreshing...". This matches the behavior of the built-in `plan` command | #### Native Environment Variables @@ -664,6 +670,13 @@ as the environment variable value. - env: name: ENV_NAME_2 command: 'echo "dynamic-value-$(date)"' +- env: + name: ENV_NAME_3 + command: echo ${DIR%$REPO_REL_DIR} + shell: bash + shellArgs: + - "--verbose" + - "-c" ``` | Key | Type | Default | Required | Description | @@ -672,6 +685,8 @@ as the environment variable value. | env.name | string | none | yes | Name of the environment variable | | env.value | string | none | no | Set the value of the environment variable to a hard-coded string. Cannot be set at the same time as `command` | | env.command | string | none | no | Set the value of the environment variable to the output of a command. Cannot be set at the same time as `value` | +| env.shell | string | "sh" | no | Name of the shell to use for command execution. Cannot be set without `command` | +| env.shellArgs | string or []string | "-c" | no | Command line arguments to be passed to the shell. Cannot be set without `shell` | ::: tip Notes @@ -699,14 +714,20 @@ Full: ```yaml - multienv: command: custom-command + shell: bash + shellArgs: + - "--verbose" + - "-c" output: show ``` -| Key | Type | Default | Required | Description | -|------------------|-----------------------|---------|----------|-------------------------------------------------------------------------------------| -| multienv | map[string -> string] | none | no | Run a custom command and add printed environment variables | -| multienv.command | string | none | yes | Name of the custom script to run | -| multienv.output | string | "show" | no | Setting output to "hide" will supress the message obout added environment variables | +| Key | Type | Default | Required | Description | +|--------------------|-----------------------|---------|----------|-------------------------------------------------------------------------------------| +| multienv | map[string -> string] | none | no | Run a custom command and add printed environment variables | +| multienv.command | string | none | yes | Name of the custom script to run | +| multienv.shell | string | "sh" | no | Name of the shell to use for command execution | +| multienv.shellArgs | string or []string | "-c" | no | Command line arguments to be passed to the shell. Cannot be set without `shell` | +| multienv.output | string | "show" | no | Setting output to "hide" will supress the message obout added environment variables | The output of the command execution must have the following format: `EnvVar1Name=value1,EnvVar2Name=value2,EnvVar3Name=value3` diff --git a/server/core/config/raw/step.go b/server/core/config/raw/step.go index 581be49c64..6ada93488c 100644 --- a/server/core/config/raw/step.go +++ b/server/core/config/raw/step.go @@ -9,6 +9,7 @@ import ( validation "github.com/go-ozzo/ozzo-validation" "github.com/runatlantis/atlantis/server/core/config/valid" + "github.com/runatlantis/atlantis/server/utils" ) const ( @@ -27,45 +28,58 @@ const ( MultiEnvStepName = "multienv" ImportStepName = "import" StateRmStepName = "state_rm" + ShellArgKey = "shell" + ShellArgsArgKey = "shellArgs" ) -// Step represents a single action/command to perform. In YAML, it can be set as -// 1. A single string for a built-in command: -// - init -// - plan -// - policy_check -// -// 2. A map for an env step with name and command or value, or a run step with a command and output config -// - env: -// name: test -// command: echo 312 -// value: value -// - multienv: -// command: envs.sh -// outpiut: hide -// - run: -// command: my custom command -// output: hide -// -// 3. A map for a built-in command and extra_args: -// - plan: -// extra_args: [-var-file=staging.tfvars] -// -// 4. A map for a custom run command: -// - run: my custom command -// -// Here we parse step in the most generic fashion possible. See fields for more -// details. +/* +Step represents a single action/command to perform. In YAML, it can be set as +1. A single string for a built-in command: + - init + - plan + - policy_check + +2. A map for an env step with name and command or value, or a run step with a command and output config + - env: + name: test_command + command: echo 312 + - env: + name: test_value + value: value + - env: + name: test_bash_command + command: echo ${test_value::7} + shell: bash + shellArgs: ["--verbose", "-c"] + - multienv: + command: envs.sh + output: hide + shell: sh + shellArgs: -c + - run: + command: my custom command + output: hide + +3. A map for a built-in command and extra_args: + - plan: + extra_args: [-var-file=staging.tfvars] + +4. A map for a custom run command: + - run: my custom command + +Here we parse step in the most generic fashion possible. See fields for more +details. +*/ type Step struct { // Key will be set in case #1 and #3 above to the key. In case #2, there // could be multiple keys (since the element is a map) so we don't set Key. Key *string - // CommandMap will be set in case #2 above. - CommandMap map[string]map[string]string - // Map will be set in case #3 above. - Map map[string]map[string][]string // StringVal will be set in case #4 above. StringVal map[string]string + // Map will be set in case #3 above. + Map map[string]map[string][]string + // CommandMap will be set in case #2 above. + CommandMap map[string]map[string]interface{} } func (s *Step) UnmarshalYAML(unmarshal func(interface{}) error) error { @@ -142,7 +156,8 @@ func (s Step) Validate() error { } for k := range args { if k != ExtraArgsKey { - return fmt.Errorf("built-in steps only support a single %s key, found %q in step %s", ExtraArgsKey, k, stepName) + return fmt.Errorf("built-in steps only support a single %s key, found %q in step %s", + ExtraArgsKey, k, stepName) } } } @@ -150,7 +165,7 @@ func (s Step) Validate() error { } envOrRunOrMultiEnvStep := func(value interface{}) error { - elem := value.(map[string]map[string]string) + elem := value.(map[string]map[string]interface{}) var keys []string for k := range elem { keys = append(keys, k) @@ -169,63 +184,100 @@ func (s Step) Validate() error { stepName := keys[0] args := elem[keys[0]] - switch stepName { - case EnvStepName: - var argKeys []string - for k := range args { - argKeys = append(argKeys, k) + var argKeys []string + for k := range args { + argKeys = append(argKeys, k) + } + argMap := make(map[string]interface{}) + for k, v := range args { + argMap[k] = v + } + // Sort so tests can be deterministic. + sort.Strings(argKeys) + + // Validate keys common for all the steps. + if utils.SlicesContains(argKeys, ShellArgKey) && !utils.SlicesContains(argKeys, CommandArgKey) { + return fmt.Errorf("workflow steps only support %q key in combination with %q key", + ShellArgKey, CommandArgKey) + } + if utils.SlicesContains(argKeys, ShellArgsArgKey) && !utils.SlicesContains(argKeys, ShellArgKey) { + return fmt.Errorf("workflow steps only support %q key in combination with %q key", + ShellArgsArgKey, ShellArgKey) + } + + switch t := argMap[ShellArgsArgKey].(type) { + case nil: + case string: + case []interface{}: + for _, e := range t { + if _, ok := e.(string); !ok { + return fmt.Errorf("%q step %q option must contain only strings, found %v\n", + stepName, ShellArgsArgKey, e) + } } - // Sort so tests can be deterministic. - sort.Strings(argKeys) + default: + return fmt.Errorf("%q step %q option must be a string or a list of strings, found %v\n", + stepName, ShellArgsArgKey, t) + } + delete(argMap, ShellArgsArgKey) + delete(argMap, ShellArgKey) + // Validate keys per step type. + switch stepName { + case EnvStepName: foundNameKey := false for _, k := range argKeys { - if k != NameArgKey && k != CommandArgKey && k != ValueArgKey { - return fmt.Errorf("env steps only support keys %q, %q and %q, found key %q", NameArgKey, ValueArgKey, CommandArgKey, k) + if k != NameArgKey && k != CommandArgKey && k != ValueArgKey && k != ShellArgKey && k != ShellArgsArgKey { + return fmt.Errorf("env steps only support keys %q, %q, %q, %q and %q, found key %q", + NameArgKey, ValueArgKey, CommandArgKey, ShellArgKey, ShellArgsArgKey, k) } if k == NameArgKey { foundNameKey = true } } + delete(argMap, CommandArgKey) if !foundNameKey { return fmt.Errorf("env steps must have a %q key set", NameArgKey) } - // If we have 3 keys at this point then they've set both command and value. - if len(argKeys) != 2 { + delete(argMap, NameArgKey) + if utils.SlicesContains(argKeys, ValueArgKey) && utils.SlicesContains(argKeys, CommandArgKey) { return fmt.Errorf("env steps only support one of the %q or %q keys, found both", ValueArgKey, CommandArgKey) } + delete(argMap, ValueArgKey) case RunStepName, MultiEnvStepName: - argsCopy := make(map[string]string) - for k, v := range args { - argsCopy[k] = v - } - args = argsCopy - if _, ok := args[CommandArgKey]; !ok { + if _, ok := argMap[CommandArgKey].(string); !ok { return fmt.Errorf("%q step must have a %q key set", stepName, CommandArgKey) } - delete(args, CommandArgKey) - if v, ok := args[OutputArgKey]; ok { - if stepName == RunStepName && !(v == valid.PostProcessRunOutputShow || v == valid.PostProcessRunOutputHide || v == valid.PostProcessRunOutputStripRefreshing) { - return fmt.Errorf("run step %q option must be one of %q, %q, or %q", OutputArgKey, valid.PostProcessRunOutputShow, valid.PostProcessRunOutputHide, valid.PostProcessRunOutputStripRefreshing) - } else if stepName == MultiEnvStepName && !(v == valid.PostProcessRunOutputShow || v == valid.PostProcessRunOutputHide) { - return fmt.Errorf("multienv step %q option must be %q or %q", OutputArgKey, valid.PostProcessRunOutputShow, valid.PostProcessRunOutputHide) - } - } - delete(args, OutputArgKey) - if len(args) > 0 { - var argKeys []string - for k := range args { - argKeys = append(argKeys, k) + delete(argMap, CommandArgKey) + if v, ok := argMap[OutputArgKey].(string); ok { + if stepName == RunStepName && !(v == valid.PostProcessRunOutputShow || + v == valid.PostProcessRunOutputHide || v == valid.PostProcessRunOutputStripRefreshing) { + return fmt.Errorf("run step %q option must be one of %q, %q, or %q", + OutputArgKey, valid.PostProcessRunOutputShow, valid.PostProcessRunOutputHide, + valid.PostProcessRunOutputStripRefreshing) + } else if stepName == MultiEnvStepName && !(v == valid.PostProcessRunOutputShow || + v == valid.PostProcessRunOutputHide) { + return fmt.Errorf("multienv step %q option must be %q or %q", + OutputArgKey, valid.PostProcessRunOutputShow, valid.PostProcessRunOutputHide) } - // Sort so tests can be deterministic. - sort.Strings(argKeys) - return fmt.Errorf("%q steps only support keys %q and %q, found extra keys %q", stepName, CommandArgKey, OutputArgKey, strings.Join(argKeys, ",")) } + delete(argMap, OutputArgKey) default: return fmt.Errorf("%q is not a valid step type", stepName) } + if len(argMap) > 0 { + var argKeys []string + for k := range argMap { + argKeys = append(argKeys, k) + } + // Sort so tests can be deterministic. + sort.Strings(argKeys) + return fmt.Errorf("%q steps only support keys %q, %q, %q and %q, found extra keys %q", + stepName, CommandArgKey, OutputArgKey, ShellArgKey, ShellArgsArgKey, strings.Join(argKeys, ",")) + } + return nil } @@ -278,16 +330,40 @@ func (s Step) ToValid() valid.Step { // After validation we assume there's only one key and it's a valid // step name so we just use the first one. for stepName, stepArgs := range s.CommandMap { - step := valid.Step{ - StepName: stepName, - EnvVarName: stepArgs[NameArgKey], - RunCommand: stepArgs[CommandArgKey], - EnvVarValue: stepArgs[ValueArgKey], - Output: valid.PostProcessRunOutputOption(stepArgs[OutputArgKey]), + step := valid.Step{StepName: stepName} + if name, ok := stepArgs[NameArgKey].(string); ok { + step.EnvVarName = name + } + if command, ok := stepArgs[CommandArgKey].(string); ok { + step.RunCommand = command + } + if value, ok := stepArgs[ValueArgKey].(string); ok { + step.EnvVarValue = value + } + if output, ok := stepArgs[OutputArgKey].(string); ok { + step.Output = valid.PostProcessRunOutputOption(output) + } + if shell, ok := stepArgs[ShellArgKey].(string); ok { + step.RunShell = &valid.CommandShell{ + Shell: shell, + ShellArgs: []string{"-c"}, + } } if step.StepName == RunStepName && step.Output == "" { step.Output = valid.PostProcessRunOutputShow } + + switch t := stepArgs[ShellArgsArgKey].(type) { + case nil: + case string: + step.RunShell.ShellArgs = strings.Split(t, " ") + case []interface{}: + step.RunShell.ShellArgs = []string{} + for _, e := range t { + step.RunShell.ShellArgs = append(step.RunShell.ShellArgs, e.(string)) + } + } + return step } } @@ -341,6 +417,17 @@ func (s *Step) unmarshalGeneric(unmarshal func(interface{}) error) error { return nil } + // Try to unmarshal as a custom run step, ex. + // steps: + // - run: my command + // We validate if the key is run later. + var runStep map[string]string + err = unmarshal(&runStep) + if err == nil { + s.StringVal = runStep + return nil + } + // This represents a step with extra_args, ex: // init: // extra_args: [a, b] @@ -353,26 +440,20 @@ func (s *Step) unmarshalGeneric(unmarshal func(interface{}) error) error { return nil } - // This represents an env step, ex: - // env: - // name: k - // value: hi //optional - // command: exec - var envStep map[string]map[string]string - err = unmarshal(&envStep) - if err == nil { - s.CommandMap = envStep - return nil - } - - // Try to unmarshal as a custom run step, ex. + // This represents a command steps env, run, and multienv, ex: // steps: - // - run: my command - // We validate if the key is run later. - var runStep map[string]string - err = unmarshal(&runStep) + // - env: + // name: k + // command: exec + // - run: + // name: test_bash_command + // command: echo ${test_value::7} + // shell: bash + // shellArgs: ["--verbose", "-c"] + var commandStep map[string]map[string]interface{} + err = unmarshal(&commandStep) if err == nil { - s.StringVal = runStep + s.CommandMap = commandStep return nil } diff --git a/server/core/config/raw/step_test.go b/server/core/config/raw/step_test.go index f47c497e6f..f8b9ae8b11 100644 --- a/server/core/config/raw/step_test.go +++ b/server/core/config/raw/step_test.go @@ -143,12 +143,12 @@ key: value`, // Errors { - description: "extra args style no slice strings", + description: "extra args style no map strings", input: ` key: - value: - another: map`, - expErr: "yaml: unmarshal errors:\n line 3: cannot unmarshal !!map into string", + - value: + another: map`, + expErr: "yaml: unmarshal errors:\n line 3: cannot unmarshal !!seq into map[string]interface {}", }, } @@ -236,6 +236,47 @@ func TestStep_Validate(t *testing.T) { }, expErr: "", }, + { + description: "env shell", + input: raw.Step{ + CommandMap: EnvType{ + "env": { + "name": "test", + "command": "echo 123", + "shell": "bash", + }, + }, + }, + expErr: "", + }, + { + description: "env shellArgs string", + input: raw.Step{ + CommandMap: EnvType{ + "env": { + "name": "test", + "command": "echo 123", + "shell": "bash", + "shellArgs": "-c", + }, + }, + }, + expErr: "", + }, + { + description: "env shellArgs list of strings", + input: raw.Step{ + CommandMap: EnvType{ + "env": { + "name": "test", + "command": "echo 123", + "shell": "bash", + "shellArgs": []interface{}{"-c", "--debug"}, + }, + }, + }, + expErr: "", + }, { description: "apply extra_args", input: raw.Step{ @@ -371,7 +412,7 @@ func TestStep_Validate(t *testing.T) { }, }, }, - expErr: "env steps only support keys \"name\", \"value\" and \"command\", found key \"abc\"", + expErr: "env steps only support keys \"name\", \"value\", \"command\", \"shell\" and \"shellArgs\", found key \"abc\"", }, { description: "env step with both command and value set", @@ -386,6 +427,58 @@ func TestStep_Validate(t *testing.T) { }, expErr: "env steps only support one of the \"value\" or \"command\" keys, found both", }, + { + description: "env step with shell set but not command", + input: raw.Step{ + CommandMap: EnvType{ + "env": { + "name": "name", + "shell": "bash", + }, + }, + }, + expErr: "workflow steps only support \"shell\" key in combination with \"command\" key", + }, + { + description: "env step with shellArgs set but not shell", + input: raw.Step{ + CommandMap: EnvType{ + "env": { + "name": "name", + "shellArgs": "-c", + }, + }, + }, + expErr: "workflow steps only support \"shellArgs\" key in combination with \"shell\" key", + }, + { + description: "run step with shellArgs is not list of strings", + input: raw.Step{ + CommandMap: EnvType{ + "run": { + "name": "name", + "command": "echo", + "shell": "shell", + "shellArgs": []int{42, 42}, + }, + }, + }, + expErr: "\"run\" step \"shellArgs\" option must be a string or a list of strings, found [42 42]\n", + }, + { + description: "run step with shellArgs contain not strings", + input: raw.Step{ + CommandMap: EnvType{ + "run": { + "name": "name", + "command": "echo", + "shell": "shell", + "shellArgs": []interface{}{"-c", 42}, + }, + }, + }, + expErr: "\"run\" step \"shellArgs\" option must contain only strings, found 42\n", + }, { // For atlantis.yaml v2, this wouldn't parse, but now there should // be no error. @@ -611,6 +704,6 @@ func TestStep_ToValid(t *testing.T) { } type MapType map[string]map[string][]string -type EnvType map[string]map[string]string -type RunType map[string]map[string]string -type MultiEnvType map[string]map[string]string +type EnvType map[string]map[string]interface{} +type RunType map[string]map[string]interface{} +type MultiEnvType map[string]map[string]interface{} diff --git a/server/core/config/valid/repo_cfg.go b/server/core/config/valid/repo_cfg.go index e5a8378bd7..e42e60158b 100644 --- a/server/core/config/valid/repo_cfg.go +++ b/server/core/config/valid/repo_cfg.go @@ -189,6 +189,16 @@ type Stage struct { Steps []Step } +// CommandShell sets up the shell for command execution +type CommandShell struct { + Shell string + ShellArgs []string +} + +func (s CommandShell) String() string { + return fmt.Sprintf("%s %s", s.Shell, strings.Join(s.ShellArgs, " ")) +} + type Step struct { StepName string ExtraArgs []string @@ -202,6 +212,8 @@ type Step struct { EnvVarName string // EnvVarValue is the value to set EnvVarName to. EnvVarValue string + // The Shell to use for RunCommand execution. + RunShell *CommandShell } type Workflow struct { diff --git a/server/core/runtime/env_step_runner.go b/server/core/runtime/env_step_runner.go index eb6556c182..5fa865fefd 100644 --- a/server/core/runtime/env_step_runner.go +++ b/server/core/runtime/env_step_runner.go @@ -15,13 +15,20 @@ type EnvStepRunner struct { // Run runs the env step command. // value is the value for the environment variable. If set this is returned as // the value. Otherwise command is run and its output is the value returned. -func (r *EnvStepRunner) Run(ctx command.ProjectContext, command string, value string, path string, envs map[string]string) (string, error) { +func (r *EnvStepRunner) Run( + ctx command.ProjectContext, + shell *valid.CommandShell, + command string, + value string, + path string, + envs map[string]string, +) (string, error) { if value != "" { return value, nil } // Pass `false` for streamOutput because this isn't interesting to the user reading the build logs // in the web UI. - res, err := r.RunStepRunner.Run(ctx, command, path, envs, false, valid.PostProcessRunOutputShow) + res, err := r.RunStepRunner.Run(ctx, shell, command, path, envs, false, valid.PostProcessRunOutputShow) // Trim newline from res to support running `echo env_value` which has // a newline. We don't recommend users run echo -n env_value to remove the // newline because -n doesn't work in the sh shell which is what we use diff --git a/server/core/runtime/env_step_runner_test.go b/server/core/runtime/env_step_runner_test.go index a26b5c1a93..0fe86f77f0 100644 --- a/server/core/runtime/env_step_runner_test.go +++ b/server/core/runtime/env_step_runner_test.go @@ -77,7 +77,7 @@ func TestEnvStepRunner_Run(t *testing.T) { TerraformVersion: tfVersion, ProjectName: c.ProjectName, } - value, err := envRunner.Run(ctx, c.Command, c.Value, tmpDir, map[string]string(nil)) + value, err := envRunner.Run(ctx, nil, c.Command, c.Value, tmpDir, map[string]string(nil)) if c.ExpErr != "" { ErrContains(t, c.ExpErr, err) return diff --git a/server/core/runtime/models/shell_command_runner.go b/server/core/runtime/models/shell_command_runner.go index 3dcd56dd8a..7271f6789e 100644 --- a/server/core/runtime/models/shell_command_runner.go +++ b/server/core/runtime/models/shell_command_runner.go @@ -9,6 +9,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/runatlantis/atlantis/server/core/config/valid" "github.com/runatlantis/atlantis/server/events/command" "github.com/runatlantis/atlantis/server/events/terraform/ansi" "github.com/runatlantis/atlantis/server/jobs" @@ -33,10 +34,27 @@ type ShellCommandRunner struct { outputHandler jobs.ProjectCommandOutputHandler streamOutput bool cmd *exec.Cmd + shell *valid.CommandShell } -func NewShellCommandRunner(command string, environ []string, workingDir string, streamOutput bool, outputHandler jobs.ProjectCommandOutputHandler) *ShellCommandRunner { - cmd := exec.Command("sh", "-c", command) // #nosec +func NewShellCommandRunner( + shell *valid.CommandShell, + command string, + environ []string, + workingDir string, + streamOutput bool, + outputHandler jobs.ProjectCommandOutputHandler, +) *ShellCommandRunner { + if shell == nil { + shell = &valid.CommandShell{ + Shell: "sh", + ShellArgs: []string{"-c"}, + } + } + var args []string + args = append(args, shell.ShellArgs...) + args = append(args, command) + cmd := exec.Command(shell.Shell, args...) // #nosec cmd.Env = environ cmd.Dir = workingDir @@ -46,6 +64,7 @@ func NewShellCommandRunner(command string, environ []string, workingDir string, outputHandler: outputHandler, streamOutput: streamOutput, cmd: cmd, + shell: shell, } } @@ -92,10 +111,10 @@ func (s *ShellCommandRunner) RunCommandAsync(ctx command.ProjectContext) (chan<- stderr, _ := s.cmd.StderrPipe() stdin, _ := s.cmd.StdinPipe() - ctx.Log.Debug("starting %q in %q", s.command, s.workingDir) + ctx.Log.Debug("starting '%s %q' in '%s'", s.shell.String(), s.command, s.workingDir) err := s.cmd.Start() if err != nil { - err = errors.Wrapf(err, "running %q in %q", s.command, s.workingDir) + err = errors.Wrapf(err, "running '%s %q' in '%s'", s.shell.String(), s.command, s.workingDir) ctx.Log.Err(err.Error()) outCh <- Line{Err: err} return @@ -154,11 +173,13 @@ func (s *ShellCommandRunner) RunCommandAsync(ctx command.ProjectContext) (chan<- // We're done now. Send an error if there was one. if err != nil { - err = errors.Wrapf(err, "running %q in %q", s.command, s.workingDir) + err = errors.Wrapf(err, "running '%s %q' in '%s'", + s.shell.String(), s.command, s.workingDir) log.Err(err.Error()) outCh <- Line{Err: err} } else { - log.Info("successfully ran %q in %q", s.command, s.workingDir) + log.Info("successfully ran '%s %q' in '%s'", + s.shell.String(), s.command, s.workingDir) } }() diff --git a/server/core/runtime/models/shell_command_runner_test.go b/server/core/runtime/models/shell_command_runner_test.go index 0555c7144c..e8edc32fb1 100644 --- a/server/core/runtime/models/shell_command_runner_test.go +++ b/server/core/runtime/models/shell_command_runner_test.go @@ -54,7 +54,7 @@ func TestShellCommandRunner_Run(t *testing.T) { expectedOutput := fmt.Sprintf("%s\n", strings.Join(c.ExpLines, "\n")) // Run once with streaming enabled - runner := models.NewShellCommandRunner(c.Command, environ, cwd, true, projectCmdOutputHandler) + runner := models.NewShellCommandRunner(nil, c.Command, environ, cwd, true, projectCmdOutputHandler) output, err := runner.Run(ctx) Ok(t, err) Equals(t, expectedOutput, output) @@ -68,7 +68,7 @@ func TestShellCommandRunner_Run(t *testing.T) { // command output handler should not have received anything projectCmdOutputHandler = mocks.NewMockProjectCommandOutputHandler() - runner = models.NewShellCommandRunner(c.Command, environ, cwd, false, projectCmdOutputHandler) + runner = models.NewShellCommandRunner(nil, c.Command, environ, cwd, false, projectCmdOutputHandler) output, err = runner.Run(ctx) Ok(t, err) Equals(t, expectedOutput, output) diff --git a/server/core/runtime/multienv_step_runner.go b/server/core/runtime/multienv_step_runner.go index 17e2ae1963..6e4434111f 100644 --- a/server/core/runtime/multienv_step_runner.go +++ b/server/core/runtime/multienv_step_runner.go @@ -16,8 +16,15 @@ type MultiEnvStepRunner struct { // Run runs the multienv step command. // The command must return a json string containing the array of name-value pairs that are being added as extra environment variables -func (r *MultiEnvStepRunner) Run(ctx command.ProjectContext, command string, path string, envs map[string]string, postProcessOutput valid.PostProcessRunOutputOption) (string, error) { - res, err := r.RunStepRunner.Run(ctx, command, path, envs, false, postProcessOutput) +func (r *MultiEnvStepRunner) Run( + ctx command.ProjectContext, + shell *valid.CommandShell, + command string, + path string, + envs map[string]string, + postProcessOutput valid.PostProcessRunOutputOption, +) (string, error) { + res, err := r.RunStepRunner.Run(ctx, shell, command, path, envs, false, postProcessOutput) if err != nil { return "", err } diff --git a/server/core/runtime/multienv_step_runner_test.go b/server/core/runtime/multienv_step_runner_test.go index adf51a8b60..360adce3f5 100644 --- a/server/core/runtime/multienv_step_runner_test.go +++ b/server/core/runtime/multienv_step_runner_test.go @@ -85,7 +85,7 @@ func TestMultiEnvStepRunner_Run(t *testing.T) { ProjectName: c.ProjectName, } envMap := make(map[string]string) - value, err := multiEnvStepRunner.Run(ctx, c.Command, tmpDir, envMap, valid.PostProcessRunOutputShow) + value, err := multiEnvStepRunner.Run(ctx, nil, c.Command, tmpDir, envMap, valid.PostProcessRunOutputShow) if c.ExpErr != "" { ErrContains(t, c.ExpErr, err) return diff --git a/server/core/runtime/run_step_runner.go b/server/core/runtime/run_step_runner.go index 1e3335762c..76629ba460 100644 --- a/server/core/runtime/run_step_runner.go +++ b/server/core/runtime/run_step_runner.go @@ -22,7 +22,15 @@ type RunStepRunner struct { ProjectCmdOutputHandler jobs.ProjectCommandOutputHandler } -func (r *RunStepRunner) Run(ctx command.ProjectContext, command string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) (string, error) { +func (r *RunStepRunner) Run( + ctx command.ProjectContext, + shell *valid.CommandShell, + command string, + path string, + envs map[string]string, + streamOutput bool, + postProcessOutput valid.PostProcessRunOutputOption, +) (string, error) { tfVersion := r.DefaultTFVersion if ctx.TerraformVersion != nil { tfVersion = ctx.TerraformVersion @@ -68,7 +76,7 @@ func (r *RunStepRunner) Run(ctx command.ProjectContext, command string, path str finalEnvVars = append(finalEnvVars, fmt.Sprintf("%s=%s", key, val)) } - runner := models.NewShellCommandRunner(command, finalEnvVars, path, streamOutput, r.ProjectCmdOutputHandler) + runner := models.NewShellCommandRunner(shell, command, finalEnvVars, path, streamOutput, r.ProjectCmdOutputHandler) output, err := runner.Run(ctx) if postProcessOutput == valid.PostProcessRunOutputStripRefreshing { diff --git a/server/core/runtime/run_step_runner_test.go b/server/core/runtime/run_step_runner_test.go index d011254a09..4672fa2bb0 100644 --- a/server/core/runtime/run_step_runner_test.go +++ b/server/core/runtime/run_step_runner_test.go @@ -145,7 +145,7 @@ func TestRunStepRunner_Run(t *testing.T) { ProjectName: c.ProjectName, EscapedCommentArgs: []string{"-target=resource1", "-target=resource2"}, } - out, err := r.Run(ctx, c.Command, tmpDir, map[string]string{"test": "var"}, true, valid.PostProcessRunOutputShow) + out, err := r.Run(ctx, nil, c.Command, tmpDir, map[string]string{"test": "var"}, true, valid.PostProcessRunOutputShow) if c.ExpErr != "" { ErrContains(t, c.ExpErr, err) return diff --git a/server/core/terraform/terraform_client.go b/server/core/terraform/terraform_client.go index 09cf88f564..cd2a0d8ad7 100644 --- a/server/core/terraform/terraform_client.go +++ b/server/core/terraform/terraform_client.go @@ -466,7 +466,7 @@ func (c *DefaultClient) RunCommandAsync(ctx command.ProjectContext, path string, envVars = append(envVars, fmt.Sprintf("%s=%s", key, val)) } - runner := models.NewShellCommandRunner(cmd, envVars, path, true, c.projectCmdOutputHandler) + runner := models.NewShellCommandRunner(nil, cmd, envVars, path, true, c.projectCmdOutputHandler) inCh, outCh := runner.RunCommandAsync(ctx) return inCh, outCh } diff --git a/server/core/terraform/terraform_client_internal_test.go b/server/core/terraform/terraform_client_internal_test.go index 6dd4c89e85..8c6be3ee43 100644 --- a/server/core/terraform/terraform_client_internal_test.go +++ b/server/core/terraform/terraform_client_internal_test.go @@ -344,7 +344,7 @@ func TestDefaultClient_RunCommandAsync_ExitOne(t *testing.T) { _, outCh := client.RunCommandAsync(ctx, tmp, []string{"dying", "&&", "exit", "1"}, map[string]string{}, nil, "workspace") out, err := waitCh(outCh) - ErrEquals(t, fmt.Sprintf(`running "echo dying && exit 1" in %q: exit status 1`, tmp), err) + ErrEquals(t, fmt.Sprintf(`running 'sh -c "echo dying && exit 1"' in '%s': exit status 1`, tmp), err) // Test that we still get our output. Equals(t, "dying", out) diff --git a/server/events/mocks/mock_custom_step_runner.go b/server/events/mocks/mock_custom_step_runner.go index 8805706322..7662d22ba0 100644 --- a/server/events/mocks/mock_custom_step_runner.go +++ b/server/events/mocks/mock_custom_step_runner.go @@ -26,7 +26,7 @@ func NewMockCustomStepRunner(options ...pegomock.Option) *MockCustomStepRunner { func (mock *MockCustomStepRunner) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh } func (mock *MockCustomStepRunner) FailHandler() pegomock.FailHandler { return mock.fail } -func (mock *MockCustomStepRunner) Run(ctx command.ProjectContext, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) (string, error) { +func (mock *MockCustomStepRunner) Run(ctx command.ProjectContext, shell *valid.CommandShell, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) (string, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockCustomStepRunner().") } @@ -82,7 +82,7 @@ type VerifierMockCustomStepRunner struct { timeout time.Duration } -func (verifier *VerifierMockCustomStepRunner) Run(ctx command.ProjectContext, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) *MockCustomStepRunner_Run_OngoingVerification { +func (verifier *VerifierMockCustomStepRunner) Run(ctx command.ProjectContext, shell *valid.CommandShell, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) *MockCustomStepRunner_Run_OngoingVerification { params := []pegomock.Param{ctx, cmd, path, envs, streamOutput, postProcessOutput} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Run", params, verifier.timeout) return &MockCustomStepRunner_Run_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} diff --git a/server/events/mocks/mock_env_step_runner.go b/server/events/mocks/mock_env_step_runner.go index bfc7f97a57..0d99311987 100644 --- a/server/events/mocks/mock_env_step_runner.go +++ b/server/events/mocks/mock_env_step_runner.go @@ -4,10 +4,12 @@ package mocks import ( - pegomock "github.com/petergtz/pegomock/v4" - command "github.com/runatlantis/atlantis/server/events/command" "reflect" "time" + + pegomock "github.com/petergtz/pegomock/v4" + "github.com/runatlantis/atlantis/server/core/config/valid" + command "github.com/runatlantis/atlantis/server/events/command" ) type MockEnvStepRunner struct { @@ -25,7 +27,7 @@ func NewMockEnvStepRunner(options ...pegomock.Option) *MockEnvStepRunner { func (mock *MockEnvStepRunner) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh } func (mock *MockEnvStepRunner) FailHandler() pegomock.FailHandler { return mock.fail } -func (mock *MockEnvStepRunner) Run(ctx command.ProjectContext, cmd string, value string, path string, envs map[string]string) (string, error) { +func (mock *MockEnvStepRunner) Run(ctx command.ProjectContext, shell *valid.CommandShell, cmd string, value string, path string, envs map[string]string) (string, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockEnvStepRunner().") } @@ -81,7 +83,7 @@ type VerifierMockEnvStepRunner struct { timeout time.Duration } -func (verifier *VerifierMockEnvStepRunner) Run(ctx command.ProjectContext, cmd string, value string, path string, envs map[string]string) *MockEnvStepRunner_Run_OngoingVerification { +func (verifier *VerifierMockEnvStepRunner) Run(ctx command.ProjectContext, shell *valid.CommandShell, cmd string, value string, path string, envs map[string]string) *MockEnvStepRunner_Run_OngoingVerification { params := []pegomock.Param{ctx, cmd, value, path, envs} methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Run", params, verifier.timeout) return &MockEnvStepRunner_Run_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} diff --git a/server/events/project_command_runner.go b/server/events/project_command_runner.go index 153269c7e2..26d4dc2cc2 100644 --- a/server/events/project_command_runner.go +++ b/server/events/project_command_runner.go @@ -65,20 +65,42 @@ type StepRunner interface { // CustomStepRunner runs custom run steps. type CustomStepRunner interface { // Run cmd in path. - Run(ctx command.ProjectContext, cmd string, path string, envs map[string]string, streamOutput bool, postProcessOutput valid.PostProcessRunOutputOption) (string, error) + Run( + ctx command.ProjectContext, + shell *valid.CommandShell, + cmd string, + path string, + envs map[string]string, + streamOutput bool, + postProcessOutput valid.PostProcessRunOutputOption, + ) (string, error) } //go:generate pegomock generate --package mocks -o mocks/mock_env_step_runner.go EnvStepRunner // EnvStepRunner runs env steps. type EnvStepRunner interface { - Run(ctx command.ProjectContext, cmd string, value string, path string, envs map[string]string) (string, error) + Run( + ctx command.ProjectContext, + shell *valid.CommandShell, + cmd string, + value string, + path string, + envs map[string]string, + ) (string, error) } // MultiEnvStepRunner runs multienv steps. type MultiEnvStepRunner interface { // Run cmd in path. - Run(ctx command.ProjectContext, cmd string, path string, envs map[string]string, postProcessOutput valid.PostProcessRunOutputOption) (string, error) + Run( + ctx command.ProjectContext, + shell *valid.CommandShell, + cmd string, + path string, + envs map[string]string, + postProcessOutput valid.PostProcessRunOutputOption, + ) (string, error) } //go:generate pegomock generate --package mocks -o mocks/mock_webhooks_sender.go WebhooksSender @@ -790,15 +812,15 @@ func (p *DefaultProjectCommandRunner) runSteps(steps []valid.Step, ctx command.P case "state_rm": out, err = p.StateRmStepRunner.Run(ctx, step.ExtraArgs, absPath, envs) case "run": - out, err = p.RunStepRunner.Run(ctx, step.RunCommand, absPath, envs, true, step.Output) + out, err = p.RunStepRunner.Run(ctx, step.RunShell, step.RunCommand, absPath, envs, true, step.Output) case "env": - out, err = p.EnvStepRunner.Run(ctx, step.RunCommand, step.EnvVarValue, absPath, envs) + out, err = p.EnvStepRunner.Run(ctx, step.RunShell, step.RunCommand, step.EnvVarValue, absPath, envs) envs[step.EnvVarName] = out // We reset out to the empty string because we don't want it to // be printed to the PR, it's solely to set the environment variable. out = "" case "multienv": - out, err = p.MultiEnvStepRunner.Run(ctx, step.RunCommand, absPath, envs, step.Output) + out, err = p.MultiEnvStepRunner.Run(ctx, step.RunShell, step.RunCommand, absPath, envs, step.Output) } if out != "" { diff --git a/server/events/project_command_runner_test.go b/server/events/project_command_runner_test.go index d241d44569..68548efdd0 100644 --- a/server/events/project_command_runner_test.go +++ b/server/events/project_command_runner_test.go @@ -99,7 +99,7 @@ func TestDefaultProjectCommandRunner_Plan(t *testing.T) { When(mockInit.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("init", nil) When(mockPlan.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("plan", nil) When(mockApply.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("apply", nil) - When(mockRun.Run(ctx, "", repoDir, expEnvs, true, "")).ThenReturn("run", nil) + When(mockRun.Run(ctx, nil, "", repoDir, expEnvs, true, "")).ThenReturn("run", nil) res := runner.Plan(ctx) Assert(t, res.PlanSuccess != nil, "exp plan success") @@ -115,7 +115,7 @@ func TestDefaultProjectCommandRunner_Plan(t *testing.T) { case "apply": mockApply.VerifyWasCalledOnce().Run(ctx, nil, repoDir, expEnvs) case "run": - mockRun.VerifyWasCalledOnce().Run(ctx, "", repoDir, expEnvs, true, "") + mockRun.VerifyWasCalledOnce().Run(ctx, nil, "", repoDir, expEnvs, true, "") } } } @@ -455,8 +455,8 @@ func TestDefaultProjectCommandRunner_Apply(t *testing.T) { When(mockInit.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("init", nil) When(mockPlan.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("plan", nil) When(mockApply.Run(ctx, nil, repoDir, expEnvs)).ThenReturn("apply", nil) - When(mockRun.Run(ctx, "", repoDir, expEnvs, true, "")).ThenReturn("run", nil) - When(mockEnv.Run(ctx, "", "value", repoDir, make(map[string]string))).ThenReturn("value", nil) + When(mockRun.Run(ctx, nil, "", repoDir, expEnvs, true, "")).ThenReturn("run", nil) + When(mockEnv.Run(ctx, nil, "", "value", repoDir, make(map[string]string))).ThenReturn("value", nil) res := runner.Apply(ctx) Equals(t, c.expOut, res.ApplySuccess) @@ -471,9 +471,9 @@ func TestDefaultProjectCommandRunner_Apply(t *testing.T) { case "apply": mockApply.VerifyWasCalledOnce().Run(ctx, nil, repoDir, expEnvs) case "run": - mockRun.VerifyWasCalledOnce().Run(ctx, "", repoDir, expEnvs, true, "") + mockRun.VerifyWasCalledOnce().Run(ctx, nil, "", repoDir, expEnvs, true, "") case "env": - mockEnv.VerifyWasCalledOnce().Run(ctx, "", "value", repoDir, expEnvs) + mockEnv.VerifyWasCalledOnce().Run(ctx, nil, "", "value", repoDir, expEnvs) } } }) From 841f7b427780c38363b52597ebd1b0a64301a0b5 Mon Sep 17 00:00:00 2001 From: bakayolo Date: Mon, 4 Nov 2024 14:04:13 -0800 Subject: [PATCH 09/38] feat: add ignore-vcs-status-names (github only) (#4978) Signed-off-by: bakayolo --- cmd/server.go | 11 +++ cmd/server_test.go | 1 + runatlantis.io/docs/server-configuration.md | 14 ++++ .../events/events_controller_e2e_test.go | 4 +- server/events/command_runner_test.go | 2 +- server/events/vcs/azuredevops_client.go | 2 +- server/events/vcs/azuredevops_client_test.go | 2 +- server/events/vcs/bitbucketcloud/client.go | 2 +- .../events/vcs/bitbucketcloud/client_test.go | 2 +- server/events/vcs/bitbucketserver/client.go | 2 +- server/events/vcs/client.go | 2 +- server/events/vcs/gitea/client.go | 2 +- server/events/vcs/github_client.go | 14 ++-- server/events/vcs/github_client_test.go | 17 +++- server/events/vcs/gitlab_client.go | 2 +- server/events/vcs/gitlab_client_test.go | 2 +- server/events/vcs/instrumented_client.go | 4 +- server/events/vcs/mocks/mock_client.go | 4 +- .../events/vcs/not_configured_vcs_client.go | 2 +- server/events/vcs/proxy.go | 4 +- server/events/vcs/pull_status_fetcher.go | 14 ++-- .../ruleset-check-failed-other-atlantis.json | 80 +++++++++++++++++++ .../ruleset-check-pending-other-atlantis.json | 80 +++++++++++++++++++ server/server.go | 2 +- server/user_config.go | 1 + 25 files changed, 239 insertions(+), 33 deletions(-) create mode 100644 server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-failed-other-atlantis.json create mode 100644 server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-pending-other-atlantis.json diff --git a/cmd/server.go b/cmd/server.go index e53ca20418..84ff1c23e9 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -146,6 +146,7 @@ const ( UseTFPluginCache = "use-tf-plugin-cache" VarFileAllowlistFlag = "var-file-allowlist" VCSStatusName = "vcs-status-name" + IgnoreVCSStatusNames = "ignore-vcs-status-names" TFEHostnameFlag = "tfe-hostname" TFELocalExecutionModeFlag = "tfe-local-execution-mode" TFETokenFlag = "tfe-token" @@ -175,6 +176,7 @@ const ( DefaultGitlabHostname = "gitlab.com" DefaultLockingDBType = "boltdb" DefaultLogLevel = "info" + DefaultIgnoreVCSStatusNames = "" DefaultMaxCommentsPerCommand = 100 DefaultParallelPoolSize = 15 DefaultStatsNamespace = "atlantis" @@ -439,6 +441,12 @@ var stringFlags = map[string]stringFlag{ description: "Comma-separated list of additional paths where variable definition files can be read from." + " If this argument is not provided, it defaults to Atlantis' data directory, determined by the --data-dir argument.", }, + IgnoreVCSStatusNames: { + description: "Comma separated list of VCS status names from other atlantis services." + + " When `gh-allow-mergeable-bypass-apply` is true, will ignore status checks (e.g. `status1/plan`, `status1/apply`, `status2/plan`, `status2/apply`) from other Atlantis services when checking if the PR is mergeable." + + " Currently only implemented for GitHub.", + defaultValue: DefaultIgnoreVCSStatusNames, + }, VCSStatusName: { description: "Name used to identify Atlantis for pull request statuses.", defaultValue: DefaultVCSStatusName, @@ -918,6 +926,9 @@ func (s *ServerCmd) setDefaults(c *server.UserConfig, v *viper.Viper) { if c.VCSStatusName == "" { c.VCSStatusName = DefaultVCSStatusName } + if c.IgnoreVCSStatusNames == "" { + c.IgnoreVCSStatusNames = DefaultIgnoreVCSStatusNames + } if c.TFEHostname == "" { c.TFEHostname = DefaultTFEHostname } diff --git a/cmd/server_test.go b/cmd/server_test.go index cccf9cc055..2c9034af48 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -145,6 +145,7 @@ var testFlags = map[string]interface{}{ UseTFPluginCache: true, VarFileAllowlistFlag: "/path", VCSStatusName: "my-status", + IgnoreVCSStatusNames: "", WebBasicAuthFlag: false, WebPasswordFlag: "atlantis", WebUsernameFlag: "atlantis", diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 610838f262..3c062ae853 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -857,6 +857,20 @@ This is useful when you have many projects and want to keep the pull request cle Used for example with CDKTF pre-workflow hooks that dynamically generate Terraform files. +### `--ignore-vcs-status-names` + + ```bash + atlantis server --ignore-vcs-status-names="status1,status2" + # or + ATLANTIS_IGNORE_VCS_STATUS_NAMES=status1,status2 + ``` + + Comma separated list of VCS status names from other atlantis services. + When `gh-allow-mergeable-bypass-apply` is true, will ignore status checks + (e.g. `status1/plan`, `status1/apply`, `status2/plan`, `status2/apply`) + from other Atlantis services when checking if the PR is mergeable. + Currently only implemented for GitHub. + ### `--locking-db-type` ```bash diff --git a/server/controllers/events/events_controller_e2e_test.go b/server/controllers/events/events_controller_e2e_test.go index 7a80b13b07..6b985fd9b8 100644 --- a/server/controllers/events/events_controller_e2e_test.go +++ b/server/controllers/events/events_controller_e2e_test.go @@ -1191,7 +1191,7 @@ func TestGitHubWorkflowWithPolicyCheck(t *testing.T) { // Setup test dependencies. w := httptest.NewRecorder() When(vcsClient.PullIsMergeable( - Any[logging.SimpleLogging](), Any[models.Repo](), Any[models.PullRequest](), Eq("atlantis-test"))).ThenReturn(true, nil) + Any[logging.SimpleLogging](), Any[models.Repo](), Any[models.PullRequest](), Eq("atlantis-test"), Eq([]string{}))).ThenReturn(true, nil) When(vcsClient.PullIsApproved( Any[logging.SimpleLogging](), Any[models.Repo](), Any[models.PullRequest]())).ThenReturn(models.ApprovalStatus{ IsApproved: true, @@ -1505,7 +1505,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers userConfig.QuietPolicyChecks, ) - e2ePullReqStatusFetcher := vcs.NewPullReqStatusFetcher(e2eVCSClient, "atlantis-test") + e2ePullReqStatusFetcher := vcs.NewPullReqStatusFetcher(e2eVCSClient, "atlantis-test", []string{}) planCommandRunner := events.NewPlanCommandRunner( false, diff --git a/server/events/command_runner_test.go b/server/events/command_runner_test.go index c4d41b441a..fa2dee091c 100644 --- a/server/events/command_runner_test.go +++ b/server/events/command_runner_test.go @@ -1148,7 +1148,7 @@ func TestApplyMergeablityWhenPolicyCheckFails(t *testing.T) { }, }) - When(ch.VCSClient.PullIsMergeable(Any[logging.SimpleLogging](), Eq(testdata.GithubRepo), Eq(modelPull), Eq("atlantis-test"))).ThenReturn(true, nil) + When(ch.VCSClient.PullIsMergeable(Any[logging.SimpleLogging](), Eq(testdata.GithubRepo), Eq(modelPull), Eq("atlantis-test"), Eq([]string{}))).ThenReturn(true, nil) When(projectCommandBuilder.BuildApplyCommands(Any[*command.Context](), Any[*events.CommentCommand]())).Then(func(args []Param) ReturnValues { return ReturnValues{ diff --git a/server/events/vcs/azuredevops_client.go b/server/events/vcs/azuredevops_client.go index 1d115d28e9..63344d85e7 100644 --- a/server/events/vcs/azuredevops_client.go +++ b/server/events/vcs/azuredevops_client.go @@ -177,7 +177,7 @@ func (g *AzureDevopsClient) DiscardReviews(repo models.Repo, pull models.PullReq } // PullIsMergeable returns true if the merge request can be merged. -func (g *AzureDevopsClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) { //nolint: revive +func (g *AzureDevopsClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, _ string, _ []string) (bool, error) { //nolint: revive owner, project, repoName := SplitAzureDevopsRepoFullName(repo.FullName) opts := azuredevops.PullRequestGetOptions{IncludeWorkItemRefs: true} diff --git a/server/events/vcs/azuredevops_client_test.go b/server/events/vcs/azuredevops_client_test.go index 1e428023b8..a7095262d2 100644 --- a/server/events/vcs/azuredevops_client_test.go +++ b/server/events/vcs/azuredevops_client_test.go @@ -428,7 +428,7 @@ func TestAzureDevopsClient_PullIsMergeable(t *testing.T) { }, }, models.PullRequest{ Num: 1, - }, "atlantis-test") + }, "atlantis-test", []string{}) Ok(t, err) Equals(t, c.expMergeable, actMergeable) }) diff --git a/server/events/vcs/bitbucketcloud/client.go b/server/events/vcs/bitbucketcloud/client.go index a0dcfd7161..c9a88f0245 100644 --- a/server/events/vcs/bitbucketcloud/client.go +++ b/server/events/vcs/bitbucketcloud/client.go @@ -139,7 +139,7 @@ func (b *Client) PullIsApproved(logger logging.SimpleLogging, repo models.Repo, } // PullIsMergeable returns true if the merge request has no conflicts and can be merged. -func (b *Client) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, _ string) (bool, error) { +func (b *Client) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, _ string, _ []string) (bool, error) { nextPageURL := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d/diffstat", b.BaseURL, repo.FullName, pull.Num) // We'll only loop 1000 times as a safety measure. maxLoops := 1000 diff --git a/server/events/vcs/bitbucketcloud/client_test.go b/server/events/vcs/bitbucketcloud/client_test.go index 0a4bd48db6..59108a14f3 100644 --- a/server/events/vcs/bitbucketcloud/client_test.go +++ b/server/events/vcs/bitbucketcloud/client_test.go @@ -352,7 +352,7 @@ func TestClient_PullIsMergeable(t *testing.T) { }, }, models.PullRequest{ Num: 1, - }, "atlantis-test") + }, "atlantis-test", []string{}) Ok(t, err) Equals(t, c.ExpMergeable, actMergeable) }) diff --git a/server/events/vcs/bitbucketserver/client.go b/server/events/vcs/bitbucketserver/client.go index 69dbbde518..83956db629 100644 --- a/server/events/vcs/bitbucketserver/client.go +++ b/server/events/vcs/bitbucketserver/client.go @@ -203,7 +203,7 @@ func (b *Client) DiscardReviews(_ models.Repo, _ models.PullRequest) error { } // PullIsMergeable returns true if the merge request has no conflicts and can be merged. -func (b *Client) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, _ string) (bool, error) { +func (b *Client) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, _ string, _ []string) (bool, error) { projectKey, err := b.GetProjectKey(repo.Name, repo.SanitizedCloneURL) if err != nil { return false, err diff --git a/server/events/vcs/client.go b/server/events/vcs/client.go index b6ad7cb9cd..9e32981a82 100644 --- a/server/events/vcs/client.go +++ b/server/events/vcs/client.go @@ -30,7 +30,7 @@ type Client interface { ReactToComment(logger logging.SimpleLogging, repo models.Repo, pullNum int, commentID int64, reaction string) error HidePrevCommandComments(logger logging.SimpleLogging, repo models.Repo, pullNum int, command string, dir string) error PullIsApproved(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest) (models.ApprovalStatus, error) - PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) + PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string, ignoreVCSStatusNames []string) (bool, error) // UpdateStatus updates the commit status to state for pull. src is the // source of this status. This should be relatively static across runs, // ex. atlantis/plan or atlantis/apply. diff --git a/server/events/vcs/gitea/client.go b/server/events/vcs/gitea/client.go index f9deb2cb74..9867d4c8f9 100644 --- a/server/events/vcs/gitea/client.go +++ b/server/events/vcs/gitea/client.go @@ -283,7 +283,7 @@ func (c *GiteaClient) PullIsApproved(logger logging.SimpleLogging, repo models.R } // PullIsMergeable returns true if the pull request is mergeable -func (c *GiteaClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) { +func (c *GiteaClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, _ string, _ []string) (bool, error) { logger.Debug("Checking if Gitea pull request %d is mergeable", pull.Num) pullRequest, _, err := c.giteaClient.GetPullRequest(repo.Owner, repo.Name, int64(pull.Num)) diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index 698d4dc999..d9c3d90541 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -751,7 +751,7 @@ func (g *GithubClient) ExpectedWorkflowPassed(expectedWorkflow WorkflowFileRefer } // IsMergeableMinusApply checks review decision (which takes into account CODEOWNERS) and required checks for PR (excluding the atlantis apply check). -func (g *GithubClient) IsMergeableMinusApply(logger logging.SimpleLogging, repo models.Repo, pull *github.PullRequest, vcsstatusname string) (bool, error) { +func (g *GithubClient) IsMergeableMinusApply(logger logging.SimpleLogging, repo models.Repo, pull *github.PullRequest, vcsstatusname string, ignoreVCSStatusNames []string) (bool, error) { if pull.Number == nil { return false, errors.New("pull request number is nil") } @@ -778,8 +778,8 @@ func (g *GithubClient) IsMergeableMinusApply(logger logging.SimpleLogging, repo // Ignore atlantis apply check(s) continue } - if !ExpectedCheckPassed(requiredCheck, checkRuns, statusContexts, vcsstatusname) { - logger.Debug("%s: Expected Required Check: %s", notMergeablePrefix, requiredCheck) + if !slices.Contains(ignoreVCSStatusNames, GetVCSStatusNameFromRequiredCheck(requiredCheck)) && !ExpectedCheckPassed(requiredCheck, checkRuns, statusContexts, vcsstatusname) { + logger.Debug("%s: Expected Required Check: %s VCS Status Name: %s Ignore VCS Status Names: %s", notMergeablePrefix, requiredCheck, vcsstatusname, ignoreVCSStatusNames) return false, nil } } @@ -797,8 +797,12 @@ func (g *GithubClient) IsMergeableMinusApply(logger logging.SimpleLogging, repo return true, nil } +func GetVCSStatusNameFromRequiredCheck(requiredCheck githubv4.String) string { + return strings.Split(string(requiredCheck), "/")[0] +} + // PullIsMergeable returns true if the pull request is mergeable. -func (g *GithubClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) { +func (g *GithubClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string, ignoreVCSStatusNames []string) (bool, error) { logger.Debug("Checking if GitHub pull request %d is mergeable", pull.Num) githubPR, err := g.GetPullRequest(logger, repo, pull.Num) if err != nil { @@ -820,7 +824,7 @@ func (g *GithubClient) PullIsMergeable(logger logging.SimpleLogging, repo models case "blocked": if g.config.AllowMergeableBypassApply { logger.Debug("AllowMergeableBypassApply feature flag is enabled - attempting to bypass apply from mergeable requirements") - isMergeableMinusApply, err := g.IsMergeableMinusApply(logger, repo, githubPR, vcsstatusname) + isMergeableMinusApply, err := g.IsMergeableMinusApply(logger, repo, githubPR, vcsstatusname, ignoreVCSStatusNames) if err != nil { return false, errors.Wrap(err, "getting pull request status") } diff --git a/server/events/vcs/github_client_test.go b/server/events/vcs/github_client_test.go index b668f9e919..05f1ff939b 100644 --- a/server/events/vcs/github_client_test.go +++ b/server/events/vcs/github_client_test.go @@ -609,7 +609,7 @@ func TestGithubClient_PullIsMergeable(t *testing.T) { }, }, models.PullRequest{ Num: 1, - }, vcsStatusName) + }, vcsStatusName, []string{}) Ok(t, err) Equals(t, c.expMergeable, actMergeable) }) @@ -619,6 +619,7 @@ func TestGithubClient_PullIsMergeable(t *testing.T) { func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApply(t *testing.T) { logger := logging.NewNoopLogger(t) vcsStatusName := "atlantis" + ignoreVCSStatusNames := []string{"other-atlantis"} cases := []struct { state string statusCheckRollupFilePath string @@ -715,6 +716,12 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApply(t *testing.T) `"APPROVED"`, false, }, + { + "blocked", + "ruleset-check-pending-other-atlantis.json", + `"APPROVED"`, + true, + }, { "blocked", "ruleset-check-skipped.json", @@ -763,6 +770,12 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApply(t *testing.T) `"APPROVED"`, false, }, + { + "blocked", + "ruleset-check-failed-other-atlantis.json", + `"APPROVED"`, + true, + }, { "blocked", "ruleset-check-passed.json", @@ -879,7 +892,7 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApply(t *testing.T) }, }, models.PullRequest{ Num: 1, - }, vcsStatusName) + }, vcsStatusName, ignoreVCSStatusNames) Ok(t, err) Equals(t, c.expMergeable, actMergeable) }) diff --git a/server/events/vcs/gitlab_client.go b/server/events/vcs/gitlab_client.go index 289b1a0d8a..b78c82b271 100644 --- a/server/events/vcs/gitlab_client.go +++ b/server/events/vcs/gitlab_client.go @@ -300,7 +300,7 @@ func (g *GitlabClient) PullIsApproved(logger logging.SimpleLogging, repo models. // See: // - https://gitlab.com/gitlab-org/gitlab-ee/issues/3169 // - https://gitlab.com/gitlab-org/gitlab-ce/issues/42344 -func (g *GitlabClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) { +func (g *GitlabClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string, _ []string) (bool, error) { logger.Debug("Checking if GitLab merge request %d is mergeable", pull.Num) mr, resp, err := g.Client.MergeRequests.GetMergeRequest(repo.FullName, pull.Num, nil) if resp != nil { diff --git a/server/events/vcs/gitlab_client_test.go b/server/events/vcs/gitlab_client_test.go index cef424bcb0..98de58287f 100644 --- a/server/events/vcs/gitlab_client_test.go +++ b/server/events/vcs/gitlab_client_test.go @@ -659,7 +659,7 @@ func TestGitlabClient_PullIsMergeable(t *testing.T) { Num: c.mrID, BaseRepo: repo, HeadCommit: "67cb91d3f6198189f433c045154a885784ba6977", - }, vcsStatusName) + }, vcsStatusName, []string{}) Ok(t, err) Equals(t, c.expState, mergeable) diff --git a/server/events/vcs/instrumented_client.go b/server/events/vcs/instrumented_client.go index 942a4823e1..32022438f1 100644 --- a/server/events/vcs/instrumented_client.go +++ b/server/events/vcs/instrumented_client.go @@ -183,7 +183,7 @@ func (c *InstrumentedClient) PullIsApproved(logger logging.SimpleLogging, repo m return approved, err } -func (c *InstrumentedClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) { +func (c *InstrumentedClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string, ignoreVCSStatusNames []string) (bool, error) { scope := c.StatsScope.SubScope("pull_is_mergeable") scope = SetGitScopeTags(scope, repo.FullName, pull.Num) @@ -193,7 +193,7 @@ func (c *InstrumentedClient) PullIsMergeable(logger logging.SimpleLogging, repo executionSuccess := scope.Counter(metrics.ExecutionSuccessMetric) executionError := scope.Counter(metrics.ExecutionErrorMetric) - mergeable, err := c.Client.PullIsMergeable(logger, repo, pull, vcsstatusname) + mergeable, err := c.Client.PullIsMergeable(logger, repo, pull, vcsstatusname, ignoreVCSStatusNames) if err != nil { executionError.Inc(1) diff --git a/server/events/vcs/mocks/mock_client.go b/server/events/vcs/mocks/mock_client.go index ffa37fe8cb..f51036b87a 100644 --- a/server/events/vcs/mocks/mock_client.go +++ b/server/events/vcs/mocks/mock_client.go @@ -223,11 +223,11 @@ func (mock *MockClient) PullIsApproved(logger logging.SimpleLogging, repo models return ret0, ret1 } -func (mock *MockClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) { +func (mock *MockClient) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string, ignoreVCSStatusNames []string) (bool, error) { if mock == nil { panic("mock must not be nil. Use myMock := NewMockClient().") } - params := []pegomock.Param{logger, repo, pull, vcsstatusname} + params := []pegomock.Param{logger, repo, pull, vcsstatusname, ignoreVCSStatusNames} 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 diff --git a/server/events/vcs/not_configured_vcs_client.go b/server/events/vcs/not_configured_vcs_client.go index 6fd7a731cb..41b14ad2c6 100644 --- a/server/events/vcs/not_configured_vcs_client.go +++ b/server/events/vcs/not_configured_vcs_client.go @@ -45,7 +45,7 @@ func (a *NotConfiguredVCSClient) PullIsApproved(_ logging.SimpleLogging, _ model func (a *NotConfiguredVCSClient) DiscardReviews(_ models.Repo, _ models.PullRequest) error { return nil } -func (a *NotConfiguredVCSClient) PullIsMergeable(_ logging.SimpleLogging, _ models.Repo, _ models.PullRequest, _ string) (bool, error) { +func (a *NotConfiguredVCSClient) PullIsMergeable(_ logging.SimpleLogging, _ models.Repo, _ models.PullRequest, _ string, _ []string) (bool, error) { return false, a.err() } func (a *NotConfiguredVCSClient) UpdateStatus(_ logging.SimpleLogging, _ models.Repo, _ models.PullRequest, _ models.CommitStatus, _ string, _ string, _ string) error { diff --git a/server/events/vcs/proxy.go b/server/events/vcs/proxy.go index cd67b84c90..68aa45bf58 100644 --- a/server/events/vcs/proxy.go +++ b/server/events/vcs/proxy.go @@ -81,8 +81,8 @@ func (d *ClientProxy) DiscardReviews(repo models.Repo, pull models.PullRequest) return d.clients[repo.VCSHost.Type].DiscardReviews(repo, pull) } -func (d *ClientProxy) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string) (bool, error) { - return d.clients[repo.VCSHost.Type].PullIsMergeable(logger, repo, pull, vcsstatusname) +func (d *ClientProxy) PullIsMergeable(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, vcsstatusname string, ignoreVCSStatusNames []string) (bool, error) { + return d.clients[repo.VCSHost.Type].PullIsMergeable(logger, repo, pull, vcsstatusname, ignoreVCSStatusNames) } func (d *ClientProxy) UpdateStatus(logger logging.SimpleLogging, repo models.Repo, pull models.PullRequest, state models.CommitStatus, src string, description string, url string) error { diff --git a/server/events/vcs/pull_status_fetcher.go b/server/events/vcs/pull_status_fetcher.go index b5b8a46383..b96d69011a 100644 --- a/server/events/vcs/pull_status_fetcher.go +++ b/server/events/vcs/pull_status_fetcher.go @@ -13,14 +13,16 @@ type PullReqStatusFetcher interface { } type pullReqStatusFetcher struct { - client Client - vcsStatusName string + client Client + vcsStatusName string + ignoreVCSStatusNames []string } -func NewPullReqStatusFetcher(client Client, vcsStatusName string) PullReqStatusFetcher { +func NewPullReqStatusFetcher(client Client, vcsStatusName string, ignoreVCSStatusNames []string) PullReqStatusFetcher { return &pullReqStatusFetcher{ - client: client, - vcsStatusName: vcsStatusName, + client: client, + vcsStatusName: vcsStatusName, + ignoreVCSStatusNames: ignoreVCSStatusNames, } } @@ -30,7 +32,7 @@ func (f *pullReqStatusFetcher) FetchPullStatus(logger logging.SimpleLogging, pul return pullStatus, errors.Wrapf(err, "fetching pull approval status for repo: %s, and pull number: %d", pull.BaseRepo.FullName, pull.Num) } - mergeable, err := f.client.PullIsMergeable(logger, pull.BaseRepo, pull, f.vcsStatusName) + mergeable, err := f.client.PullIsMergeable(logger, pull.BaseRepo, pull, f.vcsStatusName, f.ignoreVCSStatusNames) if err != nil { return pullStatus, errors.Wrapf(err, "fetching mergeability status for repo: %s, and pull number: %d", pull.BaseRepo.FullName, pull.Num) } diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-failed-other-atlantis.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-failed-other-atlantis.json new file mode 100644 index 0000000000..38d291aa74 --- /dev/null +++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-failed-other-atlantis.json @@ -0,0 +1,80 @@ +{ + "data": { + "repository": { + "pullRequest": { + "reviewDecision": null, + "baseRef": { + "branchProtectionRule": { + "requiredStatusChecks": [] + }, + "rules": { + "pageInfo": { + "endCursor": "QWERTY", + "hasNextPage": false + }, + "nodes": [ + { + "type": "REQUIRED_STATUS_CHECKS", + "repositoryRuleset": { + "enforcement": "ACTIVE" + }, + "parameters": { + "requiredStatusChecks": [ + { + "context": "atlantis/apply" + }, + { + "context": "other-atlantis/apply" + } + ] + } + } + ] + } + }, + "commits": { + "nodes": [ + { + "commit": { + "statusCheckRollup": { + "contexts": { + "pageInfo": { + "endCursor": "QWERTY", + "hasNextPage": false + }, + "nodes": [ + { + "__typename": "StatusContext", + "context": "atlantis/apply", + "state": "PENDING", + "isRequired": true + }, + { + "__typename": "StatusContext", + "context": "atlantis/plan", + "state": "SUCCESS", + "isRequired": false + }, + { + "__typename": "StatusContext", + "context": "other-atlantis/apply", + "state": "FAILED", + "isRequired": true + }, + { + "__typename": "StatusContext", + "context": "other-atlantis/apply", + "state": "SUCCESS", + "isRequired": false + } + ] + } + } + } + } + ] + } + } + } + } +} diff --git a/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-pending-other-atlantis.json b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-pending-other-atlantis.json new file mode 100644 index 0000000000..f0936309e0 --- /dev/null +++ b/server/events/vcs/testdata/github-pull-request-mergeability/ruleset-check-pending-other-atlantis.json @@ -0,0 +1,80 @@ +{ + "data": { + "repository": { + "pullRequest": { + "reviewDecision": null, + "baseRef": { + "branchProtectionRule": { + "requiredStatusChecks": [] + }, + "rules": { + "pageInfo": { + "endCursor": "QWERTY", + "hasNextPage": false + }, + "nodes": [ + { + "type": "REQUIRED_STATUS_CHECKS", + "repositoryRuleset": { + "enforcement": "ACTIVE" + }, + "parameters": { + "requiredStatusChecks": [ + { + "context": "atlantis/apply" + }, + { + "context": "other-atlantis/apply" + } + ] + } + } + ] + } + }, + "commits": { + "nodes": [ + { + "commit": { + "statusCheckRollup": { + "contexts": { + "pageInfo": { + "endCursor": "QWERTY", + "hasNextPage": false + }, + "nodes": [ + { + "__typename": "StatusContext", + "context": "atlantis/apply", + "state": "PENDING", + "isRequired": true + }, + { + "__typename": "StatusContext", + "context": "atlantis/plan", + "state": "SUCCESS", + "isRequired": false + }, + { + "__typename": "StatusContext", + "context": "other-atlantis/apply", + "state": "PENDING", + "isRequired": true + }, + { + "__typename": "StatusContext", + "context": "other-atlantis/plan", + "state": "PENDING", + "isRequired": false + } + ] + } + } + } + } + ] + } + } + } + } +} diff --git a/server/server.go b/server/server.go index af940f3787..f2d93b5a49 100644 --- a/server/server.go +++ b/server/server.go @@ -722,7 +722,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { userConfig.QuietPolicyChecks, ) - pullReqStatusFetcher := vcs.NewPullReqStatusFetcher(vcsClient, userConfig.VCSStatusName) + pullReqStatusFetcher := vcs.NewPullReqStatusFetcher(vcsClient, userConfig.VCSStatusName, strings.Split(userConfig.IgnoreVCSStatusNames, ",")) planCommandRunner := events.NewPlanCommandRunner( userConfig.SilenceVCSStatusNoPlans, userConfig.SilenceVCSStatusNoProjects, diff --git a/server/user_config.go b/server/user_config.go index 9a6247ae09..8693a55549 100644 --- a/server/user_config.go +++ b/server/user_config.go @@ -75,6 +75,7 @@ type UserConfig struct { LogLevel string `mapstructure:"log-level"` MarkdownTemplateOverridesDir string `mapstructure:"markdown-template-overrides-dir"` MaxCommentsPerCommand int `mapstructure:"max-comments-per-command"` + IgnoreVCSStatusNames string `mapstructure:"ignore-vcs-status-names"` ParallelPoolSize int `mapstructure:"parallel-pool-size"` ParallelPlan bool `mapstructure:"parallel-plan"` ParallelApply bool `mapstructure:"parallel-apply"` From 2c154132b5d05c2ff93528cda37168c14c9f0346 Mon Sep 17 00:00:00 2001 From: Henry Muru Paenga Date: Tue, 5 Nov 2024 11:30:03 +1300 Subject: [PATCH 10/38] fix: Run dumb-init with --single-child for graceful termination (#4913) --- Dockerfile | 3 ++- docker-entrypoint.sh | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index db9e7b4f4c..c5fb61a828 100644 --- a/Dockerfile +++ b/Dockerfile @@ -167,7 +167,8 @@ RUN apk add --no-cache \ bash~=5 \ openssh~=9 \ dumb-init~=1 \ - gcompat~=1 + gcompat~=1 \ + coreutils-env~=9 # Set the entry point to the atlantis user and run the atlantis command USER atlantis diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 18a8f6ceaf..4f307e1666 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,4 +1,10 @@ -#!/usr/bin/dumb-init /bin/sh +#!/usr/bin/env -S dumb-init --single-child /bin/sh + +# dumb-init is run in single child mode. By default dumb-init will forward +# interrupts to all child processes, causing Terraform to cancel and Terraform +# providers to exit uncleanly. We forward the signal to Atlantis only, allowing +# it to trap the interrupt, and exit gracefully. + set -e # Modified: https://github.com/hashicorp/docker-consul/blob/2c2873f9d619220d1eef0bc46ec78443f55a10b5/0.X/docker-entrypoint.sh From 74916fbf3887b1e7abea52f1289a7e483e4d4f62 Mon Sep 17 00:00:00 2001 From: Henry Muru Paenga Date: Tue, 5 Nov 2024 12:14:59 +1300 Subject: [PATCH 11/38] feat: GitHub - Support loading git token from disk (#4928) --- cmd/server.go | 24 ++++++--- cmd/server_test.go | 20 +++++++- runatlantis.io/docs/server-configuration.md | 10 ++++ server/events/vcs/git_cred_writer.go | 2 +- .../events/vcs/github_client_internal_test.go | 4 +- server/events/vcs/github_client_test.go | 36 ++++++------- server/events/vcs/github_credentials.go | 50 ++++++++++++++++--- ...eds_rotator.go => github_token_rotator.go} | 27 +++++----- ...r_test.go => github_token_rotator_test.go} | 6 +-- server/server.go | 16 ++++-- server/user_config.go | 1 + 11 files changed, 142 insertions(+), 54 deletions(-) rename server/events/vcs/{gh_app_creds_rotator.go => github_token_rotator.go} (62%) rename server/events/vcs/{gh_app_creds_rotator_test.go => github_token_rotator_test.go} (89%) diff --git a/cmd/server.go b/cmd/server.go index 84ff1c23e9..5722b38cfa 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -91,6 +91,7 @@ const ( GHHostnameFlag = "gh-hostname" GHTeamAllowlistFlag = "gh-team-allowlist" GHTokenFlag = "gh-token" + GHTokenFileFlag = "gh-token-file" // nolint: gosec GHUserFlag = "gh-user" GHAppIDFlag = "gh-app-id" GHAppKeyFlag = "gh-app-key" @@ -317,6 +318,9 @@ var stringFlags = map[string]stringFlag{ GHTokenFlag: { description: "GitHub token of API user. Can also be specified via the ATLANTIS_GH_TOKEN environment variable.", }, + GHTokenFileFlag: { + description: "A path to a file containing the GitHub token of API user. Can also be specified via the ATLANTIS_GH_TOKEN_FILE environment variable.", + }, GHAppKeyFlag: { description: "The GitHub App's private key", defaultValue: "", @@ -965,26 +969,29 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error { } // The following combinations are valid. - // 1. github user and token set + // 1. github user and (token or token file) // 2. github app ID and (key file set or key set) // 3. gitea user and token set // 4. gitlab user and token set // 5. bitbucket user and token set // 6. azuredevops user and token set // 7. any combination of the above - vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GHAppIDFlag, GHAppKeyFileFlag, GHAppIDFlag, GHAppKeyFlag, GiteaUserFlag, GiteaTokenFlag, GitlabUserFlag, GitlabTokenFlag, BitbucketUserFlag, BitbucketTokenFlag, ADUserFlag, ADTokenFlag) - if ((userConfig.GithubUser == "") != (userConfig.GithubToken == "")) || - ((userConfig.GiteaUser == "") != (userConfig.GiteaToken == "")) || + vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GHUserFlag, GHTokenFileFlag, GHAppIDFlag, GHAppKeyFileFlag, GHAppIDFlag, GHAppKeyFlag, GiteaUserFlag, GiteaTokenFlag, GitlabUserFlag, GitlabTokenFlag, BitbucketUserFlag, BitbucketTokenFlag, ADUserFlag, ADTokenFlag) + if ((userConfig.GiteaUser == "") != (userConfig.GiteaToken == "")) || ((userConfig.GitlabUser == "") != (userConfig.GitlabToken == "")) || ((userConfig.BitbucketUser == "") != (userConfig.BitbucketToken == "")) || ((userConfig.AzureDevopsUser == "") != (userConfig.AzureDevopsToken == "")) { return vcsErr } - if (userConfig.GithubAppID != 0) && ((userConfig.GithubAppKey == "") && (userConfig.GithubAppKeyFile == "")) { - return vcsErr + if userConfig.GithubUser != "" { + if (userConfig.GithubToken == "") == (userConfig.GithubTokenFile == "") { + return vcsErr + } } - if (userConfig.GithubAppID == 0) && ((userConfig.GithubAppKey != "") || (userConfig.GithubAppKeyFile != "")) { - return vcsErr + if userConfig.GithubAppID != 0 { + if (userConfig.GithubAppKey == "") == (userConfig.GithubAppKeyFile == "") { + return vcsErr + } } // At this point, we know that there can't be a single user/token without // its partner, but we haven't checked if any user/token is set at all. @@ -1026,6 +1033,7 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error { // Warn if any tokens have newlines. for name, token := range map[string]string{ GHTokenFlag: userConfig.GithubToken, + GHTokenFileFlag: userConfig.GithubTokenFile, GHWebhookSecretFlag: userConfig.GithubWebhookSecret, GitlabTokenFlag: userConfig.GitlabToken, GitlabWebhookSecretFlag: userConfig.GitlabWebhookSecret, diff --git a/cmd/server_test.go b/cmd/server_test.go index 2c9034af48..c14e43cdd6 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -86,6 +86,7 @@ var testFlags = map[string]interface{}{ GHHostnameFlag: "ghhostname", GHTeamAllowlistFlag: "", GHTokenFlag: "token", + GHTokenFileFlag: "", GHUserFlag: "user", GHAppIDFlag: int64(0), GHAppKeyFlag: "", @@ -433,7 +434,7 @@ func TestExecute_ValidateSSLConfig(t *testing.T) { } func TestExecute_ValidateVCSConfig(t *testing.T) { - expErr := "--gh-user/--gh-token or --gh-app-id/--gh-app-key-file or --gh-app-id/--gh-app-key or --gitea-user/--gitea-token or --gitlab-user/--gitlab-token or --bitbucket-user/--bitbucket-token or --azuredevops-user/--azuredevops-token must be set" + expErr := "--gh-user/--gh-token or --gh-user/--gh-token-file or --gh-app-id/--gh-app-key-file or --gh-app-id/--gh-app-key or --gitea-user/--gitea-token or --gitlab-user/--gitlab-token or --bitbucket-user/--bitbucket-token or --azuredevops-user/--azuredevops-token must be set" cases := []struct { description string flags map[string]interface{} @@ -583,6 +584,23 @@ func TestExecute_ValidateVCSConfig(t *testing.T) { }, false, }, + { + "github user and github token file and should be successful", + map[string]interface{}{ + GHUserFlag: "user", + GHTokenFileFlag: "/path/to/token", + }, + false, + }, + { + "github user, github token, and github token file and should fail", + map[string]interface{}{ + GHUserFlag: "user", + GHTokenFlag: "token", + GHTokenFileFlag: "/path/to/token", + }, + true, + }, { "gitea user and gitea token set and should be successful", map[string]interface{}{ diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 3c062ae853..d4892aed8b 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -743,6 +743,16 @@ based on the organization or user that triggered the webhook. GitHub token of API user. +### `--gh-token-file` + + ```bash + atlantis server --gh-token-file="/path/to/token" + # or + ATLANTIS_GH_TOKEN_FILE="/path/to/token" + ``` + + GitHub token of API user. The token is loaded from disk regularly to allow for rotation of the token without the need to restart the Atlantis server. + ### `--gh-user` ```bash diff --git a/server/events/vcs/git_cred_writer.go b/server/events/vcs/git_cred_writer.go index eca5dc00d7..f877abcfdf 100644 --- a/server/events/vcs/git_cred_writer.go +++ b/server/events/vcs/git_cred_writer.go @@ -47,7 +47,7 @@ func WriteGitCreds(gitUser string, gitToken string, gitHostname string, home str if err := fileLineReplace(config, gitUser, gitHostname, credsFile); err != nil { return errors.Wrap(err, "replacing git credentials line for github app") } - logger.Info("updated git app credentials in %s", credsFile) + logger.Info("updated git credentials in %s", credsFile) } else { if err := fileAppend(config, credsFile); err != nil { return err diff --git a/server/events/vcs/github_client_internal_test.go b/server/events/vcs/github_client_internal_test.go index 798264d269..63f7a73e6b 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"}, GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := NewGithubClient("github.com", &GithubUserCredentials{"user", "pass", ""}, GithubConfig{}, 0, 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"}, GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := NewGithubClient("example.com", &GithubUserCredentials{"user", "pass", ""}, GithubConfig{}, 0, 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 05f1ff939b..81ec7ee7a4 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"}, vcs.GithubConfig{}, 0, logger) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logger) Ok(t, err) defer disableSSLVerification()() @@ -121,7 +121,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -218,7 +218,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -334,7 +334,7 @@ func TestGithubClient_HideOldComments(t *testing.T) { testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{atlantisUser, "pass"}, vcs.GithubConfig{}, 0, + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{atlantisUser, "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -407,7 +407,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -496,7 +496,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -591,7 +591,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -874,7 +874,7 @@ func TestGithubClient_PullIsMergeableWithAllowMergeableBypassApply(t *testing.T) })) testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, vcs.GithubConfig{AllowMergeableBypassApply: true}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{AllowMergeableBypassApply: true}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -959,7 +959,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -1139,7 +1139,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() @@ -1173,7 +1173,7 @@ func TestGithubClient_MergePullCorrectMethod(t *testing.T) { } func TestGithubClient_MarkdownPullLink(t *testing.T) { - client, err := vcs.NewGithubClient("hostname", &vcs.GithubUserCredentials{"user", "pass"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient("hostname", &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) pull := models.PullRequest{Num: 1} s, _ := client.MarkdownPullLink(pull) @@ -1229,7 +1229,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() pull := models.PullRequest{Num: 1} @@ -1288,7 +1288,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() repo := models.Repo{ @@ -1334,7 +1334,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"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() repo := models.Repo{ @@ -1387,7 +1387,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"}, vcs.GithubConfig{}, 0, logger) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logger) Ok(t, err) defer disableSSLVerification()() @@ -1585,7 +1585,7 @@ func TestGithubClient_DiscardReviews(t *testing.T) { })) testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logging.NewNoopLogger(t)) Ok(t, err) defer disableSSLVerification()() if err := client.DiscardReviews(tt.args.repo, tt.args.pull); (err != nil) != tt.wantErr { @@ -1654,7 +1654,7 @@ func TestGithubClient_GetPullLabels(t *testing.T) { })) testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, vcs.GithubConfig{}, 0, logger) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logger) Ok(t, err) defer disableSSLVerification()() @@ -1691,7 +1691,7 @@ func TestGithubClient_GetPullLabels_EmptyResponse(t *testing.T) { })) testServerURL, err := url.Parse(testServer.URL) Ok(t, err) - client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, vcs.GithubConfig{}, 0, logger) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass", ""}, vcs.GithubConfig{}, 0, logger) Ok(t, err) defer disableSSLVerification()() diff --git a/server/events/vcs/github_credentials.go b/server/events/vcs/github_credentials.go index b00ec146a5..4b322fa6cb 100644 --- a/server/events/vcs/github_credentials.go +++ b/server/events/vcs/github_credentials.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/url" + "os" "strings" "github.com/bradleyfalzon/ghinstallation/v2" @@ -42,17 +43,45 @@ func (c *GithubAnonymousCredentials) GetToken() (string, error) { // GithubUserCredentials implements GithubCredentials for the personal auth token flow. type GithubUserCredentials struct { - User string - Token string + User string + Token string + TokenFile string +} + +type GitHubUserTransport struct { + Credentials *GithubUserCredentials + Transport *github.BasicAuthTransport +} + +func (t *GitHubUserTransport) RoundTrip(req *http.Request) (*http.Response, error) { + // update token + token, err := t.Credentials.GetToken() + if err != nil { + return nil, err + } + t.Transport.Password = token + + // defer to the underlying transport + return t.Transport.RoundTrip(req) } // Client returns a client for basic auth user credentials. func (c *GithubUserCredentials) Client() (*http.Client, error) { - tr := &github.BasicAuthTransport{ - Username: strings.TrimSpace(c.User), - Password: strings.TrimSpace(c.Token), + password, err := c.GetToken() + if err != nil { + return nil, err + } + + client := &http.Client{ + Transport: &GitHubUserTransport{ + Credentials: c, + Transport: &github.BasicAuthTransport{ + Username: strings.TrimSpace(c.User), + Password: strings.TrimSpace(password), + }, + }, } - return tr.Client(), nil + return client, nil } // GetUser returns the username for these credentials. @@ -62,6 +91,15 @@ func (c *GithubUserCredentials) GetUser() (string, error) { // GetToken returns the user token. func (c *GithubUserCredentials) GetToken() (string, error) { + if c.TokenFile != "" { + content, err := os.ReadFile(c.TokenFile) + if err != nil { + return "", fmt.Errorf("failed reading github token file: %w", err) + } + + return string(content), nil + } + return c.Token, nil } diff --git a/server/events/vcs/gh_app_creds_rotator.go b/server/events/vcs/github_token_rotator.go similarity index 62% rename from server/events/vcs/gh_app_creds_rotator.go rename to server/events/vcs/github_token_rotator.go index d5b059f972..2b184bd6b8 100644 --- a/server/events/vcs/gh_app_creds_rotator.go +++ b/server/events/vcs/github_token_rotator.go @@ -8,37 +8,40 @@ import ( "github.com/runatlantis/atlantis/server/scheduled" ) -// GitCredsTokenRotator continuously tries to rotate the github app access token every 30 seconds and writes the ~/.git-credentials file -type GitCredsTokenRotator interface { +// GithubTokenRotator continuously tries to rotate the github app access token every 30 seconds and writes the ~/.git-credentials file +type GithubTokenRotator interface { Run() GenerateJob() (scheduled.JobDefinition, error) } -type githubAppTokenRotator struct { +type githubTokenRotator struct { log logging.SimpleLogging githubCredentials GithubCredentials githubHostname string + gitUser string homeDirPath string } -func NewGithubAppTokenRotator( +func NewGithubTokenRotator( log logging.SimpleLogging, githubCredentials GithubCredentials, githubHostname string, - homeDirPath string) GitCredsTokenRotator { + gitUser string, + homeDirPath string) GithubTokenRotator { - return &githubAppTokenRotator{ + return &githubTokenRotator{ log: log, githubCredentials: githubCredentials, githubHostname: githubHostname, + gitUser: gitUser, homeDirPath: homeDirPath, } } // make sure interface is implemented correctly -var _ GitCredsTokenRotator = (*githubAppTokenRotator)(nil) +var _ GithubTokenRotator = (*githubTokenRotator)(nil) -func (r *githubAppTokenRotator) GenerateJob() (scheduled.JobDefinition, error) { +func (r *githubTokenRotator) GenerateJob() (scheduled.JobDefinition, error) { return scheduled.JobDefinition{ Job: r, @@ -46,7 +49,7 @@ func (r *githubAppTokenRotator) GenerateJob() (scheduled.JobDefinition, error) { }, r.rotate() } -func (r *githubAppTokenRotator) Run() { +func (r *githubTokenRotator) Run() { err := r.rotate() if err != nil { // at least log the error message here, as we want to notify the that user that the key rotation wasn't successful @@ -54,8 +57,8 @@ func (r *githubAppTokenRotator) Run() { } } -func (r *githubAppTokenRotator) rotate() error { - r.log.Debug("Refreshing git tokens for Github App") +func (r *githubTokenRotator) rotate() error { + r.log.Debug("Refreshing Github tokens for .git-credentials") token, err := r.githubCredentials.GetToken() if err != nil { @@ -64,7 +67,7 @@ func (r *githubAppTokenRotator) rotate() error { r.log.Debug("Token successfully refreshed") // https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#http-based-git-access-by-an-installation - if err := WriteGitCreds("x-access-token", token, r.githubHostname, r.homeDirPath, r.log, true); err != nil { + if err := WriteGitCreds(r.gitUser, token, r.githubHostname, r.homeDirPath, r.log, true); err != nil { return errors.Wrap(err, "Writing ~/.git-credentials file") } return nil diff --git a/server/events/vcs/gh_app_creds_rotator_test.go b/server/events/vcs/github_token_rotator_test.go similarity index 89% rename from server/events/vcs/gh_app_creds_rotator_test.go rename to server/events/vcs/github_token_rotator_test.go index 8aa65c707a..19e30b95f1 100644 --- a/server/events/vcs/gh_app_creds_rotator_test.go +++ b/server/events/vcs/github_token_rotator_test.go @@ -13,7 +13,7 @@ import ( . "github.com/runatlantis/atlantis/testing" ) -func Test_githubAppTokenRotator_GenerateJob(t *testing.T) { +func Test_githubTokenRotator_GenerateJob(t *testing.T) { logger := logging.NewNoopLogger(t) defer disableSSLVerification()() testServer, err := testdata.GithubAppTestServer(t) @@ -68,10 +68,10 @@ func Test_githubAppTokenRotator_GenerateJob(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tmpDir := t.TempDir() t.Setenv("HOME", tmpDir) - r := vcs.NewGithubAppTokenRotator(logging.NewNoopLogger(t), tt.fields.githubCredentials, testServer, tmpDir) + r := vcs.NewGithubTokenRotator(logging.NewNoopLogger(t), tt.fields.githubCredentials, testServer, "x-access-token", tmpDir) got, err := r.GenerateJob() if (err != nil) != tt.wantErr { - t.Errorf("githubAppTokenRotator.GenerateJob() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("githubTokenRotator.GenerateJob() error = %v, wantErr %v", err, tt.wantErr) return } if tt.credsFileWritten { diff --git a/server/server.go b/server/server.go index f2d93b5a49..39c5ae1bc7 100644 --- a/server/server.go +++ b/server/server.go @@ -230,8 +230,9 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { supportedVCSHosts = append(supportedVCSHosts, models.Github) if userConfig.GithubUser != "" { githubCredentials = &vcs.GithubUserCredentials{ - User: userConfig.GithubUser, - Token: userConfig.GithubToken, + User: userConfig.GithubUser, + Token: userConfig.GithubToken, + TokenFile: userConfig.GithubTokenFile, } } else if userConfig.GithubAppID != 0 && userConfig.GithubAppKeyFile != "" { privateKey, err := os.ReadFile(userConfig.GithubAppKeyFile) @@ -514,7 +515,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { GithubHostname: userConfig.GithubHostname, } - githubAppTokenRotator := vcs.NewGithubAppTokenRotator(logger, githubCredentials, userConfig.GithubHostname, home) + githubAppTokenRotator := vcs.NewGithubTokenRotator(logger, githubCredentials, userConfig.GithubHostname, "x-access-token", home) tokenJd, err := githubAppTokenRotator.GenerateJob() if err != nil { return nil, errors.Wrap(err, "could not write credentials") @@ -522,6 +523,15 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { scheduledExecutorService.AddJob(tokenJd) } + if userConfig.GithubUser != "" && userConfig.GithubTokenFile != "" && userConfig.WriteGitCreds { + githubTokenRotator := vcs.NewGithubTokenRotator(logger, githubCredentials, userConfig.GithubHostname, userConfig.GithubUser, home) + tokenJd, err := githubTokenRotator.GenerateJob() + if err != nil { + return nil, errors.Wrap(err, "could not write credentials") + } + scheduledExecutorService.AddJob(tokenJd) + } + projectLocker := &events.DefaultProjectLocker{ Locker: lockingClient, NoOpLocker: noOpLocker, diff --git a/server/user_config.go b/server/user_config.go index 8693a55549..10e6e6b9fc 100644 --- a/server/user_config.go +++ b/server/user_config.go @@ -50,6 +50,7 @@ type UserConfig struct { GithubAllowMergeableBypassApply bool `mapstructure:"gh-allow-mergeable-bypass-apply"` GithubHostname string `mapstructure:"gh-hostname"` GithubToken string `mapstructure:"gh-token"` + GithubTokenFile string `mapstructure:"gh-token-file"` GithubUser string `mapstructure:"gh-user"` GithubWebhookSecret string `mapstructure:"gh-webhook-secret"` GithubOrg string `mapstructure:"gh-org"` From 3f97fc07431f0991fb5a6e8f801aaa8a0495f26e Mon Sep 17 00:00:00 2001 From: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Date: Mon, 4 Nov 2024 23:22:45 +0000 Subject: [PATCH 12/38] chore(docs): Fix formatting on server-configuration.md (#5059) Signed-off-by: X-Guardian --- runatlantis.io/docs/server-configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index d4892aed8b..8b00ef7c7a 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -1266,6 +1266,7 @@ This is useful when you have many projects and want to keep the pull request cle Namespace for emitting stats/metrics. See [stats](stats.md) section. ### `--tf-distribution` + ```bash atlantis server --tf-distribution="terraform" # or From ed2108a6713ce4ae2321ee745fdea01c4e0402e2 Mon Sep 17 00:00:00 2001 From: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Date: Tue, 5 Nov 2024 00:41:41 +0000 Subject: [PATCH 13/38] chore(docs): Fix formatting on server-configuration.md (#5061) Signed-off-by: X-Guardian --- runatlantis.io/docs/server-configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 8b00ef7c7a..986d5dbc11 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -1272,6 +1272,7 @@ This is useful when you have many projects and want to keep the pull request cle # or ATLANTIS_TF_DISTRIBUTION="terraform" ``` + Which TF distribution to use. Can be set to `terraform` or `opentofu`. ### `--tf-download` From ba44659e4d80a7bb50fcddf4d3550a889671a79c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 01:06:39 +0000 Subject: [PATCH 14/38] chore(deps): update module github.com/golang-jwt/jwt/v4 to v4.5.1 [security] (main) (#5062) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index c6f4fd40da..4fa673cc96 100644 --- a/go.mod +++ b/go.mod @@ -86,7 +86,7 @@ require ( github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v62 v62.0.0 // indirect diff --git a/go.sum b/go.sum index 0d0760b774..8613b6dde2 100644 --- a/go.sum +++ b/go.sum @@ -164,8 +164,9 @@ github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= From 053f494107450ef0f10cf5c6551183b79999a694 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 01:13:56 +0000 Subject: [PATCH 15/38] chore(deps): update github/codeql-action action to v3.27.0 in .github/workflows/scorecard.yml (main) (#5063) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 23d3a79af1..9b66c58652 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -51,6 +51,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 + uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 with: sarif_file: results.sarif From 2517945baeb9f2f8b5f96fd3b9b7bc4d3c5e7040 Mon Sep 17 00:00:00 2001 From: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:24:22 +0000 Subject: [PATCH 16/38] chore(docs): Fix Using Atlantis Merge Method Link (#5065) Signed-off-by: X-Guardian --- runatlantis.io/docs/using-atlantis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runatlantis.io/docs/using-atlantis.md b/runatlantis.io/docs/using-atlantis.md index aa0a372231..16f5ade9a3 100644 --- a/runatlantis.io/docs/using-atlantis.md +++ b/runatlantis.io/docs/using-atlantis.md @@ -149,7 +149,7 @@ atlantis apply -w staging * `-p project` Apply the plan for this project. Refers to the name of the project configured in the repo's [`atlantis.yaml` file](repo-level-atlantis-yaml.md). Cannot be used at same time as `-d` or `-w`. * `-w workspace` Apply the plan for this [Terraform workspace](https://developer.hashicorp.com/terraform/language/state/workspaces). Ignore this if Terraform workspaces are unused. * `--auto-merge-disabled` Disable [automerge](automerging.md) for this apply command. -* `--auto-merge-method method` Specify which [merge method](automerging.md#how-to-set-merge-method-for-automerge) use for the apply command if [automerge](automerging.md) is enabled. Implemented only for GitHub. +* `--auto-merge-method method` Specify which [merge method](automerging.md#how-to-set-the-merge-method-for-automerge) use for the apply command if [automerge](automerging.md) is enabled. Implemented only for GitHub. * `--verbose` Append Atlantis log to comment. ### Additional Terraform flags From ff9c16c2ab6066ad7f970802c592dc25167bef22 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:27:59 +0000 Subject: [PATCH 17/38] chore(deps): update dependency mermaid to v10.9.3 [security] (main) (#5026) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 9 ++-- pnpm-lock.yaml | 110 +++++++++++++++++++++++----------------------- 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index f155218c86..7580664a92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2691,10 +2691,11 @@ } }, "node_modules/mermaid": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.1.tgz", - "integrity": "sha512-Mx45Obds5W1UkW1nv/7dHRsbfMM1aOKA2+Pxs/IGHNonygDHwmng8xTHyS9z4KWVi0rbko8gjiBmuwwXQ7tiNA==", + "version": "10.9.3", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.3.tgz", + "integrity": "sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw==", "dev": true, + "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^6.0.1", "@types/d3-scale": "^4.0.3", @@ -2705,7 +2706,7 @@ "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.10", "dayjs": "^1.11.7", - "dompurify": "^3.0.5", + "dompurify": "^3.0.5 <3.1.7", "elkjs": "^0.9.0", "katex": "^0.16.9", "khroma": "^2.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0216c0d194..1c552a6b50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: ^0.40.0 version: 0.40.0 mermaid: - specifier: ^10.9.1 - version: 10.9.1 + specifier: ^10.9.3 + version: 10.9.3 sitemap-ts: specifier: ^1.7.3 version: 1.8.0 @@ -34,7 +34,7 @@ importers: version: 1.3.4(@algolia/client-search@4.23.3)(@types/node@20.12.12)(postcss@8.4.47)(sass@1.77.2)(search-insights@2.13.0) vitepress-plugin-mermaid: specifier: ^2.0.16 - version: 2.0.16(mermaid@10.9.1)(vitepress@1.3.4(@algolia/client-search@4.23.3)(@types/node@20.12.12)(postcss@8.4.47)(sass@1.77.2)(search-insights@2.13.0)) + version: 2.0.16(mermaid@10.9.3)(vitepress@1.3.4(@algolia/client-search@4.23.3)(@types/node@20.12.12)(postcss@8.4.47)(sass@1.77.2)(search-insights@2.13.0)) vue: specifier: ^3.4.27 version: 3.4.27 @@ -484,8 +484,8 @@ packages: '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} - '@types/unist@2.0.10': - resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -740,8 +740,8 @@ packages: peerDependencies: cytoscape: ^3.2.0 - cytoscape@3.29.2: - resolution: {integrity: sha512-2G1ycU28Nh7OHT9rkXRLpCDP30MKH1dXJORZuBhtEhEW7pKwgPi77ImqlCWinouyE1PNepIOGZBOrE84DG7LyQ==} + cytoscape@3.30.3: + resolution: {integrity: sha512-HncJ9gGJbVtw7YXtIs3+6YAFSSiKsom0amWc33Z7QbylbY2JGMrA0yz4EwrdTScZxnwclXeEZHzO5pxoy0ZE4g==} engines: {node: '>=0.10'} d3-array@2.12.1: @@ -886,11 +886,11 @@ packages: dagre-d3-es@7.0.10: resolution: {integrity: sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==} - dayjs@1.11.11: - resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -919,8 +919,8 @@ packages: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} - dompurify@3.1.5: - resolution: {integrity: sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==} + dompurify@3.1.6: + resolution: {integrity: sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==} eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1007,8 +1007,8 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} - immutable@4.3.6: - resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==} + immutable@4.3.7: + resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} ini@4.1.3: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} @@ -1063,8 +1063,8 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - katex@0.16.10: - resolution: {integrity: sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==} + katex@0.16.11: + resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==} hasBin: true khroma@2.1.0: @@ -1135,8 +1135,8 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - mermaid@10.9.1: - resolution: {integrity: sha512-Mx45Obds5W1UkW1nv/7dHRsbfMM1aOKA2+Pxs/IGHNonygDHwmng8xTHyS9z4KWVi0rbko8gjiBmuwwXQ7tiNA==} + mermaid@10.9.3: + resolution: {integrity: sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw==} micromark-core-commonmark@1.1.0: resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} @@ -1241,8 +1241,8 @@ packages: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} @@ -1415,8 +1415,8 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - stylis@4.3.2: - resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} + stylis@4.3.4: + resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==} superjson@2.2.1: resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==} @@ -1843,9 +1843,9 @@ snapshots: '@mermaid-js/mermaid-mindmap@9.3.0': dependencies: '@braintree/sanitize-url': 6.0.4 - cytoscape: 3.29.2 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.29.2) - cytoscape-fcose: 2.2.0(cytoscape@3.29.2) + cytoscape: 3.30.3 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.30.3) + cytoscape-fcose: 2.2.0(cytoscape@3.30.3) d3: 7.9.0 khroma: 2.1.0 non-layered-tidy-tree-layout: 2.0.2 @@ -1965,7 +1965,7 @@ snapshots: '@types/hast@3.0.4': dependencies: - '@types/unist': 2.0.10 + '@types/unist': 3.0.3 '@types/linkify-it@5.0.0': {} @@ -1976,7 +1976,7 @@ snapshots: '@types/mdast@3.0.15': dependencies: - '@types/unist': 2.0.10 + '@types/unist': 2.0.11 '@types/mdast@4.0.4': dependencies: @@ -1996,7 +1996,7 @@ snapshots: dependencies: '@types/node': 20.12.12 - '@types/unist@2.0.10': {} + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -2292,18 +2292,18 @@ snapshots: csstype@3.1.3: {} - cytoscape-cose-bilkent@4.1.0(cytoscape@3.29.2): + cytoscape-cose-bilkent@4.1.0(cytoscape@3.30.3): dependencies: cose-base: 1.0.3 - cytoscape: 3.29.2 + cytoscape: 3.30.3 - cytoscape-fcose@2.2.0(cytoscape@3.29.2): + cytoscape-fcose@2.2.0(cytoscape@3.30.3): dependencies: cose-base: 2.2.0 - cytoscape: 3.29.2 + cytoscape: 3.30.3 optional: true - cytoscape@3.29.2: {} + cytoscape@3.30.3: {} d3-array@2.12.1: dependencies: @@ -2477,11 +2477,11 @@ snapshots: d3: 7.9.0 lodash-es: 4.17.21 - dayjs@1.11.11: {} + dayjs@1.11.13: {} - debug@4.3.5: + debug@4.3.7: dependencies: - ms: 2.1.2 + ms: 2.1.3 decode-named-character-reference@1.0.2: dependencies: @@ -2501,7 +2501,7 @@ snapshots: diff@5.2.0: {} - dompurify@3.1.5: {} + dompurify@3.1.6: {} eastasianwidth@0.2.0: {} @@ -2614,7 +2614,7 @@ snapshots: ignore@5.3.1: {} - immutable@4.3.6: + immutable@4.3.7: optional: true ini@4.1.3: {} @@ -2656,7 +2656,7 @@ snapshots: jsonpointer@5.0.1: {} - katex@0.16.10: + katex@0.16.11: dependencies: commander: 8.3.0 @@ -2722,7 +2722,7 @@ snapshots: mdast-util-from-markdown@1.3.1: dependencies: '@types/mdast': 3.0.15 - '@types/unist': 2.0.10 + '@types/unist': 2.0.11 decode-named-character-reference: 1.0.2 mdast-util-to-string: 3.2.0 micromark: 3.2.0 @@ -2756,25 +2756,25 @@ snapshots: merge2@1.4.1: {} - mermaid@10.9.1: + mermaid@10.9.3: dependencies: '@braintree/sanitize-url': 6.0.4 '@types/d3-scale': 4.0.8 '@types/d3-scale-chromatic': 3.0.3 - cytoscape: 3.29.2 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.29.2) + cytoscape: 3.30.3 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.30.3) d3: 7.9.0 d3-sankey: 0.12.3 dagre-d3-es: 7.0.10 - dayjs: 1.11.11 - dompurify: 3.1.5 + dayjs: 1.11.13 + dompurify: 3.1.6 elkjs: 0.9.3 - katex: 0.16.10 + katex: 0.16.11 khroma: 2.1.0 lodash-es: 4.17.21 mdast-util-from-markdown: 1.3.1 non-layered-tidy-tree-layout: 2.0.2 - stylis: 4.3.2 + stylis: 4.3.4 ts-dedent: 2.2.0 uuid: 9.0.1 web-worker: 1.3.0 @@ -2912,7 +2912,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.5 + debug: 4.3.7 decode-named-character-reference: 1.0.2 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -2950,7 +2950,7 @@ snapshots: mri@1.2.0: {} - ms@2.1.2: {} + ms@2.1.3: {} nanoid@3.3.7: {} @@ -3055,7 +3055,7 @@ snapshots: sass@1.77.2: dependencies: chokidar: 3.6.0 - immutable: 4.3.6 + immutable: 4.3.7 source-map-js: 1.2.1 optional: true @@ -3129,7 +3129,7 @@ snapshots: strip-json-comments@3.1.1: {} - stylis@4.3.2: {} + stylis@4.3.4: {} superjson@2.2.1: dependencies: @@ -3163,7 +3163,7 @@ snapshots: unist-util-stringify-position@3.0.3: dependencies: - '@types/unist': 2.0.10 + '@types/unist': 2.0.11 unist-util-stringify-position@4.0.0: dependencies: @@ -3209,9 +3209,9 @@ snapshots: fsevents: 2.3.3 sass: 1.77.2 - vitepress-plugin-mermaid@2.0.16(mermaid@10.9.1)(vitepress@1.3.4(@algolia/client-search@4.23.3)(@types/node@20.12.12)(postcss@8.4.47)(sass@1.77.2)(search-insights@2.13.0)): + vitepress-plugin-mermaid@2.0.16(mermaid@10.9.3)(vitepress@1.3.4(@algolia/client-search@4.23.3)(@types/node@20.12.12)(postcss@8.4.47)(sass@1.77.2)(search-insights@2.13.0)): dependencies: - mermaid: 10.9.1 + mermaid: 10.9.3 vitepress: 1.3.4(@algolia/client-search@4.23.3)(@types/node@20.12.12)(postcss@8.4.47)(sass@1.77.2)(search-insights@2.13.0) optionalDependencies: '@mermaid-js/mermaid-mindmap': 9.3.0 From 7541ebd852bf3c54858923e78f68d0f010b8f4a9 Mon Sep 17 00:00:00 2001 From: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:34:41 +0000 Subject: [PATCH 18/38] chore(workflow): Fix Website GitHub Workflow link-check Job Filter (#5066) --- .github/workflows/website.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index ece3b28e7c..0277122436 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -32,7 +32,7 @@ jobs: with: filters: | src: - - '**.js' + - 'runatlantis.io/**' - 'package-lock.json' - 'package.json' - '.github/workflows/website.yml' From dc3892888f7fc8aa5798ec57c3488791eff3e520 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:58:03 +0000 Subject: [PATCH 19/38] chore(deps): update actions/checkout digest to 11bd719 in .github/workflows/website.yml (main) (#4987) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/atlantis-image.yml | 6 +++--- .github/workflows/codeql.yml | 4 ++-- .github/workflows/lint.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/renovate-config.yml | 2 +- .github/workflows/test.yml | 8 ++++---- .github/workflows/testing-env-image.yml | 4 ++-- .github/workflows/website.yml | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/atlantis-image.yml b/.github/workflows/atlantis-image.yml index 63d88a23f5..34c2e3e430 100644 --- a/.github/workflows/atlantis-image.yml +++ b/.github/workflows/atlantis-image.yml @@ -29,7 +29,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 id: changes with: @@ -56,7 +56,7 @@ jobs: PUSH: ${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 # Lint the Dockerfile first before setting anything up - name: Lint Dockerfile @@ -160,7 +160,7 @@ jobs: DOCKER_REPO: ghcr.io/${{ github.repository }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7d242718f5..2cfc8eaa24 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -43,7 +43,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 id: changes with: @@ -73,7 +73,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 398101e06d..115068ed48 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -30,7 +30,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 id: changes with: @@ -47,7 +47,7 @@ jobs: name: Linting runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 # need to setup go toolchain explicitly - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2d3d1ebe5d..4acf1d4b13 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: goreleaser: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: submodules: true diff --git a/.github/workflows/renovate-config.yml b/.github/workflows/renovate-config.yml index 8e7a9def6e..435ac78b22 100644 --- a/.github/workflows/renovate-config.yml +++ b/.github/workflows/renovate-config.yml @@ -19,6 +19,6 @@ jobs: validate: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4 - run: npx --package renovate -c 'renovate-config-validator' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf8b437926..0d5d739db1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 id: changes with: @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-24.04 container: ghcr.io/runatlantis/testing-env:latest@sha256:5c56ee1df3dd9ea426bee50df43e2407df054e81f4b4eb183173e90a11f86922 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 # need to setup go toolchain explicitly - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 @@ -118,7 +118,7 @@ jobs: ATLANTIS_GH_TOKEN: ${{ secrets.ATLANTISBOT_GITHUB_TOKEN }} NGROK_AUTH_TOKEN: ${{ secrets.ATLANTISBOT_NGROK_AUTH_TOKEN }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: go-version-file: go.mod @@ -155,7 +155,7 @@ jobs: ATLANTIS_GITLAB_TOKEN: ${{ secrets.ATLANTISBOT_GITLAB_TOKEN }} NGROK_AUTH_TOKEN: ${{ secrets.ATLANTISBOT_NGROK_AUTH_TOKEN }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5 with: go-version-file: go.mod diff --git a/.github/workflows/testing-env-image.yml b/.github/workflows/testing-env-image.yml index 1ecdfa69b1..cf61663805 100644 --- a/.github/workflows/testing-env-image.yml +++ b/.github/workflows/testing-env-image.yml @@ -25,7 +25,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 id: changes with: @@ -40,7 +40,7 @@ jobs: name: Build Testing Env Image runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Set up QEMU uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3 diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 0277122436..832181f164 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -26,7 +26,7 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 id: changes with: @@ -46,7 +46,7 @@ jobs: name: Website Link Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: markdown-lint uses: DavidAnson/markdownlint-cli2-action@b4c9feab76d8025d1e83c653fa3990936df0e6c8 # v16 From 1d4da81c859197d6cbac1a54d09c4db684e69334 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:38:03 +0000 Subject: [PATCH 20/38] chore(deps): update actions/setup-node digest to 39370e3 in .github/workflows/website.yml (main) (#5042) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/renovate-config.yml | 2 +- .github/workflows/website.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/renovate-config.yml b/.github/workflows/renovate-config.yml index 435ac78b22..06283df876 100644 --- a/.github/workflows/renovate-config.yml +++ b/.github/workflows/renovate-config.yml @@ -20,5 +20,5 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4 + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 - run: npx --package renovate -c 'renovate-config-validator' diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 832181f164..822384b82c 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -55,7 +55,7 @@ jobs: globs: 'runatlantis.io/**/*.md' - name: setup npm - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4 + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 with: node-version: '20' cache: 'npm' From 840305504dad452554963e43e865debc5d32891f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:47:00 +0000 Subject: [PATCH 21/38] chore(deps-dev): bump micromatch from 4.0.7 to 4.0.8 (#4977) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- pnpm-lock.yaml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7580664a92..9807b006af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3162,9 +3162,9 @@ ] }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { "braces": "^3.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c552a6b50..75965cc606 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1216,8 +1216,8 @@ packages: micromark@3.2.0: resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} minimatch@9.0.4: @@ -2547,7 +2547,7 @@ snapshots: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.7 + micromatch: 4.0.8 fastq@1.17.1: dependencies: @@ -2931,7 +2931,7 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@4.0.7: + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 From 24a5458246d5812e55b64d548d2e7de929904b35 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:51:31 +0000 Subject: [PATCH 22/38] chore(deps): update go to v1.23.2 in go.mod (main) (#4912) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Simon Heather <32168619+X-Guardian@users.noreply.github.com> --- Dockerfile | 2 +- e2e/go.mod | 2 +- go.mod | 2 +- testing/Dockerfile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index c5fb61a828..2fd762f018 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # what distro is the image being built for ARG ALPINE_TAG=3.20.3@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d ARG DEBIAN_TAG=12.7-slim@sha256:36e591f228bb9b99348f584e83f16e012c33ba5cad44ef5981a1d7c0a93eca22 -ARG GOLANG_TAG=1.23.0-alpine@sha256:d0b31558e6b3e4cc59f6011d79905835108c919143ebecc58f35965bf79948f4 +ARG GOLANG_TAG=1.23.2-alpine@sha256:9dd2625a1ff2859b8d8b01d8f7822c0f528942fe56cfe7a1e7c38d3b8d72d679 # renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp ARG DEFAULT_TERRAFORM_VERSION=1.9.8 diff --git a/e2e/go.mod b/e2e/go.mod index cccf3f4471..3b04a1f852 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -1,6 +1,6 @@ module github.com/runatlantis/atlantis/e2e -go 1.23.0 +go 1.23.2 require ( github.com/google/go-github/v65 v65.0.0 diff --git a/go.mod b/go.mod index 4fa673cc96..e61905540f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/runatlantis/atlantis -go 1.23.0 +go 1.23.2 require ( code.gitea.io/sdk/gitea v0.19.0 diff --git a/testing/Dockerfile b/testing/Dockerfile index 68613b0e3d..5c62679bf6 100644 --- a/testing/Dockerfile +++ b/testing/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.23.0@sha256:613a108a4a4b1dfb6923305db791a19d088f77632317cfc3446825c54fb862cd +FROM golang:1.23.2@sha256:ad5c126b5cf501a8caef751a243bb717ec204ab1aa56dc41dc11be089fafcb4f RUN apt-get update && apt-get --no-install-recommends -y install unzip \ && apt-get clean \ From 30c46303822276f68a5f9b7a39d60e5541903ee6 Mon Sep 17 00:00:00 2001 From: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:13:33 +0000 Subject: [PATCH 23/38] chore(docs): Fix formatting on provider-credentials.md (#5068) Signed-off-by: X-Guardian --- runatlantis.io/docs/provider-credentials.md | 1 + 1 file changed, 1 insertion(+) diff --git a/runatlantis.io/docs/provider-credentials.md b/runatlantis.io/docs/provider-credentials.md index 09dd289759..8dcddb7463 100644 --- a/runatlantis.io/docs/provider-credentials.md +++ b/runatlantis.io/docs/provider-credentials.md @@ -58,6 +58,7 @@ provider "aws" { ``` Atlantis runs `terraform` with the following variables: + | `-var` Argument | Description | |--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| | `atlantis_user=lkysow` | The VCS username of who is running the plan command. | From ae972ad72a9032de33ea2a95d6c28def66e87756 Mon Sep 17 00:00:00 2001 From: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:56:23 +0000 Subject: [PATCH 24/38] chore: Add User Agent to Website Link GitHub Check Muffet Step (#5069) Signed-off-by: X-Guardian --- .github/workflows/website.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 822384b82c..3ae90767a9 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -96,6 +96,7 @@ jobs: -e 'https://github.com/runatlantis/helm-charts#customization' \ -e 'https://github.com/sethvargo/atlantis-on-gke/blob/master/terraform/tls.tf#L64-L84' \ -e 'https://confluence.atlassian.com/*' \ + --header 'User-Agent: Muffet' \ --header 'Accept-Encoding:deflate, gzip' \ --buffer-size 8192 \ http://localhost:8080/ From c721f936f33b9174ff7ca642950420c5df4eefc6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 18:00:27 +0000 Subject: [PATCH 25/38] chore(deps): update davidanson/markdownlint-cli2-action action to v17 in .github/workflows/website.yml (main) (#4959) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/website.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 3ae90767a9..90bfea84ec 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: markdown-lint - uses: DavidAnson/markdownlint-cli2-action@b4c9feab76d8025d1e83c653fa3990936df0e6c8 # v16 + uses: DavidAnson/markdownlint-cli2-action@db43aef879112c3119a410d69f66701e0d530809 # v17 with: config: .markdownlint.yaml globs: 'runatlantis.io/**/*.md' From 240b6b1a0b8f3b8dc7e3d6ef4b10bd0ced318b57 Mon Sep 17 00:00:00 2001 From: Henry Muru Paenga Date: Wed, 6 Nov 2024 11:17:01 +1300 Subject: [PATCH 26/38] fix: GitHub - Support Token File for Git Commands (#5067) Signed-off-by: Henry Muru Paenga --- server/events/event_parser.go | 23 +++++++++++++++++++++-- server/events/event_parser_test.go | 1 + server/server.go | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/server/events/event_parser.go b/server/events/event_parser.go index a6b4b363ac..5cbc029f48 100644 --- a/server/events/event_parser.go +++ b/server/events/event_parser.go @@ -17,6 +17,7 @@ import ( "encoding/json" "fmt" "net/url" + "os" "path" "strings" @@ -357,6 +358,7 @@ type EventParsing interface { type EventParser struct { GithubUser string GithubToken string + GithubTokenFile string GitlabUser string GitlabToken string GiteaUser string @@ -372,7 +374,15 @@ type EventParser struct { func (e *EventParser) ParseAPIPlanRequest(vcsHostType models.VCSHostType, repoFullName string, cloneURL string) (models.Repo, error) { switch vcsHostType { case models.Github: - return models.NewRepo(vcsHostType, repoFullName, cloneURL, e.GithubUser, e.GithubToken) + token := e.GithubToken + if e.GithubTokenFile != "" { + content, err := os.ReadFile(e.GithubTokenFile) + if err != nil { + return models.Repo{}, fmt.Errorf("failed reading github token file: %w", err) + } + token = string(content) + } + return models.NewRepo(vcsHostType, repoFullName, cloneURL, e.GithubUser, token) case models.Gitea: return models.NewRepo(vcsHostType, repoFullName, cloneURL, e.GiteaUser, e.GiteaToken) case models.Gitlab: @@ -626,7 +636,16 @@ func (e *EventParser) ParseGithubPull(logger logging.SimpleLogging, pull *github // returns a repo into the Atlantis model. // See EventParsing for return value docs. func (e *EventParser) ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error) { - return models.NewRepo(models.Github, ghRepo.GetFullName(), ghRepo.GetCloneURL(), e.GithubUser, e.GithubToken) + token := e.GithubToken + if e.GithubTokenFile != "" { + content, err := os.ReadFile(e.GithubTokenFile) + if err != nil { + return models.Repo{}, fmt.Errorf("failed reading github token file: %w", err) + } + token = string(content) + } + + return models.NewRepo(models.Github, ghRepo.GetFullName(), ghRepo.GetCloneURL(), e.GithubUser, token) } // ParseGiteaRepo parses the response from the Gitea API endpoint that diff --git a/server/events/event_parser_test.go b/server/events/event_parser_test.go index fffe30e3eb..6350ea76ca 100644 --- a/server/events/event_parser_test.go +++ b/server/events/event_parser_test.go @@ -36,6 +36,7 @@ import ( var parser = events.EventParser{ GithubUser: "github-user", GithubToken: "github-token", + GithubTokenFile: "", GitlabUser: "gitlab-user", GitlabToken: "gitlab-token", AllowDraftPRs: false, diff --git a/server/server.go b/server/server.go index 39c5ae1bc7..056550a355 100644 --- a/server/server.go +++ b/server/server.go @@ -560,6 +560,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { eventParser := &events.EventParser{ GithubUser: userConfig.GithubUser, GithubToken: userConfig.GithubToken, + GithubTokenFile: userConfig.GithubTokenFile, GitlabUser: userConfig.GitlabUser, GitlabToken: userConfig.GitlabToken, GiteaUser: userConfig.GiteaUser, From a5fee5482881f5db3e650a331290f0aaf991b65b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 01:44:45 +0000 Subject: [PATCH 27/38] chore(deps): update ghcr.io/runatlantis/testing-env:latest docker digest to b176e5b in .github/workflows/test.yml (main) (#5072) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0d5d739db1..6bc471e3ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,7 +48,7 @@ jobs: if: needs.changes.outputs.should-run-tests == 'true' name: Tests runs-on: ubuntu-24.04 - container: ghcr.io/runatlantis/testing-env:latest@sha256:5c56ee1df3dd9ea426bee50df43e2407df054e81f4b4eb183173e90a11f86922 + container: ghcr.io/runatlantis/testing-env:latest@sha256:b176e5b60579b194bad6c1a19724c9f13021684397d0effa6b3746f6f56cbd02 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 From 4bae63be9156e6fd4880a67a1f7fd9cdb92c7d6c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 01:26:27 +0000 Subject: [PATCH 28/38] fix(deps): update module github.com/redis/go-redis/v9 to v9.7.0 in go.mod (main) (#5075) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e61905540f..c0897c948a 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/opentofu/tofudl v0.0.0-20240923062014-8c1e00f33ce6 github.com/petergtz/pegomock/v4 v4.1.0 github.com/pkg/errors v0.9.1 - github.com/redis/go-redis/v9 v9.6.2 + github.com/redis/go-redis/v9 v9.7.0 github.com/remeh/sizedwaitgroup v1.0.0 github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 github.com/slack-go/slack v0.15.0 diff --git a/go.sum b/go.sum index 8613b6dde2..0d27dc3795 100644 --- a/go.sum +++ b/go.sum @@ -395,8 +395,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/redis/go-redis/v9 v9.6.2 h1:w0uvkRbc9KpgD98zcvo5IrVUsn0lXpRMuhNgiHDJzdk= -github.com/redis/go-redis/v9 v9.6.2/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E= github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= From 842def067f323e4cd872aab68c7ec8e81b22edac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 03:48:27 +0000 Subject: [PATCH 29/38] fix(deps): update module github.com/xanzy/go-gitlab to v0.112.0 in go.mod (main) (#5076) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- e2e/go.mod | 2 +- e2e/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/e2e/go.mod b/e2e/go.mod index 3b04a1f852..4e66756dfd 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -5,7 +5,7 @@ go 1.23.2 require ( github.com/google/go-github/v65 v65.0.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/xanzy/go-gitlab v0.111.0 + github.com/xanzy/go-gitlab v0.112.0 ) require ( diff --git a/e2e/go.sum b/e2e/go.sum index 42fc2c0195..9a26e9d8ef 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -33,8 +33,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/xanzy/go-gitlab v0.111.0 h1:4zT52QdDVxGYAGxN2VY8upSvZIiuiI+Z4d+c+7D/lII= -github.com/xanzy/go-gitlab v0.111.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= +github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw= +github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= diff --git a/go.mod b/go.mod index c0897c948a..3a7841eec0 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/stretchr/testify v1.9.0 github.com/uber-go/tally/v4 v4.1.10 github.com/urfave/negroni/v3 v3.1.1 - github.com/xanzy/go-gitlab v0.111.0 + github.com/xanzy/go-gitlab v0.112.0 go.etcd.io/bbolt v1.3.11 go.uber.org/zap v1.27.0 golang.org/x/term v0.25.0 diff --git a/go.sum b/go.sum index 0d27dc3795..5b4eedaa4a 100644 --- a/go.sum +++ b/go.sum @@ -461,8 +461,8 @@ github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/negroni/v3 v3.1.1 h1:6MS4nG9Jk/UuCACaUlNXCbiKa0ywF9LXz5dGu09v8hw= github.com/urfave/negroni/v3 v3.1.1/go.mod h1:jWvnX03kcSjDBl/ShB0iHvx5uOs7mAzZXW+JvJ5XYAs= -github.com/xanzy/go-gitlab v0.111.0 h1:4zT52QdDVxGYAGxN2VY8upSvZIiuiI+Z4d+c+7D/lII= -github.com/xanzy/go-gitlab v0.111.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= +github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw= +github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From d4247bc0691ef853f8afcc24ca4f9d1df8561e7b Mon Sep 17 00:00:00 2001 From: Gusts Jonasts <101050452+0x0013@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:32:39 +0200 Subject: [PATCH 30/38] fix: avoid resetting fetch head when doing branch fetch unshallow (#4577) --- server/events/working_dir.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/events/working_dir.go b/server/events/working_dir.go index c2e56d8dc7..f77378a2a4 100644 --- a/server/events/working_dir.go +++ b/server/events/working_dir.go @@ -353,6 +353,12 @@ func (w *FileWorkspace) mergeToBaseBranch(logger logging.SimpleLogging, c wrappe if err := w.wrappedGit(logger, c, "fetch", "--unshallow"); err != nil { return err } + + // fetch once more, otherwise `FETCH_HEAD` was reset to base when we ran + // fetch --unshallow + if err := w.wrappedGit(logger, c, "fetch", fetchRemote, fetchRef); err != nil { + return err + } } // We use --no-ff because we always want there to be a merge commit. From 280fff56e0f95fa4c2a3bb3c5673f7ff82df92a2 Mon Sep 17 00:00:00 2001 From: Hiroaki Izu <7583548+akihiro17@users.noreply.github.com> Date: Fri, 8 Nov 2024 06:50:04 +0900 Subject: [PATCH 31/38] docs: remove note abount autoplan modules limitation (#5077) Signed-off-by: akihiro17 Co-authored-by: akihiro17 --- runatlantis.io/docs/server-configuration.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 986d5dbc11..303e9df067 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -185,10 +185,6 @@ Values are chosen in this order: By default, changes to modules will not trigger autoplanning. See the flags below. ::: -::: warning NOTE -If any projects are defined in a repo atlantis.yaml file, the logic for this flag will not execute. See issue [#3122](https://github.com/runatlantis/atlantis/issues/3122). -::: - ### `--autoplan-modules` ```bash @@ -202,10 +198,6 @@ Included project are projects with files included by `--autoplan-file-list`. After tracing, Atlantis will plan any project that includes a changed module. This is equivalent to setting `--autoplan-modules-from-projects` to the value of `--autoplan-file-list`. See below. -::: warning NOTE -If any projects are defined in a repo atlantis.yaml file, the logic for this flag will not execute. See issue [#3122](https://github.com/runatlantis/atlantis/issues/3122). -::: - ### `--autoplan-modules-from-projects` ```bash From aedc1b0d9a0a9770b12450878454e02b5803938c Mon Sep 17 00:00:00 2001 From: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:32:36 +0000 Subject: [PATCH 32/38] fix: code scanning alert no. 12: Reflected cross-site scripting (#5078) Signed-off-by: Simon Heather <32168619+X-Guardian@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- server/controllers/events/events_controller.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/controllers/events/events_controller.go b/server/controllers/events/events_controller.go index b2687182ff..3d0e97cbc7 100644 --- a/server/controllers/events/events_controller.go +++ b/server/controllers/events/events_controller.go @@ -16,6 +16,7 @@ package events import ( "encoding/json" "fmt" + "html" "io" "net/http" "strings" @@ -178,7 +179,7 @@ func (e *VCSEventsController) handleGithubPost(w http.ResponseWriter, r *http.Re return } - githubReqID := "X-Github-Delivery=" + r.Header.Get("X-Github-Delivery") + githubReqID := "X-Github-Delivery=" + html.EscapeString(r.Header.Get("X-Github-Delivery")) logger := e.Logger.With("gh-request-id", githubReqID) scope := e.Scope.SubScope("github_event") From cbecb01ea57de04ec88c3809fd083fb5868f28f8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:39:05 +0000 Subject: [PATCH 33/38] chore(deps): update goreleaser/goreleaser-action digest to 9ed2f89 in .github/workflows/release.yml (main) (#5079) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4acf1d4b13..cb2c060ff0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: go-version-file: go.mod - name: Run GoReleaser for stable release - uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6 + uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6 if: (!contains(github.ref, 'pre')) with: # You can pass flags to goreleaser via GORELEASER_ARGS From 893ba90ba8f348ebf21d43693af4b87f1b204e70 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:41:26 +0000 Subject: [PATCH 34/38] fix(deps): update module github.com/uber-go/tally/v4 to v4.1.16 in go.mod (main) (#4228) Signed-off-by: X-Guardian Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: X-Guardian --- go.mod | 3 ++- go.sum | 13 +++++++++++-- server/metrics/scope.go | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 3a7841eec0..7784c2d553 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 - github.com/uber-go/tally/v4 v4.1.10 + github.com/uber-go/tally/v4 v4.1.16 github.com/urfave/negroni/v3 v3.1.1 github.com/xanzy/go-gitlab v0.112.0 go.etcd.io/bbolt v1.3.11 @@ -87,6 +87,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/golang-jwt/jwt/v4 v4.5.1 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v62 v62.0.0 // indirect diff --git a/go.sum b/go.sum index 5b4eedaa4a..7270bf677e 100644 --- a/go.sum +++ b/go.sum @@ -182,6 +182,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -455,8 +457,8 @@ github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSW github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -github.com/uber-go/tally/v4 v4.1.10 h1:2GSX7Tmq26wjAvOtQEc5EvRROIkX2OX4vpROt6mlRLM= -github.com/uber-go/tally/v4 v4.1.10/go.mod h1:pPR56rjthjtLB8xQlEx2I1VwAwRGCh/i4xMUcmG+6z4= +github.com/uber-go/tally/v4 v4.1.16 h1:by2hveWRh/cUReButk6ns1sHK/hiKry7BuOV6iY16XI= +github.com/uber-go/tally/v4 v4.1.16/go.mod h1:RW5DgqsyEPs0lA4b0YNf4zKj7DveKHd73hnO6zVlyW0= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/negroni/v3 v3.1.1 h1:6MS4nG9Jk/UuCACaUlNXCbiKa0ywF9LXz5dGu09v8hw= @@ -468,6 +470,7 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= @@ -537,6 +540,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= @@ -570,6 +574,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -598,6 +603,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= @@ -635,7 +641,9 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -714,6 +722,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= diff --git a/server/metrics/scope.go b/server/metrics/scope.go index a0f35b3ef3..e1701931fc 100644 --- a/server/metrics/scope.go +++ b/server/metrics/scope.go @@ -27,7 +27,8 @@ func NewScope(cfg valid.Metrics, logger logging.SimpleLogging, statsNamespace st } scopeOpts := tally.ScopeOptions{ - Prefix: statsNamespace, + Prefix: statsNamespace, + SanitizeOptions: &tallyprom.DefaultSanitizerOpts, } if r, ok := reporter.(tally.StatsReporter); ok { From 22b37512ae4205df901e8b8a3746a8ada4b52c50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:08:25 -0800 Subject: [PATCH 35/38] chore(deps): update github/codeql-action digest to 4f3212b in .github/workflows/codeql.yml (main) (#5080) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2cfc8eaa24..3c394d09c1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -77,7 +77,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3 + uses: github/codeql-action/init@4f3212b61783c3c68e8309a0f18a699764811cda # v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -91,7 +91,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@662472033e021d55d94146f66f6058822b0b39fd # v3 + uses: github/codeql-action/autobuild@4f3212b61783c3c68e8309a0f18a699764811cda # v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -104,7 +104,7 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3 + uses: github/codeql-action/analyze@4f3212b61783c3c68e8309a0f18a699764811cda # v3 with: category: "/language:${{matrix.language}}" From fc4e9295b072e520fe2ba9cf63679dabe48d3603 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 10 Nov 2024 00:56:18 +0000 Subject: [PATCH 36/38] chore(deps): update dependency opentofu/opentofu to v1.8.5 in dockerfile (main) (#5082) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2fd762f018..01bb172894 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ ARG GOLANG_TAG=1.23.2-alpine@sha256:9dd2625a1ff2859b8d8b01d8f7822c0f528942fe56cf # renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp ARG DEFAULT_TERRAFORM_VERSION=1.9.8 # renovate: datasource=github-releases depName=opentofu/opentofu versioning=hashicorp -ARG DEFAULT_OPENTOFU_VERSION=1.8.4 +ARG DEFAULT_OPENTOFU_VERSION=1.8.5 # renovate: datasource=github-releases depName=open-policy-agent/conftest ARG DEFAULT_CONFTEST_VERSION=0.56.0 From 08da6621234a63463f854d8bdc6a9565c41debe2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2024 19:14:55 -0800 Subject: [PATCH 37/38] chore(deps): update dependency node to v22 in .node-version (main) (#5083) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .node-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.node-version b/.node-version index 2a393af592..7af24b7ddb 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20.18.0 +22.11.0 From 41017f48b51d74a8d496c328d4c02ea2c57d24ee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 10 Nov 2024 03:15:42 +0000 Subject: [PATCH 38/38] chore(deps): update dependency raviqqe/muffet to v2.10.6 in .github/workflows/website.yml (main) (#5081) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/website.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 90bfea84ec..88bb91e24c 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -63,7 +63,7 @@ jobs: - name: run http-server env: # renovate: datasource=github-releases depName=raviqqe/muffet - MUFFET_VERSION: 2.10.3 + MUFFET_VERSION: 2.10.6 run: | # install raviqqe/muffet to check for broken links. curl -Ls https://github.com/raviqqe/muffet/releases/download/v${MUFFET_VERSION}/muffet_linux_amd64.tar.gz | tar -xz