Skip to content

Commit b48bedc

Browse files
authored
Support last-modified (#149)
Signed-off-by: Galo Navarro <anglorvaroa@gmail.com>
1 parent b42a2b2 commit b48bedc

File tree

8 files changed

+156
-25
lines changed

8 files changed

+156
-25
lines changed

.github/labeler.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
version: 1
22

33
labels:
4+
# Type: Recent changes
5+
- label: "@type/new"
6+
last-modified:
7+
at-most: 1d
8+
9+
# Type: Old changes
10+
- label: "@type/old"
11+
last-modified:
12+
at-least: 30d
13+
414
# Type: Build-related changes
515
- label: "@type/build"
616
title: '^build(?:\(.+\))?\!?:'

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ alphabetical order. Some important considerations:
309309
This condition is satisfied when the age of the PR or Issue are larger than
310310
the given one. The age is calculated from the creation date.
311311

312+
If you're looking to evaluate on the modification date of the issue or PR,
313+
check on <a href="#last-modified" ></a>
314+
312315
This condition is best used when with a <a href="#schedule">schedule trigger</a>.
313316

314317
Example:
@@ -407,6 +410,41 @@ regular expressions (Regex). Special characters need to be escaped with double
407410
backslashes. This is because the backslash in Go strings is an escape character
408411
and therefore must be escaped itself to appear as a literal in the regex.
409412

413+
### Last Modified (PRs and Issues) <a name="last-modified" />
414+
415+
This condition evaluates the modification date of the PR or Issue.
416+
417+
If you're looking to evaluate on the creation date of the issue or PR,
418+
check on <a href="#age" ></a>
419+
420+
This condition is best used when with a <a href="#schedule">schedule trigger</a>.
421+
422+
Examples:
423+
424+
```yaml
425+
last-modified:
426+
at-most: 1d
427+
```
428+
Will label PRs or issues that were last modified at most one day ago
429+
430+
```yaml
431+
last-modified:
432+
at-least: 1d
433+
```
434+
435+
Will label PRs or issues that were last modified at least one day ago
436+
437+
The syntax for values is based on a number, followed by a suffix:
438+
439+
* s: seconds
440+
* m: minutes
441+
* h: hours
442+
* d: days
443+
* w: weeks
444+
* y: years
445+
446+
For example, `2d` means 2 days, `4w` means 4 weeks, and so on.
447+
410448
### Mergeable status (PRs only) <a name="mergeable" />
411449

412450
This condition is satisfied when the [mergeable

pkg/condition_age.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package labeler
22

33
import (
44
"fmt"
5-
"strconv"
6-
"strings"
75
"time"
86
)
97

@@ -36,26 +34,3 @@ func AgeCondition(l *Labeler) Condition {
3634
},
3735
}
3836
}
39-
40-
func parseExtendedDuration(s string) (time.Duration, error) {
41-
multiplier := time.Hour * 24 // default to days
42-
43-
if strings.HasSuffix(s, "w") {
44-
multiplier = time.Hour * 24 * 7 // weeks
45-
s = strings.TrimSuffix(s, "w")
46-
} else if strings.HasSuffix(s, "y") {
47-
multiplier = time.Hour * 24 * 365 // years
48-
s = strings.TrimSuffix(s, "y")
49-
} else if strings.HasSuffix(s, "d") {
50-
s = strings.TrimSuffix(s, "d") // days
51-
} else {
52-
return time.ParseDuration(s) // default to time.ParseDuration for hours, minutes, seconds
53-
}
54-
55-
value, err := strconv.Atoi(s)
56-
if err != nil {
57-
return 0, err
58-
}
59-
60-
return time.Duration(value) * multiplier, nil
61-
}

