feat(doctor): initial doctor command

This commit is contained in:
Lewis Wynne 2026-02-11 20:10:35 +00:00
parent ce7336324f
commit 0c5b73154d
4 changed files with 138 additions and 0 deletions

128
cmd/doctor.go Normal file
View file

@ -0,0 +1,128 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/cobra"
)
var doctorCmd = &cobra.Command{
Use: "doctor",
Short: "Check environment health",
RunE: doctor,
SilenceUsage: true,
}
func init() {
rootCmd.AddCommand(doctorCmd)
}
func doctor(cmd *cobra.Command, args []string) error {
tty := stdoutIsTerminal()
hasError := false
emit := func(level, msg string) {
var code string
switch level {
case "ok":
code = "32"
case "info":
code = "34"
case "FAIL":
code = "31"
hasError = true
}
fmt.Fprintf(os.Stdout, "%s %s\n", keyword(code, level, tty), msg)
}
// Config
cfgPath, err := configPath()
if err != nil {
emit("FAIL", fmt.Sprintf("Cannot determine config path: %v", err))
} else if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
emit("info", "Using default configuration")
} else if err != nil {
emit("FAIL", fmt.Sprintf("Cannot access config: %v", err))
} else {
emit("ok", "Configuration loaded")
}
// Data directory
store := &Store{}
dataDir, err := store.path()
if err != nil {
emit("FAIL", fmt.Sprintf("Data directory inaccessible: %v", err))
} else {
emit("ok", "Data directory accessible")
}
// Identity
idPath, err := identityPath()
if err != nil {
emit("FAIL", fmt.Sprintf("Cannot determine identity path: %v", err))
} else if _, err := os.Stat(idPath); os.IsNotExist(err) {
emit("info", "No identity file. One will be created on first encrypt.")
} else if err != nil {
emit("FAIL", fmt.Sprintf("Cannot access identity file: %v", err))
} else {
emit("ok", "Identity file present")
}
// Git
gitInitialised := false
if dataDir != "" {
gitDir := filepath.Join(dataDir, ".git")
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
emit("info", "Git not initialised")
} else if err != nil {
emit("FAIL", fmt.Sprintf("Cannot check git status: %v", err))
} else {
gitInitialised = true
emit("ok", "Git initialised")
}
}
// Git remote (only when git is initialised)
if gitInitialised {
hasOrigin, err := repoHasRemote(dataDir, "origin")
if err != nil {
emit("FAIL", fmt.Sprintf("Cannot check git remote: %v", err))
} else if hasOrigin {
emit("ok", "Git remote configured")
} else {
emit("info", "No git remote configured")
}
}
// Stores
stores, err := store.AllStores()
if err != nil {
emit("FAIL", fmt.Sprintf("Cannot list stores: %v", err))
} else if len(stores) == 0 {
emit("info", "No stores")
} else {
var parseErrors int
for _, name := range stores {
p, pErr := store.storePath(name)
if pErr != nil {
parseErrors++
continue
}
if _, rErr := readStoreFile(p, nil); rErr != nil {
parseErrors++
}
}
if parseErrors > 0 {
emit("FAIL", fmt.Sprintf("%d store(s), %d with errors", len(stores), parseErrors))
} else {
emit("ok", fmt.Sprintf("%d store(s)", len(stores)))
}
}
if hasError {
os.Exit(1)
}
return nil
}

7
testdata/doctor.ct vendored Normal file
View file

@ -0,0 +1,7 @@
# Doctor reports environment health
$ pda doctor
info Using default configuration
ok Data directory accessible
ok Identity file present
info Git not initialised
ok 5 store(s)

2
testdata/help.ct vendored
View file

@ -36,6 +36,7 @@ Git commands:
Additional Commands: Additional Commands:
completion Generate the autocompletion script for the specified shell completion Generate the autocompletion script for the specified shell
doctor Check environment health
help Help about any command help Help about any command
version Display pda! version version Display pda! version
@ -79,6 +80,7 @@ Git commands:
Additional Commands: Additional Commands:
completion Generate the autocompletion script for the specified shell completion Generate the autocompletion script for the specified shell
doctor Check environment health
help Help about any command help Help about any command
version Display pda! version version Display pda! version

1
testdata/root.ct vendored
View file

@ -35,6 +35,7 @@ Git commands:
Additional Commands: Additional Commands:
completion Generate the autocompletion script for the specified shell completion Generate the autocompletion script for the specified shell
doctor Check environment health
help Help about any command help Help about any command
version Display pda! version version Display pda! version