-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboot_check.go
More file actions
79 lines (70 loc) · 2.96 KB
/
Copy pathboot_check.go
File metadata and controls
79 lines (70 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package nexus
import (
"fmt"
"os"
"sort"
"github.com/paulmanoni/nexus/manifest"
)
// BootCheck is a boot-time self-check: it inspects live app topology after
// wiring and returns any foot-guns as manifest.Issues. It exists to promote
// failures that would otherwise only surface at runtime — e.g. pubsub's "topic
// has no transport bound", which today fails at the first Publish (possibly at
// 2am) — into a loud, early report at boot in dev, so a junior sees the problem
// before deploy. See ERRORS.md.
type BootCheck func() []manifest.Issue
var (
bootChecks []BootCheck
pendingBootIssues []manifest.Issue // config-file lint issues collected during load
)
// RegisterBootCheck adds a boot-time self-check. Call it from an init() in the
// package that owns the invariant — the same pay-for-what-you-use pattern as
// dashboard.RegisterSnapshotExtra: the check only exists if the app imports
// that package, so the framework core stays free of the dependency. Safe to
// call before Run.
func RegisterBootCheck(fn BootCheck) {
if fn != nil {
bootChecks = append(bootChecks, fn)
}
}
// addPendingBootIssues stashes issues discovered before the DI graph exists
// (e.g. the nexus.toml lint in autoLoad) so runBootChecks can report them
// alongside the topology checks in one block.
func addPendingBootIssues(issues []manifest.Issue) {
pendingBootIssues = append(pendingBootIssues, issues...)
}
// collectBootIssues gathers the config-file issues plus every registered
// topology check's findings. Pure and drainable — separated from reporting so
// tests can assert on the issues without capturing stderr.
func collectBootIssues() []manifest.Issue {
issues := append([]manifest.Issue(nil), pendingBootIssues...)
for _, c := range bootChecks {
issues = append(issues, c()...)
}
pendingBootIssues = nil // drain so a second Run in-process doesn't double-report
return issues
}
// runBootChecks collects and reports boot self-check issues. Called from a DI
// invoke that Run appends LAST (so it runs after pubsub's BindTopics and every
// other wiring invoke) and only in dev — so production pays nothing and a clean
// app prints nothing. Advisory: it never aborts boot (genuine fatal misconfig
// already fails elsewhere); the value is EARLY VISIBILITY.
func runBootChecks() { reportBootIssues(collectBootIssues()) }
// reportBootIssues prints issues to stderr, errors first, deep-linkable by path.
func reportBootIssues(issues []manifest.Issue) {
if len(issues) == 0 {
return
}
sort.SliceStable(issues, func(i, j int) bool {
if issues[i].Severity != issues[j].Severity {
return issues[i].Severity == manifest.SeverityError // errors before warnings
}
return issues[i].Path < issues[j].Path
})
fmt.Fprintf(os.Stderr,
"\nnexus: boot self-check found %d issue(s) — dev-only, fix before deploy (see ERRORS.md):\n",
len(issues))
for _, is := range issues {
fmt.Fprintf(os.Stderr, " [%s] %s: %s\n", is.Severity, is.Path, is.Message)
}
fmt.Fprintln(os.Stderr)
}