feat(cli): add core go cov command
- Add `core go cov` for coverage reports - Generate HTML report with --html - Open in browser with --open - Fail on threshold with --threshold 80 - Colour-coded coverage output - Update SKILL.md documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5b0a0eac7c
commit
a7ee58d29e
2 changed files with 151 additions and 5 deletions
|
|
@ -14,7 +14,7 @@ The `core` command provides a unified interface for Go/Wails development, multi-
|
||||||
| Task | Command | Notes |
|
| Task | Command | Notes |
|
||||||
|------|---------|-------|
|
|------|---------|-------|
|
||||||
| Run Go tests | `core go test` | Sets macOS deployment target, filters warnings |
|
| Run Go tests | `core go test` | Sets macOS deployment target, filters warnings |
|
||||||
| Run Go tests with coverage | `core go test --coverage` | Per-package breakdown |
|
| Run Go tests with coverage | `core go cov` | HTML report, thresholds |
|
||||||
| Format Go code | `core go fmt --fix` | Uses goimports/gofmt |
|
| Format Go code | `core go fmt --fix` | Uses goimports/gofmt |
|
||||||
| Lint Go code | `core go lint` | Uses golangci-lint |
|
| Lint Go code | `core go lint` | Uses golangci-lint |
|
||||||
| Tidy Go modules | `core go mod tidy` | go mod tidy wrapper |
|
| Tidy Go modules | `core go mod tidy` | go mod tidy wrapper |
|
||||||
|
|
@ -233,8 +233,8 @@ core pkg outdated
|
||||||
|
|
||||||
| Task | Command | Notes |
|
| Task | Command | Notes |
|
||||||
|------|---------|-------|
|
|------|---------|-------|
|
||||||
| Run tests | `core go test` | CGO_ENABLED=0, filters warnings |
|
| Run tests | `core go test` | Filters warnings, colour output |
|
||||||
| Run tests with coverage | `core go test --coverage` | Per-package breakdown |
|
| Coverage report | `core go cov` | HTML report, thresholds |
|
||||||
| Format code | `core go fmt --fix` | Uses goimports if available |
|
| Format code | `core go fmt --fix` | Uses goimports if available |
|
||||||
| Lint code | `core go lint` | Uses golangci-lint |
|
| Lint code | `core go lint` | Uses golangci-lint |
|
||||||
| Install binary | `core go install` | Auto-detects cmd/, --no-cgo option |
|
| Install binary | `core go install` | Auto-detects cmd/, --no-cgo option |
|
||||||
|
|
@ -257,6 +257,25 @@ core go install --no-cgo
|
||||||
core go install -v
|
core go install -v
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Coverage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests with coverage summary
|
||||||
|
core go cov
|
||||||
|
|
||||||
|
# Generate HTML report
|
||||||
|
core go cov --html
|
||||||
|
|
||||||
|
# Generate and open in browser
|
||||||
|
core go cov --open
|
||||||
|
|
||||||
|
# Fail if coverage below threshold
|
||||||
|
core go cov --threshold 80
|
||||||
|
|
||||||
|
# Specific package
|
||||||
|
core go cov --pkg ./pkg/release
|
||||||
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,16 @@ func AddGoCommands(parent *clir.Cli) {
|
||||||
goCmd := parent.NewSubCommand("go", "Go development tools")
|
goCmd := parent.NewSubCommand("go", "Go development tools")
|
||||||
goCmd.LongDescription("Go development tools with enhanced output and environment setup.\n\n" +
|
goCmd.LongDescription("Go development tools with enhanced output and environment setup.\n\n" +
|
||||||
"Commands:\n" +
|
"Commands:\n" +
|
||||||
" test Run tests with coverage\n" +
|
" test Run tests\n" +
|
||||||
|
" cov Run tests with coverage report\n" +
|
||||||
" fmt Format Go code\n" +
|
" fmt Format Go code\n" +
|
||||||
" lint Run golangci-lint\n" +
|
" lint Run golangci-lint\n" +
|
||||||
" install Install Go binary (CGO_ENABLED=0)\n" +
|
" install Install Go binary\n" +
|
||||||
" mod Module management (tidy, download, verify)\n" +
|
" mod Module management (tidy, download, verify)\n" +
|
||||||
" work Workspace management")
|
" work Workspace management")
|
||||||
|
|
||||||
addGoTestCommand(goCmd)
|
addGoTestCommand(goCmd)
|
||||||
|
addGoCovCommand(goCmd)
|
||||||
addGoFmtCommand(goCmd)
|
addGoFmtCommand(goCmd)
|
||||||
addGoLintCommand(goCmd)
|
addGoLintCommand(goCmd)
|
||||||
addGoInstallCommand(goCmd)
|
addGoInstallCommand(goCmd)
|
||||||
|
|
@ -186,6 +188,131 @@ func parseOverallCoverage(output string) float64 {
|
||||||
return total / float64(len(matches))
|
return total / float64(len(matches))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addGoCovCommand(parent *clir.Command) {
|
||||||
|
var (
|
||||||
|
pkg string
|
||||||
|
html bool
|
||||||
|
open bool
|
||||||
|
threshold float64
|
||||||
|
)
|
||||||
|
|
||||||
|
covCmd := parent.NewSubCommand("cov", "Run tests with coverage report")
|
||||||
|
covCmd.LongDescription("Run tests and generate coverage report.\n\n" +
|
||||||
|
"Examples:\n" +
|
||||||
|
" core go cov # Run with coverage summary\n" +
|
||||||
|
" core go cov --html # Generate HTML report\n" +
|
||||||
|
" core go cov --open # Generate and open HTML report\n" +
|
||||||
|
" core go cov --threshold 80 # Fail if coverage < 80%")
|
||||||
|
|
||||||
|
covCmd.StringFlag("pkg", "Package to test (default: ./...)", &pkg)
|
||||||
|
covCmd.BoolFlag("html", "Generate HTML coverage report", &html)
|
||||||
|
covCmd.BoolFlag("open", "Generate and open HTML report in browser", &open)
|
||||||
|
covCmd.Float64Flag("threshold", "Minimum coverage percentage (exit 1 if below)", &threshold)
|
||||||
|
|
||||||
|
covCmd.Action(func() error {
|
||||||
|
if pkg == "" {
|
||||||
|
pkg = "./..."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create temp file for coverage data
|
||||||
|
covFile, err := os.CreateTemp("", "coverage-*.out")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create coverage file: %w", err)
|
||||||
|
}
|
||||||
|
covPath := covFile.Name()
|
||||||
|
covFile.Close()
|
||||||
|
defer os.Remove(covPath)
|
||||||
|
|
||||||
|
fmt.Printf("%s Running tests with coverage\n", dimStyle.Render("Coverage:"))
|
||||||
|
fmt.Printf(" %s %s\n", dimStyle.Render("Package:"), pkg)
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
// Run tests with coverage
|
||||||
|
args := []string{"test", "-coverprofile=" + covPath, "-covermode=atomic", pkg}
|
||||||
|
cmd := exec.Command("go", args...)
|
||||||
|
cmd.Env = append(os.Environ(), "MACOSX_DEPLOYMENT_TARGET=26.0")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
testErr := cmd.Run()
|
||||||
|
|
||||||
|
// Get coverage percentage
|
||||||
|
covCmd := exec.Command("go", "tool", "cover", "-func="+covPath)
|
||||||
|
covOutput, err := covCmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
if testErr != nil {
|
||||||
|
return testErr
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to get coverage: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse total coverage from last line
|
||||||
|
lines := strings.Split(strings.TrimSpace(string(covOutput)), "\n")
|
||||||
|
var totalCov float64
|
||||||
|
if len(lines) > 0 {
|
||||||
|
lastLine := lines[len(lines)-1]
|
||||||
|
// Format: "total: (statements) XX.X%"
|
||||||
|
if strings.Contains(lastLine, "total:") {
|
||||||
|
parts := strings.Fields(lastLine)
|
||||||
|
if len(parts) >= 3 {
|
||||||
|
covStr := strings.TrimSuffix(parts[len(parts)-1], "%")
|
||||||
|
fmt.Sscanf(covStr, "%f", &totalCov)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print coverage summary
|
||||||
|
fmt.Println()
|
||||||
|
covStyle := successStyle
|
||||||
|
if totalCov < 50 {
|
||||||
|
covStyle = errorStyle
|
||||||
|
} else if totalCov < 80 {
|
||||||
|
covStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#f59e0b"))
|
||||||
|
}
|
||||||
|
fmt.Printf(" %s %s\n", dimStyle.Render("Total:"), covStyle.Render(fmt.Sprintf("%.1f%%", totalCov)))
|
||||||
|
|
||||||
|
// Generate HTML if requested
|
||||||
|
if html || open {
|
||||||
|
htmlPath := "coverage.html"
|
||||||
|
htmlCmd := exec.Command("go", "tool", "cover", "-html="+covPath, "-o="+htmlPath)
|
||||||
|
if err := htmlCmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate HTML: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf(" %s %s\n", dimStyle.Render("HTML:"), htmlPath)
|
||||||
|
|
||||||
|
if open {
|
||||||
|
// Open in browser
|
||||||
|
var openCmd *exec.Cmd
|
||||||
|
switch {
|
||||||
|
case exec.Command("which", "open").Run() == nil:
|
||||||
|
openCmd = exec.Command("open", htmlPath)
|
||||||
|
case exec.Command("which", "xdg-open").Run() == nil:
|
||||||
|
openCmd = exec.Command("xdg-open", htmlPath)
|
||||||
|
default:
|
||||||
|
fmt.Printf(" %s\n", dimStyle.Render("(open manually)"))
|
||||||
|
}
|
||||||
|
if openCmd != nil {
|
||||||
|
openCmd.Run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check threshold
|
||||||
|
if threshold > 0 && totalCov < threshold {
|
||||||
|
fmt.Printf("\n%s Coverage %.1f%% is below threshold %.1f%%\n",
|
||||||
|
errorStyle.Render("FAIL"), totalCov, threshold)
|
||||||
|
return fmt.Errorf("coverage below threshold")
|
||||||
|
}
|
||||||
|
|
||||||
|
if testErr != nil {
|
||||||
|
return testErr
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n%s\n", successStyle.Render("OK"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func addGoFmtCommand(parent *clir.Command) {
|
func addGoFmtCommand(parent *clir.Command) {
|
||||||
var (
|
var (
|
||||||
fix bool
|
fix bool
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue