Skip to content

Commit

Permalink
add reaction to command comments
Browse files Browse the repository at this point in the history
  • Loading branch information
corymurphy committed Oct 18, 2024
1 parent b094264 commit 3d87aa0
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 34 deletions.
4 changes: 2 additions & 2 deletions charts/argobot/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ apiVersion: v2
name: argobot
description: Helm chart for the corymurphy/argobot app
type: application
version: 0.16.4
appVersion: 0.16.4
version: 0.16.5
appVersion: 0.16.5
2 changes: 1 addition & 1 deletion pkg/events/apply_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func NewApplyRunner(vcsClient *github.Client, config *env.Config, log logging.Si
func (a *ApplyRunner) Run(ctx context.Context, app string, event vsc.Event) (CommentResponse, error) {
var resp CommentResponse

status, err := vsc.NewPullRequestStatusFetcher(ctx, a.Log, a.vcsClient).Fetch(event)
status, err := vsc.NewPullRequestStatusFetcher(a.Log, a.vcsClient).Fetch(ctx, event)
if err != nil {
return resp, fmt.Errorf("unable to get status of pr %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/events/comment_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func Test_Comment_IsHelp(t *testing.T) {
`
var comment github.IssueCommentEvent
json.Unmarshal([]byte(serialized), &comment)
event := vsc.InitializeFromIssueComment(comment, "")
event, _ := vsc.InitializeFromIssueComment(comment, "")
parser := NewCommentParser(logging.NewLogger(logging.Silent))

result := parser.Parse(event)
Expand Down Expand Up @@ -57,7 +57,7 @@ func Test_Comment_IsBot(t *testing.T) {
`
var comment github.IssueCommentEvent
json.Unmarshal([]byte(serialized), &comment)
event := vsc.InitializeFromIssueComment(comment, "")
event, _ := vsc.InitializeFromIssueComment(comment, "")
parser := NewCommentParser(logging.NewLogger(logging.Silent))

result := parser.Parse(event)
Expand Down Expand Up @@ -87,7 +87,7 @@ func Test_PlanHasApplicationName(t *testing.T) {

var comment github.IssueCommentEvent
json.Unmarshal([]byte(serialized), &comment)
event := vsc.InitializeFromIssueComment(comment, "")
event, _ := vsc.InitializeFromIssueComment(comment, "")
parser := NewCommentParser(logging.NewLogger(logging.Silent))

result := parser.Parse(event)
Expand Down Expand Up @@ -121,7 +121,7 @@ func Test_ApplyHasApplicationName(t *testing.T) {

var comment github.IssueCommentEvent
json.Unmarshal([]byte(serialized), &comment)
event := vsc.InitializeFromIssueComment(comment, "")
event, _ := vsc.InitializeFromIssueComment(comment, "")
parser := NewCommentParser(logging.NewLogger(logging.Silent))

result := parser.Parse(event)
Expand Down
21 changes: 17 additions & 4 deletions pkg/events/issue_comment_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (h *IssueCommentHandler) Handle(ctx context.Context, eventType string, deli
h.Log.Err(err, "unable to get revision from pull request")
return nil
}
event := vsc.InitializeFromIssueComment(issue, *pr.GetHead().SHA)
event, commentId := vsc.InitializeFromIssueComment(issue, *pr.GetHead().SHA)

comment := NewCommentParser(h.Log).Parse(event)
if (comment.Ignore || comment.ImmediateResponse) && !comment.HasResponseComment {
Expand All @@ -65,6 +65,11 @@ func (h *IssueCommentHandler) Handle(ctx context.Context, eventType string, deli
}
}

_, _, err = client.Reactions.CreateIssueCommentReaction(ctx, event.Repository.Owner, event.Repository.Name, *commentId, "eyes")
if err != nil {
h.Log.Err(err, "unable to create reaction on comment")
}

var apps []string
if comment.Command.ExplicitApplication {
apps = comment.Command.Applications
Expand Down Expand Up @@ -117,18 +122,26 @@ func (h *IssueCommentHandler) Handle(ctx context.Context, eventType string, deli
h.Log.Info("requested apply with more than 1 app, only one app allowed when applying")
return nil
}
if !comment.Command.ExplicitApplication {
h.Log.Info("apply does not supply auto discovery. an application must be explicitly defined.")
return nil
}
go func() {
applyContext, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
for _, app := range apps {
applyContext, cancel := context.WithTimeout(ctx, 60*time.Second)
defer cancel()
apply := NewApplyRunner(client, h.Config, h.Log, &h.ArgoClient)
response, err := apply.Run(applyContext, app, event)
if err != nil {
h.Log.Err(err, "unable to apply")
return
}
comment := fmt.Sprintf("apply result for `%s`\n\n", app) + "```\n" + response.Message + "\n```"
commenter.Comment(&event, &comment)
err = commenter.Comment(&event, &comment)
if err != nil {
h.Log.Err(err, "unable to comment with apply result")
return
}
}
}()
return nil
Expand Down
5 changes: 5 additions & 0 deletions pkg/github/commenter.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (

const maxCommentLength = 32768

type VscCommenter interface {
Plan(event *Event, app string, command string, comment string)
Comment(event *Event, comment *string)
}

type Commenter struct {
client *github.Client
log logging.SimpleLogging
Expand Down
21 changes: 21 additions & 0 deletions pkg/github/commit_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package github

type CommitState int

const (
PendingCommitState CommitState = iota
SuccessCommitState
FailedCommitState
)

func (s CommitState) String() string {
switch s {
case PendingCommitState:
return "pending"
case SuccessCommitState:
return "success"
case FailedCommitState:
return "failure"
}
return "failure"
}
4 changes: 2 additions & 2 deletions pkg/github/event_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (e *Event) HasMessage() bool {
return e.Message != ""
}

func InitializeFromIssueComment(source github.IssueCommentEvent, revision string) Event {
func InitializeFromIssueComment(source github.IssueCommentEvent, revision string) (Event, *int64) {
return Event{
Actor: Actor{Name: source.GetComment().GetUser().GetLogin()},
Action: Comment,
Expand All @@ -83,7 +83,7 @@ func InitializeFromIssueComment(source github.IssueCommentEvent, revision string
},
Message: *source.GetComment().Body,
InstallationProvider: &source,
}
}, source.Comment.ID
}

func InitializeFromPullRequest(source github.PullRequestEvent) Event {
Expand Down
22 changes: 10 additions & 12 deletions pkg/github/pull_status_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,30 @@ import (
)

type VscPullRequestStatusFetcher interface {
Fetch(event Event) (models.PullRequestStatus, error)
Fetch(ctx context.Context, event Event) (models.PullRequestStatus, error)
}

type GithubPullRequestStatusFetcher struct {
client *github.Client
ctx context.Context
logger logging.SimpleLogging
}

func NewPullRequestStatusFetcher(ctx context.Context, logger logging.SimpleLogging, client *github.Client) VscPullRequestStatusFetcher {
func NewPullRequestStatusFetcher(logger logging.SimpleLogging, client *github.Client) VscPullRequestStatusFetcher {
return &GithubPullRequestStatusFetcher{
ctx: ctx,
client: client,
logger: logger,
}
}

func (g *GithubPullRequestStatusFetcher) Fetch(event Event) (status models.PullRequestStatus, err error) {
func (g *GithubPullRequestStatusFetcher) Fetch(ctx context.Context, event Event) (status models.PullRequestStatus, err error) {

g.logger.Debug("getting approval status of pull request %d", event.PullRequest.Number)
approvalStatus, err := g.PullIsApproved(event)
approvalStatus, err := g.PullIsApproved(ctx, event)
if err != nil {
return status, errors.Wrapf(err, "fetching pull approval status for repo: %s/%s, and pull number: %d", event.Repository.Owner, event.Repository.Name, event.PullRequest.Number)
}

mergeable, err := g.PullIsMergeable(event, "")
mergeable, err := g.PullIsMergeable(ctx, event, "")
if err != nil {
return status, errors.Wrapf(err, "fetching mergeability status for repo: %s/%s, and pull number: %d", event.Repository.Owner, event.Repository.Name, event.PullRequest.Number)
}
Expand All @@ -46,8 +44,8 @@ func (g *GithubPullRequestStatusFetcher) Fetch(event Event) (status models.PullR
}, err
}

func (g *GithubPullRequestStatusFetcher) PullIsApproved(event Event) (approvalStatus models.ApprovalStatus, err error) {
g.logger.Debug("Checking if GitHub pull request %d is approved", event.PullRequest.Number)
func (g *GithubPullRequestStatusFetcher) PullIsApproved(ctx context.Context, event Event) (approvalStatus models.ApprovalStatus, err error) {
g.logger.Debug("checking if pull request %d is approved", event.PullRequest.Number)
nextPage := 0
for {
opts := github.ListOptions{
Expand All @@ -56,7 +54,7 @@ func (g *GithubPullRequestStatusFetcher) PullIsApproved(event Event) (approvalSt
if nextPage != 0 {
opts.Page = nextPage
}
pageReviews, resp, err := g.client.PullRequests.ListReviews(g.ctx, event.Repository.Owner, event.Repository.Name, event.PullRequest.Number, &opts)
pageReviews, resp, err := g.client.PullRequests.ListReviews(ctx, event.Repository.Owner, event.Repository.Name, event.PullRequest.Number, &opts)
if resp != nil {
g.logger.Debug("GET /repos/%v/%v/pulls/%d/reviews returned: %v", event.Repository.Owner, event.Repository.Name, event.PullRequest.Number, resp.StatusCode)
}
Expand All @@ -82,9 +80,9 @@ func (g *GithubPullRequestStatusFetcher) PullIsApproved(event Event) (approvalSt

// TODO: complete the bypass functionality from atlantis
// TODO: check if approved by codeowner
func (g *GithubPullRequestStatusFetcher) PullIsMergeable(event Event, vcsstatusname string) (bool, error) {
func (g *GithubPullRequestStatusFetcher) PullIsMergeable(ctx context.Context, event Event, vcsstatusname string) (bool, error) {
g.logger.Debug("Checking if GitHub pull request %d is mergeable", event.PullRequest.Number)
githubPR, _, err := g.client.PullRequests.Get(g.ctx, event.Repository.Owner, event.Repository.Name, event.PullRequest.Number)
githubPR, _, err := g.client.PullRequests.Get(ctx, event.Repository.Owner, event.Repository.Name, event.PullRequest.Number)
if err != nil {
return false, errors.Wrap(err, "getting pull request")
}
Expand Down
29 changes: 29 additions & 0 deletions pkg/github/vsc_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package github

import (
"context"

"github.com/corymurphy/argobot/pkg/logging"
"github.com/google/go-github/v53/github"
)

type VscClient struct {
client *github.Client
logger logging.SimpleLogging
}

func (v *VscClient) SetStatusCheck(ctx context.Context, event Event, state CommitState, context string, description string) error {

url := ""
status := &github.RepoStatus{
State: github.String(state.String()),
Description: github.String(description),
Context: github.String(context),
TargetURL: &url,
}
_, resp, err := v.client.Repositories.CreateStatus(ctx, event.Repository.Owner, event.Repository.Name, event.Revision, status)
if resp != nil {
v.logger.Debug("POST /repos/%v/%v/statuses/%s returned: %v", event.Repository.Owner, event.Repository.Name, event.Revision, resp.StatusCode)
}
return err
}
20 changes: 12 additions & 8 deletions pkg/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,14 @@ func Test_HealthCheck(t *testing.T) {
func Test_PRCommentHandler(t *testing.T) {

requests := map[string]bool{
"/app/installations/345345345/access_tokens": false,
"/repos/atlas8518/argocd-data/issues/1/comments": false,
"/repos/atlas8518/argocd-data/pulls/1": false,
"/api/v1/applications/testapp/managed-resources": false,
"/api/v1/applications/testapp": false,
"/app/installations/345345345/access_tokens": false,
"/repos/atlas8518/argocd-data/issues/1/comments": false,
"/repos/atlas8518/argocd-data/pulls/1": false,
"/api/v1/applications/testapp/managed-resources": false,
"/api/v1/applications/testapp": false,
"/repos/atlas8518/argocd-data/issues/comments/456456/reactions": false,
}
mu := sync.Mutex{}

// ctx := context.Background()

mockServer := mockServer(requests, &mu, t)
defer mockServer.Close()

Expand Down Expand Up @@ -240,6 +238,12 @@ func mockServer(requests map[string]bool, mu *sync.Mutex, t *testing.T) *httptes
mu.Unlock()
fmt.Fprint(w, string(response))
})
router.HandleFunc("/repos/atlas8518/argocd-data/issues/comments/456456/reactions", func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
requests["/repos/atlas8518/argocd-data/issues/comments/456456/reactions"] = true
mu.Unlock()
fmt.Fprint(w, string(""))
})
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
t.Error(r)
})
Expand Down
2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.16.4
0.16.5

0 comments on commit 3d87aa0

Please sign in to comment.