From 1cecdf725529cbf3fcdefd2fe1f4b3f91d85aab3 Mon Sep 17 00:00:00 2001 From: Divyansh Garg Date: Mon, 12 Oct 2026 18:11:48 +0530 Subject: [PATCH] fix: validate merged GitHub stack ordering --- .../extension/merger/github/github_merger.go | 6 ++-- .../merger/github/github_merger_test.go | 34 +++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/runway/extension/merger/github/github_merger.go b/runway/extension/merger/github/github_merger.go index 3c6501801..386e30060 100644 --- a/runway/extension/merger/github/github_merger.go +++ b/runway/extension/merger/github/github_merger.go @@ -417,9 +417,9 @@ func (m *githubMerger) validateStepIsGitHubStack(ctx context.Context, step *runw if len(pending) == 0 { return nil } - for _, p := range pulls[:len(pulls)-len(pending)] { - if !p.pr.Merged { - return fmt.Errorf("%w: step %q: %s is unmerged but listed below merged pull requests", merger.ErrInvalidRequest, step.GetStepId(), p.ref.Label) + for _, p := range pending { + if p.pr.Merged { + return fmt.Errorf("%w: step %q: %s is already merged but listed after an unmerged pull request", merger.ErrInvalidRequest, step.GetStepId(), p.ref.Label) } } diff --git a/runway/extension/merger/github/github_merger_test.go b/runway/extension/merger/github/github_merger_test.go index 1b178cc90..b2405b6d4 100644 --- a/runway/extension/merger/github/github_merger_test.go +++ b/runway/extension/merger/github/github_merger_test.go @@ -811,3 +811,37 @@ func TestNewRejectsUnusableParams(t *testing.T) { _, err := New(valid) require.NoError(t, err) } + +func stepPullForTest(n int, merged bool, base string) stepPull { + return stepPull{ + ref: changeRef{PRNumber: n, SHA: headSHA(n), Label: fmt.Sprintf("uber/submitqueue#%d", n)}, + pr: pullRequest{Number: n, Merged: merged, Base: gitRef{Ref: base}}, + } +} + +func TestValidateStepIsGitHubStack_MergedMustPrecedeUnmerged(t *testing.T) { + m := &githubMerger{target: testTarget} + step := &runwaymq.MergeStep{} + + tests := []struct { + name string + pulls []stepPull + wantErr bool + }{ + {name: "all merged", pulls: []stepPull{stepPullForTest(1, true, testTarget), stepPullForTest(2, true, testTarget)}}, + {name: "merged prefix then one open pull on target", pulls: []stepPull{stepPullForTest(1, true, testTarget), stepPullForTest(2, false, testTarget)}}, + {name: "merged pull listed after an unmerged one", pulls: []stepPull{stepPullForTest(1, false, testTarget), stepPullForTest(2, true, testTarget)}, wantErr: true}, + {name: "merged pull between unmerged ones", pulls: []stepPull{stepPullForTest(1, false, testTarget), stepPullForTest(2, true, "branch-1"), stepPullForTest(3, false, "branch-2")}, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := m.validateStepIsGitHubStack(context.Background(), step, tt.pulls) + if tt.wantErr { + require.Error(t, err) + assert.ErrorIs(t, err, merger.ErrInvalidRequest) + return + } + require.NoError(t, err) + }) + } +}