Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions internal/handlers/issue_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,23 @@ func (h *PRCommentHandler) Handle(ctx context.Context, eventType, deliveryID str

err = processor.processWorkflowsForTrigger(ctx, submatch, prNumber, contextRef, headSHA, baseSHA, workflowsToTrigger, dependsOn, commenter)
if err != nil {
comment := fmt.Sprintf("Failed to process workflows for trigger: %v", err)
logger.Error().Err(err).Msg(comment)
var comment string
err, ok := err.(TriggerSkippedDependencyInProgressError)
if ok {
comment = err.Error()
logger.Debug().Err(err).Msg(comment)
commentErr := commenter.reactToComment(ctx, commentID, "+1")
if commentErr == nil {
logger.Error().Err(err).Msg("Failed to react to comment with thumbs up emoji")
}
} else {
comment = fmt.Sprintf("Failed to process workflows for trigger: %v", err)
logger.Error().Err(err).Msg(comment)
commentErr := commenter.reactToComment(ctx, commentID, "confused")
if commentErr == nil {
logger.Error().Err(err).Msg("Failed to react to comment with confused emoji")
}
}
if arianeConfig.GetVerbose() {
_ = commenter.commentOnPullRequest(ctx, prNumber, comment)
}
Expand Down
194 changes: 180 additions & 14 deletions internal/handlers/issue_comment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
Expand Down Expand Up @@ -971,7 +972,7 @@ func TestHandle_FeedbackDisabled(t *testing.T) {
mockCtrl := gomock.NewController(t)
mockClientCreator := NewMockClientCreator(mockCtrl)

server := setMockServerWithFeedbackConfig(false, false)
server := setMockServerWithFeedbackConfig(false, false, &[]string{}, false, false)
defer server.Close()

client := github.NewClient(nil)
Expand Down Expand Up @@ -1027,7 +1028,7 @@ func TestHandle_VerboseEnabled(t *testing.T) {
mockCtrl := gomock.NewController(t)
mockClientCreator := NewMockClientCreator(mockCtrl)

server := setMockServerWithFeedbackConfig(true, false)
server := setMockServerWithFeedbackConfig(true, false, &[]string{}, false, false)
defer server.Close()

client := github.NewClient(nil)
Expand Down Expand Up @@ -1082,7 +1083,8 @@ func TestHandle_WorkflowsReportEnabled(t *testing.T) {
mockCtrl := gomock.NewController(t)
mockClientCreator := NewMockClientCreator(mockCtrl)

server := setMockServerWithFeedbackConfig(true, true)
reactions := make([]string, 0, 10)
server := setMockServerWithFeedbackConfig(true, true, &reactions, false, false)
defer server.Close()

client := github.NewClient(nil)
Expand Down Expand Up @@ -1126,6 +1128,8 @@ func TestHandle_WorkflowsReportEnabled(t *testing.T) {

err := handler.Handle(ctx, "issue_comment", "1", payload)
assert.NoError(t, err)

assert.EqualValues(t, []string{"eyes", "rocket"}, reactions)
}

func TestHandle_WorkflowsReportDisabled(t *testing.T) {
Expand All @@ -1137,7 +1141,7 @@ func TestHandle_WorkflowsReportDisabled(t *testing.T) {
mockCtrl := gomock.NewController(t)
mockClientCreator := NewMockClientCreator(mockCtrl)

server := setMockServerWithFeedbackConfig(true, false)
server := setMockServerWithFeedbackConfig(true, false, &[]string{}, false, false)
defer server.Close()

client := github.NewClient(nil)
Expand Down Expand Up @@ -1183,7 +1187,111 @@ func TestHandle_WorkflowsReportDisabled(t *testing.T) {
assert.NoError(t, err)
}

func setMockServerWithFeedbackConfig(verbose bool, workflowsReport bool) *httptest.Server {
func TestHandle_WorkflowsDependencyRunningReaction(t *testing.T) {
mockCtrl := gomock.NewController(t)
mockClientCreator := NewMockClientCreator(mockCtrl)

reactions := make([]string, 0, 10)
server := setMockServerWithFeedbackConfig(false, false, &reactions, true, true)
defer server.Close()

client := github.NewClient(nil)
client.BaseURL, _ = url.Parse(server.URL + "/")
mockClientCreator.EXPECT().NewInstallationClient(int64(0)).Return(client, nil)

handler := &PRCommentHandler{
ClientCreator: mockClientCreator,
RunDelay: time.Second,
MaxRetryAttempts: config.DefaultMaxRetryAttempts,
}

payload := []byte(`{
"issue": {
"pull_request": {},
"number": 0
},
"action": "created",
"comment": {
"id": 1,
"body": "/test",
"user": {
"login": "user"
}
},
"repository": {
"owner": {
"login": "owner"
},
"name": "repo"
},
"installation": {
"id": 0
}
}`)

var event github.IssueCommentEvent
_ = json.Unmarshal(payload, &event)

ctx := context.Background()

err := handler.Handle(ctx, "issue_comment", "1", payload)
assert.Error(t, err)
assert.EqualValues(t, []string{"eyes", "+1"}, reactions)
}

func TestHandle_WorkflowsDependencyFailedReaction(t *testing.T) {
mockCtrl := gomock.NewController(t)
mockClientCreator := NewMockClientCreator(mockCtrl)

reactions := make([]string, 0, 10)
server := setMockServerWithFeedbackConfig(false, false, &reactions, true, false)
defer server.Close()

client := github.NewClient(nil)
client.BaseURL, _ = url.Parse(server.URL + "/")
mockClientCreator.EXPECT().NewInstallationClient(int64(0)).Return(client, nil)

handler := &PRCommentHandler{
ClientCreator: mockClientCreator,
RunDelay: time.Second,
MaxRetryAttempts: config.DefaultMaxRetryAttempts,
}

payload := []byte(`{
"issue": {
"pull_request": {},
"number": 0
},
"action": "created",
"comment": {
"id": 1,
"body": "/test",
"user": {
"login": "user"
}
},
"repository": {
"owner": {
"login": "owner"
},
"name": "repo"
},
"installation": {
"id": 0
}
}`)

var event github.IssueCommentEvent
_ = json.Unmarshal(payload, &event)

ctx := context.Background()

err := handler.Handle(ctx, "issue_comment", "1", payload)
assert.Error(t, err)
assert.EqualValues(t, []string{"eyes", "confused"}, reactions)
}

func setMockServerWithFeedbackConfig(verbose bool, workflowsReport bool, reactions *[]string, dependencyTest bool, dependencyRunning bool) *httptest.Server {
mux := http.NewServeMux()

// Mock individual PR endpoint
Expand Down Expand Up @@ -1216,9 +1324,56 @@ func setMockServerWithFeedbackConfig(verbose bool, workflowsReport bool) *httpte
_ = json.NewEncoder(w).Encode([]*github.PullRequest{&pr})
})

// Mock config file endpoint with feedback settings
mux.HandleFunc("/repos/owner/repo/contents/.github/ariane-config.yaml", func(w http.ResponseWriter, r *http.Request) {
configContent := fmt.Sprintf(`feedback:
if dependencyTest {
// Mock config file endpoint with feedback settings
mux.HandleFunc("/repos/owner/repo/contents/.github/ariane-config.yaml", func(w http.ResponseWriter, r *http.Request) {
configContent := `
triggers:
/test:
workflows:
- foo.yaml
depends-on:
- /dependency
/dependency:
workflows:
- bar.yaml
workflows:
foo.yaml:
paths-regex: ".*"
`

content := github.RepositoryContent{
Content: github.Ptr(configContent),
Encoding: github.Ptr(""),
}
_ = json.NewEncoder(w).Encode(&content)
})
mux.HandleFunc("/repos/owner/repo/actions/workflows/bar.yaml/runs", func(w http.ResponseWriter, r *http.Request) {
var conclusion string
var status string
if dependencyRunning {
status = "in_progress"
conclusion = ""
} else {
status = "completed"
conclusion = "failure"
}
workflowRuns := github.WorkflowRuns{
WorkflowRuns: []*github.WorkflowRun{
{
ID: github.Ptr(int64(2)),
Status: github.Ptr(status),
Conclusion: github.Ptr(conclusion),
},
},
TotalCount: github.Ptr(1),
}
_ = json.NewEncoder(w).Encode(&workflowRuns)
})
} else {
// Mock config file endpoint with feedback settings
mux.HandleFunc("/repos/owner/repo/contents/.github/ariane-config.yaml", func(w http.ResponseWriter, r *http.Request) {
configContent := fmt.Sprintf(`feedback:
verbose: %t
workflows-report: %t
triggers:
Expand All @@ -1230,12 +1385,13 @@ workflows:
paths-regex: ".*"
`, verbose, workflowsReport)

content := github.RepositoryContent{
Content: github.Ptr(configContent),
Encoding: github.Ptr(""),
}
_ = json.NewEncoder(w).Encode(&content)
})
content := github.RepositoryContent{
Content: github.Ptr(configContent),
Encoding: github.Ptr(""),
}
_ = json.NewEncoder(w).Encode(&content)
})
}

// Mock PR files endpoint
mux.HandleFunc("/repos/owner/repo/pulls/0/files", func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -1277,6 +1433,16 @@ workflows:

// Mock reactions endpoint
mux.HandleFunc("/repos/owner/repo/issues/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) {
bytes, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "setMockServerWithFeedbackConfig: could not read request body", http.StatusInternalServerError)
return
}

incomingReaction := github.Reaction{}
_ = json.Unmarshal(bytes, &incomingReaction)
*reactions = append(*reactions, incomingReaction.GetContent())

reaction := github.Reaction{
ID: github.Ptr(int64(1)),
}
Expand Down
13 changes: 11 additions & 2 deletions internal/handlers/workflow_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ func (w *WorkflowProcessor) getPRFiles(ctx context.Context, prNumber int) ([]*gi
return files, nil
}

type TriggerSkippedDependencyInProgressError string

func (e TriggerSkippedDependencyInProgressError) Error() string {
return string(e)
}

func (w *WorkflowProcessor) processWorkflowsForTrigger(ctx context.Context, submatch []string, prNumber int, contextRef, headSHA, baseSHA string, workflowsToTrigger, dependsOn []string, commenter *GithubCommenter) error {
w.logger.Debug().Msgf("Found trigger phrase: %q", submatch)

Expand All @@ -227,11 +233,14 @@ func (w *WorkflowProcessor) processWorkflowsForTrigger(ctx context.Context, subm
if !canProceed {
var comment string
if inProgress {
comment = fmt.Sprintf("Skipping trigger: dependency %q is still in progress", dep)
comment = fmt.Sprintf("Skipping trigger: dependency %q is still in progress. Trigger will be retriggered after dependency completes.", dep)
w.logger.Info().Msg(comment)
err := TriggerSkippedDependencyInProgressError(comment)
return err
} else {
comment = fmt.Sprintf("Skipping trigger: dependency %q has not completed successfully", dep)
w.logger.Info().Msg(comment)
}
w.logger.Info().Msg(comment)

return errors.New(comment)
}
Expand Down