2023-03-08 08:05:13 +00:00
|
|
|
package gitea
|
|
|
|
|
2023-03-08 13:40:04 +00:00
|
|
|
import (
|
2023-03-08 15:13:49 +00:00
|
|
|
"bytes"
|
|
|
|
"embed"
|
2023-03-09 13:28:33 +00:00
|
|
|
"strings"
|
2023-03-08 15:13:49 +00:00
|
|
|
"text/template"
|
2023-03-08 08:05:13 +00:00
|
|
|
|
2023-03-08 13:40:04 +00:00
|
|
|
"code.gitea.io/sdk/gitea"
|
|
|
|
"github.com/golangci/golangci-lint/pkg/report"
|
|
|
|
"github.com/golangci/golangci-lint/pkg/result"
|
2023-03-09 13:28:33 +00:00
|
|
|
"golang.org/x/exp/slog"
|
2023-03-08 08:05:13 +00:00
|
|
|
|
2023-03-08 13:40:04 +00:00
|
|
|
"code.icb4dc0.de/prskr/nitter/nitters"
|
|
|
|
)
|
|
|
|
|
2023-03-09 13:28:33 +00:00
|
|
|
const summaryFooter = "\n\n(Created by nitter)"
|
|
|
|
|
2023-03-08 15:13:49 +00:00
|
|
|
var (
|
|
|
|
_ nitters.Nitter = (*prNitter)(nil)
|
2023-03-08 13:40:04 +00:00
|
|
|
|
2023-03-08 15:13:49 +00:00
|
|
|
//go:embed templates/*
|
2023-03-08 16:13:17 +00:00
|
|
|
templatesFS embed.FS
|
2023-03-08 15:13:49 +00:00
|
|
|
templates *template.Template
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2023-03-08 16:13:17 +00:00
|
|
|
if tmpl, err := template.New("issue_templates").ParseFS(templatesFS, "templates/*.tmpl.md"); err != nil {
|
2023-03-08 15:13:49 +00:00
|
|
|
panic(err)
|
|
|
|
} else {
|
|
|
|
templates = tmpl
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-09 13:28:33 +00:00
|
|
|
func NewPRNitter(cli Client, cfg *GiteaPRConfig) *prNitter {
|
2023-03-08 13:40:04 +00:00
|
|
|
return &prNitter{
|
2023-03-09 13:28:33 +00:00
|
|
|
Client: cli,
|
|
|
|
cfg: cfg,
|
2023-03-08 13:40:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type prNitter struct {
|
2023-03-09 13:28:33 +00:00
|
|
|
Client
|
2023-03-08 13:40:04 +00:00
|
|
|
cfg *GiteaPRConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p prNitter) Report(report *report.Data, issues []result.Issue) error {
|
2023-03-09 14:46:32 +00:00
|
|
|
me, _, err := p.GetMyUserInfo()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
slog.Debug("Running as", slog.String("name", me.UserName))
|
|
|
|
|
|
|
|
pr, _, err := p.GetPullRequest(p.cfg.Namespace, p.cfg.Repo, p.cfg.PRIndex)
|
|
|
|
if err != nil {
|
|
|
|
slog.Error("Failed to get PR details", err, slog.Int64("index", p.cfg.PRIndex))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
review, err := p.preparePullReview(report, issues, me, pr)
|
2023-03-09 13:28:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-03-09 14:46:32 +00:00
|
|
|
if err := p.cleanOutdatedReviews(me); err != nil {
|
2023-03-09 13:28:33 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, _, err := p.CreatePullReview(p.cfg.Namespace, p.cfg.Repo, p.cfg.PRIndex, *review); err != nil {
|
|
|
|
slog.Error("Failed to submit new PR review", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-09 14:46:32 +00:00
|
|
|
func (p prNitter) preparePullReview(
|
|
|
|
report *report.Data,
|
|
|
|
issues []result.Issue,
|
|
|
|
me *gitea.User,
|
|
|
|
pr *gitea.PullRequest,
|
|
|
|
) (*gitea.CreatePullReviewOptions, error) {
|
2023-03-08 15:13:49 +00:00
|
|
|
templateBuf := bytes.Buffer{}
|
|
|
|
summaryData := map[string]any{
|
|
|
|
"Report": report,
|
|
|
|
"Issues": issues,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := templates.ExecuteTemplate(&templateBuf, "issue_summary.tmpl.md", summaryData); err != nil {
|
2023-03-09 13:28:33 +00:00
|
|
|
return nil, err
|
2023-03-08 13:40:04 +00:00
|
|
|
}
|
|
|
|
|
2023-03-09 14:46:32 +00:00
|
|
|
_, _ = templateBuf.WriteString(summaryFooter)
|
|
|
|
|
2023-03-09 13:28:33 +00:00
|
|
|
pullReviewOptions := &gitea.CreatePullReviewOptions{
|
2023-03-09 11:04:11 +00:00
|
|
|
State: p.cfg.ReviewState,
|
2023-03-08 15:13:49 +00:00
|
|
|
Body: templateBuf.String(),
|
2023-03-08 13:40:04 +00:00
|
|
|
Comments: make([]gitea.CreatePullReviewComment, 0, len(issues)),
|
|
|
|
}
|
|
|
|
|
2023-03-09 11:04:11 +00:00
|
|
|
if len(issues) == 0 {
|
2023-03-09 14:46:32 +00:00
|
|
|
if me.ID != pr.Poster.ID {
|
|
|
|
pullReviewOptions.State = gitea.ReviewStateApproved
|
|
|
|
} else {
|
|
|
|
slog.Warn(
|
|
|
|
"Cannot approve PR if token is issued by the same user - fallback to comment state",
|
|
|
|
slog.String("nitter_user", me.UserName),
|
|
|
|
slog.String("pr_poster", pr.Poster.UserName),
|
|
|
|
)
|
|
|
|
|
|
|
|
pullReviewOptions.State = gitea.ReviewStateComment
|
|
|
|
}
|
2023-03-09 11:04:11 +00:00
|
|
|
} else {
|
2023-03-08 15:13:49 +00:00
|
|
|
templateBuf.Reset()
|
2023-03-09 11:04:11 +00:00
|
|
|
for i := range issues {
|
|
|
|
templateData := map[string]any{
|
|
|
|
"Issue": issues[i],
|
|
|
|
}
|
|
|
|
if err := templates.ExecuteTemplate(&templateBuf, "issue_comment.tmpl.md", templateData); err != nil {
|
2023-03-09 13:28:33 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-03-09 11:04:11 +00:00
|
|
|
pullReviewOptions.Comments = append(pullReviewOptions.Comments, gitea.CreatePullReviewComment{
|
|
|
|
Path: issues[i].Pos.Filename,
|
|
|
|
Body: templateBuf.String(),
|
|
|
|
NewLineNum: int64(issues[i].Pos.Line),
|
|
|
|
})
|
|
|
|
|
|
|
|
templateBuf.Reset()
|
|
|
|
}
|
2023-03-08 13:40:04 +00:00
|
|
|
}
|
|
|
|
|
2023-03-09 13:28:33 +00:00
|
|
|
return pullReviewOptions, nil
|
|
|
|
}
|
|
|
|
|
2023-03-09 14:46:32 +00:00
|
|
|
func (p prNitter) cleanOutdatedReviews(me *gitea.User) error {
|
2023-03-09 13:28:33 +00:00
|
|
|
reviews, _, err := p.ListPullReviews(p.cfg.Namespace, p.cfg.Repo, p.cfg.PRIndex, gitea.ListPullReviewsOptions{})
|
2023-03-08 13:40:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-03-09 13:28:33 +00:00
|
|
|
for i := range reviews {
|
|
|
|
review := reviews[i]
|
|
|
|
if review.Reviewer.ID == me.ID && strings.HasSuffix(review.Body, summaryFooter) {
|
|
|
|
if _, err = p.DeletePullReview(p.cfg.Namespace, p.cfg.Repo, p.cfg.PRIndex, review.ID); err != nil {
|
|
|
|
slog.Error("Failed to delete existing review", err, slog.Int64("reviewId", review.ID))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-08 13:40:04 +00:00
|
|
|
return nil
|
2023-03-08 08:05:13 +00:00
|
|
|
}
|