From 5aed66077c0573558e539512a140e0ca0c9ad991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1?= Date: Thu, 9 Jul 2026 10:31:56 +0100 Subject: [PATCH] Set merge as optional --- git.go | 33 +++++++++++++-------------------- git_test.go | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/git.go b/git.go index df6f18b..d62a130 100644 --- a/git.go +++ b/git.go @@ -109,7 +109,7 @@ var ( ) // Create new pull request -func (ghApp *GitHubApp) NewPullRequest(source, target, title, body string) error { +func (ghApp *GitHubApp) NewPullRequest(source, target, title, body string, merge bool) error { ctx := context.Background() // Create PR @@ -126,25 +126,24 @@ func (ghApp *GitHubApp) NewPullRequest(source, target, title, body string) error return err } - // Optionally wait for checks to pass on the PR's head commit before merging. - if ghApp.Config.WaitForChecksToPass { - err = waitForChecksToPass(ctx, ghApp.githubClient, "kununu", ghApp.Config.repoName, pr.GetHead().GetSHA()) - if err != nil { - return err + if merge { + + if ghApp.Config.WaitForChecksToPass { + err = waitForChecksToPass(ctx, ghApp.githubClient, "kununu", ghApp.Config.repoName, pr.GetHead().GetSHA()) + if err != nil { + return err + } } + + // Merge PR + return mergePullRequest(ctx, ghApp.githubClient, "kununu", ghApp.Config.repoName, pr.GetNumber()) } - // Merge PR - return mergePullRequest(ctx, ghApp.githubClient, "kununu", ghApp.Config.repoName, pr.GetNumber()) + return nil + } -// waitForChecksToPass polls the GitHub Actions check runs for the given ref -// until they all complete successfully, one of them fails, or the timeout is -// reached. It relies on the Check Runs API (what Actions report to) rather than -// the legacy commit-status API, which does not reflect Actions results. func waitForChecksToPass(ctx context.Context, client *github.Client, owner, repo, ref string) error { - // Bound the whole wait — and every API call made within it — so a stalled - // request can never hang indefinitely. ctx, cancel := context.WithTimeout(ctx, checksWaitTimeout) defer cancel() @@ -152,8 +151,6 @@ func waitForChecksToPass(ctx context.Context, client *github.Client, owner, repo defer ticker.Stop() for { - // Wait one interval before polling so GitHub has time to register the - // check runs for a freshly created PR. select { case <-ctx.Done(): return fmt.Errorf("timeout while waiting for checks to pass: %w", ctx.Err()) @@ -173,15 +170,11 @@ func waitForChecksToPass(ctx context.Context, client *github.Client, owner, repo } switch run.GetConclusion() { case "success", "skipped", "neutral": - // Passed (or intentionally not blocking). default: - // failure, cancelled, timed_out, action_required, stale... return fmt.Errorf("check %q did not pass: %s", run.GetName(), run.GetConclusion()) } } - // Require at least one completed check so we never merge before CI has - // started. Once every registered check has completed successfully, done. if !pending && result.GetTotal() > 0 { return nil } diff --git a/git_test.go b/git_test.go index 865324d..ecd3adc 100644 --- a/git_test.go +++ b/git_test.go @@ -167,7 +167,7 @@ func TestNewPullRequestSkipsChecksWhenDisabled(t *testing.T) { githubClient: newTestClient(t, handler), } - if err := ghApp.NewPullRequest("feature", "main", "title", "body"); err != nil { + if err := ghApp.NewPullRequest("feature", "main", "title", "body", true); err != nil { t.Fatalf("unexpected error: %v", err) } if checksCalled { @@ -201,7 +201,7 @@ func TestNewPullRequestWaitsForChecksWhenEnabled(t *testing.T) { githubClient: newTestClient(t, handler), } - if err := ghApp.NewPullRequest("feature", "main", "title", "body"); err != nil { + if err := ghApp.NewPullRequest("feature", "main", "title", "body", true); err != nil { t.Fatalf("unexpected error: %v", err) } if !checksCalled { @@ -211,3 +211,35 @@ func TestNewPullRequestWaitsForChecksWhenEnabled(t *testing.T) { t.Errorf("PR was not merged") } } + +func TestNewPullRequestSkipsMergeWhenNotRequested(t *testing.T) { + var checksCalled, mergeCalled bool + + handler := http.NewServeMux() + handler.HandleFunc("POST /repos/kununu/test-repo/pulls", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{"number":7,"head":{"sha":"abc123"}}`)) + }) + handler.HandleFunc("GET /repos/kununu/test-repo/commits/{ref}/check-runs", func(w http.ResponseWriter, r *http.Request) { + checksCalled = true + w.Write([]byte(`{"total_count":1,"check_runs":[{"name":"build","status":"completed","conclusion":"success"}]}`)) + }) + handler.HandleFunc("PUT /repos/kununu/test-repo/pulls/7/merge", func(w http.ResponseWriter, r *http.Request) { + mergeCalled = true + w.Write([]byte(`{"merged":true}`)) + }) + + ghApp := &GitHubApp{ + Config: &GitHubAppConfig{repoName: "test-repo", WaitForChecksToPass: true}, + githubClient: newTestClient(t, handler), + } + + if err := ghApp.NewPullRequest("feature", "main", "title", "body", false); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if checksCalled { + t.Errorf("check runs were polled even though merge was not requested") + } + if mergeCalled { + t.Errorf("PR was merged even though merge was not requested") + } +}