Files
commandkit/builtins.go

130 lines
3.8 KiB
Go
Raw Normal View History

package commandkit
import (
"fmt"
"io"
"runtime/debug"
2026-11-07 03:45:12 +00:00
"text/template"
2026-11-04 03:49:38 +00:00
internalcobra "go.maximumdirect.net/commandkit/internal/cobra"
commandmeta "go.maximumdirect.net/commandkit/internal/commandmeta"
)
2026-11-04 03:49:38 +00:00
const commandKitModulePath = "go.maximumdirect.net/commandkit"
type helpInput struct {
Targets Supplied[[]string]
}
type versionInput struct {
2026-11-07 03:45:12 +00:00
Scope string
}
type versionDocument struct {
2026-11-07 03:45:12 +00:00
Name string `json:"name"`
Version string `json:"version"`
Revision string `json:"revision"`
}
2026-11-07 03:45:12 +00:00
var versionTextTemplate = template.Must(ParseTextTemplate("version", "{{.Name}} {{.Version}}{{if .Revision}} ({{.Revision}}){{end}}\n", nil))
2026-10-19 17:23:32 +00:00
func (a *App) builtinCommands() []Declaration {
2026-10-11 01:21:52 +00:00
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 })},
2026-10-11 01:21:52 +00:00
}, func(current commandExecution, input helpInput) Outcome {
return current.renderHelpTargets(input.Targets.Value)
})
scopeDefault := "app"
2026-10-11 01:15:13 +00:00
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"
}
2026-11-07 03:45:12 +00:00
return renderVersionOutcome(identity, invocation.Reporter())
},
})
2026-10-19 17:23:32 +00:00
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 {
2026-10-19 19:40:55 +00:00
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()
}
2026-11-07 03:45:12 +00:00
func renderVersionOutcome(identity Identity, reporter *Reporter) Outcome {
if identity.Version == "" {
identity.Version = "dev"
}
2026-11-07 03:45:12 +00:00
_ = 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
}