2026-10-07 13:37:57 +00:00
package commandkit
import (
"fmt"
"io"
"runtime/debug"
2026-11-07 03:45:12 +00:00
"text/template"
2026-10-07 13:37:57 +00:00
2026-11-04 03:49:38 +00:00
internalcobra "go.maximumdirect.net/commandkit/internal/cobra"
commandmeta "go.maximumdirect.net/commandkit/internal/commandmeta"
2026-10-07 13:37:57 +00:00
)
2026-11-04 03:49:38 +00:00
const commandKitModulePath = "go.maximumdirect.net/commandkit"
2026-10-07 13:37:57 +00:00
type helpInput struct {
Targets Supplied [ [ ] string ]
}
type versionInput struct {
2026-11-07 03:45:12 +00:00
Scope string
2026-10-07 13:37:57 +00:00
}
type versionDocument struct {
2026-11-07 03:45:12 +00:00
Name string ` json:"name" `
Version string ` json:"version" `
Revision string ` json:"revision" `
2026-10-07 13:37:57 +00:00
}
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 ] {
2026-10-07 13:37:57 +00:00
Name : "help" ,
Summary : "Show help for commands." ,
2026-11-07 23:31:14 +00:00
Description : "Show available commands, flags, and arguments." ,
Arguments : [ ] Argument [ helpInput ] { RepeatArgument ( ArgumentMeta { Name : "COMMAND" , Usage : "Command path to describe." } , 0 , - 1 ,
2026-10-07 13:37:57 +00:00
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 )
2026-10-07 13:37:57 +00:00
} )
scopeDefault := "app"
2026-10-11 01:15:13 +00:00
version := NewCommand ( CommandSpec [ versionInput ] {
2026-10-07 13:37:57 +00:00
Name : "version" ,
Summary : "Show application or CommandKit version." ,
2026-11-07 23:31:14 +00:00
Description : "Show the name, version, and revision without loading application configuration." ,
2026-10-07 13:37:57 +00:00
Flags : [ ] Flag [ versionInput ] {
2026-11-07 23:31:14 +00:00
ValueFlag ( FlagMeta { Name : "scope" , Usage : "Show the app or CommandKit version." , Completion : CompleteChoices ( CompletionChoice { Value : "app" } , CompletionChoice { Value : "commandkit" } ) } , & scopeDefault , parseVersionScope , func ( input * versionInput , value Supplied [ string ] ) { input . Scope = value . Value } ) ,
2026-10-07 13:37:57 +00:00
} ,
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-07 13:37:57 +00:00
} ,
} )
2026-10-19 17:23:32 +00:00
return [ ] Declaration { help , version }
2026-10-07 13:37:57 +00:00
}
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
}
2026-10-26 03:02:08 +00:00
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 )
2026-10-07 13:37:57 +00:00
if failure != nil {
2026-10-11 01:25:45 +00:00
if failure . Usage {
2026-10-26 03:02:08 +00:00
return Usage ( failure . Message )
2026-10-11 01:25:45 +00:00
}
2026-10-26 03:02:08 +00:00
return Failure ( failure . Message )
2026-10-07 13:37:57 +00:00
}
2026-10-26 03:02:08 +00:00
_ = reporter . RenderOutput ( func ( w io . Writer ) error {
_ , err := io . WriteString ( w , content )
return err
} )
2026-10-07 13:37:57 +00:00
return Success ( )
}
2026-11-07 03:45:12 +00:00
func renderVersionOutcome ( identity Identity , reporter * Reporter ) Outcome {
2026-10-26 03:02:08 +00:00
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 } )
2026-10-07 13:37:57 +00:00
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
}