From 5296ff61d47abefd04e333b51cf73d47bf19ba74 Mon Sep 17 00:00:00 2001 From: Ynah537 Date: Mon, 18 May 2026 10:21:02 +0800 Subject: [PATCH 1/4] feat: add new slack notif when rerun --- main.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index a934c3e..c48ed18 100644 --- a/main.go +++ b/main.go @@ -91,6 +91,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 +606,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{ @@ -620,6 +655,19 @@ func process(ctx any, data []byte) error { 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 +776,7 @@ func handleScenarioCompletion(ctx any, data []byte) error { } if repslack != "" { + isRerun := msg.TriggerType == "rerun" color := "good" title := "Tests Done." var text string @@ -752,14 +801,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 +825,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) } From 557656bbb3cac3041ad732280a7fc8bbe865d950 Mon Sep 17 00:00:00 2001 From: Ynah537 Date: Mon, 18 May 2026 10:41:54 +0800 Subject: [PATCH 2/4] fix: add rerun row id not passed on scenarios --- scenario.go | 1 + 1 file changed, 1 insertion(+) diff --git a/scenario.go b/scenario.go index 0d1dafd..6294dff 100644 --- a/scenario.go +++ b/scenario.go @@ -458,6 +458,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_row_id", } { if v, ok := in.Metadata[key].(string); ok && v != "" { attr[key] = v From e4fcf76e0e3ee000391e208efdee987118b6c7b5 Mon Sep 17 00:00:00 2001 From: Ynah537 Date: Mon, 18 May 2026 11:00:10 +0800 Subject: [PATCH 3/4] fix: rerun modenot passed on scenario --- main.go | 4 ++-- scenario.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index c48ed18..e0618ef 100644 --- a/main.go +++ b/main.go @@ -810,7 +810,7 @@ func handleScenarioCompletion(ctx any, data []byte) error { sb.WriteString(header) if isRerun && msg.RerunMode == "specific" { scenarioName := filepath.Base(msg.Scenario) - fmt.Fprintf(&sb, "*Scenario:* %s\n*Result:* ❌ Failed", scenarioName) + 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 { @@ -829,7 +829,7 @@ func handleScenarioCompletion(ctx any, data []byte) error { 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) + 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) } diff --git a/scenario.go b/scenario.go index 6294dff..c4b9d3c 100644 --- a/scenario.go +++ b/scenario.go @@ -458,7 +458,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_row_id", + "rerun_row_id", "rerun_mode", } { if v, ok := in.Metadata[key].(string); ok && v != "" { attr[key] = v From a8d272dffc61748c065dd8d32c5e1bbd2300ecd3 Mon Sep 17 00:00:00 2001 From: Ynah537 Date: Thu, 21 May 2026 11:15:25 +0800 Subject: [PATCH 4/4] fix: add groupid --- main.go | 4 ++++ scenario.go | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index e0618ef..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 { @@ -649,6 +652,7 @@ func process(ctx any, data []byte) error { Verbose: verbose, Metadata: c.Metadata, RunID: c.ID, + GroupID: c.GroupID, }) } diff --git a/scenario.go b/scenario.go index c4b9d3c..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,7 +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_row_id", "rerun_mode", + "rerun_mode", } { if v, ok := in.Metadata[key].(string); ok && v != "" { attr[key] = v @@ -484,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)