diff --git a/internal/cli/doctor.go b/internal/cli/doctor.go index 3a4d227..3ad6792 100644 --- a/internal/cli/doctor.go +++ b/internal/cli/doctor.go @@ -9,6 +9,7 @@ import ( "github.com/open-source-cloud/devstack/internal/docker" "github.com/open-source-cloud/devstack/internal/state" + "github.com/open-source-cloud/devstack/internal/trust" "github.com/open-source-cloud/devstack/internal/xdg" ) @@ -134,6 +135,20 @@ func runDoctor(cmd *cobra.Command) []docker.Check { checks = append(checks, docker.Check{Name: "state ledger", Status: docker.StatusOK, Detail: fmt.Sprintf("schema v%d @ %s", v, ctxName)}) } + // Local-CA trust (spec 05) — opt-in, so never fatal: report readiness as a + // warning with the exact remediation when not fully set up. + ts := trust.New().Status(ctx) + if ts.OK() { + checks = append(checks, docker.Check{Name: "trust (mkcert)", Status: docker.StatusOK, Detail: "local CA installed (" + ts.CARoot + ")"}) + } else { + checks = append(checks, docker.Check{ + Name: "trust (mkcert)", + Status: docker.StatusWarn, + Detail: fmt.Sprintf("mkcert=%v CA=%v firefox=%v", ts.MkcertFound, ts.CAInstalled, ts.FirefoxTrust), + Remediation: ts.Remediation, + }) + } + return checks } diff --git a/internal/cli/gc_test.go b/internal/cli/gc_test.go index b98e662..ca7c4c4 100644 --- a/internal/cli/gc_test.go +++ b/internal/cli/gc_test.go @@ -45,3 +45,17 @@ func TestDoctorRebuildStateFlag(t *testing.T) { t.Error("doctor is missing the --rebuild-state flag") } } + +func TestDoctorIncludesTrustProbe(t *testing.T) { + var out strings.Builder + root := NewRootCmd(Options{}) + root.SetArgs([]string{"doctor", "--json"}) + root.SetOut(&out) + root.SetErr(&out) + // doctor exits non-zero only on a hard FAIL; the trust probe is a warning, so + // in CI (no mkcert) doctor still succeeds and the JSON lists the probe. + _ = root.Execute() + if !strings.Contains(out.String(), `"trust (mkcert)"`) { + t.Errorf("doctor --json missing the trust probe:\n%s", out.String()) + } +}