Skip to content

Commit

Permalink
Support last-modified (#149)
Browse files Browse the repository at this point in the history
Signed-off-by: Galo Navarro <anglorvaroa@gmail.com>
  • Loading branch information
srvaroa authored Sep 6, 2024
1 parent b42a2b2 commit b48bedc
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 25 deletions.
10 changes: 10 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
version: 1

labels:
# Type: Recent changes
- label: "@type/new"
last-modified:
at-most: 1d

# Type: Old changes
- label: "@type/old"
last-modified:
at-least: 30d

# Type: Build-related changes
- label: "@type/build"
title: '^build(?:\(.+\))?\!?:'
Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ alphabetical order. Some important considerations:
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.

If you're looking to evaluate on the modification date of the issue or PR,
check on <a href="#last-modified" ></a>

This condition is best used when with a <a href="#schedule">schedule trigger</a>.

Example:
Expand Down Expand Up @@ -407,6 +410,41 @@ 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.

### Last Modified (PRs and Issues) <a name="last-modified" />

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,
check on <a href="#age" ></a>

This condition is best used when with a <a href="#schedule">schedule trigger</a>.

Examples:

```yaml
last-modified:
at-most: 1d
```
Will label PRs or issues that were last modified at most one day ago

```yaml
last-modified:
at-least: 1d
```

Will label PRs or issues that were last modified at least one day ago

The syntax for values is based on a number, followed by a suffix:

* s: seconds
* m: minutes
* h: hours
* d: days
* w: weeks
* y: years

For example, `2d` means 2 days, `4w` means 4 weeks, and so on.

### Mergeable status (PRs only) <a name="mergeable" />

This condition is satisfied when the [mergeable
Expand Down
25 changes: 0 additions & 25 deletions pkg/condition_age.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package labeler

import (
"fmt"
"strconv"
"strings"
"time"
)

Expand Down Expand Up @@ -36,26 +34,3 @@ func AgeCondition(l *Labeler) Condition {
},
}
}

func parseExtendedDuration(s string) (time.Duration, error) {
multiplier := time.Hour * 24 // default to days

if strings.HasSuffix(s, "w") {
multiplier = time.Hour * 24 * 7 // weeks
s = strings.TrimSuffix(s, "w")
} else if strings.HasSuffix(s, "y") {
multiplier = time.Hour * 24 * 365 // years
s = strings.TrimSuffix(s, "y")
} else if strings.HasSuffix(s, "d") {
s = strings.TrimSuffix(s, "d") // days
} else {
return time.ParseDuration(s) // default to time.ParseDuration for hours, minutes, seconds
}

value, err := strconv.Atoi(s)
if err != nil {
return 0, err
}

return time.Duration(value) * multiplier, nil
}
53 changes: 53 additions & 0 deletions pkg/condition_last_modified.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package labeler

import (
"fmt"
"time"

"github.com/google/go-github/v50/github"
)

func LastModifiedCondition(l *Labeler) Condition {
return Condition{
GetName: func() string {
return "Last modification of issue/PR"
},
CanEvaluate: func(target *Target) bool {
return target.ghIssue != nil || target.ghPR != nil
},
Evaluate: func(target *Target, matcher LabelMatcher) (bool, error) {
if matcher.LastModified == nil {
return false, fmt.Errorf("no last modified conditions are set in config")
}
// Determine the last modification time of the issue or PR
var lastModifiedAt *github.Timestamp
if target.ghIssue != nil {
lastModifiedAt = target.ghIssue.UpdatedAt
} else if target.ghPR != nil {
lastModifiedAt = target.ghPR.UpdatedAt
} else {
return false, fmt.Errorf("no issue or PR found in target")
}
duration := time.Since(lastModifiedAt.Time)

if matcher.LastModified.AtMost != "" {
maxDuration, err := parseExtendedDuration(matcher.LastModified.AtMost)
if err != nil {
return false, fmt.Errorf("failed to parse `last-modified.at-most` parameter in configuration: %v", err)
}
return duration <= maxDuration, nil
}

if matcher.LastModified.AtLeast != "" {
minDuration, err := parseExtendedDuration(matcher.LastModified.AtLeast)
if err != nil {
return false, fmt.Errorf("failed to parse `last-modified.at-least` parameter in configuration: %v", err)
}
return duration >= minDuration, nil
}

return false, fmt.Errorf("no last modified conditions are set in config")

},
}
}
7 changes: 7 additions & 0 deletions pkg/labeler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import (
gh "github.com/google/go-github/v50/github"
)

type DurationConfig struct {
AtLeast string `yaml:"at-least"`
AtMost string `yaml:"at-most"`
}

type SizeConfig struct {
ExcludeFiles []string `yaml:"exclude-files"`
Above string
Expand All @@ -23,6 +28,7 @@ type LabelMatcher struct {
Draft string
Files []string
Label string
LastModified *DurationConfig `yaml:"last-modified"`
Mergeable string
Negate bool
Size *SizeConfig
Expand Down Expand Up @@ -223,6 +229,7 @@ func (l *Labeler) findMatches(target *Target, config *LabelerConfigV1) (LabelUpd
BodyCondition(),
BranchCondition(),
FilesCondition(l),
LastModifiedCondition(l),
IsDraftCondition(),
IsMergeableCondition(),
SizeCondition(l),
Expand Down
18 changes: 18 additions & 0 deletions pkg/labeler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,24 @@ func TestHandleEvent(t *testing.T) {
initialLabels: []string{"Meh"},
expectedLabels: []string{"Meh", "Test"},
},
{
event: "pull_request",
payloads: []string{"create_pr"},
name: "Add a label to pull request when last-modified.at-least matches",
config: LabelerConfigV1{
Version: 1,
Labels: []LabelMatcher{
{
Label: "Test",
LastModified: &DurationConfig{
AtLeast: "1d",
},
},
},
},
initialLabels: []string{"Meh"},
expectedLabels: []string{"Meh", "Test"},
},
}

for _, tc := range testCases {
Expand Down
30 changes: 30 additions & 0 deletions pkg/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package labeler

import (
"strconv"
"strings"
"time"
)

func parseExtendedDuration(s string) (time.Duration, error) {
multiplier := time.Hour * 24 // default to days

if strings.HasSuffix(s, "w") {
multiplier = time.Hour * 24 * 7 // weeks
s = strings.TrimSuffix(s, "w")
} else if strings.HasSuffix(s, "y") {
multiplier = time.Hour * 24 * 365 // years
s = strings.TrimSuffix(s, "y")
} else if strings.HasSuffix(s, "d") {
s = strings.TrimSuffix(s, "d") // days
} else {
return time.ParseDuration(s) // default to time.ParseDuration for hours, minutes, seconds
}

value, err := strconv.Atoi(s)
if err != nil {
return 0, err
}

return time.Duration(value) * multiplier, nil
}
File renamed without changes.

0 comments on commit b48bedc

Please sign in to comment.