Repository: srvaroa/labeler
Pull request #131 (2023-12-28 18:45:35)

Size: support regex exclusions
Signed-off-by: Galo Navarro <anglorvaroa@gmail.com>


akutz: Thanks @srvaroa , looks perfect! @dilyar85, thoughts? Since you implemented the labeler, do you think this will work for us to exclude the generated assets?
dilyar85: > Thanks @srvaroa , looks perfect! @dilyar85, thoughts? Since you implemented the labeler, do you think this will work for us to exclude the generated assets?

Looks good to me too. Thanks @srvaroa for adding this support :) 

Patch:
diff --git a/README.md b/README.md
index ae1d312..47b863b 100644
--- a/README.md
+++ b/README.md
@@ -400,10 +400,11 @@ files:
 - ".*\\/subfolder\\/.*\\.md"
 ```
 
-> **NOTICE** the double backslash (`\\`) in the example above. This GitHub Action is coded in Go (Golang), which means
-> you need to pay special attention to regular expressions (Regex). Special characters need to be escaped with double
-> backslashes. This is because the backslash in Go strings is an escape character and therefore must be escaped itself 
-> to appear as a literal in the regex.
+> **NOTICE** the double backslash (`\\`) in the example above. This GitHub
+Action is coded in Go (Golang), which means you need to pay special attention to
+regular expressions (Regex). Special characters need to be escaped with double
+backslashes. This is because the backslash in Go strings is an escape character
+and therefore must be escaped itself to appear as a literal in the regex.
 
 ### Mergeable status (PRs only) <a name="mergeable" />
 
@@ -457,17 +458,25 @@ reported by the [GitHub API](https://developer.github.com/v3/pulls).
 
 You can exclude some files so that their changes are not taken into
 account for the overall count. This can be useful for `yarn.lock`,
-`go.sum` and such. Use `exclude-files`:
+`go.sum` and such. Use `exclude-files`, which supports both an explicit
+file or a Regex expression:
 
 ```yaml
 - label: "L"
     size:
