Files
bootstrap_dev_env/issues.go
T
JMR-devandCopilot de7f71fea6 Suppress intentional errLog output during tests
Tests that exercise error paths in errLog/warn were causing
GitHub Actions to annotate test output with 'Error: R] ...'
(GHA eats '[ERRO' from '[ERROR]' lines and surfaces the rest as
workflow errors), making real failures hard to distinguish from
expected behavior.

- issues.go: route logIssue output through issueLogWriter (io.Writer,
  defaults to os.Stdout). errorCount/issues/notices recording is
  unchanged so tests still assert via errorCount.
- testmain_test.go: TestMain sets issueLogWriter = io.Discard for
  the whole test binary.
- issues_test.go: TestIssuesLogging temporarily redirects
  issueLogWriter to a bytes.Buffer (replacing os.Pipe stdout
  capture).

go test -v ./... now contains zero stray '[ERROR]' lines; real
test failures remain visible and 'go test' still exits non-zero
on failure, failing the workflow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-26 14:55:51 -05:00

89 lines
1.9 KiB
Go

package main
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
// Issue log accumulated over the run; written to bootstrap_run.log at the end
// when there's something to report.
var (
issuesMu sync.Mutex
issues []string
notices []string
errorCount int
// issueLogWriter is the destination for human-facing issue log lines.
// Overridden during tests to suppress intentional error-path output.
issueLogWriter io.Writer = os.Stdout
)
func logIssue(level, msg string) {
issuesMu.Lock()
defer issuesMu.Unlock()
fmt.Fprintf(issueLogWriter, " [%s] %s\n", level, msg)
issues = append(issues, fmt.Sprintf("[%s] %s", level, msg))
if level == "ERROR" {
errorCount++
}
}
func warn(msg string) { logIssue("WARN", msg) }
func errLog(msg string) { logIssue("ERROR", msg) }
func hasErrors() bool {
issuesMu.Lock()
defer issuesMu.Unlock()
return errorCount > 0
}
func notice(msg string) {
issuesMu.Lock()
defer issuesMu.Unlock()
notices = append(notices, msg)
}
func runLogPath() string {
exe, err := os.Executable()
if err != nil {
return "bootstrap_run.log"
}
return filepath.Join(filepath.Dir(exe), "bootstrap_run.log")
}
func writeRunLog() {
issuesMu.Lock()
defer issuesMu.Unlock()
if len(issues) == 0 {
fmt.Println("\nNo issues — log file not written.")
return
}
path := runLogPath()
ts := time.Now().Format("2006-01-02 15:04:05")
lines := []string{fmt.Sprintf("# Bootstrap run — %s", ts), ""}
lines = append(lines, issues...)
content := strings.Join(lines, "\n") + "\n"
if err := osWriteFile(path, []byte(content), 0o644); err != nil {
fmt.Fprintf(os.Stderr, "failed to write run log: %v\n", err)
return
}
fmt.Printf("\n%d issue(s) logged to: %s\n", len(issues), path)
}
func printNotices() {
issuesMu.Lock()
defer issuesMu.Unlock()
if len(notices) == 0 {
return
}
fmt.Println("\nNotices:")
for _, n := range notices {
fmt.Printf(" • %s\n", n)
}
}