Repository: srvaroa/labeler
Pull request #67 (2023-02-19 23:17:29)

Allow negating conditions
Mainly a workaround for Go's lack of support to negative-lookahead regex.

This feature allows negating the result of the conditions applied to each label.

Signed-off-by: Galo Navarro <anglorvaroa@gmail.com>



Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/go.yml
similarity index 88%
rename from .github/workflows/build.yml
rename to .github/workflows/go.yml
index 9805228..e575885 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/go.yml
@@ -1,9 +1,7 @@
 ---
-name: Build and test
+name: Build and test Go
 on:
   pull_request:
-  push:
-    branches: [master]
 
 jobs:
   build:
diff --git a/.github/workflows/apply_labels.yml b/.github/workflows/main.yml
similarity index 100%
rename from .github/workflows/apply_labels.yml
rename to .github/workflows/main.yml
diff --git a/README.md b/README.md
index 79c8f84..fc9c626 100644
--- a/README.md
+++ b/README.md
@@ -157,6 +157,35 @@ labels:
 The `WIP` label will be set if the title matches `^WIP:.*` OR the label
 is not in a mergeable state.
 
+Adding a `negate` property inside the label block will negate the
+result of the evaluation of all conditions inside the label. For
+example:
+
+```yaml
+version: 1
+labels:
+- label: "unknown"
+  negate: true
+  branch: "(master|hotfix)"
+```
+
+In this case, label `unknown` will be set if the branch does NOT match
+`master` or `hotfix`.
+
+The same behaviour occurs with multiple conditions:
+
+```yaml
+version: 1
+labels:
+- label: "unknown"
+  negate: true
+  branch: "master"
+  title: "(feat).*"
+```
+
+Only PRs that do NOT match one of the two conditions will get the
+`unknown` label.
+
 ## Append-only mode
 
 The default behaviour of this action includes *removing* labels that
diff --git a/pkg/labeler.go b/pkg/labeler.go
index c60fb82..0adcad9 100644
--- a/pkg/labeler.go
+++ b/pkg/labeler.go
@@ -10,6 +10,7 @@ import (
 
 type LabelMatcher struct {
 	Label      string
+	Negate     bool
 	Title      string
 	Branch     string
 	BaseBranch string `yaml:"base-branch"`
@@ -233,6 +234,12 @@ func (l *Labeler) findMatches(target *Target, config *LabelerConfigV1) (LabelUpd
 				continue
 			}
 		}
+
+		if matcher.Negate {
+			result, _ := labelUpdates.set[label]
+			labelUpdates.set[label] = !result
+			log.Printf("[%s] is negated from %t", label, result)
+		}
 	}
 
 	return labelUpdates, nil


Test patch:
diff --git a/pkg/labeler_test.go b/pkg/labeler_test.go
index 10be9b3..affea20 100644
--- a/pkg/labeler_test.go
+++ b/pkg/labeler_test.go
@@ -306,6 +306,23 @@ func TestHandleEvent(t *testing.T) {
 			initialLabels:  []string{},
 			expectedLabels: []string{},
 		},
+		{
+			event:    "pull_request",
+			payloads: []string{"small_pr"},
+			name:     "Test the branch rule (matching, but negated)",
+			config: LabelerConfigV1{
+				Version: 1,
+				Labels: []LabelMatcher{
+					{
+						Label:  "Branch",
+						Negate: true,
+						Branch: "^does/not-match/*",
+					},
+				},
+			},
+			initialLabels:  []string{},
+			expectedLabels: []string{"Branch"},
+		},
 		{
 			event:    "pull_request",
 			payloads: []string{"create_pr"},
@@ -388,6 +405,41 @@ func TestHandleEvent(t *testing.T) {
 			initialLabels:  []string{},
 			expectedLabels: []string{"Files"},
 		},
+		{
+			event:    "pull_request",
+			payloads: []string{"small_pr"},
+			name:     "Multiple conditions for the same label function as AND",
+			config: LabelerConfigV1{
+				Version: 1,
+				Labels: []LabelMatcher{
+					{
+						Label:  "Branch",
+						Branch: "^srvaroa-patch.*",
+						Title:  "^W.*Update.*",
+					},
+				},
+			},
+			initialLabels:  []string{},
+			expectedLabels: []string{"Branch"},
+		},
+		{
+			event:    "pull_request",
+			payloads: []string{"small_pr"},
+			name:     "Multiple conditions for the same label function as AND, but negated make a true",
+			config: LabelerConfigV1{
+				Version: 1,
+				Labels: []LabelMatcher{
+					{
+						Label:  "Branch",
+						Negate: true,
+						Title:  "^Update.*",
+						Branch: "^srvaroa-patch.*",
+					},
+				},
+			},
+			initialLabels:  []string{"Branch"},
+			expectedLabels: []string{"Branch"},
+		},
 		{
 			event:    "pull_request",
 			payloads: []string{"small_pr"},