nitter/nitters/gitea/pr_nitter.go
Peter Kurfer eee9a30503
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
test(gitea): test approval behavior
2023-03-10 10:25:07 +01:00

161 lines
3.7 KiB
Go

package gitea
import (
"bytes"
"embed"
"strings"
"text/template"
"code.gitea.io/sdk/gitea"
"github.com/golangci/golangci-lint/pkg/report"
"github.com/golangci/golangci-lint/pkg/result"
"golang.org/x/exp/slog"
"code.icb4dc0.de/prskr/nitter/nitters"
)
const summaryFooter = "\n\n(Created by nitter)"
var (
_ nitters.Nitter = (*prNitter)(nil)
//go:embed templates/*
templatesFS embed.FS
templates *template.Template
)
func init() {
if tmpl, err := template.New("issue_templates").ParseFS(templatesFS, "templates/*.tmpl.md"); err != nil {
panic(err)
} else {
templates = tmpl
}
}
func NewPRNitter(logger *slog.Logger, cli Client, cfg *GiteaPRConfig) *prNitter {
if logger == nil {
logger = slog.Default()
}
return &prNitter{
Client: cli,
logger: logger,
cfg: cfg,
}
}
type prNitter struct {
Client
logger *slog.Logger
cfg *GiteaPRConfig
}
func (p prNitter) Report(report *report.Data, issues []result.Issue) error {
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)
if err != nil {
return err
}
if err := p.cleanOutdatedReviews(me); err != nil {
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
}
func (p prNitter) preparePullReview(
report *report.Data,
issues []result.Issue,
me *gitea.User,
pr *gitea.PullRequest,
) (*gitea.CreatePullReviewOptions, error) {
templateBuf := bytes.Buffer{}
summaryData := map[string]any{
"Report": report,
"Issues": issues,
}
if err := templates.ExecuteTemplate(&templateBuf, "issue_summary.tmpl.md", summaryData); err != nil {
return nil, err
}
_, _ = templateBuf.WriteString(summaryFooter)
pullReviewOptions := &gitea.CreatePullReviewOptions{
State: p.cfg.ReviewState,
Body: templateBuf.String(),
Comments: make([]gitea.CreatePullReviewComment, 0, len(issues)),
}
if len(issues) == 0 {
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
}
} else {
templateBuf.Reset()
for i := range issues {
templateData := map[string]any{
"Issue": issues[i],
}
if err := templates.ExecuteTemplate(&templateBuf, "issue_comment.tmpl.md", templateData); err != nil {
return nil, err
}
pullReviewOptions.Comments = append(pullReviewOptions.Comments, gitea.CreatePullReviewComment{
Path: issues[i].Pos.Filename,
Body: templateBuf.String(),
NewLineNum: int64(issues[i].Pos.Line),
})
templateBuf.Reset()
}
}
return pullReviewOptions, nil
}
func (p prNitter) cleanOutdatedReviews(me *gitea.User) error {
reviews, _, err := p.ListPullReviews(p.cfg.Namespace, p.cfg.Repo, p.cfg.PRIndex, gitea.ListPullReviewsOptions{})
if err != nil {
return err
}
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
}
}
}
return nil
}