diff --git a/main.go b/main.go index a934c3e..7b1dfd9 100644 --- a/main.go +++ b/main.go @@ -82,6 +82,9 @@ type cmd struct { // Metadata for cancellation requests Metadata map[string]interface{} `json:"metadata,omitempty"` + + // Links the original run and all its reruns together. + GroupID string `json:"group_id,omitempty"` } type ScenarioProgressMessage struct { @@ -91,6 +94,8 @@ type ScenarioProgressMessage struct { Data string `json:"data"` TotalScenarios string `json:"total_scenarios"` Code string `json:"code"` + TriggerType string `json:"trigger_type,omitempty"` + RerunMode string `json:"rerun_mode,omitempty"` OverallStatus string `json:"overall_status,omitempty"` FailedCount int64 `json:"failed_count,omitempty"` FailedScenarios []string `json:"failed_scenarios,omitempty"` @@ -604,6 +609,39 @@ func process(ctx any, data []byte) error { log.Printf("Notify (slack) failed: %v", err) } } + case "rerun_started": + mode, _ := c.Metadata["rerun_mode"].(string) + rerunTotal, _ := c.Metadata["rerun_total"].(string) + repository, _ := c.Metadata["repository"].(string) + runURL, _ := c.Metadata["run_url"].(string) + + log.Printf("rerun started: run_id=%s mode=%s rerun_total=%s repo=%s", c.ID, mode, rerunTotal, repository) + + if repslack != "" { + modeLabel := rerunModeLabel(mode) + text := fmt.Sprintf("*Run ID:* `%s`\n*Scenarios queued:* %s", c.ID, rerunTotal) + if repository != "" { + text += fmt.Sprintf("\n*Repository:* %s", repository) + } + if runURL != "" { + text += fmt.Sprintf("\n\n<%s|View run>", runURL) + } + payload := SlackMessage{ + Attachments: []SlackAttachment{ + { + Color: "#439FE0", + Title: fmt.Sprintf("Rerun Started — %s", modeLabel), + Text: text, + Footer: fmt.Sprintf("oops • rerun • runid: %s", c.ID), + Timestamp: time.Now().Unix(), + MrkdwnIn: []string{"text"}, + }, + }, + } + if err := payload.Notify(repslack); err != nil { + log.Printf("Notify (slack) rerun_started failed: %v", err) + } + } case "process": log.Printf("process: %+v", c) doScenario(&doScenarioInput{ @@ -614,12 +652,26 @@ func process(ctx any, data []byte) error { Verbose: verbose, Metadata: c.Metadata, RunID: c.ID, + GroupID: c.GroupID, }) } return nil } +func rerunModeLabel(mode string) string { + switch mode { + case "all": + return "All Scenarios" + case "failed": + return "Failed Scenarios" + case "specific": + return "Specific Scenario" + default: + return "" + } +} + func handleScenarioCompletion(ctx any, data []byte) error { var msg ScenarioProgressMessage if err := json.Unmarshal(data, &msg); err != nil { @@ -728,6 +780,7 @@ func handleScenarioCompletion(ctx any, data []byte) error { } if repslack != "" { + isRerun := msg.TriggerType == "rerun" color := "good" title := "Tests Done." var text string @@ -752,14 +805,23 @@ func handleScenarioCompletion(ctx any, data []byte) error { if msg.OverallStatus == "failure" || msg.FailedCount > 0 { color = "danger" - title = "Test Run Complete (With Failures)" + if isRerun { + title = fmt.Sprintf("Rerun Complete (With Failures) — %s", rerunModeLabel(msg.RerunMode)) + } else { + title = "Test Run Complete (With Failures)" + } var sb strings.Builder sb.WriteString(header) - fmt.Fprintf(&sb, "*Run Summary*\nTotal: %s\nPassed: %d\nFailed: %d", total, successCount, msg.FailedCount) - if len(msg.FailedScenarios) > 0 { - sb.WriteString("\n\n*Failed scenarios:*") - for _, name := range msg.FailedScenarios { - fmt.Fprintf(&sb, "\n• %v", name) + if isRerun && msg.RerunMode == "specific" { + scenarioName := filepath.Base(msg.Scenario) + fmt.Fprintf(&sb, "*Scenario:* %s\n*Result:* Failed", scenarioName) + } else { + fmt.Fprintf(&sb, "*Run Summary*\nTotal: %s\nPassed: %d\nFailed: %d", total, successCount, msg.FailedCount) + if len(msg.FailedScenarios) > 0 { + sb.WriteString("\n\n*Failed scenarios:*") + for _, name := range msg.FailedScenarios { + fmt.Fprintf(&sb, "\n• %v", name) + } } } if msg.RunURL != "" { @@ -767,8 +829,18 @@ func handleScenarioCompletion(ctx any, data []byte) error { } text = sb.String() } else { - title = "Test Run Complete" - text = header + fmt.Sprintf("*Run Summary*\nTotal: %s\nPassed: %s\nFailed: 0", total, total) + if isRerun { + title = fmt.Sprintf("Rerun Complete — %s", rerunModeLabel(msg.RerunMode)) + if msg.RerunMode == "specific" { + scenarioName := filepath.Base(msg.Scenario) + text = header + fmt.Sprintf("*Scenario:* %s\n*Result:* Passed", scenarioName) + } else { + text = header + fmt.Sprintf("*Run Summary*\nTotal: %s\nPassed: %s\nFailed: 0", total, total) + } + } else { + title = "Test Run Complete" + text = header + fmt.Sprintf("*Run Summary*\nTotal: %s\nPassed: %s\nFailed: 0", total, total) + } if msg.RunURL != "" { text += fmt.Sprintf("\n\n<%s|View run>", msg.RunURL) } diff --git a/scenario.go b/scenario.go index 0d1dafd..bf00aad 100644 --- a/scenario.go +++ b/scenario.go @@ -50,6 +50,7 @@ type ReportPubsub struct { Data string `json:"data"` MessageID string `json:"message_id"` // Unique oops-generated tracking ID RunID string `json:"run_id"` // Batch run ID from the initiating workflow + GroupID string `json:"group_id"` // Links original run + all reruns together } // Scenario represents a single scenario file to run. @@ -174,6 +175,7 @@ type doScenarioInput struct { Verbose bool Metadata map[string]interface{} RunID string + GroupID string OnScenarioDone func(scenario, status string) } @@ -194,6 +196,7 @@ func publishCancelledReport(in *doScenarioInput, scenarioFile string, startedAt for _, key := range []string{ "pr_number", "branch", "commit_sha", "actor", "trigger_type", "run_url", "repository", "workflow", "total_scenarios", + "rerun_mode", } { if v, ok := in.Metadata[key].(string); ok && v != "" { attr[key] = v @@ -215,6 +218,7 @@ func publishCancelledReport(in *doScenarioInput, scenarioFile string, startedAt MessageID: uuid.NewString(), RunID: in.RunID, Attributes: attr, + GroupID: in.GroupID, } if err := in.app.rpub.Publish(r.MessageID, r); err != nil { @@ -458,6 +462,7 @@ func doScenario(in *doScenarioInput) error { for _, key := range []string{ "pr_number", "branch", "commit_sha", "actor", "trigger_type", "run_url", "repository", "workflow", "total_scenarios", + "rerun_mode", } { if v, ok := in.Metadata[key].(string); ok && v != "" { attr[key] = v @@ -483,6 +488,7 @@ func doScenario(in *doScenarioInput) error { Data: data, MessageID: uuid.NewString(), RunID: in.RunID, + GroupID: in.GroupID, } err := in.app.rpub.Publish(r.MessageID, r)