diff --git a/testutil/verifypr/verify.go b/testutil/verifypr/verify.go index 71f84af34f..48d1388047 100644 --- a/testutil/verifypr/verify.go +++ b/testutil/verifypr/verify.go @@ -22,10 +22,15 @@ import ( var titlePrefix = regexp.MustCompile(`^[*\w]+(/[*\w]+)?$`) +type PRUser struct { + Login string `json:"login"` +} + type PR struct { - Title string `json:"title"` - Body string `json:"body"` - ID string `json:"node_id"` + Title string `json:"title"` + Body string `json:"body"` + ID string `json:"node_id"` + Creator PRUser `json:"user"` } // PRFromEnv returns the PR by parsing it from "GITHUB_PR" env var or an error. @@ -42,7 +47,7 @@ func PRFromEnv() (PR, error) { return PR{}, errors.Wrap(err, "unmarshal PR body") } - if pr.Title == "" || pr.Body == "" || pr.ID == "" { + if pr.Title == "" || pr.Body == "" || pr.ID == "" || pr.Creator.Login == "" { return PR{}, errors.New("pr field not set") } @@ -61,12 +66,12 @@ func verify() error { } // Skip dependabot PRs. - if strings.Contains(pr.Title, "build(deps)") && strings.Contains(pr.Body, "dependabot") { + if strings.Contains(pr.Title, "build(deps)") && pr.Creator.Login == "dependabot[bot]" { return nil } // Skip Renovate PRs. - if strings.Contains(pr.Title, "chore(deps)") && strings.Contains(pr.Body, "Renovate") { + if strings.Contains(pr.Title, "chore(deps)") && pr.Creator.Login == "renovate[bot]" { return nil } diff --git a/testutil/verifypr/verify_internal_test.go b/testutil/verifypr/verify_internal_test.go index 8223c614e0..309733aff6 100644 --- a/testutil/verifypr/verify_internal_test.go +++ b/testutil/verifypr/verify_internal_test.go @@ -3,6 +3,7 @@ package main import ( + "encoding/json" "strconv" "strings" "testing" @@ -180,3 +181,94 @@ func TestBody(t *testing.T) { }) } } + +func TestVerifyBotSkip(t *testing.T) { + tests := []struct { + Name string + PR PR + Skipped bool // true means verify() should return nil without format checks + }{ + { + Name: "dependabot skipped", + PR: PR{ + Title: "build(deps): bump some-lib from 1.0 to 2.0", + Body: "Bumps some-lib.", + ID: "node_1", + Creator: PRUser{Login: "dependabot[bot]"}, + }, + Skipped: true, + }, + { + Name: "renovate skipped", + PR: PR{ + Title: "chore(deps): update some-lib to v2", + Body: "This PR contains the following updates.", + ID: "node_2", + Creator: PRUser{Login: "renovate[bot]"}, + }, + Skipped: true, + }, + { + Name: "dependabot title but wrong creator not skipped", + PR: PR{ + Title: "build(deps): bump some-lib from 1.0 to 2.0", + Body: "Bumps some-lib.", + ID: "node_3", + Creator: PRUser{Login: "someuser"}, + }, + Skipped: false, + }, + { + Name: "renovate title but wrong creator not skipped", + PR: PR{ + Title: "chore(deps): update some-lib to v2", + Body: "This PR contains the following updates.", + ID: "node_4", + Creator: PRUser{Login: "someuser"}, + }, + Skipped: false, + }, + { + Name: "dependabot creator but wrong title not skipped", + PR: PR{ + Title: "chore(deps): bump some-lib from 1.0 to 2.0", + Body: "Bumps some-lib.", + ID: "node_5", + Creator: PRUser{Login: "dependabot[bot]"}, + }, + Skipped: false, + }, + { + Name: "renovate creator but wrong title not skipped", + PR: PR{ + Title: "build(deps): update some-lib to v2", + Body: "This PR contains the following updates.", + ID: "node_6", + Creator: PRUser{Login: "renovate[bot]"}, + }, + Skipped: false, + }, + } + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + b, err := json.Marshal(test.PR) + if err != nil { + t.Fatalf("marshal PR: %v", err) + } + + t.Setenv("GITHUB_PR", string(b)) + + err = verify() + if test.Skipped { + if err != nil { + t.Fatalf("expected bot PR to be skipped (nil error), got: %v", err) + } + } else { + // Non-bot PRs will fail title/body format checks — that's expected. + if err == nil { + t.Fatalf("expected non-bot PR to fail format checks, got nil error") + } + } + }) + } +}