Skip to content

Commit f6bffef

Browse files
committed
by default make label matching case insensitive but make it sensitive by using the --case-sensitive-labels flag
1 parent 5b312fd commit f6bffef

File tree

4 files changed

+68
-6
lines changed

4 files changed

+68
-6
lines changed

internal/cmd/match_criteria.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/cli/go-gh/v2/pkg/api"
1111
graphql "github.com/cli/shurcooL-graphql"
12+
"github.com/github/gh-combine/internal/common"
1213
)
1314

1415
// checks if a PR matches all filtering criteria
@@ -77,6 +78,12 @@ func labelsMatch(prLabels, ignoreLabels, selectLabels []string) bool {
7778
return true
7879
}
7980

81+
if !caseSensitiveLabels {
82+
prLabels = common.NormalizeArray(prLabels)
83+
ignoreLabels = common.NormalizeArray(ignoreLabels)
84+
selectLabels = common.NormalizeArray(selectLabels)
85+
}
86+
8087
// If the pull request contains any of the ignore labels, it doesn't match
8188
for _, l := range ignoreLabels {
8289
if slices.Contains(prLabels, l) {

internal/cmd/match_criteria_test.go

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,107 +6,147 @@ func TestLabelsMatch(t *testing.T) {
66
t.Parallel()
77

88
tests := []struct {
9-
prLabels []string
10-
ignoreLabels []string
11-
selectLabels []string
12-
want bool
9+
name string
10+
prLabels []string
11+
ignoreLabels []string
12+
selectLabels []string
13+
caseSensitive bool
14+
want bool
1315
}{
1416
{
1517
want: true,
1618
},
1719

1820
{
21+
name: "--ignore-labels match",
1922
prLabels: []string{"a", "b"},
2023
ignoreLabels: []string{"b"},
2124
want: false,
2225
},
2326
{
27+
name: "--ignore-labels match (with one out of two)",
2428
prLabels: []string{"a", "b"},
2529
ignoreLabels: []string{"b", "c"},
2630
want: false,
2731
},
2832

2933
{
34+
name: "no labels match (select or ignore)",
3035
prLabels: []string{"a"},
3136
ignoreLabels: []string{"b"},
3237
selectLabels: []string{"c"},
3338
want: false,
3439
},
3540
{
41+
name: "--select-labels match",
3642
prLabels: []string{"a", "c"},
3743
ignoreLabels: []string{"b"},
3844
selectLabels: []string{"c"},
3945
want: true,
4046
},
4147
{
48+
name: "--select-labels match (with one out of two) and ignore labels don't match",
4249
prLabels: []string{"a"},
4350
ignoreLabels: []string{"b"},
4451
selectLabels: []string{"a", "c"},
4552
want: true,
4653
},
4754
{
55+
name: "the pull request has no labels",
4856
prLabels: []string{},
4957
ignoreLabels: []string{"b"},
5058
selectLabels: []string{"a", "c"},
5159
want: false,
5260
},
5361
{
62+
name: "the pull request has no labels and ignore labels don't match so it matches - but select labels is empty so it means all labels or even no labels match",
5463
prLabels: []string{},
5564
ignoreLabels: []string{"b"},
5665
selectLabels: []string{},
5766
want: true,
5867
},
5968
{
69+
name: "the pull request has no labels but we want to match the a label",
6070
prLabels: []string{},
6171
ignoreLabels: []string{},
6272
selectLabels: []string{"a"},
6373
want: false,
6474
},
6575
{
76+
name: "no label match criteria, so it matches",
6677
prLabels: []string{},
6778
ignoreLabels: []string{},
6879
selectLabels: []string{},
6980
want: true,
7081
},
7182
{
83+
name: "with one matching label and no matching ignore labels so it matches",
7284
prLabels: []string{"a"},
7385
selectLabels: []string{"a"},
7486
ignoreLabels: []string{"b"},
7587
want: true,
7688
},
7789
{
90+
name: "the pr labels match the select and ignore labels so it doesn't match",
7891
prLabels: []string{"a"},
7992
selectLabels: []string{"a"},
8093
ignoreLabels: []string{"a"},
8194
want: false,
8295
},
8396
{
97+
name: "the pr has one label but no defined ignore or select labels so it matches",
8498
prLabels: []string{"a"},
8599
selectLabels: []string{},
86100
ignoreLabels: []string{},
87101
want: true,
88102
},
89103
{
104+
name: "the pr has one label and it is the select label so it matches",
90105
prLabels: []string{"a"},
91106
selectLabels: []string{"a"},
92107
ignoreLabels: []string{},
93108
want: true,
94109
},
95110
{
111+
name: "the pr has labels and matching select labels but it matches an ignore label so it doesn't match",
96112
prLabels: []string{"a", "b", "c"},
97113
selectLabels: []string{"a", "b"},
98114
ignoreLabels: []string{"c"},
99115
want: false,
100116
},
117+
{
118+
name: "the pr has uppercase labels and we are using case insensitive labels so it matches",
119+
prLabels: []string{"Dependencies", "rUby", "ready-for-Review"},
120+
selectLabels: []string{"dependencies", "ready-for-review"},
121+
ignoreLabels: []string{"blocked"},
122+
want: true,
123+
caseSensitive: false,
124+
},
125+
{
126+
name: "the pr has uppercase labels and we are using case sensitive labels so it doesn't match",
127+
prLabels: []string{"Dependencies", "rUby", "ready-for-Review"},
128+
selectLabels: []string{"dependencies", "ready-for-review"},
129+
ignoreLabels: []string{"blocked"},
130+
want: false,
131+
caseSensitive: true,
132+
},
101133
}
102134

103135
for _, test := range tests {
104-
t.Run("", func(t *testing.T) {
136+
t.Run(test.name, func(t *testing.T) {
105137
t.Parallel()
106138

139+
// Save the original value of caseSensitiveLabels
140+
originalCaseSensitive := caseSensitiveLabels
141+
defer func() { caseSensitiveLabels = originalCaseSensitive }() // Restore after test
142+
143+
// Set caseSensitiveLabels for this test
144+
caseSensitiveLabels = test.caseSensitive
145+
146+
// Run the function
107147
got := labelsMatch(test.prLabels, test.ignoreLabels, test.selectLabels)
108148
if got != test.want {
109-
t.Errorf("want %v, got %v", test.want, got)
149+
t.Errorf("Test %q failed: want %v, got %v", test.name, test.want, got)
110150
}
111151
})
112152
}

internal/cmd/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ var (
3434
combineBranchName string
3535
workingBranchSuffix string
3636
dependabot bool
37+
caseSensitiveLabels bool
3738
)
3839

3940
// NewRootCmd creates the root command for the gh-combine CLI
@@ -77,6 +78,7 @@ func NewRootCmd() *cobra.Command {
7778
# Filter PRs by labels
7879
gh combine owner/repo --labels dependencies # PRs must have this single label
7980
gh combine owner/repo --labels security,dependencies # PRs must have ALL these labels
81+
gh combine owner/repo --labels Dependencies --case-sensitive-labels # PRs must have this label, case-sensitive
8082
8183
# Exclude PRs by labels
8284
gh combine owner/repo --ignore-labels wip # Ignore PRs with this label
@@ -124,6 +126,7 @@ func NewRootCmd() *cobra.Command {
124126
rootCmd.Flags().StringVar(&reposFile, "file", "", "File containing repository names, one per line")
125127
rootCmd.Flags().IntVar(&minimum, "minimum", 2, "Minimum number of PRs to combine")
126128
rootCmd.Flags().StringVar(&defaultOwner, "owner", "", "Default owner for repositories (if not specified in repo name or missing from file inputs)")
129+
rootCmd.Flags().BoolVar(&caseSensitiveLabels, "case-sensitive-labels", false, "Use case-sensitive label matching")
127130

128131
// Add deprecated flags for backward compatibility
129132
// rootCmd.Flags().IntVar(&minimum, "min-combine", 2, "Minimum number of PRs to combine (deprecated, use --minimum)")

internal/common/common.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package common
2+
3+
import "strings"
4+
5+
// Normalize an array of strings to lowercase
6+
func NormalizeArray(array []string) []string {
7+
normalized := make([]string, len(array))
8+
for i, label := range array {
9+
normalized[i] = strings.ToLower(label)
10+
}
11+
return normalized
12+
}

0 commit comments

Comments
 (0)