diff --git a/fixtures/config.yaml b/fixtures/config.yaml index 12875a9..efbc35b 100644 --- a/fixtures/config.yaml +++ b/fixtures/config.yaml @@ -21,6 +21,7 @@ github_pr_notifications: - gh_owner: my-org gh_repo: my-repo-1 + # PR must have ALL labels in this list to match gh_pr_labels: - enhancement gh_pr_include_drafts: true @@ -28,6 +29,9 @@ github_pr_notifications: gh_pr_ignore_changes_requested: true gh_pr_conditions: older_than_seconds: 3600 + does_not_have_labels: + - "WIP" + - "do not merge" # Mon-Fri every 2 hours during business hours schedule: "CRON_TZ=Europe/Berlin 00 10-18/2 * * 1-5" notify: diff --git a/internal/cfg/cfg.go b/internal/cfg/cfg.go index 8f65299..5b10ec5 100644 --- a/internal/cfg/cfg.go +++ b/internal/cfg/cfg.go @@ -79,7 +79,8 @@ type Notification struct { // PrConditions struct describes additional conditions for PRs type PrConditions struct { - OlderThanSeconds int `yaml:"older_than_seconds"` + OlderThanSeconds int `yaml:"older_than_seconds"` + DoesNotHaveLabels []string `yaml:"does_not_have_labels"` } // PrNotification is a struct for a single GH repo PRs notifications diff --git a/internal/cfg/cfg_test.go b/internal/cfg/cfg_test.go index 013745f..89e0f90 100644 --- a/internal/cfg/cfg_test.go +++ b/internal/cfg/cfg_test.go @@ -85,3 +85,31 @@ func TestGetSlackUID(t *testing.T) { t.Errorf("Did not expect to find Slack user ID for GitHub login 'nonexistent-gh-user', but found one") } } + +func TestPrConditions(t *testing.T) { + config := AppConfig{} + err := config.LoadConfig("../../fixtures/config.yaml") + if err != nil { + t.Errorf("Failed to load ./fixtures/config.yaml: %s", err.Error()) + } + + if len(config.PrNotifications) < 1 { + t.Errorf("Length of github_pr_notifications is %d, but it should be not empty", len(config.PrNotifications)) + } + + conditions := config.PrNotifications[0].Conditions + if conditions.OlderThanSeconds != 3600 { + t.Errorf("Expected gh_pr_conditions.older_than_seconds=3600, but got %d", conditions.OlderThanSeconds) + } + + expectedLabels := []string{"WIP", "do not merge"} + if len(conditions.DoesNotHaveLabels) != len(expectedLabels) { + t.Errorf("Expected gh_pr_conditions.does_not_have_labels length=%d, but got %d", len(expectedLabels), len(conditions.DoesNotHaveLabels)) + } else { + for i, label := range expectedLabels { + if conditions.DoesNotHaveLabels[i] != label { + t.Errorf("Expected gh_pr_conditions.does_not_have_labels[%d]=%q, but got %q", i, label, conditions.DoesNotHaveLabels[i]) + } + } + } +} diff --git a/internal/gh/gh.go b/internal/gh/gh.go index cceaa44..1a31f6f 100644 --- a/internal/gh/gh.go +++ b/internal/gh/gh.go @@ -17,6 +17,7 @@ type Github struct { Client *github.Client } +// labelsMatched checks if all labels in filterLabels are present in PR labels func labelsMatched(prLabels []*github.Label, filterLabels []string) bool { matched := 0 @@ -80,6 +81,7 @@ func (g *Github) GetPullRequests(prn cfg.PrNotification) ([]*github.PullRequest, } glog.V(8).Infof("Checking PR-%d %q: %s", *pr.Number, *pr.Title, *pr.State) + // If ALL labels in prn.Labels are present on the PR, include it if labelsMatched(pr.Labels, prn.Labels) { addPR := true @@ -121,6 +123,13 @@ func (g *Github) MatchesConditions(pr *github.PullRequest, prn cfg.PrNotificatio prOlderThan := createdAt.Add(time.Duration(prn.Conditions.OlderThanSeconds) * time.Second) isAfter := time.Now().After(prOlderThan) if !isAfter { + // PR is not older than the specified time + return false + } + } + if len(prn.Conditions.DoesNotHaveLabels) > 0 { + if labelsMatched(pr.Labels, prn.Conditions.DoesNotHaveLabels) { + // If the PR has any of the labels in DoesNotHaveLabels, it doesn't match the conditions return false } } diff --git a/internal/gh/gh_test.go b/internal/gh/gh_test.go index 49bdaa2..29dd7dd 100644 --- a/internal/gh/gh_test.go +++ b/internal/gh/gh_test.go @@ -66,3 +66,33 @@ func TestMatchesConditions_ZeroOlderThanSeconds(t *testing.T) { t.Error("Expected MatchesConditions to return true when OlderThanSeconds is zero") } } + +func TestMatchesConditions_DoesNotHaveLabels(t *testing.T) { + g := &Github{} + + label := "WIP" + pr := &github.PullRequest{ + CreatedAt: &github.Timestamp{Time: time.Now().Add(-2 * time.Hour)}, + Labels: []*github.Label{ + {Name: &label}, + }, + } + + prNoLabels := &github.PullRequest{ + CreatedAt: &github.Timestamp{Time: time.Now().Add(-2 * time.Hour)}, + } + + prn := cfg.PrNotification{ + Conditions: cfg.PrConditions{ + DoesNotHaveLabels: []string{"WIP"}, + }, + } + + if g.MatchesConditions(pr, prn) { + t.Error("Expected MatchesConditions to return false for PR with labels in DoesNotHaveLabels") + } + + if !g.MatchesConditions(prNoLabels, prn) { + t.Error("Expected MatchesConditions to return true for PR without labels") + } +}