-        exclude-files: ["yarn.lock"]
+        exclude-files: ["yarn.lock", "\\/root\\/.+\\/test.md"]
         above: 100
 ``` 
 
 This condition will apply the `L` label if the diff is above 100 lines,
-but NOT taking into account changes in `yarn.lock`.
+but NOT taking into account changes in `yarn.lock`, or any `test.md`
+file that is in a subdirectory of `root`.
+
+**NOTICE** the double backslash (`\\`) in the example above. This GitHub
+Action is coded in Go (Golang), which means you need to pay special attention to
+regular expressions (Regex). Special characters need to be escaped with double
+backslashes. This is because the backslash in Go strings is an escape character
+and therefore must be escaped itself to appear as a literal in the regex.
 
 **NOTICE** the old format for specifying size properties (`size-above`
 and `size-below`) has been deprecated. The action will continue
diff --git a/pkg/condition_size.go b/pkg/condition_size.go
index 847de88..2cd654a 100644
--- a/pkg/condition_size.go
+++ b/pkg/condition_size.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"log"
 	"math"
+	"regexp"
 	"strconv"
 	"strings"
 
@@ -68,9 +69,9 @@ func isOldConfig(matcher LabelMatcher) bool {
 	return matcher.SizeAbove != "" || matcher.SizeBelow != ""
 }
 
-func (l *Labeler) getModifiedLinesCount(pr *gh.PullRequest, excludedFiles []string) (int64, error) {
+func (l *Labeler) getModifiedLinesCount(pr *gh.PullRequest, exclusions []string) (int64, error) {
 
-	if len(excludedFiles) == 0 {
+	if len(exclusions) == 0 {
 		// no exclusions so we can just rely on GH's summary which is
 		// more lightweight
 		return int64(math.Abs(float64(pr.GetAdditions() + pr.GetDeletions()))), nil
@@ -97,9 +98,11 @@ func (l *Labeler) getModifiedLinesCount(pr *gh.PullRequest, excludedFiles []stri
 			// We're in a file's block
 			path := strings.TrimPrefix(line, "---")
 			path = strings.TrimPrefix(path, "+++")
+			path = strings.TrimPrefix(path, "a/")
+			path = strings.TrimPrefix(path, "b/")
 			path = strings.TrimSpace(path)
 			// Check if the file path matches any of the excluded files
-			countFile = !isFileExcluded(path, excludedFiles)
+			countFile = !isFileExcluded(path, exclusions)
 			if countFile {
 				log.Printf("Counting changes in file %s", path)
 			} else {
@@ -118,9 +121,12 @@ func (l *Labeler) getModifiedLinesCount(pr *gh.PullRequest, excludedFiles []stri
 	return count, nil
 }
 
-func isFileExcluded(path string, excludedFiles []string) bool {
-	for _, excludedFile := range excludedFiles {
-		if strings.HasSuffix(path, excludedFile) {
+func isFileExcluded(path string, exclusions []string) bool {
+	for _, exclusion := range exclusions {
+		exclusionRegex, err := regexp.Compile(exclusion)
+		if err != nil {
+			log.Printf("Error compiling file exclusion regex %s: %s", exclusion, err)
+		} else if exclusionRegex.MatchString(path) {
 			return true
 		}
 	}


Test patch:
diff --git a/cmd/action_test.go b/cmd/action_test.go
index 5ba13ce..be7f3e7 100644
--- a/cmd/action_test.go
+++ b/cmd/action_test.go
@@ -225,6 +225,13 @@ func TestGetLabelerConfigV1WithCompositeSize(t *testing.T) {
 					Below:        "100",
 				},
 			},
+			{
+				Label: "L",
+				Size: &labeler.SizeConfig{
+					ExcludeFiles: []string{"test.yaml", "\\/dir\\/test.+.yaml"},
+					Above:        "100",
+				},
+			},
 		},
 	}
 
diff --git a/pkg/labeler_test.go b/pkg/labeler_test.go
index 5a6aea7..a9d3b5c 100644
--- a/pkg/labeler_test.go
+++ b/pkg/labeler_test.go
@@ -334,7 +334,42 @@ func TestHandleEvent(t *testing.T) {
 					{
 						Label: "L",
 						Size: &SizeConfig{
-							ExcludeFiles: []string{"README.md", "new_file", "dependabot.yml", "root/sub/test.md"},
+							ExcludeFiles: []string{
+								"R.+.md", // captures README.md
+								"new_file",
+								"dependabot.yml",
+								"\\/root\\/.+\\/test.md", // captures root/sub/test.md
+							},
+							// our test file has a diff in four files,
+							// including added/removed which have a
+							// slightly trickier diff.  Adding any of
+							// these will get us above 2 lines and fail
+							// the test.
+							Below: "2",
+						},
+					},
+				},
+			},
+			initialLabels:  []string{},
+			expectedLabels: []string{"L"},
+		},
+		{
+			event:    "pull_request",
+			payloads: []string{"big_pr"},
+			name:     "Test the size_above rule applying file dodgy exclusions",
+			config: LabelerConfigV1{
+				Version: 1,
+				Labels: []LabelMatcher{
+					{
+						Label: "L",
+						Size: &SizeConfig{
+							ExcludeFiles: []string{
+								"R.+.md", // captures README.md
+								"new_file",
+								"dependabot.yml",
+								"root/**/test",           // dodgy regex should NOT break the evaluation
+								"\\/root\\/.+\\/test.md", // captures root/sub/test.md
+							},
 							// our test file has a diff in four files,
 							// including added/removed which have a
 							// slightly trickier diff.  Adding any of
diff --git a/test_data/config_v1_composite_size.yml b/test_data/config_v1_composite_size.yml
index 3acf8f1..f0d1bbb 100644
--- a/test_data/config_v1_composite_size.yml
+++ b/test_data/config_v1_composite_size.yml
@@ -8,3 +8,7 @@ labels:
       exclude-files: ["test.yaml"]
       above: 9
       below: 100
+  - label: L
+    size:
+      exclude-files: ["test.yaml", "\\/dir\\/test.+.yaml"]
+      above: 100