feat: add lint check to bd preflight --check (bd-lfak.3)
- Add Skipped field to CheckResult for graceful handling of missing tools - Implement runLintCheck() that runs golangci-lint run ./... - Skip lint check gracefully if golangci-lint not in PATH - Update summary to show skipped count separately - Add test for skipped state 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Executed-By: beads/crew/dave Rig: beads Role: crew
This commit is contained in:
committed by
Steve Yegge
parent
64c65974f0
commit
1b432ad9b6
@@ -14,6 +14,7 @@ import (
|
||||
type CheckResult struct {
|
||||
Name string `json:"name"`
|
||||
Passed bool `json:"passed"`
|
||||
Skipped bool `json:"skipped,omitempty"`
|
||||
Output string `json:"output,omitempty"`
|
||||
Command string `json:"command"`
|
||||
}
|
||||
@@ -89,18 +90,29 @@ func runChecks(jsonOutput bool) {
|
||||
testResult := runTestCheck()
|
||||
results = append(results, testResult)
|
||||
|
||||
// Run lint check
|
||||
lintResult := runLintCheck()
|
||||
results = append(results, lintResult)
|
||||
|
||||
// Calculate overall result
|
||||
allPassed := true
|
||||
passCount := 0
|
||||
skipCount := 0
|
||||
for _, r := range results {
|
||||
if r.Passed {
|
||||
if r.Skipped {
|
||||
skipCount++
|
||||
} else if r.Passed {
|
||||
passCount++
|
||||
} else {
|
||||
allPassed = false
|
||||
}
|
||||
}
|
||||
|
||||
summary := fmt.Sprintf("%d/%d checks passed", passCount, len(results))
|
||||
runCount := len(results) - skipCount
|
||||
summary := fmt.Sprintf("%d/%d checks passed", passCount, runCount)
|
||||
if skipCount > 0 {
|
||||
summary += fmt.Sprintf(" (%d skipped)", skipCount)
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
result := PreflightResult{
|
||||
@@ -114,13 +126,18 @@ func runChecks(jsonOutput bool) {
|
||||
} else {
|
||||
// Human-readable output
|
||||
for _, r := range results {
|
||||
if r.Passed {
|
||||
if r.Skipped {
|
||||
fmt.Printf("⚠ %s (skipped)\n", r.Name)
|
||||
} else if r.Passed {
|
||||
fmt.Printf("✓ %s\n", r.Name)
|
||||
} else {
|
||||
fmt.Printf("✗ %s\n", r.Name)
|
||||
}
|
||||
fmt.Printf(" Command: %s\n", r.Command)
|
||||
if !r.Passed && r.Output != "" {
|
||||
if r.Skipped && r.Output != "" {
|
||||
// Show skip reason
|
||||
fmt.Printf(" Reason: %s\n", r.Output)
|
||||
} else if !r.Passed && r.Output != "" {
|
||||
// Truncate output for terminal display
|
||||
output := truncateOutput(r.Output, 500)
|
||||
fmt.Printf(" Output:\n")
|
||||
@@ -152,6 +169,32 @@ func runTestCheck() CheckResult {
|
||||
}
|
||||
}
|
||||
|
||||
// runLintCheck runs golangci-lint and returns the result.
|
||||
func runLintCheck() CheckResult {
|
||||
command := "golangci-lint run ./..."
|
||||
|
||||
// Check if golangci-lint is available
|
||||
if _, err := exec.LookPath("golangci-lint"); err != nil {
|
||||
return CheckResult{
|
||||
Name: "Lint passes",
|
||||
Passed: false,
|
||||
Skipped: true,
|
||||
Output: "golangci-lint not found in PATH",
|
||||
Command: command,
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command("golangci-lint", "run", "./...")
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
return CheckResult{
|
||||
Name: "Lint passes",
|
||||
Passed: err == nil,
|
||||
Output: string(output),
|
||||
Command: command,
|
||||
}
|
||||
}
|
||||
|
||||
// truncateOutput truncates output to maxLen characters, adding ellipsis if truncated.
|
||||
func truncateOutput(s string, maxLen int) string {
|
||||
if len(s) <= maxLen {
|
||||
|
||||
@@ -100,6 +100,32 @@ func TestPreflightResult_SomeFailed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreflightResult_WithSkipped(t *testing.T) {
|
||||
results := PreflightResult{
|
||||
Checks: []CheckResult{
|
||||
{Name: "Tests pass", Passed: true, Command: "go test ./..."},
|
||||
{Name: "Lint passes", Passed: false, Skipped: true, Command: "golangci-lint run", Output: "not installed"},
|
||||
},
|
||||
Passed: true,
|
||||
Summary: "1/1 checks passed (1 skipped)",
|
||||
}
|
||||
|
||||
// Skipped checks don't count as failures
|
||||
if !results.Passed {
|
||||
t.Error("Expected result to pass (skipped doesn't count as failure)")
|
||||
}
|
||||
|
||||
skipCount := 0
|
||||
for _, c := range results.Checks {
|
||||
if c.Skipped {
|
||||
skipCount++
|
||||
}
|
||||
}
|
||||
if skipCount != 1 {
|
||||
t.Errorf("Expected 1 skipped, got %d", skipCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncateOutput(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
Reference in New Issue
Block a user