diff --git a/pkg/runstream/interfaces.go b/pkg/runstream/interfaces.go index 1d57f24..392c79c 100644 --- a/pkg/runstream/interfaces.go +++ b/pkg/runstream/interfaces.go @@ -38,6 +38,7 @@ type RunMetadata interface { GetCommitSHA() string GetOrganization() string GetVcsProvider() string + GetAutoMerge() bool } type RunPollingTask interface { diff --git a/pkg/runstream/run_metadata.go b/pkg/runstream/run_metadata.go index 956cb4b..a266fa4 100644 --- a/pkg/runstream/run_metadata.go +++ b/pkg/runstream/run_metadata.go @@ -43,6 +43,8 @@ type TFRunMetadata struct { RootNoteID int64 VcsProvider string + + AutoMerge bool } func (r *TFRunMetadata) GetAction() string { @@ -75,6 +77,9 @@ func (r *TFRunMetadata) GetOrganization() string { func (r *TFRunMetadata) GetVcsProvider() string { return r.VcsProvider } +func (r *TFRunMetadata) GetAutoMerge() bool { + return r.AutoMerge +} func (s *Stream) AddRunMeta(rmd RunMetadata) error { b, err := encodeTFRunMetadata(rmd) if err != nil { diff --git a/pkg/tfc_trigger/project_config.go b/pkg/tfc_trigger/project_config.go index 3610b7f..eeac779 100644 --- a/pkg/tfc_trigger/project_config.go +++ b/pkg/tfc_trigger/project_config.go @@ -91,6 +91,7 @@ type TFCWorkspace struct { Dir string `yaml:"dir"` Mode string `yaml:"mode" default:"apply-before-merge" validate:"one_of=apply-before-merge,merge-before-apply,tfc-vcs-repo"` TriggerDirs []string `yaml:"triggerDirs"` + AutoMerge bool `yaml:"autoMerge" default:"true"` } func getProjectConfigFile(ctx context.Context, gl vcs.GitClient, trigger *TFCTrigger) (*ProjectConfig, error) { diff --git a/pkg/tfc_trigger/project_config_test.go b/pkg/tfc_trigger/project_config_test.go index d14fffc..e66245c 100644 --- a/pkg/tfc_trigger/project_config_test.go +++ b/pkg/tfc_trigger/project_config_test.go @@ -176,6 +176,7 @@ func Test_loadProjectConfig(t *testing.T) { Dir: "terraform/dev/", Mode: "apply-before-merge", TriggerDirs: nil, + AutoMerge: true, }, }}, wantErr: false, @@ -202,6 +203,7 @@ func Test_loadProjectConfig(t *testing.T) { Dir: "terraform/dev/", Mode: "apply-before-merge", TriggerDirs: nil, + AutoMerge: true, }, }}, wantErr: false, @@ -215,6 +217,7 @@ func Test_loadProjectConfig(t *testing.T) { Organization: "foo-corp", Dir: "terraform/dev/", Mode: "apply-before-merge", + AutoMerge: true, TriggerDirs: []string{ "modules/**", }, @@ -231,6 +234,7 @@ func Test_loadProjectConfig(t *testing.T) { Organization: "foo-corp", Dir: "terraform/dev/", Mode: "apply-before-merge", + AutoMerge: true, TriggerDirs: []string{ "modules/database/", }, @@ -247,6 +251,7 @@ func Test_loadProjectConfig(t *testing.T) { Organization: "foo-corp", Dir: "terraform/dev/", Mode: "apply-before-merge", + AutoMerge: true, }, }}, wantErr: false, @@ -266,12 +271,14 @@ func Test_loadProjectConfig(t *testing.T) { Organization: "foo-corp", Dir: "terraform/dev/", Mode: "apply-before-merge", + AutoMerge: true, }, { Name: "service-tfbuddy-tooling", Organization: "foo-corp", Dir: "terraform/tooling/", Mode: "apply-before-merge", + AutoMerge: true, }, }}, wantErr: false, diff --git a/pkg/tfc_trigger/tfc_trigger.go b/pkg/tfc_trigger/tfc_trigger.go index ab22e54..b6fc472 100644 --- a/pkg/tfc_trigger/tfc_trigger.go +++ b/pkg/tfc_trigger/tfc_trigger.go @@ -620,10 +620,10 @@ func (t *TFCTrigger) triggerRunForWorkspace(ctx context.Context, cfgWS *TFCWorks Bool("speculative", run.ConfigurationVersion.Speculative). Msg("created TFC run") - return t.publishRunToStream(ctx, run) + return t.publishRunToStream(ctx, run, cfgWS) } -func (t *TFCTrigger) publishRunToStream(ctx context.Context, run *tfe.Run) error { +func (t *TFCTrigger) publishRunToStream(ctx context.Context, run *tfe.Run, cfgWS *TFCWorkspace) error { ctx, span := otel.Tracer("TFC").Start(ctx, "publishRunToStream") defer span.End() @@ -639,6 +639,8 @@ func (t *TFCTrigger) publishRunToStream(ctx context.Context, run *tfe.Run) error DiscussionID: t.GetMergeRequestDiscussionID(), RootNoteID: t.GetMergeRequestRootNoteID(), VcsProvider: t.GetVcsProvider(), + //set Auto Merge if both conditions are met. + AutoMerge: cfgWS.AutoMerge && cfgWS.Mode == "apply-before-merge", } err := t.runstream.AddRunMeta(rmd) if err != nil { diff --git a/pkg/vcs/common.go b/pkg/vcs/common.go new file mode 100644 index 0000000..9bd7424 --- /dev/null +++ b/pkg/vcs/common.go @@ -0,0 +1,10 @@ +package vcs + +import "os" + +const TF_BUDDY_AUTO_MERGE = "TFBUDDY_ENABLE_AUTO_MERGE" + +func IsGlobalAutoMergeEnabled() bool { + //empty or true will permit auto merge. + return os.Getenv(TF_BUDDY_AUTO_MERGE) != "false" +} diff --git a/pkg/vcs/github/run_events_worker.go b/pkg/vcs/github/run_events_worker.go index 73d077c..a8bd329 100644 --- a/pkg/vcs/github/run_events_worker.go +++ b/pkg/vcs/github/run_events_worker.go @@ -86,6 +86,12 @@ func (w *RunEventsWorker) postRunStatusComment(ctx context.Context, run *tfe.Run return } // The applying phase of a run has completed. - w.client.MergeMR(ctx, rmd.GetMRInternalID(), rmd.GetMRProjectNameWithNamespace()) + w.mergePRIfPossible(ctx, rmd) } } +func (w *RunEventsWorker) mergePRIfPossible(ctx context.Context, rmd runstream.RunMetadata) { + if !rmd.GetAutoMerge() || !vcs.IsGlobalAutoMergeEnabled() { + return + } + w.client.MergeMR(ctx, rmd.GetMRInternalID(), rmd.GetMRProjectNameWithNamespace()) +} diff --git a/pkg/vcs/gitlab/mr_status_updater.go b/pkg/vcs/gitlab/mr_status_updater.go index 2e305cc..488ac7c 100644 --- a/pkg/vcs/gitlab/mr_status_updater.go +++ b/pkg/vcs/gitlab/mr_status_updater.go @@ -11,6 +11,7 @@ import ( "github.com/rs/zerolog/log" gogitlab "github.com/xanzy/go-gitlab" "github.com/zapier/tfbuddy/pkg/runstream" + "github.com/zapier/tfbuddy/pkg/vcs" "go.opentelemetry.io/otel" ) @@ -197,10 +198,20 @@ func (p *RunStatusUpdater) getLatestPipelineID(ctx context.Context, rmd runstrea return nil } -func (p *RunStatusUpdater) mergeMRIfPossible(ctx context.Context, rmd runstream.RunMetadata) error { +func (p *RunStatusUpdater) mergeMRIfPossible(ctx context.Context, rmd runstream.RunMetadata) { + ctx, span := otel.Tracer("TFC").Start(ctx, "mergeMRIfPossible") + defer span.End() + + if !rmd.GetAutoMerge() || !vcs.IsGlobalAutoMergeEnabled() { + return + } + err := p.client.MergeMR(ctx, rmd.GetMRInternalID(), rmd.GetMRProjectNameWithNamespace()) + if err != nil { + span.RecordError(err) + } log.Debug().AnErr("err", err).Msg("merge MR") - return err + } // configureBackOff returns a backoff configuration to use to retry requests