package supervisor import ( "fmt" "os/exec" "regexp" "strings" "time" ) // CheckResult holds the result of a single verification check. type CheckResult struct { Name string // "build", "test", "vet" Status string // "pass", "fail" Output string // stdout + stderr Duration time.Duration TestCount int // only for test (parsed from output) } // VerifyResult holds the combined result of all verification checks. type VerifyResult struct { Build CheckResult Vet CheckResult Test CheckResult AllPassed bool } // Verify runs build, vet, and test checks on the given Go project path. // If build fails, vet and test are skipped. func Verify(projectPath string) VerifyResult { var result VerifyResult result.Build = runCheck("build", "go build ./...", projectPath) if result.Build.Status == "fail" { result.Vet = CheckResult{Name: "vet", Status: "fail", Output: "skipped: build failed"} result.Test = CheckResult{Name: "test", Status: "fail", Output: "skipped: build failed"} return result } result.Vet = runCheck("vet", "go vet ./...", projectPath) result.Test = runCheck("test", "go test ./... -count=1 -v", projectPath) result.Test.TestCount = parseTestCount(result.Test.Output) result.AllPassed = result.Build.Status == "pass" && result.Vet.Status == "pass" && result.Test.Status == "pass" return result } // runCheck executes a single shell command in the given directory and returns // a CheckResult with the output, status, and duration. func runCheck(name, command, projectPath string) CheckResult { start := time.Now() parts := strings.Fields(command) cmd := exec.Command(parts[0], parts[1:]...) cmd.Dir = projectPath output, err := cmd.CombinedOutput() duration := time.Since(start) status := "pass" if err != nil { status = "fail" } return CheckResult{ Name: name, Status: status, Output: string(output), Duration: duration, } } // testCountRegex matches lines like: // // ok github.com/dal/kaos/internal/config 0.003s // ok github.com/dal/kaos/internal/supervisor 0.008s var testCountRegex = regexp.MustCompile(`(?m)^---\s+(PASS|FAIL):\s+`) // passLineRegex matches "ok" lines from go test output. var passLineRegex = regexp.MustCompile(`(?m)^ok\s+\S+\s+[\d.]+s`) // parseTestCount counts the number of individual test results in go test -v output. // It counts lines matching "--- PASS:" or "--- FAIL:". func parseTestCount(output string) int { matches := testCountRegex.FindAllString(output, -1) return len(matches) } // FormatResult returns a human-readable summary of the verification. func FormatResult(r VerifyResult) string { var b strings.Builder for _, c := range []CheckResult{r.Build, r.Vet, r.Test} { icon := "✅" if c.Status == "fail" { icon = "❌" } line := fmt.Sprintf("%s %s: %s (%s)", icon, c.Name, c.Status, c.Duration.Round(time.Millisecond)) if c.Name == "test" && c.TestCount > 0 { line += fmt.Sprintf(" [%d tests]", c.TestCount) } b.WriteString(line + "\n") } if r.AllPassed { b.WriteString("\nAll checks passed.") } else { b.WriteString("\nSome checks failed.") } return b.String() }