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"},