package commandkit import ( "fmt" "io" "runtime/debug" "text/template" internalcobra "go.maximumdirect.net/commandkit/internal/cobra" commandmeta "go.maximumdirect.net/commandkit/internal/commandmeta" ) const commandKitModulePath = "go.maximumdirect.net/commandkit" type helpInput struct { Targets Supplied[[]string] } type versionInput struct { Scope string } type versionDocument struct { Name string `json:"name"` Version string `json:"version"` Revision string `json:"revision"` } var versionTextTemplate = template.Must(ParseTextTemplate("version", "{{.Name}} {{.Version}}{{if .Revision}} ({{.Revision}}){{end}}\n", nil)) func (a *App) builtinCommands() []Declaration { help := captureCommand(CommandSpec[helpInput]{ Name: "help", Summary: "Show help for commands.", Description: "Describe registered commands and their inputs.", Arguments: []Argument[helpInput]{RepeatArgument(ArgumentMeta{Name: "COMMAND", Usage: "registered command path"}, 0, -1, func(value string) (string, error) { return value, nil }, func(input *helpInput, value Supplied[[]string]) { input.Targets = value })}, }, func(current commandExecution, input helpInput) Outcome { return current.renderHelpTargets(input.Targets.Value) }) scopeDefault := "app" version := NewCommand(CommandSpec[versionInput]{ Name: "version", Summary: "Show application or CommandKit version.", Description: "Report identity and build metadata without loading application configuration.", Flags: []Flag[versionInput]{ ValueFlag(FlagMeta{Name: "scope", Usage: "version subject", Completion: CompleteChoices(CompletionChoice{Value: "app"}, CompletionChoice{Value: "commandkit"})}, &scopeDefault, parseVersionScope, func(input *versionInput, value Supplied[string]) { input.Scope = value.Value }), }, Run: func(invocation Invocation, input versionInput) Outcome { identity := a.identity if input.Scope == "commandkit" { identity = commandKitBuildIdentity(readBuildInfo()) } if identity.Version == "" { identity.Version = "dev" } return renderVersionOutcome(identity, invocation.Reporter()) }, }) return []Declaration{help, version} } func parseVersionScope(value string) (string, error) { if value != "app" && value != "commandkit" { return "", fmt.Errorf("scope must be app or commandkit, got %q", value) } return value, nil } func (a *App) renderHelpViewOutcomeWithSession(view commandmeta.HelpView, reporter *Reporter, session *internalcobra.Session) Outcome { content, failure := session.RenderHelpView(view) if failure != nil { if failure.Usage { return Usage(failure.Message) } return Failure(failure.Message) } _ = reporter.RenderOutput(func(w io.Writer) error { _, err := io.WriteString(w, content) return err }) return Success() } func renderVersionOutcome(identity Identity, reporter *Reporter) Outcome { if identity.Version == "" { identity.Version = "dev" } _ = reporter.Report(Report{Value: versionDocument{Name: identity.Name, Version: identity.Version, Revision: identity.Revision}, TextTemplate: versionTextTemplate}) return Success() } func readBuildInfo() *debug.BuildInfo { info, ok := debug.ReadBuildInfo() if !ok { return nil } return info } func commandKitBuildIdentity(info *debug.BuildInfo) Identity { identity := Identity{Name: "CommandKit", Version: "dev"} if info == nil { return identity } var module *debug.Module if info.Main.Path == commandKitModulePath { module = &info.Main } else { for _, dependency := range info.Deps { if dependency != nil && dependency.Path == commandKitModulePath { module = dependency break } } } if module == nil { return identity } version := module.Version if module.Replace != nil { version = module.Replace.Version } if version != "" && version != "(devel)" { identity.Version = version } return identity }