moar milpa stuff
This commit is contained in:
parent
831e68c7b7
commit
8ea5f42ef8
@ -12,3 +12,10 @@ indent_style = space
|
||||
indent_size = 2
|
||||
max_line_length = 120
|
||||
|
||||
[*.go]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
max_line_length = 120
|
||||
|
1
go.mod
1
go.mod
@ -10,6 +10,7 @@ require (
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
4
go.sum
4
go.sum
@ -29,9 +29,11 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
@ -62,6 +64,7 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
@ -106,6 +109,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
29
internal/commands/generate_completions.go
Normal file
29
internal/commands/generate_completions.go
Normal file
@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright © 2021 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var GenerateCompletions = &cobra.Command{
|
||||
Use: "__generate_completions [bash|zsh|fish]",
|
||||
Short: "Outputs a shell-specific script for autocompletions that can be piped into a file",
|
||||
Hidden: true,
|
||||
DisableAutoGenTag: true,
|
||||
SilenceUsage: true,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
err = cmd.Root().GenBashCompletionV2(os.Stdout, true)
|
||||
case "zsh":
|
||||
err = cmd.Root().GenZshCompletion(os.Stdout)
|
||||
case "fish":
|
||||
err = cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
74
internal/commands/help.go
Normal file
74
internal/commands/help.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/pkg/env"
|
||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||
"git.rob.mx/nidito/chinampa/pkg/statuscode"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Help = &cobra.Command{
|
||||
Use: _c.HelpCommandName + " [command]",
|
||||
Short: "Display usage information for any command",
|
||||
Long: `Help provides the valid arguments and options for any command known to ` + runtime.Executable + `. By default, ﹅` + runtime.Executable + ` help﹅ will query the environment variable ﹅COLORFGBG﹅ to decide which style to use when rendering help, except if ﹅` + env.HelpUnstyled + `﹅ is set. Valid styles are: **light**, **dark**, and **auto**.`,
|
||||
ValidArgsFunction: func(c *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
var completions []string
|
||||
cmd, _, e := c.Root().Find(args)
|
||||
if e != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
if cmd == nil {
|
||||
// Root help command.
|
||||
cmd = c.Root()
|
||||
}
|
||||
for _, subCmd := range cmd.Commands() {
|
||||
if subCmd.IsAvailableCommand() || subCmd.Name() == _c.HelpCommandName {
|
||||
if strings.HasPrefix(subCmd.Name(), toComplete) {
|
||||
completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short))
|
||||
}
|
||||
}
|
||||
}
|
||||
return completions, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmd, _, e := c.Root().Find(args)
|
||||
if cmd == nil || e != nil || (len(args) > 0 && cmd != nil && cmd.Name() != args[len(args)-1]) {
|
||||
if cmd == nil {
|
||||
err := c.Root().Help()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
os.Exit(statuscode.ProgrammerError)
|
||||
}
|
||||
logrus.Errorf("Unknown help topic %s", args)
|
||||
os.Exit(statuscode.NotFound)
|
||||
} else {
|
||||
err := cmd.Help()
|
||||
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
os.Exit(statuscode.ProgrammerError)
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
logrus.Errorf("Unknown help topic %s for %s", args[1], args[0])
|
||||
} else {
|
||||
logrus.Errorf("Unknown help topic %s for %s", runtime.Executable, args[0])
|
||||
}
|
||||
os.Exit(statuscode.NotFound)
|
||||
}
|
||||
} else {
|
||||
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
|
||||
cobra.CheckErr(cmd.Help())
|
||||
}
|
||||
|
||||
os.Exit(statuscode.RenderHelp)
|
||||
},
|
||||
}
|
37
internal/commands/version.go
Normal file
37
internal/commands/version.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"git.rob.mx/nidito/chinampa/pkg/statuscode"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Version = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display program version",
|
||||
Hidden: false,
|
||||
DisableAutoGenTag: true,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
output := cmd.ErrOrStderr()
|
||||
version := cmd.Root().Annotations["version"]
|
||||
if cmd.CalledAs() == "" {
|
||||
// user asked for --version directly
|
||||
output = cmd.OutOrStderr()
|
||||
version += "\n"
|
||||
}
|
||||
|
||||
_, err := output.Write([]byte(version))
|
||||
if err != nil {
|
||||
logrus.Errorf("version error: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
os.Exit(statuscode.Ok)
|
||||
return nil
|
||||
},
|
||||
}
|
@ -1,91 +1,13 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package constants
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
// Embed requires an import so the compiler knows what's up. Golint requires a comment. Gotta please em both.
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
const HelpCommandName = "help"
|
||||
|
||||
// Environment Variables.
|
||||
const EnvVarHelpUnstyled = "HELP_STYLE_PLAIN"
|
||||
const EnvVarHelpStyle = "HELP_STYLE"
|
||||
const EnvVarMilpaVerbose = "VERBOSE"
|
||||
const EnvVarMilpaSilent = "SILENT"
|
||||
const EnvVarMilpaUnstyled = "NO_COLOR"
|
||||
const EnvVarMilpaForceColor = "COLOR"
|
||||
const EnvVarValidationDisabled = "SKIP_VALIDATION"
|
||||
const EnvVarDebug = "DEBUG"
|
||||
|
||||
// EnvFlagNames are flags also available as environment variables.
|
||||
var EnvFlagNames = map[string]string{
|
||||
"no-color": EnvVarMilpaUnstyled,
|
||||
"color": EnvVarMilpaForceColor,
|
||||
"silent": EnvVarMilpaSilent,
|
||||
"verbose": EnvVarMilpaVerbose,
|
||||
"skip-validation": EnvVarValidationDisabled,
|
||||
}
|
||||
|
||||
// Exit statuses
|
||||
// see man sysexits || grep "#define EX" /usr/include/sysexits.h
|
||||
// and https://tldp.org/LDP/abs/html/exitcodes.html
|
||||
const (
|
||||
// 0 means everything is fine.
|
||||
ExitStatusOk = 0
|
||||
// 42 provides answers to life, the universe and everything; also, renders help.
|
||||
ExitStatusRenderHelp = 42
|
||||
// 64 bad arguments
|
||||
// EX_USAGE The command was used incorrectly, e.g., with the wrong number of arguments, a bad flag, a bad syntax in a parameter, or whatever.
|
||||
ExitStatusUsage = 64
|
||||
// EX_SOFTWARE An internal software error has been detected. This should be limited to non-operating system related errors as possible.
|
||||
ExitStatusProgrammerError = 70
|
||||
// EX_CONFIG Something was found in an unconfigured or misconfigured state.
|
||||
ExitStatusConfigError = 78
|
||||
// 127 command not found.
|
||||
ExitStatusNotFound = 127
|
||||
)
|
||||
|
||||
// ContextKeyRuntimeIndex is the string key used to store context in a cobra Command.
|
||||
const ContextKeyRuntimeIndex = "x-chinampa-runtime-index"
|
||||
|
||||
//go:embed help.md
|
||||
var helpTemplateText string
|
||||
|
||||
// TemplateFuncs is a FuncMap with aliases to the strings package.
|
||||
var TemplateFuncs = template.FuncMap{
|
||||
"contains": strings.Contains,
|
||||
"hasSuffix": strings.HasSuffix,
|
||||
"hasPrefix": strings.HasPrefix,
|
||||
"replace": strings.ReplaceAll,
|
||||
"toUpper": strings.ToUpper,
|
||||
"toLower": strings.ToLower,
|
||||
"trim": strings.TrimSpace,
|
||||
"trimSuffix": strings.TrimSuffix,
|
||||
"trimPrefix": strings.TrimPrefix,
|
||||
}
|
||||
|
||||
// TemplateCommandHelp holds a template for rendering command help.
|
||||
var TemplateCommandHelp *template.Template
|
||||
|
||||
func HelpTemplate(executableName string) *template.Template {
|
||||
if TemplateCommandHelp == nil {
|
||||
TemplateCommandHelp = template.Must(template.New("help").Funcs(TemplateFuncs).Parse(strings.ReplaceAll(helpTemplateText, "@chinampa@", executableName)))
|
||||
}
|
||||
|
||||
return TemplateCommandHelp
|
||||
}
|
||||
var HelpTemplate string
|
||||
|
@ -1,24 +1,13 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
||||
"git.rob.mx/nidito/chinampa/pkg/command"
|
||||
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||
"github.com/fatih/color"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -29,7 +18,7 @@ func newCobraRoot(root *command.Command) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: root.Name() + " [--silent|-v|--verbose] [--[no-]color] [-h|--help] [--version]",
|
||||
Annotations: map[string]string{
|
||||
_c.ContextKeyRuntimeIndex: root.Name(),
|
||||
ContextKeyRuntimeIndex: root.Name(),
|
||||
},
|
||||
Short: root.Summary,
|
||||
Long: root.Description,
|
||||
@ -62,13 +51,12 @@ func newCobraRoot(root *command.Command) *cobra.Command {
|
||||
}
|
||||
return errors.NotFound{Msg: "No subcommand provided", Group: []string{}}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func toCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command {
|
||||
func ToCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command {
|
||||
localName := cmd.Name()
|
||||
useSpec := []string{localName, "[options]"}
|
||||
for _, arg := range cmd.Arguments {
|
||||
@ -81,8 +69,9 @@ func toCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command
|
||||
DisableAutoGenTag: true,
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
Hidden: cmd.Hidden,
|
||||
Annotations: map[string]string{
|
||||
_c.ContextKeyRuntimeIndex: cmd.FullName(),
|
||||
ContextKeyRuntimeIndex: cmd.FullName(),
|
||||
},
|
||||
Args: func(cc *cobra.Command, supplied []string) error {
|
||||
skipValidation, _ := cc.Flags().GetBool("skip-validation")
|
||||
@ -114,8 +103,8 @@ func toCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command
|
||||
return cc
|
||||
}
|
||||
|
||||
func fromCobra(cc *cobra.Command) *command.Command {
|
||||
rtidx, hasAnnotation := cc.Annotations[_c.ContextKeyRuntimeIndex]
|
||||
func FromCobra(cc *cobra.Command) *command.Command {
|
||||
rtidx, hasAnnotation := cc.Annotations[ContextKeyRuntimeIndex]
|
||||
if hasAnnotation {
|
||||
return Get(rtidx)
|
||||
}
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package registry
|
||||
|
||||
import (
|
||||
@ -18,14 +8,18 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
||||
"git.rob.mx/nidito/chinampa/internal/commands"
|
||||
"git.rob.mx/nidito/chinampa/pkg/command"
|
||||
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||
"git.rob.mx/nidito/chinampa/pkg/statuscode"
|
||||
"github.com/fatih/color"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ContextKeyRuntimeIndex is the string key used to store context in a cobra Command.
|
||||
const ContextKeyRuntimeIndex = "x-chinampa-runtime-index"
|
||||
|
||||
var registry = &CommandRegistry{
|
||||
kv: map[string]*command.Command{},
|
||||
}
|
||||
@ -36,28 +30,9 @@ func (cmds ByPath) Len() int { return len(cmds) }
|
||||
func (cmds ByPath) Swap(i, j int) { cmds[i], cmds[j] = cmds[j], cmds[i] }
|
||||
func (cmds ByPath) Less(i, j int) bool { return cmds[i].FullName() < cmds[j].FullName() }
|
||||
|
||||
type CommandTree struct {
|
||||
Command *command.Command `json:"command"`
|
||||
Children []*CommandTree `json:"children"`
|
||||
}
|
||||
|
||||
func (t *CommandTree) Traverse(fn func(cmd *command.Command) error) error {
|
||||
for _, child := range t.Children {
|
||||
if err := fn(child.Command); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := child.Traverse(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CommandRegistry struct {
|
||||
kv map[string]*command.Command
|
||||
byPath []*command.Command
|
||||
tree *CommandTree
|
||||
}
|
||||
|
||||
func Register(cmd *command.Command) {
|
||||
@ -82,87 +57,54 @@ func CommandList() []*command.Command {
|
||||
return registry.byPath
|
||||
}
|
||||
|
||||
func BuildTree(cc *cobra.Command, depth int) {
|
||||
tree := &CommandTree{
|
||||
Command: fromCobra(cc),
|
||||
Children: []*CommandTree{},
|
||||
}
|
||||
|
||||
var populateTree func(cmd *cobra.Command, ct *CommandTree, maxDepth int, depth int)
|
||||
populateTree = func(cmd *cobra.Command, ct *CommandTree, maxDepth int, depth int) {
|
||||
newDepth := depth + 1
|
||||
for _, subcc := range cmd.Commands() {
|
||||
if subcc.Hidden {
|
||||
continue
|
||||
}
|
||||
|
||||
if cmd := fromCobra(subcc); cmd != nil {
|
||||
leaf := &CommandTree{Children: []*CommandTree{}}
|
||||
leaf.Command = cmd
|
||||
ct.Children = append(ct.Children, leaf)
|
||||
|
||||
if newDepth < maxDepth {
|
||||
populateTree(subcc, leaf, maxDepth, newDepth)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
populateTree(cc, tree, depth, 0)
|
||||
|
||||
registry.tree = tree
|
||||
}
|
||||
|
||||
func SerializeTree(serializationFn func(any) ([]byte, error)) (string, error) {
|
||||
bytes, err := serializationFn(registry.tree)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func ChildrenNames() []string {
|
||||
if registry.tree == nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
ret := make([]string, len(registry.tree.Children))
|
||||
for idx, cmd := range registry.tree.Children {
|
||||
ret[idx] = cmd.Command.Name()
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func Execute(version string) error {
|
||||
cmdRoot := command.Root
|
||||
ccRoot := newCobraRoot(command.Root)
|
||||
ccRoot.Annotations["version"] = version
|
||||
ccRoot.CompletionOptions.HiddenDefaultCmd = true
|
||||
ccRoot.PersistentFlags().AddFlagSet(cmdRoot.FlagSet())
|
||||
ccRoot.SetHelpCommand(commands.Help)
|
||||
ccRoot.AddCommand(commands.Version)
|
||||
ccRoot.AddCommand(commands.GenerateCompletions)
|
||||
|
||||
for name, opt := range cmdRoot.Options {
|
||||
if err := ccRoot.RegisterFlagCompletionFunc(name, opt.CompletionFunction); err != nil {
|
||||
logrus.Errorf("Failed setting up autocompletion for option <%s> of command <%s>", name, cmdRoot.FullName())
|
||||
}
|
||||
}
|
||||
// ccRoot.SetHelpFunc(func(cc *cobra.Command, args []string) {
|
||||
// cmdRoot.HelpRenderer(cmdRoot.Options)(cc, args)
|
||||
// os.Exit(statuscode.RenderHelp)
|
||||
// })
|
||||
ccRoot.SetHelpFunc(cmdRoot.HelpRenderer(cmdRoot.Options))
|
||||
|
||||
for _, cmd := range CommandList() {
|
||||
cmd := cmd
|
||||
leaf := toCobra(cmd, cmdRoot.Options)
|
||||
container := ccRoot
|
||||
for idx, cp := range cmd.Path {
|
||||
if idx == len(cmd.Path)-1 {
|
||||
leaf := ToCobra(cmd, cmdRoot.Options)
|
||||
logrus.Debugf("adding command %s to %s", leaf.Name(), container.Name())
|
||||
container.AddCommand(leaf)
|
||||
break
|
||||
}
|
||||
|
||||
query := []string{cp}
|
||||
if cc, _, err := container.Find(query); err == nil && cc != container {
|
||||
container = cc
|
||||
found := false
|
||||
if cp == "help" && container == ccRoot {
|
||||
container = commands.Help
|
||||
} else {
|
||||
for _, sub := range container.Commands() {
|
||||
if sub.Name() == cp {
|
||||
container = sub
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
groupName := strings.Join(query, " ")
|
||||
groupPath := append(cmd.Path[0:idx], query...) // nolint:gocritic
|
||||
groupPath := append(cmdRoot.Path, append(cmd.Path[0:idx], query...)...) // nolint:gocritic
|
||||
cc := &cobra.Command{
|
||||
Use: cp,
|
||||
Short: fmt.Sprintf("%s subcommands", groupName),
|
||||
@ -171,7 +113,7 @@ func Execute(version string) error {
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
Annotations: map[string]string{
|
||||
_c.ContextKeyRuntimeIndex: strings.Join(groupPath, " "),
|
||||
ContextKeyRuntimeIndex: strings.Join(groupPath, " "),
|
||||
},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if err := cobra.OnlyValidArgs(cmd, args); err == nil {
|
||||
@ -196,13 +138,13 @@ func Execute(version string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.NotFound{Msg: "No subcommand provided", Group: []string{}}
|
||||
}
|
||||
os.Exit(_c.ExitStatusNotFound)
|
||||
os.Exit(statuscode.NotFound)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
groupParent := &command.Command{
|
||||
Path: cmd.Path[0 : len(cmd.Path)-1],
|
||||
Path: groupPath,
|
||||
Summary: fmt.Sprintf("%s subcommands", groupName),
|
||||
Description: fmt.Sprintf("Runs subcommands within %s", groupName),
|
||||
Arguments: command.Arguments{},
|
||||
@ -219,12 +161,21 @@ func Execute(version string) error {
|
||||
}
|
||||
cmdRoot.SetCobra(ccRoot)
|
||||
|
||||
current, _, err := ccRoot.Find(os.Args[1:])
|
||||
current, remaining, err := ccRoot.Find(os.Args[1:])
|
||||
if err != nil {
|
||||
current = ccRoot
|
||||
}
|
||||
logrus.Debugf("Chinampa found command %s, remaining %s", current.Name(), remaining)
|
||||
|
||||
// if current.HasSubCommands() && current.
|
||||
if sub, _, err := current.Find(remaining); err == nil && sub != current {
|
||||
logrus.Debugf("Chinampa found sub-command %s, of %s", sub.Name(), current.Name())
|
||||
current = sub
|
||||
}
|
||||
logrus.Debugf("Chinampa is going to call command %s", current.Name())
|
||||
err = current.Execute()
|
||||
if err != nil {
|
||||
logrus.Debugf("Chinampa found error calling command %s", current.Name())
|
||||
errors.HandleCobraExit(current, err)
|
||||
}
|
||||
|
||||
|
12
main.go
12
main.go
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package chinampa
|
||||
|
||||
import (
|
||||
|
@ -1,22 +1,12 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
||||
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -55,10 +45,11 @@ func (args *Arguments) Parse(supplied []string) {
|
||||
if !argumentProvided {
|
||||
if arg.Default != nil {
|
||||
if arg.Variadic {
|
||||
defaultSlice := []string{}
|
||||
for _, valI := range arg.Default.([]any) {
|
||||
defaultSlice = append(defaultSlice, valI.(string))
|
||||
}
|
||||
defaultSlice := arg.Default.([]string)
|
||||
// defaultSlice := []string{}
|
||||
// for _, valI := range arg.Default.([]string) {
|
||||
// defaultSlice = append(defaultSlice, valI.(string))
|
||||
// }
|
||||
arg.provided = &defaultSlice
|
||||
} else {
|
||||
defaultString := arg.Default.(string)
|
||||
@ -194,11 +185,11 @@ func (arg *Argument) ToValue() any {
|
||||
} else {
|
||||
if arg.Default != nil {
|
||||
if arg.Variadic {
|
||||
defaultSlice := []string{}
|
||||
for _, valI := range arg.Default.([]any) {
|
||||
valStr := valI.(string)
|
||||
defaultSlice = append(defaultSlice, valStr)
|
||||
}
|
||||
defaultSlice := arg.Default
|
||||
// for _, valI := range arg.Default.([]any) {
|
||||
// valStr := valI.(string)
|
||||
// defaultSlice = append(defaultSlice, valStr)
|
||||
// }
|
||||
|
||||
value = defaultSlice
|
||||
} else {
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command_test
|
||||
|
||||
import (
|
||||
|
@ -1,22 +1,13 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
||||
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@ -38,9 +29,12 @@ type Command struct {
|
||||
Options Options `json:"options" yaml:"options" validate:"dive"`
|
||||
HelpFunc HelpFunc `json:"-" yaml:"-"`
|
||||
// The action to take upon running
|
||||
Action Action
|
||||
Action Action `json:"-" yaml:"-"`
|
||||
runtimeFlags *pflag.FlagSet
|
||||
Cobra *cobra.Command
|
||||
Cobra *cobra.Command `json:"-" yaml:"-"`
|
||||
// Meta stores application specific stuff
|
||||
Meta any `json:"meta" yaml:"meta"`
|
||||
Hidden bool `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
func (cmd *Command) IsRoot() bool {
|
||||
@ -88,6 +82,22 @@ func (cmd *Command) FlagSet() *pflag.FlagSet {
|
||||
}
|
||||
|
||||
fs.BoolP(name, opt.ShortName, def, opt.Description)
|
||||
case ValueTypeInt:
|
||||
def := -1
|
||||
if opt.Default != nil {
|
||||
switch val := opt.Default.(type) {
|
||||
case int:
|
||||
def = opt.Default.(int)
|
||||
case string:
|
||||
casted, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
logrus.Warnf("Could not parse default with value <%s> as integer for option <%s>", val, name)
|
||||
}
|
||||
def = casted
|
||||
}
|
||||
}
|
||||
|
||||
fs.IntP(name, opt.ShortName, def, opt.Description)
|
||||
case ValueTypeDefault, ValueTypeString:
|
||||
opt.Type = ValueTypeString
|
||||
def := ""
|
||||
@ -119,7 +129,7 @@ func (cmd *Command) ParseInput(cc *cobra.Command, args []string) error {
|
||||
|
||||
logrus.Debug("Validating flags")
|
||||
if err := cmd.Options.AreValid(); err != nil {
|
||||
logrus.Debugf("Invalid flags for %s: %w", cmd.FullName(), err)
|
||||
logrus.Debugf("Invalid flags for %s: %s", cmd.FullName(), err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,11 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/internal/render"
|
||||
"git.rob.mx/nidito/chinampa/pkg/render"
|
||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -64,7 +53,7 @@ func (cmd *Command) ShowHelp(globalOptions Options, args []string) ([]byte, erro
|
||||
GlobalOptions: globalOptions,
|
||||
HTMLOutput: runtime.UnstyledHelpEnabled(),
|
||||
}
|
||||
err := _c.HelpTemplate(runtime.Executable).Execute(&buf, c)
|
||||
err := render.HelpTemplate(runtime.Executable).Execute(&buf, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command
|
||||
|
||||
import (
|
||||
@ -17,7 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
||||
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
@ -41,54 +31,6 @@ func (opts *Options) AllKnownStr() map[string]string {
|
||||
return col
|
||||
}
|
||||
|
||||
// func envValue(opts Options, f *pflag.Flag) (*string, *string) {
|
||||
// name := f.Name
|
||||
// if name == _c.HelpCommandName {
|
||||
// return nil, nil
|
||||
// }
|
||||
// envName := ""
|
||||
// value := f.Value.String()
|
||||
|
||||
// if cname, ok := _c.EnvFlagNames[name]; ok {
|
||||
// if value == "false" {
|
||||
// return nil, nil
|
||||
// }
|
||||
// envName = cname
|
||||
// } else {
|
||||
// envName = fmt.Sprintf("%s%s", _c.OutputPrefixOpt, strings.ToUpper(strings.ReplaceAll(name, "-", "_")))
|
||||
// opt := opts[name]
|
||||
// if opt != nil {
|
||||
// value = opt.ToString(true)
|
||||
// }
|
||||
|
||||
// if value == "false" && opt.Type == ValueTypeBoolean {
|
||||
// // makes dealing with false flags in shell easier
|
||||
// value = ""
|
||||
// }
|
||||
// }
|
||||
|
||||
// return &envName, &value
|
||||
// }
|
||||
|
||||
// // ToEnv writes shell variables to dst.
|
||||
// func (opts *Options) ToEnv(command *Command, dst *[]string, prefix string) {
|
||||
// command.cc.Flags().VisitAll(func(f *pflag.Flag) {
|
||||
// envName, value := envValue(*opts, f)
|
||||
// if envName != nil && value != nil {
|
||||
// *dst = append(*dst, fmt.Sprintf("%s%s=%s", prefix, *envName, *value))
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
// func (opts *Options) EnvMap(command *Command, dst *map[string]string) {
|
||||
// command.cc.Flags().VisitAll(func(f *pflag.Flag) {
|
||||
// envName, value := envValue(*opts, f)
|
||||
// if envName != nil && value != nil {
|
||||
// (*dst)[*envName] = *value
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
func (opts *Options) Parse(supplied *pflag.FlagSet) {
|
||||
// logrus.Debugf("Parsing supplied flags, %v", supplied)
|
||||
for name, opt := range *opts {
|
||||
@ -98,6 +40,11 @@ func (opts *Options) Parse(supplied *pflag.FlagSet) {
|
||||
opt.provided = val
|
||||
continue
|
||||
}
|
||||
case ValueTypeInt:
|
||||
if val, err := supplied.GetInt(name); err == nil {
|
||||
opt.provided = val
|
||||
continue
|
||||
}
|
||||
default:
|
||||
opt.Type = ValueTypeString
|
||||
if val, err := supplied.GetString(name); err == nil {
|
||||
@ -121,7 +68,7 @@ func (opts *Options) AreValid() error {
|
||||
// Option represents a command line flag.
|
||||
type Option struct {
|
||||
ShortName string `json:"short-name,omitempty" yaml:"short-name,omitempty"` // nolint:tagliatelle
|
||||
Type ValueType `json:"type" yaml:"type" validate:"omitempty,oneof=string bool"`
|
||||
Type ValueType `json:"type" yaml:"type" validate:"omitempty,oneof=string bool int"`
|
||||
Description string `json:"description" yaml:"description" validate:"required"`
|
||||
Default any `json:"default,omitempty" yaml:"default,omitempty"`
|
||||
Values *ValueSource `json:"values,omitempty" yaml:"values,omitempty" validate:"omitempty"`
|
||||
@ -144,13 +91,20 @@ func (opt *Option) ToValue() any {
|
||||
func (opt *Option) ToString() string {
|
||||
value := opt.ToValue()
|
||||
stringValue := ""
|
||||
if opt.Type == "bool" {
|
||||
switch opt.Type {
|
||||
case ValueTypeBoolean:
|
||||
if value == nil {
|
||||
stringValue = ""
|
||||
} else {
|
||||
stringValue = strconv.FormatBool(value.(bool))
|
||||
}
|
||||
} else {
|
||||
case ValueTypeInt:
|
||||
if value == nil {
|
||||
stringValue = ""
|
||||
} else {
|
||||
stringValue = fmt.Sprintf("%i", value)
|
||||
}
|
||||
default:
|
||||
if value != nil {
|
||||
stringValue = value.(string)
|
||||
}
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command
|
||||
|
||||
import (
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command
|
||||
|
||||
import (
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command
|
||||
|
||||
import (
|
||||
@ -21,9 +11,11 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/internal/exec"
|
||||
"git.rob.mx/nidito/chinampa/pkg/exec"
|
||||
"git.rob.mx/nidito/chinampa/pkg/render"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ValueType represent the kinds of or option.
|
||||
@ -36,6 +28,8 @@ const (
|
||||
ValueTypeString ValueType = "string"
|
||||
// ValueTypeBoolean is a value treated like a boolean.
|
||||
ValueTypeBoolean ValueType = "bool"
|
||||
// ValueTypeBoolean is a value treated like an integer.
|
||||
ValueTypeInt ValueType = "int"
|
||||
)
|
||||
|
||||
type SourceCommand struct {
|
||||
@ -43,7 +37,7 @@ type SourceCommand struct {
|
||||
Args string
|
||||
}
|
||||
|
||||
type CompletionFunc func(cmd *Command, currentValue string) (values []string, flag cobra.ShellCompDirective, err error)
|
||||
type CompletionFunc func(cmd *Command, currentValue string, config string) (values []string, flag cobra.ShellCompDirective, err error)
|
||||
|
||||
// ValueSource represents the source for an auto-completed and/or validated option/argument.
|
||||
type ValueSource struct {
|
||||
@ -58,7 +52,7 @@ type ValueSource struct {
|
||||
// Command runs a subcommand and returns an option for every line of stdout.
|
||||
Command *SourceCommand `json:"command,omitempty" yaml:"command,omitempty" validate:"omitempty,excluded_with=Directories Files Func Script Static"`
|
||||
// Func runs a function
|
||||
Func CompletionFunc `json:"func,omitempty" yaml:"func,omitempty" validate:"omitempty,excluded_with=Command Directories Files Script Static"`
|
||||
Func CompletionFunc `json:"-" yaml:"-" validate:"omitempty,excluded_with=Command Directories Files Script Static"`
|
||||
// Timeout is the maximum amount of time we will wait for a Script, Command, or Func before giving up on completions/validations.
|
||||
Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty" validate:"omitempty,excluded_with=Directories Files Static"`
|
||||
// Suggestion if provided will only suggest autocomplete values but will not perform validation of a given value
|
||||
@ -68,6 +62,7 @@ type ValueSource struct {
|
||||
command *Command `json:"-" yaml:"-" validate:"-"`
|
||||
computed *[]string
|
||||
flag cobra.ShellCompDirective
|
||||
custom string // The app-defined key's value
|
||||
}
|
||||
|
||||
// Validates tells if a value needs to be validated.
|
||||
@ -92,6 +87,7 @@ func (vs *ValueSource) Resolve(currentValue string) (values []string, flag cobra
|
||||
flag = cobra.ShellCompDirectiveDefault
|
||||
timeout := time.Duration(vs.Timeout)
|
||||
|
||||
logrus.Errorf("resolving: %s", vs)
|
||||
switch {
|
||||
case vs.Static != nil:
|
||||
values = *vs.Static
|
||||
@ -114,7 +110,7 @@ func (vs *ValueSource) Resolve(currentValue string) (values []string, flag cobra
|
||||
}
|
||||
}()
|
||||
|
||||
values, flag, err = vs.Func(vs.command, currentValue)
|
||||
values, flag, err = vs.Func(vs.command, currentValue, vs.custom)
|
||||
done <- err
|
||||
}()
|
||||
select {
|
||||
@ -172,6 +168,8 @@ func (vs *ValueSource) Resolve(currentValue string) (values []string, flag cobra
|
||||
if err != nil {
|
||||
return nil, flag, err
|
||||
}
|
||||
default:
|
||||
return nil, flag, fmt.Errorf("Empty value source")
|
||||
}
|
||||
|
||||
vs.computed = &values
|
||||
@ -215,7 +213,7 @@ func (cmd *Command) ResolveTemplate(templateString string, currentValue string)
|
||||
"Current": func() string { return currentValue },
|
||||
}
|
||||
|
||||
for k, v := range _c.TemplateFuncs {
|
||||
for k, v := range render.TemplateFuncs {
|
||||
fnMap[k] = v
|
||||
}
|
||||
|
||||
@ -232,3 +230,91 @@ func (cmd *Command) ResolveTemplate(templateString string, currentValue string)
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (vs *ValueSource) UnmarshalYAML(node *yaml.Node) error {
|
||||
vs.Timeout = 0
|
||||
vs.Suggestion = false
|
||||
vs.SuggestRaw = false
|
||||
vs.computed = nil
|
||||
intermediate := map[string]yaml.Node{}
|
||||
if err := node.Decode(&intermediate); err != nil {
|
||||
logrus.Errorf("could not decode valuesource: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if t, ok := intermediate["timeout"]; ok {
|
||||
|
||||
if err := t.Decode(&vs.Timeout); err != nil {
|
||||
logrus.Errorf("could not decode timeout: %s", err)
|
||||
return err
|
||||
}
|
||||
delete(intermediate, "timeout")
|
||||
}
|
||||
|
||||
if t, ok := intermediate["suggest-only"]; ok {
|
||||
if err := t.Decode(&vs.Suggestion); err != nil {
|
||||
logrus.Errorf("could not decode suggest-only: %s", err)
|
||||
return err
|
||||
}
|
||||
delete(intermediate, "suggest-only")
|
||||
}
|
||||
|
||||
if t, ok := intermediate["suggest-raw"]; ok {
|
||||
if err := t.Decode(&vs.SuggestRaw); err != nil {
|
||||
logrus.Errorf("could not decode suggest-raw: %s", err)
|
||||
return err
|
||||
}
|
||||
delete(intermediate, "suggest-raw")
|
||||
}
|
||||
|
||||
for key, node := range intermediate {
|
||||
if cfn, ok := customCompleters[key]; ok {
|
||||
if err := node.Decode(&vs.custom); err != nil {
|
||||
logrus.Errorf("could not decode custom: %s", err)
|
||||
return err
|
||||
}
|
||||
vs.Func = cfn
|
||||
break
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "dirs":
|
||||
var res string
|
||||
if err := node.Decode(&res); err != nil {
|
||||
return err
|
||||
}
|
||||
vs.Directories = &res
|
||||
case "files":
|
||||
res := []string{}
|
||||
if err := node.Decode(&res); err != nil {
|
||||
return err
|
||||
}
|
||||
vs.Files = &res
|
||||
case "script":
|
||||
if err := node.Decode(&vs.Script); err != nil {
|
||||
return err
|
||||
}
|
||||
case "static":
|
||||
static := []string{}
|
||||
if err := node.Decode(&static); err != nil {
|
||||
logrus.Errorf("could not decode static: %s", err)
|
||||
return err
|
||||
}
|
||||
vs.Static = &static
|
||||
case "command":
|
||||
if err := node.Decode(&vs.Command); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown value source key: %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var customCompleters = map[string]CompletionFunc{}
|
||||
|
||||
func RegisterValueSource(key string, completion CompletionFunc) {
|
||||
customCompleters[key] = completion
|
||||
}
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package command_test
|
||||
|
||||
import (
|
||||
|
22
pkg/env/env.go
vendored
Normal file
22
pkg/env/env.go
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package env
|
||||
|
||||
// Environment Variables.
|
||||
var HelpUnstyled = "HELP_STYLE_PLAIN"
|
||||
var HelpStyle = "HELP_STYLE"
|
||||
var Verbose = "VERBOSE"
|
||||
var Silent = "SILENT"
|
||||
var NoColor = "NO_COLOR"
|
||||
var ForceColor = "COLOR"
|
||||
var ValidationDisabled = "SKIP_VALIDATION"
|
||||
var Debug = "DEBUG"
|
||||
|
||||
// FlagNames are flags also available as environment variables.
|
||||
var FlagNames = map[string]string{
|
||||
"no-color": NoColor,
|
||||
"color": ForceColor,
|
||||
"silent": Silent,
|
||||
"verbose": Verbose,
|
||||
"skip-validation": ValidationDisabled,
|
||||
}
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package errors
|
||||
|
||||
import "fmt"
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package errors
|
||||
|
||||
import (
|
||||
@ -20,13 +10,14 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/pkg/statuscode"
|
||||
)
|
||||
|
||||
func showHelp(cmd *cobra.Command) {
|
||||
if cmd.Name() != _c.HelpCommandName {
|
||||
err := cmd.Help()
|
||||
if err != nil {
|
||||
os.Exit(_c.ExitStatusProgrammerError)
|
||||
os.Exit(statuscode.ProgrammerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35,7 +26,7 @@ func HandleCobraExit(cmd *cobra.Command, err error) {
|
||||
if err == nil {
|
||||
ok, err := cmd.Flags().GetBool(_c.HelpCommandName)
|
||||
if cmd.Name() == _c.HelpCommandName || err == nil && ok {
|
||||
os.Exit(_c.ExitStatusRenderHelp)
|
||||
os.Exit(statuscode.RenderHelp)
|
||||
}
|
||||
|
||||
os.Exit(42)
|
||||
@ -48,26 +39,26 @@ func HandleCobraExit(cmd *cobra.Command, err error) {
|
||||
case BadArguments:
|
||||
showHelp(cmd)
|
||||
logrus.Error(err)
|
||||
os.Exit(_c.ExitStatusUsage)
|
||||
os.Exit(statuscode.Usage)
|
||||
case NotFound:
|
||||
showHelp(cmd)
|
||||
logrus.Error(err)
|
||||
os.Exit(_c.ExitStatusNotFound)
|
||||
os.Exit(statuscode.NotFound)
|
||||
case ConfigError:
|
||||
showHelp(cmd)
|
||||
logrus.Error(err)
|
||||
os.Exit(_c.ExitStatusConfigError)
|
||||
os.Exit(statuscode.ConfigError)
|
||||
case EnvironmentError:
|
||||
logrus.Error(err)
|
||||
os.Exit(_c.ExitStatusConfigError)
|
||||
os.Exit(statuscode.ConfigError)
|
||||
default:
|
||||
if strings.HasPrefix(err.Error(), "unknown command") {
|
||||
showHelp(cmd)
|
||||
os.Exit(_c.ExitStatusNotFound)
|
||||
os.Exit(statuscode.NotFound)
|
||||
} else if strings.HasPrefix(err.Error(), "unknown flag") || strings.HasPrefix(err.Error(), "unknown shorthand flag") {
|
||||
showHelp(cmd)
|
||||
logrus.Error(err)
|
||||
os.Exit(_c.ExitStatusUsage)
|
||||
os.Exit(statuscode.Usage)
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package exec
|
||||
|
||||
import (
|
||||
@ -20,7 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
||||
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package exec_test
|
||||
|
||||
import (
|
||||
@ -20,7 +10,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "git.rob.mx/nidito/chinampa/internal/exec"
|
||||
. "git.rob.mx/nidito/chinampa/pkg/exec"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -1,22 +1,15 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package render
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/pkg/env"
|
||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||
"github.com/charmbracelet/glamour"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -43,7 +36,7 @@ func Markdown(content []byte, withColor bool) ([]byte, error) {
|
||||
var styleFunc glamour.TermRendererOption
|
||||
|
||||
if withColor {
|
||||
style := os.Getenv(_c.EnvVarHelpStyle)
|
||||
style := os.Getenv(env.HelpStyle)
|
||||
switch style {
|
||||
case "dark":
|
||||
styleFunc = glamour.WithStandardStyle("dark")
|
||||
@ -68,3 +61,27 @@ func Markdown(content []byte, withColor bool) ([]byte, error) {
|
||||
|
||||
return renderer.RenderBytes(content)
|
||||
}
|
||||
|
||||
// TemplateFuncs is a FuncMap with aliases to the strings package.
|
||||
var TemplateFuncs = template.FuncMap{
|
||||
"contains": strings.Contains,
|
||||
"hasSuffix": strings.HasSuffix,
|
||||
"hasPrefix": strings.HasPrefix,
|
||||
"replace": strings.ReplaceAll,
|
||||
"toUpper": strings.ToUpper,
|
||||
"toLower": strings.ToLower,
|
||||
"trim": strings.TrimSpace,
|
||||
"trimSuffix": strings.TrimSuffix,
|
||||
"trimPrefix": strings.TrimPrefix,
|
||||
}
|
||||
|
||||
// TemplateCommandHelp holds a template for rendering command help.
|
||||
var TemplateCommandHelp *template.Template
|
||||
|
||||
func HelpTemplate(executableName string) *template.Template {
|
||||
if TemplateCommandHelp == nil {
|
||||
TemplateCommandHelp = template.Must(template.New("help").Funcs(TemplateFuncs).Parse(strings.ReplaceAll(_c.HelpTemplate, "@chinampa@", executableName)))
|
||||
}
|
||||
|
||||
return TemplateCommandHelp
|
||||
}
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package render_test
|
||||
|
||||
import (
|
||||
@ -18,13 +8,13 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/internal/render"
|
||||
"git.rob.mx/nidito/chinampa/pkg/env"
|
||||
"git.rob.mx/nidito/chinampa/pkg/render"
|
||||
)
|
||||
|
||||
func TestMarkdownUnstyled(t *testing.T) {
|
||||
content := []byte("# hello")
|
||||
os.Setenv(_c.EnvVarHelpUnstyled, "true")
|
||||
os.Setenv(env.HelpUnstyled, "true")
|
||||
res, err := render.Markdown(content, false)
|
||||
|
||||
if err != nil {
|
||||
@ -38,7 +28,7 @@ func TestMarkdownUnstyled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMarkdownNoColor(t *testing.T) {
|
||||
os.Unsetenv(_c.EnvVarHelpUnstyled)
|
||||
os.Unsetenv(env.HelpUnstyled)
|
||||
content := []byte("# hello ﹅world﹅")
|
||||
res, err := render.Markdown(content, false)
|
||||
|
||||
@ -61,7 +51,7 @@ var autoStyleTestRender = "\n\x1b[38;5;228;48;5;63;1m\x1b[0m\x1b[38;5;228;48;5;6
|
||||
const lightStyleTestRender = "\n\x1b[38;5;228;48;5;63;1m\x1b[0m\x1b[38;5;228;48;5;63;1m\x1b[0m \x1b[38;5;228;48;5;63;1m \x1b[0m\x1b[38;5;228;48;5;63;1mhello\x1b[0m\x1b[38;5;228;48;5;63;1m \x1b[0m\x1b[38;5;234m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[38;5;234m \x1b[0m\x1b[0m\n\x1b[0m\n"
|
||||
|
||||
func TestMarkdownColor(t *testing.T) {
|
||||
os.Unsetenv(_c.EnvVarHelpUnstyled)
|
||||
os.Unsetenv(env.HelpUnstyled)
|
||||
content := []byte("# hello")
|
||||
|
||||
styles := map[string][]byte{
|
||||
@ -72,7 +62,7 @@ func TestMarkdownColor(t *testing.T) {
|
||||
}
|
||||
for style, expected := range styles {
|
||||
t.Run(fmt.Sprintf("style %s", style), func(t *testing.T) {
|
||||
os.Setenv(_c.EnvVarHelpStyle, style)
|
||||
os.Setenv(env.HelpStyle, style)
|
||||
res, err := render.Markdown(content, true)
|
||||
|
||||
if err != nil {
|
@ -1,22 +1,12 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/pkg/env"
|
||||
)
|
||||
|
||||
var Executable = "chinampa"
|
||||
@ -63,45 +53,60 @@ func isTrueIsh(val string) bool {
|
||||
}
|
||||
|
||||
func DebugEnabled() bool {
|
||||
return isTrueIsh(os.Getenv(_c.EnvVarDebug))
|
||||
return isTrueIsh(os.Getenv(env.Debug))
|
||||
}
|
||||
|
||||
func ValidationEnabled() bool {
|
||||
return isFalseIsh(os.Getenv(_c.EnvVarValidationDisabled))
|
||||
return isFalseIsh(os.Getenv(env.ValidationDisabled))
|
||||
}
|
||||
|
||||
func VerboseEnabled() bool {
|
||||
return isTrueIsh(os.Getenv(_c.EnvVarMilpaVerbose))
|
||||
return isTrueIsh(os.Getenv(env.Verbose))
|
||||
}
|
||||
|
||||
func SilenceEnabled() bool {
|
||||
if isTrueIsh(os.Getenv(env.Silent)) {
|
||||
return true
|
||||
}
|
||||
if VerboseEnabled() {
|
||||
return false
|
||||
}
|
||||
for _, arg := range os.Args {
|
||||
if arg == "--silent" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ColorEnabled() bool {
|
||||
return isFalseIsh(os.Getenv(_c.EnvVarMilpaUnstyled)) && !UnstyledHelpEnabled()
|
||||
return isFalseIsh(os.Getenv(env.NoColor)) && !UnstyledHelpEnabled()
|
||||
}
|
||||
|
||||
func UnstyledHelpEnabled() bool {
|
||||
return isTrueIsh(os.Getenv(_c.EnvVarHelpUnstyled))
|
||||
return isTrueIsh(os.Getenv(env.HelpUnstyled))
|
||||
}
|
||||
|
||||
// EnvironmentMap returns the resolved environment map.
|
||||
func EnvironmentMap() map[string]string {
|
||||
env := map[string]string{}
|
||||
res := map[string]string{}
|
||||
trueString := strconv.FormatBool(true)
|
||||
|
||||
if !ColorEnabled() {
|
||||
env[_c.EnvVarMilpaUnstyled] = trueString
|
||||
} else if isTrueIsh(os.Getenv(_c.EnvVarMilpaForceColor)) {
|
||||
env[_c.EnvVarMilpaForceColor] = "always"
|
||||
res[env.NoColor] = trueString
|
||||
} else if isTrueIsh(os.Getenv(env.ForceColor)) {
|
||||
res[env.ForceColor] = "always"
|
||||
}
|
||||
|
||||
if DebugEnabled() {
|
||||
env[_c.EnvVarDebug] = trueString
|
||||
res[env.Debug] = trueString
|
||||
}
|
||||
|
||||
if VerboseEnabled() {
|
||||
env[_c.EnvVarMilpaVerbose] = trueString
|
||||
} else if isTrueIsh(os.Getenv(_c.EnvVarMilpaSilent)) {
|
||||
env[_c.EnvVarMilpaSilent] = trueString
|
||||
res[env.Verbose] = trueString
|
||||
} else if SilenceEnabled() {
|
||||
res[env.Silent] = trueString
|
||||
}
|
||||
|
||||
return env
|
||||
return res
|
||||
}
|
||||
|
@ -1,15 +1,5 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
@ -20,12 +10,12 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||
"git.rob.mx/nidito/chinampa/pkg/env"
|
||||
. "git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestEnabled(t *testing.T) {
|
||||
defer func() { os.Setenv(_c.EnvVarMilpaVerbose, "") }()
|
||||
defer func() { os.Setenv(env.Verbose, "") }()
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
@ -33,29 +23,34 @@ func TestEnabled(t *testing.T) {
|
||||
Expects bool
|
||||
}{
|
||||
{
|
||||
Name: _c.EnvVarMilpaVerbose,
|
||||
Name: env.Verbose,
|
||||
Func: VerboseEnabled,
|
||||
Expects: true,
|
||||
},
|
||||
{
|
||||
Name: _c.EnvVarValidationDisabled,
|
||||
Name: env.Verbose,
|
||||
Func: SilenceEnabled,
|
||||
Expects: false,
|
||||
},
|
||||
{
|
||||
Name: env.ValidationDisabled,
|
||||
Func: ValidationEnabled,
|
||||
},
|
||||
{
|
||||
Name: _c.EnvVarMilpaUnstyled,
|
||||
Name: env.NoColor,
|
||||
Func: ColorEnabled,
|
||||
},
|
||||
{
|
||||
Name: _c.EnvVarHelpUnstyled,
|
||||
Name: env.HelpUnstyled,
|
||||
Func: ColorEnabled,
|
||||
},
|
||||
{
|
||||
Name: _c.EnvVarDebug,
|
||||
Name: env.Debug,
|
||||
Func: DebugEnabled,
|
||||
Expects: true,
|
||||
},
|
||||
{
|
||||
Name: _c.EnvVarHelpUnstyled,
|
||||
Name: env.HelpUnstyled,
|
||||
Func: UnstyledHelpEnabled,
|
||||
Expects: true,
|
||||
},
|
||||
@ -90,9 +85,9 @@ func TestEnabled(t *testing.T) {
|
||||
|
||||
func TestEnvironmentMapEnabled(t *testing.T) {
|
||||
trueString := strconv.FormatBool(true)
|
||||
os.Setenv(_c.EnvVarMilpaForceColor, trueString)
|
||||
os.Setenv(_c.EnvVarDebug, trueString)
|
||||
os.Setenv(_c.EnvVarMilpaVerbose, trueString)
|
||||
os.Setenv(env.ForceColor, trueString)
|
||||
os.Setenv(env.Debug, trueString)
|
||||
os.Setenv(env.Verbose, trueString)
|
||||
|
||||
res := EnvironmentMap()
|
||||
if res == nil {
|
||||
@ -100,9 +95,9 @@ func TestEnvironmentMapEnabled(t *testing.T) {
|
||||
}
|
||||
|
||||
expected := map[string]string{
|
||||
_c.EnvVarMilpaForceColor: "always",
|
||||
_c.EnvVarDebug: trueString,
|
||||
_c.EnvVarMilpaVerbose: trueString,
|
||||
env.ForceColor: "always",
|
||||
env.Debug: trueString,
|
||||
env.Verbose: trueString,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
@ -113,12 +108,12 @@ func TestEnvironmentMapEnabled(t *testing.T) {
|
||||
func TestEnvironmentMapDisabled(t *testing.T) {
|
||||
trueString := strconv.FormatBool(true)
|
||||
// clear COLOR
|
||||
os.Unsetenv(_c.EnvVarMilpaForceColor)
|
||||
os.Unsetenv(env.ForceColor)
|
||||
// set NO_COLOR
|
||||
os.Setenv(_c.EnvVarMilpaUnstyled, trueString)
|
||||
os.Unsetenv(_c.EnvVarDebug)
|
||||
os.Unsetenv(_c.EnvVarMilpaVerbose)
|
||||
os.Setenv(_c.EnvVarMilpaSilent, trueString)
|
||||
os.Setenv(env.NoColor, trueString)
|
||||
os.Unsetenv(env.Debug)
|
||||
os.Unsetenv(env.Verbose)
|
||||
os.Setenv(env.Silent, trueString)
|
||||
|
||||
res := EnvironmentMap()
|
||||
if res == nil {
|
||||
@ -126,8 +121,8 @@ func TestEnvironmentMapDisabled(t *testing.T) {
|
||||
}
|
||||
|
||||
expected := map[string]string{
|
||||
_c.EnvVarMilpaUnstyled: trueString,
|
||||
_c.EnvVarMilpaSilent: trueString,
|
||||
env.NoColor: trueString,
|
||||
env.Silent: trueString,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
|
22
pkg/statuscode/statuscode.go
Normal file
22
pkg/statuscode/statuscode.go
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package statuscode
|
||||
|
||||
// Exit statuses
|
||||
// see man sysexits || grep "#define EX" /usr/include/sysexits.h
|
||||
// and https://tldp.org/LDP/abs/html/exitcodes.html
|
||||
const (
|
||||
// 0 means everything is fine.
|
||||
Ok = 0
|
||||
// 42 provides answers to life, the universe and everything; also, renders help.
|
||||
RenderHelp = 42
|
||||
// 64 bad arguments
|
||||
// EX_USAGE The command was used incorrectly, e.g., with the wrong number of arguments, a bad flag, a bad syntax in a parameter, or whatever.
|
||||
Usage = 64
|
||||
// EX_SOFTWARE An internal software error has been detected. This should be limited to non-operating system related errors as possible.
|
||||
ProgrammerError = 70
|
||||
// EX_CONFIG Something was found in an unconfigured or misconfigured state.
|
||||
ConfigError = 78
|
||||
// 127 command not found.
|
||||
NotFound = 127
|
||||
)
|
84
pkg/tree/tree.go
Normal file
84
pkg/tree/tree.go
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package tree
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"git.rob.mx/nidito/chinampa/internal/registry"
|
||||
"git.rob.mx/nidito/chinampa/pkg/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type CommandTree struct {
|
||||
Command *command.Command `json:"command"`
|
||||
Children []*CommandTree `json:"children"`
|
||||
}
|
||||
|
||||
func (t *CommandTree) Traverse(fn func(cmd *command.Command) error) error {
|
||||
for _, child := range t.Children {
|
||||
if err := fn(child.Command); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := child.Traverse(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var tree *CommandTree
|
||||
|
||||
func Build(cc *cobra.Command, depth int) {
|
||||
tree = &CommandTree{
|
||||
Command: registry.FromCobra(cc),
|
||||
Children: []*CommandTree{},
|
||||
}
|
||||
|
||||
var populateTree func(cmd *cobra.Command, ct *CommandTree, maxDepth int, depth int)
|
||||
populateTree = func(cmd *cobra.Command, ct *CommandTree, maxDepth int, depth int) {
|
||||
newDepth := depth + 1
|
||||
for _, subcc := range cmd.Commands() {
|
||||
if subcc.Hidden {
|
||||
continue
|
||||
}
|
||||
|
||||
if cmd := registry.FromCobra(subcc); cmd != nil {
|
||||
leaf := &CommandTree{Children: []*CommandTree{}}
|
||||
leaf.Command = cmd
|
||||
ct.Children = append(ct.Children, leaf)
|
||||
|
||||
if newDepth < maxDepth {
|
||||
populateTree(subcc, leaf, maxDepth, newDepth)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
populateTree(cc, tree, depth, 0)
|
||||
}
|
||||
|
||||
func Serialize(serializationFn func(any) ([]byte, error)) (string, error) {
|
||||
bytes, err := serializationFn(tree)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func ChildrenNames() []string {
|
||||
if tree == nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
ret := make([]string, len(tree.Children))
|
||||
for idx, cmd := range tree.Children {
|
||||
ret[idx] = cmd.Command.Name()
|
||||
}
|
||||
sort.Strings(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
func CommandList() []*command.Command {
|
||||
return registry.CommandList()
|
||||
}
|
Loading…
Reference in New Issue
Block a user