10000 Add configuration file support · coder/labeler@74d14a9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 74d14a9

Browse files
committed
Add configuration file support
1 parent 692046d commit 74d14a9

File tree

4 files changed

+95
-19
lines changed

4 files changed

+95
-19
lines changed

README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,28 @@ based on your past labelling decisions. You can install it on your repo
99
We currently use it on [`coder/coder`](https://github.com/coder/coder) and
1010
[`coder/code-server`](https://github.com/coder/code-server).
1111

12+
The labeler is well-suited to manage labels that _categorize_ or _reduce_ the
13+
semantic information of an issue. For example, labels like `bug`, `enhancement`,
14+
are self-evident from the contents of an issue. Often, a tracker will use labels
15+
that add information to an issue, e.g. `wontfix`, `roadmap`. These additive
16+
labels should be disabled in your configuration.
17+
1218
## Configuration
1319

14-
The labeler is configured by your label descriptions. This way, the labeler
15-
interprets your label system in the same way a human would.
20+
The labeler's primary configuration is your label descriptions. This way, the labeler interprets your label system in the same way a human would.
21+
22+
Additionally, the `labeler` reads your `.github/labeler.yml`
23+
file for a list of Regex exclusions. Here's an example:
24+
25+
```yaml
26+
# .github/labeler.yml
27+
exclude:
28+
- good first issue
29+
- customer.*$
30+
```
1631
17-
For the time being, the magic string `Only humans may set this` will forcefully prevent
18-
the labeler from adding a label, although a synonym may work as well.
32+
[#4](https://github.com/coder/labeler/issues/4) tracks the creation
33+
of a dashboard for debugging configuration.
1934
2035
## Architecture
2136

aicontext.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ func countTokens(msgs ...openai.ChatCompletionMessage) int {
7373
return tokens
7474
}
7575

76+
// magicDisableString is deprecated as the original recommendation
77+
// for disasbling additive labels.
7678
const magicDisableString = "Only humans may set this"
7779

7880
// Request generates the messages to be used in the GPT-4 context.

cmd/labeler/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ func main() {
168168
}
169169

170170
go func() {
171+
if root.indexInterval == 0 {
172+
return
173+
}
171174
ret := retry.New(time.Second, time.Minute)
172175

173176
retry:

webhook.go

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"log/slog"
99
"net/http"
10+
"regexp"
1011
"sort"
1112
"strconv"
1213
"strings"
@@ -21,10 +22,12 @@ import (
2122
githook "github.com/go-playground/webhooks/v6/github"
2223
"github.com/google/go-github/v59/github"
2324
"github.com/sashabaranov/go-openai"
25+
"golang.org/x/exp/maps"
26+
"gopkg.in/yaml.v3"
2427
)
2528

2629
type repoAddr struct {
27-
install, user, repo string
30+
InstallID, User, Repo string
2831
}
2932

3033
type Webhook struct {
@@ -68,15 +71,59 @@ func filterIssues(slice []*github.Issue, f func(*github.Issue) bool) []*github.I
6871
}
6972

7073
type InferRequest struct {
71-
InstallID string `json:"install_id"`
72-
User string `json:"user"`
73-
Repo string `json:"repo"`
74-
Issue int `json:"issue"`
74+
InstallID, User, Repo string
75+
Issue int `json:"issue"`
7576
}
7677

7778
type InferResponse struct {
78-
SetLabels []string `json:"set_labels,omitempty"`
79-
TokensUsed int `json:"tokens_used,omitempty"`
79+
SetLabels []string `json:"set_labels,omitempty"`
80+
TokensUsed int `json:"tokens_used,omitempty"`
81+
DisabledLabels []string `json:"disabled_labels,omitempty"`
82+
}
83+
84+
type repoConfig struct {
85+
Exclude []regexp.Regexp `json:"exclude"`
86+
}
87+
88+
func (c *repoConfig) checkLabel(label string) bool {
89+
for _, re := range c.Exclude {
90+
if re.MatchString(label) {
91+
return false
92+
}
93+
}
94+
return true
95+
}
96+
97+
func (s *Webhook) getRepoConfig(ctx context.Context, client *github.Client,
98+
owner, repo string,
99+
) (*repoConfig, error) {
100+
fileContent, _, _, err := client.Repositories.GetContents(
101+
ctx,
102+
owner,
103+
repo,
104+
".github/labeler.yml",
105+
&github.RepositoryContentGetOptions{},
106+
)
107+
if err != nil {
108+
var githubErr *github.ErrorResponse
109+
if errors.As(err, &githubErr) && githubErr.Response.StatusCode == http.StatusNotFound {
110+
return &repoConfig{}, nil
111+
}
112+
return nil, fmt.Errorf("get contents: %w", err)
113+
}
114+
115+
content, err := fileContent.GetContent()
116+
if err != nil {
117+
return nil, fmt.Errorf("unmarshal content: %w", err)
118+
}
119+
120+
var config repoConfig
121+
err = yaml.Unmarshal(
122+
[]byte(content),
123+
&config,
124+
)
125+
126+
return &config, err
80127
}
81128

82129
func (s *Webhook) Infer(ctx context.Context, req *InferRequest) (*InferResponse, error) {
@@ -87,10 +134,15 @@ func (s *Webhook) Infer(ctx context.Context, req *InferRequest) (*InferResponse,
87134

88135
githubClient := github.NewClient(instConfig.Client(ctx))
89136

137+
config, err := s.getRepoConfig(ctx, githubClient, req.User, req.Repo)
138+
if err != nil {
139+
return nil, fmt.Errorf("get repo config: %w", err)
140+
}
141+
90142
lastIssues, err := s.recentIssuesCache.Do(repoAddr{
91-
install: req.InstallID,
92-
user: req.User,
93-
repo: req.Repo,
143+
InstallID: req.InstallID,
144+
User: req.User,
145+
Repo: req.Repo,
94146
}, func() ([]*github.Issue, error) {
95147
return ghapi.Page(
96148
ctx,
@@ -116,9 +168,9 @@ func (s *Webhook) Infer(ctx context.Context, req *InferRequest) (*InferResponse,
116168
}
117169

118170
labels, err := s.repoLabelsCache.Do(repoAddr{
119-
install: req.InstallID,
120-
user: req.User,
121-
repo: req.Repo,
171+
InstallID: req.InstallID,
172+
User: req.User,
173+
Repo: req.Repo,
122174
}, func() ([]*github.Label, error) {
123175
return ghapi.Page(
124176
ctx,
@@ -209,6 +261,9 @@ retryAI:
209261
if strings.Contains(label.GetDescription(), magicDisableString) {
210262
disabledLabels[label.GetName()] = struct{}{}
211263
}
264+
if !config.checkLabel(label.GetName()) {
265+
disabledLabels[label.GetName()] = struct{}{}
266+
}
212267
}
213268

214269
// Remove any labels that are disabled.
@@ -221,8 +276,9 @@ retryAI:
221276
}
222277

223278
return &InferResponse{
224-
SetLabels: newLabels,
225-
TokensUsed: resp.Usage.TotalTokens,
279+
SetLabels: newLabels,
280+
TokensUsed: resp.Usage.TotalTokens,
281+
DisabledLabels: maps.Keys(disabledLabels),
226282
}, nil
227283
}
228284

0 commit comments

Comments
 (0)
0