lure-repo-bot/workers.go

177 lines
3.9 KiB
Go

package main
import (
"context"
"fmt"
"log"
"os"
"runtime"
"strings"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/google/go-github/v48/github"
"go.arsenm.dev/lure-repo-bot/internal/analyze"
"go.arsenm.dev/lure-repo-bot/internal/shutils"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
)
func startWebhookWorkers(ctx context.Context, jobQueue prQueue) {
client := newClient(ctx, os.Getenv("LURE_BOT_GITHUB_TOKEN"))
for i := 0; i < runtime.NumCPU(); i++ {
go startWebhookWorker(ctx, jobQueue, client)
}
}
func startWebhookWorker(ctx context.Context, jobQueue prQueue, client *github.Client) {
for {
select {
case <-ctx.Done():
return
case payload := <-jobQueue.Channel():
if payload.Action == "opened" || payload.Action == "ready_for_review" || payload.Action == "review_requested" {
if payload.PullRequest.Draft {
continue
}
// Check if review was requested from the bot
if payload.Action == "review_requested" {
user, _, err := client.Users.Get(ctx, "")
if err != nil {
log.Println("Error getting github user:", err)
continue
}
found := false
for _, reviewer := range payload.PullRequest.RequestedReviewers {
if reviewer.ID == *user.ID {
found = true
break
}
}
if !found {
continue
}
}
fls, _, err := client.PullRequests.ListFiles(
ctx,
payload.PullRequest.Base.Repo.Owner.Login,
payload.PullRequest.Base.Repo.Name,
int(payload.PullRequest.Number),
nil,
)
if err != nil {
log.Println("Error listing PR files:", err)
continue
}
head := payload.PullRequest.Head
tmpdir, err := os.MkdirTemp("/tmp", "lure-repo-bot.*")
if err != nil {
log.Println("Error creating temporary directory:", err)
continue
}
r, err := git.PlainClone(tmpdir, true, &git.CloneOptions{URL: head.Repo.HTMLURL})
if err != nil {
log.Println("Error cloning git repo:", err)
continue
}
files, err := getFiles(r, head.Sha)
if err != nil {
log.Println("Error getting files in repo:", err)
continue
}
err = files.ForEach(func(f *object.File) error {
if !strings.Contains(f.Name, "lure.sh") {
return nil
}
if !fileInPR(fls, f) {
return nil
}
fmt.Println(f.Name, fileInPR(fls, f))
r, err := f.Reader()
if err != nil {
return err
}
fl, err := syntax.NewParser().Parse(r, "lure.sh")
if err != nil {
return err
}
var nopRWC shutils.NopRWC
runner, err := interp.New(
interp.Env(expand.ListEnviron()),
interp.StdIO(nopRWC, nopRWC, os.Stderr),
interp.ExecHandler(shutils.NopExec),
interp.ReadDirHandler(shutils.NopReadDir),
interp.OpenHandler(shutils.NopOpen),
interp.StatHandler(shutils.NopStat),
)
if err != nil {
return err
}
err = runner.Run(ctx, fl)
if err != nil {
return err
}
findings, err := analyze.AnalyzeScript(runner, fl)
if err != nil {
return err
}
return writeFindings(ctx, client, findings, f.Name, &payload.PullRequest)
})
if err != nil {
log.Println("Error analyzing files:", err)
continue
}
err = os.RemoveAll(tmpdir)
if err != nil {
log.Println("Error removing temporary directory:", err)
continue
}
}
}
}
}
func fileInPR(prFiles []*github.CommitFile, file *object.File) bool {
for _, prFile := range prFiles {
if *prFile.Filename == file.Name {
return true
}
}
return false
}
func getFiles(r *git.Repository, sha string) (*object.FileIter, error) {
co, err := r.CommitObject(plumbing.NewHash(sha))
if err != nil {
return nil, err
}
tree, err := co.Tree()
if err != nil {
return nil, err
}
return tree.Files(), nil
}