Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 47 additions & 15 deletions pkg/mocks/mock_vcs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions pkg/tfc_trigger/tfc_trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,19 @@ func (t *TFCTrigger) TriggerTFCEvents(ctx context.Context) (*TriggeredTFCWorkspa
// Returning an error puts the workspace into status.Errored verbatim.
type workspaceDispatchFn func(ctx context.Context, ws *TFCWorkspace) error

// publishBlockedStatus sets TFC/plan and TFC/apply to failed so the merge gate
// fires for a blocked workspace. Both are set to match the existing convention
// where a pending plan pre-emptively marks apply as failed.
func (t *TFCTrigger) publishBlockedStatus(ctx context.Context, workspace string) {
const description = "Blocked: target branch modified workspace paths since divergence."
for _, action := range []string{"plan", "apply"} {
name := fmt.Sprintf("TFC/%s/%s", action, workspace)
if err := t.gl.SetMergeRequestStatus(ctx, t.GetProjectNameWithNamespace(), t.GetCommitSHA(), name, "failed", description, ""); err != nil {
log.Error().Err(err).Str("ws", workspace).Str("action", action).Msg("could not publish blocked commit status")
}
}
Comment on lines +468 to +475
}

func (t *TFCTrigger) dispatchWorkspaces(ctx context.Context, workspaces []*TFCWorkspace, blocked map[string]struct{}, dispatch workspaceDispatchFn) *TriggeredTFCWorkspaces {
status := &TriggeredTFCWorkspaces{
Errored: make([]*ErroredWorkspace, 0),
Expand All @@ -483,6 +496,7 @@ func (t *TFCTrigger) dispatchWorkspaces(ctx context.Context, workspaces []*TFCWo
Name: ws.Name,
Error: fmt.Sprintf("Blocked: workspace-relevant paths (dir: '%s', triggerDirs: %v) have been modified on the target branch since this branch diverged. Please rebase/merge the target branch to resolve this.", ws.Dir, ws.TriggerDirs),
})
t.publishBlockedStatus(ctx, ws.Name)
continue
}
if err := dispatch(ctx, ws); err != nil {
Expand Down
14 changes: 13 additions & 1 deletion pkg/tfc_trigger/tfc_trigger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,18 @@ func TestTFCEvents_WorkspaceApplyModifiedBothSrcDstBranches(t *testing.T) {
testSuite.MockGitRepo.EXPECT().GetModifiedFileNamesBetweenCommits(testSuite.MetaData.CommonSHA, "main").Return([]string{"terraform.tf"}, nil)
testSuite.MockGitClient.EXPECT().GetMergeRequestModifiedFiles(gomock.Any(), testSuite.MetaData.MRIID, testSuite.MetaData.ProjectNameNS).Return([]string{"main.tf"}, nil)

// Blocked workspaces must publish a failing commit status for both plan
// and apply so GitLab's required-status check actually gates the merge.
commitSHA := "abcd12233"
testSuite.MockGitClient.EXPECT().SetMergeRequestStatus(
gomock.Any(), testSuite.MetaData.ProjectNameNS, commitSHA,
fmt.Sprintf("TFC/plan/%s", mocks.TF_WORKSPACE_NAME), "failed", gomock.Any(), "",
).Return(nil)
testSuite.MockGitClient.EXPECT().SetMergeRequestStatus(
gomock.Any(), testSuite.MetaData.ProjectNameNS, commitSHA,
fmt.Sprintf("TFC/apply/%s", mocks.TF_WORKSPACE_NAME), "failed", gomock.Any(), "",
).Return(nil)

mockStreamClient := mocks.NewMockStreamClient(mockCtrl)

testSuite.InitTestSuite()
Expand All @@ -554,7 +566,7 @@ func TestTFCEvents_WorkspaceApplyModifiedBothSrcDstBranches(t *testing.T) {
tCfg, _ := tfc_trigger.NewTFCTriggerConfig(&tfc_trigger.TFCTriggerOptions{
Action: tfc_trigger.ApplyAction,
Branch: testSuite.MetaData.SourceBranch,
CommitSHA: "abcd12233",
CommitSHA: commitSHA,
ProjectNameWithNamespace: testSuite.MetaData.ProjectNameNS,
MergeRequestIID: testSuite.MetaData.MRIID,
TriggerSource: tfc_trigger.CommentTrigger,
Expand Down
5 changes: 5 additions & 0 deletions pkg/vcs/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,11 @@ func (c *Client) SetCommitStatus(ctx context.Context, projectWithNS string, comm
return nil, nil
}

func (c *Client) SetMergeRequestStatus(ctx context.Context, projectWithNS, commitSHA, name, state, description, targetURL string) error {
//TODO implement me
return nil
}
Comment on lines +352 to +355
Comment on lines +352 to +355

func (c *Client) GetPipelinesForCommit(ctx context.Context, projectWithNS string, commitSHA string) ([]vcs.ProjectPipeline, error) {
//TODO implement me
return nil, nil
Expand Down
22 changes: 22 additions & 0 deletions pkg/vcs/gitlab/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,28 @@ func (c *GitlabClient) SetCommitStatus(ctx context.Context, projectWithNS string
}, createBackOffWithRetries())
}

// SetMergeRequestStatus publishes a commit status without attaching a pipeline
// ID. Commit-level status is enough to gate the merge and skips the 30s
// pipeline-lookup backoff used by run-status updates.
func (c *GitlabClient) SetMergeRequestStatus(ctx context.Context, projectWithNS, commitSHA, name, state, description, targetURL string) error {
_, span := otel.Tracer("TFC").Start(ctx, "SetMergeRequestStatus")
defer span.End()

opts := &gogitlab.SetCommitStatusOptions{
Comment on lines +117 to +121
Name: ptr(name),
Context: ptr(name),
State: gogitlab.BuildStateValue(state),
}
if description != "" {
opts.Description = ptr(description)
}
if targetURL != "" {
opts.TargetURL = ptr(targetURL)
}
_, err := c.SetCommitStatus(ctx, projectWithNS, commitSHA, &GitlabCommitStatusOptions{opts})
return err
}

func (c *GitlabClient) GetCommitStatuses(ctx context.Context, projectID, commitSHA string) []*gogitlab.CommitStatus {
_, span := otel.Tracer("TFC").Start(ctx, "GetCommitStatuses")
defer span.End()
Expand Down
3 changes: 3 additions & 0 deletions pkg/vcs/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ type GitClient interface {
ResolveMergeRequestDiscussion(context.Context, string, int, string) error
AddMergeRequestDiscussionReply(ctx context.Context, mrIID int, project, discussionID, comment string) (MRNote, error)
SetCommitStatus(ctx context.Context, projectWithNS string, commitSHA string, status CommitStatusOptions) (CommitStatus, error)
// SetMergeRequestStatus publishes a commit status without a TFC run, used
// to gate the merge when tfbuddy refuses to run a workspace.
Comment on lines +19 to +20
SetMergeRequestStatus(ctx context.Context, projectWithNS string, commitSHA string, name string, state string, description string, targetURL string) error
GetPipelinesForCommit(ctx context.Context, projectWithNS string, commitSHA string) ([]ProjectPipeline, error)
GetOldRunUrls(ctx context.Context, mrIID int, project string, rootCommentID int, workspace string, action string) (string, error)
MergeMR(ctx context.Context, mrIID int, project string) error
Expand Down
Loading