pkg/condition_last_modified.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package labeler
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/google/go-github/v50/github"
8+
)
9+
10+
func LastModifiedCondition(l *Labeler) Condition {
11+
return Condition{
12+
GetName: func() string {
13+
return "Last modification of issue/PR"
14+
},
15+
CanEvaluate: func(target *Target) bool {
16+
return target.ghIssue != nil || target.ghPR != nil
17+
},
18+
Evaluate: func(target *Target, matcher LabelMatcher) (bool, error) {
19+
if matcher.LastModified == nil {
20+
return false, fmt.Errorf("no last modified conditions are set in config")
21+
}
22+
// Determine the last modification time of the issue or PR
23+
var lastModifiedAt *github.Timestamp
24+
if target.ghIssue != nil {
25+
lastModifiedAt = target.ghIssue.UpdatedAt
26+
} else if target.ghPR != nil {
27+
lastModifiedAt = target.ghPR.UpdatedAt
28+
} else {
29+
return false, fmt.Errorf("no issue or PR found in target")
30+
}
31+
duration := time.Since(lastModifiedAt.Time)
32+
33+
if matcher.LastModified.AtMost != "" {
34+
maxDuration, err := parseExtendedDuration(matcher.LastModified.AtMost)
35+
if err != nil {
36+
return false, fmt.Errorf("failed to parse `last-modified.at-most` parameter in configuration: %v", err)
37+
}
38+
return duration <= maxDuration, nil
39+
}
40+
41+
if matcher.LastModified.AtLeast != "" {
42+
minDuration, err := parseExtendedDuration(matcher.LastModified.AtLeast)
43+
if err != nil {
44+
return false, fmt.Errorf("failed to parse `last-modified.at-least` parameter in configuration: %v", err)
45+
}
46+
return duration >= minDuration, nil
47+
}
48+
49+
return false, fmt.Errorf("no last modified conditions are set in config")
50+
51+
},
52+
}
53+
}

pkg/labeler.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import (
77
gh "github.com/google/go-github/v50/github"
88
)
99

10+
type DurationConfig struct {
11+
AtLeast string `yaml:"at-least"`
12+
AtMost string `yaml:"at-most"`
13+
}
14+
1015
type SizeConfig struct {
1116
ExcludeFiles []string `yaml:"exclude-files"`
1217
Above string
@@ -23,6 +28,7 @@ type LabelMatcher struct {
2328
Draft string
2429
Files []string
2530
Label string
31+
LastModified *DurationConfig `yaml:"last-modified"`
2632
Mergeable string
2733
Negate bool
2834
Size *SizeConfig
@@ -223,6 +229,7 @@ func (l *Labeler) findMatches(target *Target, config *LabelerConfigV1) (LabelUpd
223229
BodyCondition(),
224230
BranchCondition(),
225231
FilesCondition(l),
232+
LastModifiedCondition(l),
226233
IsDraftCondition(),
227234
IsMergeableCondition(),
228235
SizeCondition(l),

pkg/labeler_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,24 @@ func TestHandleEvent(t *testing.T) {
965965
initialLabels: []string{"Meh"},
966966
expectedLabels: []string{"Meh", "Test"},
967967
},
968+
{
969+
event: "pull_request",
970+
payloads: []string{"create_pr"},
971+
name: "Add a label to pull request when last-modified.at-least matches",
972+
config: LabelerConfigV1{
973+
Version: 1,
974+
Labels: []LabelMatcher{
975+
{
976+
Label: "Test",
977+
LastModified: &DurationConfig{
978+
AtLeast: "1d",
979+
},
980+
},
981+
},
982+
},
983+
initialLabels: []string{"Meh"},
984+
expectedLabels: []string{"Meh", "Test"},
985+
},
968986
}
969987

970988
for _, tc := range testCases {

pkg/util.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package labeler
2+
3+
import (
4+
"strconv"
5+
"strings"
6+
"time"
7+
)
8+
9+
func parseExtendedDuration(s string) (time.Duration, error) {
10+
multiplier := time.Hour * 24 // default to days
11+
12+
if strings.HasSuffix(s, "w") {
13+
multiplier = time.Hour * 24 * 7 // weeks
14+
s = strings.TrimSuffix(s, "w")
15+
} else if strings.HasSuffix(s, "y") {
16+
multiplier = time.Hour * 24 * 365 // years
17+
s = strings.TrimSuffix(s, "y")
18+
} else if strings.HasSuffix(s, "d") {
19+
s = strings.TrimSuffix(s, "d") // days
20+
} else {
21+
return time.ParseDuration(s) // default to time.ParseDuration for hours, minutes, seconds
22+
}
23+
24+
value, err := strconv.Atoi(s)
25+
if err != nil {
26+
return 0, err
27+
}
28+
29+
return time.Duration(value) * multiplier, nil
30+
}
File renamed without changes.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy