Skip to content

Commit 95f5a93

Browse files
committed
Generate footer from template
Signed-off-by: David Pordomingo <David.Pordomingo.F@gmail.com>
1 parent c975fc9 commit 95f5a93

File tree

7 files changed

+131
-29
lines changed

7 files changed

+131
-29
lines changed

.helm-staging.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ providers:
2727
app_id: 17877
2828
private_key: /local/lookout/private-key.pem
2929
secretName: lookout-staging-github-key
30-
comment_footer: '_If you have feedback about this comment, please, [tell us](%s)._'
30+
comment_footer: "_If you have feedback about this comment made by the analyzer {{.Name}}, please, [tell us]({{.Feedback}})._"
3131
installation_sync_interval: 5m
3232

3333
analyzers:

config.yml.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ analyzers:
88

99
providers:
1010
github:
11-
comment_footer: '_If you have feedback about this comment, please, [tell us](%s)._'
11+
comment_footer: "_If you have feedback about this comment made by the analyzer {{.Name}}, please, [tell us]({{.Feedback}})._"
1212
# The minimum watch interval to discover new pull requests and push events
1313
watch_min_interval: 2s
1414
# Authorization with GitHub App

docs/configuration.md

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ The `providers.github` key configures how **source{d} Lookout** will connect wit
3939
```yaml
4040
providers:
4141
github:
42-
comment_footer: "_If you have feedback about this comment, please, [tell us](%s)._"
42+
comment_footer: "_Comment made by analyzer '{{.Name}}', [report]({{.Feedback}})._"
4343
# app_id: 1234
4444
# private_key: ./key.pem
4545
# installation_sync_interval: 1h
4646
# watch_min_interval: 2s
4747
```
4848

49-
`comment_footer` key defines a format-string that will be used for custom messages for every message posted on GitHub; see how to [add a custom message to the posted comments](#add-a-custom-message-to-the-posted-comments)
49+
`comment_footer` key defines the [go template](https://golang.org/pkg/text/template) that will be used for custom messages for every message posted on GitHub; see how to [add a custom message to the posted comments](#add-a-custom-message-to-the-posted-comments)
5050

5151
### Authentication with GitHub
5252

@@ -141,17 +141,33 @@ analyzers:
141141

142142
### Add a Custom Message to the Posted Comments
143143

144-
You can configure **source{d} Lookout** to add a custom message to every comment that each analyzer returns. This custom message will be created following the rule:
145-
```
146-
sprintf(providers.github.comment_footer, feedback)
147-
```
148-
If any of those two keys are not defined, the custom message won't be added.
144+
You can configure **source{d} Lookout** to add a custom message to every comment that each analyzer returns. This custom message will be created passing to the template defined by `providers.github.comment_footer`, the `name` and `feedback` defined for each analyzer configuration.
145+
146+
If the template (`providers.github.comment_footer`) is empty, or the analyzer configuration does not define any of the values that the template requires, the custom message won't be added.
149147

150148
Example:
151-
```text
152-
"_If you have feedback about this comment, please, [tell us](%s)._"
149+
```yaml
150+
providers:
151+
github:
152+
comment_footer: "Comment made by analyzer {{.Name}}, [report]({{.Feedback}})."
153153
```
154154

155+
Will require that each analyzer defines its `name` and `feedback` in order to render its custom message appended to its comments, so:
156+
157+
```yaml
158+
analyzers:
159+
- name: Fancy Analyzer
160+
addr: ipv4://localhost:9930
161+
feedback: http://example.com/report-issue
162+
- name: Awesome Analyzer
163+
addr: ipv4://localhost:9931
164+
```
165+
166+
Comments from `Fancy Analyzer` will be appended with the message:
167+
>_Comment made by analyzer Fancy Analyzer, [report](http://example.com/report-issue)._
168+
169+
but comments from `Awesome Analyzer` wont be appended with a custom message because its configuration lacks of a key defining its `feedback` value.
170+
155171

156172
## Timeouts
157173

provider/github/poster.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"reflect"
77
"strings"
8+
"text/template"
89

910
"github.com/src-d/lookout"
1011
"github.com/src-d/lookout/util/ctxlog"
@@ -32,17 +33,24 @@ const (
3233

3334
// Poster posts comments as Pull Request Reviews.
3435
type Poster struct {
35-
pool *ClientPool
36-
conf ProviderConfig
36+
pool *ClientPool
37+
conf ProviderConfig
38+
footerTemplate *template.Template
3739
}
3840

3941
var _ lookout.Poster = &Poster{}
4042

4143
// NewPoster creates a new poster for the GitHub API.
4244
func NewPoster(pool *ClientPool, conf ProviderConfig) *Poster {
45+
tpl, err := newFooterTemplate(conf.CommentFooter)
46+
if err != nil {
47+
log.DefaultLogger.Warningf("no footer template being used: %s", err)
48+
}
49+
4350
return &Poster{
44-
pool: pool,
45-
conf: conf,
51+
pool: pool,
52+
conf: conf,
53+
footerTemplate: tpl,
4654
}
4755
}
4856

@@ -157,15 +165,11 @@ func (p *Poster) createReviewRequest(
157165
}
158166

159167
var bodyComments []string
160-
tmpl := p.conf.CommentFooter
161-
162168
for _, aComments := range aCommentsList {
163169
ctx, _ := ctxlog.WithLogFields(ctx, log.Fields{
164170
"analyzer": aComments.Config.Name,
165171
})
166172

167-
url := aComments.Config.Feedback
168-
169173
forBody, ghComments := convertComments(ctx, aComments.Comments, dl)
170174

171175
if len(postedComments) > 0 {
@@ -175,13 +179,13 @@ func (p *Poster) createReviewRequest(
175179
ghComments = mergeComments(ghComments)
176180

177181
for i, c := range ghComments {
178-
body := addFootnote(c.GetBody(), tmpl, url)
182+
body := addFootnote(ctx, c.GetBody(), p.footerTemplate, &aComments.Config)
179183
ghComments[i].Body = &body
180184
}
181185

182186
bodyComments = append(
183187
bodyComments,
184-
addFootnote(strings.Join(forBody, "\n\n"), tmpl, url),
188+
addFootnote(ctx, strings.Join(forBody, "\n\n"), p.footerTemplate, &aComments.Config),
185189
)
186190
req.Comments = append(req.Comments, ghComments...)
187191
}

provider/github/poster_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,10 @@ func (s *PosterTestSuite) TestPostFooter() {
205205
aComments := mockAnalyzerComments
206206
aComments[0].Config.Feedback = "https://foo.bar/feedback"
207207

208+
footerTpl, _ := newFooterTemplate("To post feedback go to {{.Feedback}}")
208209
p := &Poster{
209210
pool: s.pool,
210-
conf: ProviderConfig{
211-
CommentFooter: "To post feedback go to %s",
212-
},
211+
footerTemplate: footerTpl,
213212
}
214213
err := p.Post(context.Background(), mockEvent, aComments, false)
215214
s.NoError(err)

provider/github/review.go

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ package github
22

33
import (
44
"context"
5-
"fmt"
65
"sort"
76
"strings"
7+
"text/template"
88
"time"
99

1010
"github.com/google/go-github/github"
1111
"github.com/src-d/lookout"
1212
"github.com/src-d/lookout/util/ctxlog"
13+
errors "gopkg.in/src-d/go-errors.v1"
1314
log "gopkg.in/src-d/go-log.v1"
1415
)
1516

@@ -207,13 +208,64 @@ func splitReviewRequest(review *github.PullRequestReviewRequest, n int) []*githu
207208
return result
208209
}
209210

211+
var (
212+
ErrEmptyTemplate = errors.NewKind("empty footer template")
213+
ErrOldTemplate = errors.NewKind("old footer template: '%%s' placeholder is no longer supported: '%s'")
214+
ErrParseTemplate = errors.NewKind("error parsing footer template: %s")
215+
ErrNoDataForFooter = errors.NewKind("there is no enough data to generate the footer")
216+
ErrTemplateError = errors.NewKind("error generating the footer: %s")
217+
)
218+
219+
func newFooterTemplate(tpl string) (*template.Template, error) {
220+
if tpl == "" {
221+
return nil, ErrEmptyTemplate.New()
222+
}
223+
224+
if strings.Index(tpl, "%s") >= 0 {
225+
return nil, ErrOldTemplate.New(tpl)
226+
}
227+
228+
template, err := template.New("footer").Parse(tpl)
229+
if err != nil {
230+
return nil, ErrParseTemplate.New(err)
231+
}
232+
233+
return template.Option("missingkey=error"), nil
234+
}
235+
210236
// addFootnote adds footnote link to text of a comment
211-
func addFootnote(text, tmpl, url string) string {
212-
if text == "" || tmpl == "" || url == "" {
213-
return text
237+
func addFootnote(
238+
ctx context.Context,
239+
comment string, tmpl *template.Template, analyzerConf *lookout.AnalyzerConfig,
240+
) string {
241+
if comment == "" || tmpl == nil {
242+
return comment
243+
}
244+
245+
footer, err := getFootnote(tmpl, analyzerConf)
246+
if err != nil {
247+
ctxlog.Get(ctx).Warningf("footer could not be generated: %s", err)
248+
}
249+
250+
return comment + footer
251+
}
252+
253+
func getFootnote(tmpl *template.Template, analyzerConf *lookout.AnalyzerConfig) (string, error) {
254+
if analyzerConf == nil || analyzerConf.Feedback == "" || analyzerConf.Name == "" {
255+
return "", ErrNoDataForFooter.New()
256+
}
257+
258+
var footer strings.Builder
259+
if err := tmpl.Execute(&footer, analyzerConf); err != nil {
260+
return "", ErrTemplateError.New(err)
261+
}
262+
263+
footerTxt := footer.String()
264+
if footerTxt == "" {
265+
return "", nil
214266
}
215267

216-
return text + footnoteSeparator + fmt.Sprintf(tmpl, url)
268+
return footnoteSeparator + footer.String(), nil
217269
}
218270

219271
// removeFootnote removes footnote and returns only text of a comment

provider/github/review_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,34 @@ func TestConvertCommentsWrongFile(t *testing.T) {
225225
Body: strptr("Line comment"),
226226
}}, ghComments)
227227
}
228+
229+
func TestCouldNotParseFooterTemplate(t *testing.T) {
230+
require := require.New(t)
231+
232+
emptyTemplate, err := newFooterTemplate("")
233+
require.True(ErrEmptyTemplate.Is(err))
234+
commentsEmptyTemplate := addFootnote(context.TODO(), "comments", emptyTemplate, nil)
235+
require.Equal("comments", commentsEmptyTemplate)
236+
237+
oldTemplate, err := newFooterTemplate("Old template %s")
238+
require.True(ErrOldTemplate.Is(err))
239+
commentsOldTemplate := addFootnote(context.TODO(), "comments", oldTemplate, nil)
240+
require.Equal("comments", commentsOldTemplate)
241+
242+
wrongTemplate, err := newFooterTemplate("Old template {{{parseerror")
243+
require.True(ErrParseTemplate.Is(err))
244+
commentsWrongTemplate := addFootnote(context.TODO(), "comments", wrongTemplate, nil)
245+
require.Equal("comments", commentsWrongTemplate)
246+
}
247+
248+
func TestCouldNotExecuteFooterTemplate(t *testing.T) {
249+
require := require.New(t)
250+
251+
unkonwnDataTemplate, err := newFooterTemplate("Old template {{.UnknownData}}")
252+
require.Nil(err)
253+
commentsWrongTemplate := addFootnote(context.TODO(), "comments", unkonwnDataTemplate, nil)
254+
require.Equal("comments", commentsWrongTemplate)
255+
footerWrongTemplate, err := getFootnote(unkonwnDataTemplate, nil)
256+
require.True(ErrNoDataForFooter.Is(err))
257+
require.Equal("", footerWrongTemplate)
258+
}

0 commit comments

Comments
 (0)