From fe4b1c73bb8abf2f14a44a6912a8b4fee835d631 Mon Sep 17 00:00:00 2001
From: Dave Lunny <4298089+himynameisdave@users.noreply.github.com>
Date: Fri, 22 Nov 2024 07:09:41 -0800
Subject: [PATCH] Convert age matcher to use a duration config (#159)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 🚀 Allow age with at-least/at-most options
Marks the old `age: 2d` option as deprecated but keeps it for backward compatability
---
README.md | 37 +++++++++++++++++++++++--------------
pkg/condition_age.go | 43 ++++++++++++++++++++++++++++++++++++++-----
pkg/labeler.go | 3 ++-
pkg/labeler_test.go | 26 ++++++++++++++++++++++++++
4 files changed, 89 insertions(+), 20 deletions(-)
diff --git a/README.md b/README.md
index 9bb2073..685cf7f 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ Please consider [sponsoring the project](https://github.com/sponsors/srvaroa) if
### How to trigger action
To trigger the action on events, add a file `.github/workflows/main.yml`
-to your repository:
+to your repository:
```yaml
name: Label PRs
@@ -294,7 +294,7 @@ With this config, the behaviour changes:
## Conditions
-Below are the conditions currently supported in label matchers, in
+Below are the conditions currently supported in label matchers, in
alphabetical order. Some important considerations:
* Conditions evaluate only when they are explicitly added in
@@ -308,20 +308,29 @@ alphabetical order. Some important considerations:
### Age (PRs and Issues)
-This condition is satisfied when the age of the PR or Issue are larger than
-the given one. The age is calculated from the creation date.
+This condition evaluates the creation date of the PR or Issue.
-If you're looking to evaluate on the modification date of the issue or PR,
+If you're looking to evaluate on the modification date of the issue or PR,
check on
This condition is best used when with a schedule trigger.
-Example:
+Examples:
+
+```yaml
+age-range:
+ at-most: 1d
+```
+
+Will label PRs or issues that were created at most one day ago.
```yaml
-age: 1d
+age-range:
+ at-least: 1w
```
+Will label PRs or issues that were created at least one week ago.
+
The syntax for values is based on a number, followed by a suffix:
* s: seconds
@@ -412,7 +421,7 @@ This condition is satisfied when any of the PR files matches on the
given regexs.
```yaml
-files:
+files:
- "cmd\\/.*_tests.go"
- ".*\\/subfolder\\/.*\\.md"
```
@@ -425,9 +434,9 @@ and therefore must be escaped itself to appear as a literal in the regex.
### Last Modified (PRs and Issues)
-This condition evaluates the modification date of the PR or Issue.
+This condition evaluates the modification date of the PR or Issue.
-If you're looking to evaluate on the creation date of the issue or PR,
+If you're looking to evaluate on the creation date of the issue or PR,
check on
This condition is best used when with a schedule trigger.
@@ -462,19 +471,19 @@ For example, `2d` means 2 days, `4w` means 4 weeks, and so on.
This condition is satisfied when the [mergeable
state](https://developer.github.com/v3/pulls/#response-1) matches that
-of the PR.
+of the PR.
```yaml
mergeable: True
```
-Will match if the label is mergeable.
+Will match if the label is mergeable.
```yaml
mergeable: False
```
-Will match if the label is not mergeable.
+Will match if the label is not mergeable.
### Size (PRs only)
@@ -518,7 +527,7 @@ file or a Regex expression:
size:
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`, or any `test.md`
diff --git a/pkg/condition_age.go b/pkg/condition_age.go
index be04a36..af03ea6 100644
--- a/pkg/condition_age.go
+++ b/pkg/condition_age.go
@@ -14,10 +14,35 @@ func AgeCondition(l *Labeler) Condition {
return target.ghIssue != nil || target.ghPR != nil
},
Evaluate: func(target *Target, matcher LabelMatcher) (bool, error) {
- // Parse the age from the configuration
- ageDuration, err := parseExtendedDuration(matcher.Age)
- if err != nil {
- return false, fmt.Errorf("failed to parse age parameter in configuration: %v", err)
+ // Backward compatibility: If "age" is provided as a string, treat it as "at-least"
+ var atLeastDuration, atMostDuration time.Duration
+ var err error
+
+ // If they have specified a legacy "age" field, use that
+ // and treat it is as "at-least"
+ if matcher.Age != "" {
+ atLeastDuration, err = parseExtendedDuration(matcher.Age)
+ if err != nil {
+ return false, fmt.Errorf("failed to parse age parameter in configuration: %v", err)
+ }
+ } else if matcher.AgeRange != nil {
+ // Parse "at-least" if specified
+ if matcher.AgeRange.AtLeast != "" {
+ atLeastDuration, err = parseExtendedDuration(matcher.AgeRange.AtLeast)
+ if err != nil {
+ return false, fmt.Errorf("failed to parse `age.at-least` parameter in configuration: %v", err)
+ }
+ }
+
+ // Parse "at-most" if specified
+ if matcher.AgeRange.AtMost != "" {
+ atMostDuration, err = parseExtendedDuration(matcher.AgeRange.AtMost)
+ if err != nil {
+ return false, fmt.Errorf("failed to parse `age.at-most` parameter in configuration: %v", err)
+ }
+ }
+ } else {
+ return false, fmt.Errorf("no age conditions are set in config")
}
// Determine the creation time of the issue or PR
@@ -30,7 +55,15 @@ func AgeCondition(l *Labeler) Condition {
age := time.Since(createdAt)
- return age > ageDuration, nil
+ // Check if the age of the issue/PR is within the specified range
+ if atLeastDuration != 0 && age < atLeastDuration {
+ return false, nil
+ }
+ if atMostDuration != 0 && age > atMostDuration {
+ return false, nil
+ }
+
+ return true, nil
},
}
}
diff --git a/pkg/labeler.go b/pkg/labeler.go
index a64016c..5a9ac33 100644
--- a/pkg/labeler.go
+++ b/pkg/labeler.go
@@ -19,7 +19,8 @@ type SizeConfig struct {
}
type LabelMatcher struct {
- Age string
+ Age string `yaml:"age,omitempty"` // Deprecated age config.
+ AgeRange *DurationConfig `yaml:"age-range,omitempty"`
AuthorCanMerge string `yaml:"author-can-merge"`
Authors []string
AuthorInTeam string `yaml:"author-in-team"`
diff --git a/pkg/labeler_test.go b/pkg/labeler_test.go
index b0aade9..9c9ad96 100644
--- a/pkg/labeler_test.go
+++ b/pkg/labeler_test.go
@@ -204,6 +204,32 @@ func TestHandleEvent(t *testing.T) {
initialLabels: []string{},
expectedLabels: []string{"ThisIsOld"},
},
+ // Test the AgeRange configuration
+ {
+ event: "pull_request",
+ payloads: []string{"create_pr"},
+ name: "Age of a PR in the past",
+ config: LabelerConfigV1{
+ Version: 1,
+ Labels: []LabelMatcher{
+ {
+ Label: "Getting Old",
+ AgeRange: &DurationConfig{
+ AtLeast: "7d",
+ AtMost: "14d",
+ },
+ },
+ {
+ Label: "Getting Ancient",
+ AgeRange: &DurationConfig{
+ AtLeast: "14d",
+ },
+ },
+ },
+ },
+ initialLabels: []string{},
+ expectedLabels: []string{"Getting Ancient"},
+ },
{
event: "pull_request",
payloads: []string{"create_draft_pr"},