moar milpa stuff
This commit is contained in:
parent
831e68c7b7
commit
8ea5f42ef8
@ -12,3 +12,10 @@ indent_style = space
|
|||||||
indent_size = 2
|
indent_size = 2
|
||||||
max_line_length = 120
|
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/cobra v1.6.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
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/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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
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/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
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.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/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 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
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 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
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.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/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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
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=
|
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 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-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/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/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=
|
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>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
// Embed requires an import so the compiler knows what's up. Golint requires a comment. Gotta please em both.
|
// Embed requires an import so the compiler knows what's up. Golint requires a comment. Gotta please em both.
|
||||||
_ "embed"
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
||||||
const HelpCommandName = "help"
|
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
|
//go:embed help.md
|
||||||
var helpTemplateText string
|
var HelpTemplate 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
|
|
||||||
}
|
|
||||||
|
@ -1,24 +1,13 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"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/command"
|
||||||
|
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -29,7 +18,7 @@ func newCobraRoot(root *command.Command) *cobra.Command {
|
|||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: root.Name() + " [--silent|-v|--verbose] [--[no-]color] [-h|--help] [--version]",
|
Use: root.Name() + " [--silent|-v|--verbose] [--[no-]color] [-h|--help] [--version]",
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
_c.ContextKeyRuntimeIndex: root.Name(),
|
ContextKeyRuntimeIndex: root.Name(),
|
||||||
},
|
},
|
||||||
Short: root.Summary,
|
Short: root.Summary,
|
||||||
Long: root.Description,
|
Long: root.Description,
|
||||||
@ -62,13 +51,12 @@ func newCobraRoot(root *command.Command) *cobra.Command {
|
|||||||
}
|
}
|
||||||
return errors.NotFound{Msg: "No subcommand provided", Group: []string{}}
|
return errors.NotFound{Msg: "No subcommand provided", Group: []string{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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()
|
localName := cmd.Name()
|
||||||
useSpec := []string{localName, "[options]"}
|
useSpec := []string{localName, "[options]"}
|
||||||
for _, arg := range cmd.Arguments {
|
for _, arg := range cmd.Arguments {
|
||||||
@ -81,8 +69,9 @@ func toCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command
|
|||||||
DisableAutoGenTag: true,
|
DisableAutoGenTag: true,
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
SilenceErrors: true,
|
SilenceErrors: true,
|
||||||
|
Hidden: cmd.Hidden,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
_c.ContextKeyRuntimeIndex: cmd.FullName(),
|
ContextKeyRuntimeIndex: cmd.FullName(),
|
||||||
},
|
},
|
||||||
Args: func(cc *cobra.Command, supplied []string) error {
|
Args: func(cc *cobra.Command, supplied []string) error {
|
||||||
skipValidation, _ := cc.Flags().GetBool("skip-validation")
|
skipValidation, _ := cc.Flags().GetBool("skip-validation")
|
||||||
@ -114,8 +103,8 @@ func toCobra(cmd *command.Command, globalOptions command.Options) *cobra.Command
|
|||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromCobra(cc *cobra.Command) *command.Command {
|
func FromCobra(cc *cobra.Command) *command.Command {
|
||||||
rtidx, hasAnnotation := cc.Annotations[_c.ContextKeyRuntimeIndex]
|
rtidx, hasAnnotation := cc.Annotations[ContextKeyRuntimeIndex]
|
||||||
if hasAnnotation {
|
if hasAnnotation {
|
||||||
return Get(rtidx)
|
return Get(rtidx)
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -18,14 +8,18 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
"git.rob.mx/nidito/chinampa/internal/commands"
|
||||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
|
||||||
"git.rob.mx/nidito/chinampa/pkg/command"
|
"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/fatih/color"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"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{
|
var registry = &CommandRegistry{
|
||||||
kv: map[string]*command.Command{},
|
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) 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() }
|
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 {
|
type CommandRegistry struct {
|
||||||
kv map[string]*command.Command
|
kv map[string]*command.Command
|
||||||
byPath []*command.Command
|
byPath []*command.Command
|
||||||
tree *CommandTree
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Register(cmd *command.Command) {
|
func Register(cmd *command.Command) {
|
||||||
@ -82,87 +57,54 @@ func CommandList() []*command.Command {
|
|||||||
return registry.byPath
|
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 {
|
func Execute(version string) error {
|
||||||
cmdRoot := command.Root
|
cmdRoot := command.Root
|
||||||
ccRoot := newCobraRoot(command.Root)
|
ccRoot := newCobraRoot(command.Root)
|
||||||
ccRoot.Annotations["version"] = version
|
ccRoot.Annotations["version"] = version
|
||||||
ccRoot.CompletionOptions.HiddenDefaultCmd = true
|
ccRoot.CompletionOptions.HiddenDefaultCmd = true
|
||||||
ccRoot.PersistentFlags().AddFlagSet(cmdRoot.FlagSet())
|
ccRoot.PersistentFlags().AddFlagSet(cmdRoot.FlagSet())
|
||||||
|
ccRoot.SetHelpCommand(commands.Help)
|
||||||
|
ccRoot.AddCommand(commands.Version)
|
||||||
|
ccRoot.AddCommand(commands.GenerateCompletions)
|
||||||
|
|
||||||
for name, opt := range cmdRoot.Options {
|
for name, opt := range cmdRoot.Options {
|
||||||
if err := ccRoot.RegisterFlagCompletionFunc(name, opt.CompletionFunction); err != nil {
|
if err := ccRoot.RegisterFlagCompletionFunc(name, opt.CompletionFunction); err != nil {
|
||||||
logrus.Errorf("Failed setting up autocompletion for option <%s> of command <%s>", name, cmdRoot.FullName())
|
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))
|
ccRoot.SetHelpFunc(cmdRoot.HelpRenderer(cmdRoot.Options))
|
||||||
|
|
||||||
for _, cmd := range CommandList() {
|
for _, cmd := range CommandList() {
|
||||||
cmd := cmd
|
cmd := cmd
|
||||||
leaf := toCobra(cmd, cmdRoot.Options)
|
|
||||||
container := ccRoot
|
container := ccRoot
|
||||||
for idx, cp := range cmd.Path {
|
for idx, cp := range cmd.Path {
|
||||||
if idx == len(cmd.Path)-1 {
|
if idx == len(cmd.Path)-1 {
|
||||||
|
leaf := ToCobra(cmd, cmdRoot.Options)
|
||||||
logrus.Debugf("adding command %s to %s", leaf.Name(), container.Name())
|
logrus.Debugf("adding command %s to %s", leaf.Name(), container.Name())
|
||||||
container.AddCommand(leaf)
|
container.AddCommand(leaf)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
query := []string{cp}
|
query := []string{cp}
|
||||||
if cc, _, err := container.Find(query); err == nil && cc != container {
|
found := false
|
||||||
container = cc
|
if cp == "help" && container == ccRoot {
|
||||||
|
container = commands.Help
|
||||||
} else {
|
} else {
|
||||||
|
for _, sub := range container.Commands() {
|
||||||
|
if sub.Name() == cp {
|
||||||
|
container = sub
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
groupName := strings.Join(query, " ")
|
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{
|
cc := &cobra.Command{
|
||||||
Use: cp,
|
Use: cp,
|
||||||
Short: fmt.Sprintf("%s subcommands", groupName),
|
Short: fmt.Sprintf("%s subcommands", groupName),
|
||||||
@ -171,7 +113,7 @@ func Execute(version string) error {
|
|||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
SilenceErrors: true,
|
SilenceErrors: true,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
_c.ContextKeyRuntimeIndex: strings.Join(groupPath, " "),
|
ContextKeyRuntimeIndex: strings.Join(groupPath, " "),
|
||||||
},
|
},
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := cobra.OnlyValidArgs(cmd, args); err == nil {
|
if err := cobra.OnlyValidArgs(cmd, args); err == nil {
|
||||||
@ -196,13 +138,13 @@ func Execute(version string) error {
|
|||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return errors.NotFound{Msg: "No subcommand provided", Group: []string{}}
|
return errors.NotFound{Msg: "No subcommand provided", Group: []string{}}
|
||||||
}
|
}
|
||||||
os.Exit(_c.ExitStatusNotFound)
|
os.Exit(statuscode.NotFound)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
groupParent := &command.Command{
|
groupParent := &command.Command{
|
||||||
Path: cmd.Path[0 : len(cmd.Path)-1],
|
Path: groupPath,
|
||||||
Summary: fmt.Sprintf("%s subcommands", groupName),
|
Summary: fmt.Sprintf("%s subcommands", groupName),
|
||||||
Description: fmt.Sprintf("Runs subcommands within %s", groupName),
|
Description: fmt.Sprintf("Runs subcommands within %s", groupName),
|
||||||
Arguments: command.Arguments{},
|
Arguments: command.Arguments{},
|
||||||
@ -219,12 +161,21 @@ func Execute(version string) error {
|
|||||||
}
|
}
|
||||||
cmdRoot.SetCobra(ccRoot)
|
cmdRoot.SetCobra(ccRoot)
|
||||||
|
|
||||||
current, _, err := ccRoot.Find(os.Args[1:])
|
current, remaining, err := ccRoot.Find(os.Args[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
current = ccRoot
|
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()
|
err = current.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logrus.Debugf("Chinampa found error calling command %s", current.Name())
|
||||||
errors.HandleCobraExit(current, err)
|
errors.HandleCobraExit(current, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
main.go
12
main.go
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package chinampa
|
package chinampa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,22 +1,12 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -55,10 +45,11 @@ func (args *Arguments) Parse(supplied []string) {
|
|||||||
if !argumentProvided {
|
if !argumentProvided {
|
||||||
if arg.Default != nil {
|
if arg.Default != nil {
|
||||||
if arg.Variadic {
|
if arg.Variadic {
|
||||||
defaultSlice := []string{}
|
defaultSlice := arg.Default.([]string)
|
||||||
for _, valI := range arg.Default.([]any) {
|
// defaultSlice := []string{}
|
||||||
defaultSlice = append(defaultSlice, valI.(string))
|
// for _, valI := range arg.Default.([]string) {
|
||||||
}
|
// defaultSlice = append(defaultSlice, valI.(string))
|
||||||
|
// }
|
||||||
arg.provided = &defaultSlice
|
arg.provided = &defaultSlice
|
||||||
} else {
|
} else {
|
||||||
defaultString := arg.Default.(string)
|
defaultString := arg.Default.(string)
|
||||||
@ -194,11 +185,11 @@ func (arg *Argument) ToValue() any {
|
|||||||
} else {
|
} else {
|
||||||
if arg.Default != nil {
|
if arg.Default != nil {
|
||||||
if arg.Variadic {
|
if arg.Variadic {
|
||||||
defaultSlice := []string{}
|
defaultSlice := arg.Default
|
||||||
for _, valI := range arg.Default.([]any) {
|
// for _, valI := range arg.Default.([]any) {
|
||||||
valStr := valI.(string)
|
// valStr := valI.(string)
|
||||||
defaultSlice = append(defaultSlice, valStr)
|
// defaultSlice = append(defaultSlice, valStr)
|
||||||
}
|
// }
|
||||||
|
|
||||||
value = defaultSlice
|
value = defaultSlice
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command_test
|
package command_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,22 +1,13 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -38,9 +29,12 @@ type Command struct {
|
|||||||
Options Options `json:"options" yaml:"options" validate:"dive"`
|
Options Options `json:"options" yaml:"options" validate:"dive"`
|
||||||
HelpFunc HelpFunc `json:"-" yaml:"-"`
|
HelpFunc HelpFunc `json:"-" yaml:"-"`
|
||||||
// The action to take upon running
|
// The action to take upon running
|
||||||
Action Action
|
Action Action `json:"-" yaml:"-"`
|
||||||
runtimeFlags *pflag.FlagSet
|
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 {
|
func (cmd *Command) IsRoot() bool {
|
||||||
@ -88,6 +82,22 @@ func (cmd *Command) FlagSet() *pflag.FlagSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fs.BoolP(name, opt.ShortName, def, opt.Description)
|
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:
|
case ValueTypeDefault, ValueTypeString:
|
||||||
opt.Type = ValueTypeString
|
opt.Type = ValueTypeString
|
||||||
def := ""
|
def := ""
|
||||||
@ -119,7 +129,7 @@ func (cmd *Command) ParseInput(cc *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
logrus.Debug("Validating flags")
|
logrus.Debug("Validating flags")
|
||||||
if err := cmd.Options.AreValid(); err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,11 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
"git.rob.mx/nidito/chinampa/pkg/render"
|
||||||
"git.rob.mx/nidito/chinampa/internal/render"
|
|
||||||
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
"git.rob.mx/nidito/chinampa/pkg/runtime"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -64,7 +53,7 @@ func (cmd *Command) ShowHelp(globalOptions Options, args []string) ([]byte, erro
|
|||||||
GlobalOptions: globalOptions,
|
GlobalOptions: globalOptions,
|
||||||
HTMLOutput: runtime.UnstyledHelpEnabled(),
|
HTMLOutput: runtime.UnstyledHelpEnabled(),
|
||||||
}
|
}
|
||||||
err := _c.HelpTemplate(runtime.Executable).Execute(&buf, c)
|
err := render.HelpTemplate(runtime.Executable).Execute(&buf, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -17,7 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
@ -41,54 +31,6 @@ func (opts *Options) AllKnownStr() map[string]string {
|
|||||||
return col
|
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) {
|
func (opts *Options) Parse(supplied *pflag.FlagSet) {
|
||||||
// logrus.Debugf("Parsing supplied flags, %v", supplied)
|
// logrus.Debugf("Parsing supplied flags, %v", supplied)
|
||||||
for name, opt := range *opts {
|
for name, opt := range *opts {
|
||||||
@ -98,6 +40,11 @@ func (opts *Options) Parse(supplied *pflag.FlagSet) {
|
|||||||
opt.provided = val
|
opt.provided = val
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
case ValueTypeInt:
|
||||||
|
if val, err := supplied.GetInt(name); err == nil {
|
||||||
|
opt.provided = val
|
||||||
|
continue
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
opt.Type = ValueTypeString
|
opt.Type = ValueTypeString
|
||||||
if val, err := supplied.GetString(name); err == nil {
|
if val, err := supplied.GetString(name); err == nil {
|
||||||
@ -121,7 +68,7 @@ func (opts *Options) AreValid() error {
|
|||||||
// Option represents a command line flag.
|
// Option represents a command line flag.
|
||||||
type Option struct {
|
type Option struct {
|
||||||
ShortName string `json:"short-name,omitempty" yaml:"short-name,omitempty"` // nolint:tagliatelle
|
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"`
|
Description string `json:"description" yaml:"description" validate:"required"`
|
||||||
Default any `json:"default,omitempty" yaml:"default,omitempty"`
|
Default any `json:"default,omitempty" yaml:"default,omitempty"`
|
||||||
Values *ValueSource `json:"values,omitempty" yaml:"values,omitempty" validate:"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 {
|
func (opt *Option) ToString() string {
|
||||||
value := opt.ToValue()
|
value := opt.ToValue()
|
||||||
stringValue := ""
|
stringValue := ""
|
||||||
if opt.Type == "bool" {
|
switch opt.Type {
|
||||||
|
case ValueTypeBoolean:
|
||||||
if value == nil {
|
if value == nil {
|
||||||
stringValue = ""
|
stringValue = ""
|
||||||
} else {
|
} else {
|
||||||
stringValue = strconv.FormatBool(value.(bool))
|
stringValue = strconv.FormatBool(value.(bool))
|
||||||
}
|
}
|
||||||
} else {
|
case ValueTypeInt:
|
||||||
|
if value == nil {
|
||||||
|
stringValue = ""
|
||||||
|
} else {
|
||||||
|
stringValue = fmt.Sprintf("%i", value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
if value != nil {
|
if value != nil {
|
||||||
stringValue = value.(string)
|
stringValue = value.(string)
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -21,9 +11,11 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
"git.rob.mx/nidito/chinampa/pkg/exec"
|
||||||
"git.rob.mx/nidito/chinampa/internal/exec"
|
"git.rob.mx/nidito/chinampa/pkg/render"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValueType represent the kinds of or option.
|
// ValueType represent the kinds of or option.
|
||||||
@ -36,6 +28,8 @@ const (
|
|||||||
ValueTypeString ValueType = "string"
|
ValueTypeString ValueType = "string"
|
||||||
// ValueTypeBoolean is a value treated like a boolean.
|
// ValueTypeBoolean is a value treated like a boolean.
|
||||||
ValueTypeBoolean ValueType = "bool"
|
ValueTypeBoolean ValueType = "bool"
|
||||||
|
// ValueTypeBoolean is a value treated like an integer.
|
||||||
|
ValueTypeInt ValueType = "int"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SourceCommand struct {
|
type SourceCommand struct {
|
||||||
@ -43,7 +37,7 @@ type SourceCommand struct {
|
|||||||
Args string
|
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.
|
// ValueSource represents the source for an auto-completed and/or validated option/argument.
|
||||||
type ValueSource struct {
|
type ValueSource struct {
|
||||||
@ -58,7 +52,7 @@ type ValueSource struct {
|
|||||||
// Command runs a subcommand and returns an option for every line of stdout.
|
// 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"`
|
Command *SourceCommand `json:"command,omitempty" yaml:"command,omitempty" validate:"omitempty,excluded_with=Directories Files Func Script Static"`
|
||||||
// Func runs a function
|
// 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 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"`
|
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
|
// 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:"-"`
|
command *Command `json:"-" yaml:"-" validate:"-"`
|
||||||
computed *[]string
|
computed *[]string
|
||||||
flag cobra.ShellCompDirective
|
flag cobra.ShellCompDirective
|
||||||
|
custom string // The app-defined key's value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validates tells if a value needs to be validated.
|
// 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
|
flag = cobra.ShellCompDirectiveDefault
|
||||||
timeout := time.Duration(vs.Timeout)
|
timeout := time.Duration(vs.Timeout)
|
||||||
|
|
||||||
|
logrus.Errorf("resolving: %s", vs)
|
||||||
switch {
|
switch {
|
||||||
case vs.Static != nil:
|
case vs.Static != nil:
|
||||||
values = *vs.Static
|
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
|
done <- err
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
@ -172,6 +168,8 @@ func (vs *ValueSource) Resolve(currentValue string) (values []string, flag cobra
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, flag, err
|
return nil, flag, err
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return nil, flag, fmt.Errorf("Empty value source")
|
||||||
}
|
}
|
||||||
|
|
||||||
vs.computed = &values
|
vs.computed = &values
|
||||||
@ -215,7 +213,7 @@ func (cmd *Command) ResolveTemplate(templateString string, currentValue string)
|
|||||||
"Current": func() string { return currentValue },
|
"Current": func() string { return currentValue },
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range _c.TemplateFuncs {
|
for k, v := range render.TemplateFuncs {
|
||||||
fnMap[k] = v
|
fnMap[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,3 +230,91 @@ func (cmd *Command) ResolveTemplate(templateString string, currentValue string)
|
|||||||
|
|
||||||
return buf.String(), nil
|
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>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package command_test
|
package command_test
|
||||||
|
|
||||||
import (
|
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>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package errors
|
package errors
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package errors
|
package errors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,13 +10,14 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
||||||
|
"git.rob.mx/nidito/chinampa/pkg/statuscode"
|
||||||
)
|
)
|
||||||
|
|
||||||
func showHelp(cmd *cobra.Command) {
|
func showHelp(cmd *cobra.Command) {
|
||||||
if cmd.Name() != _c.HelpCommandName {
|
if cmd.Name() != _c.HelpCommandName {
|
||||||
err := cmd.Help()
|
err := cmd.Help()
|
||||||
if err != nil {
|
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 {
|
if err == nil {
|
||||||
ok, err := cmd.Flags().GetBool(_c.HelpCommandName)
|
ok, err := cmd.Flags().GetBool(_c.HelpCommandName)
|
||||||
if cmd.Name() == _c.HelpCommandName || err == nil && ok {
|
if cmd.Name() == _c.HelpCommandName || err == nil && ok {
|
||||||
os.Exit(_c.ExitStatusRenderHelp)
|
os.Exit(statuscode.RenderHelp)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(42)
|
os.Exit(42)
|
||||||
@ -48,26 +39,26 @@ func HandleCobraExit(cmd *cobra.Command, err error) {
|
|||||||
case BadArguments:
|
case BadArguments:
|
||||||
showHelp(cmd)
|
showHelp(cmd)
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
os.Exit(_c.ExitStatusUsage)
|
os.Exit(statuscode.Usage)
|
||||||
case NotFound:
|
case NotFound:
|
||||||
showHelp(cmd)
|
showHelp(cmd)
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
os.Exit(_c.ExitStatusNotFound)
|
os.Exit(statuscode.NotFound)
|
||||||
case ConfigError:
|
case ConfigError:
|
||||||
showHelp(cmd)
|
showHelp(cmd)
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
os.Exit(_c.ExitStatusConfigError)
|
os.Exit(statuscode.ConfigError)
|
||||||
case EnvironmentError:
|
case EnvironmentError:
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
os.Exit(_c.ExitStatusConfigError)
|
os.Exit(statuscode.ConfigError)
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(err.Error(), "unknown command") {
|
if strings.HasPrefix(err.Error(), "unknown command") {
|
||||||
showHelp(cmd)
|
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") {
|
} else if strings.HasPrefix(err.Error(), "unknown flag") || strings.HasPrefix(err.Error(), "unknown shorthand flag") {
|
||||||
showHelp(cmd)
|
showHelp(cmd)
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
os.Exit(_c.ExitStatusUsage)
|
os.Exit(statuscode.Usage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package exec
|
package exec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.rob.mx/nidito/chinampa/internal/errors"
|
"git.rob.mx/nidito/chinampa/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package exec_test
|
package exec_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,7 +10,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "git.rob.mx/nidito/chinampa/internal/exec"
|
. "git.rob.mx/nidito/chinampa/pkg/exec"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
@ -1,22 +1,15 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
_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/runtime"
|
||||||
"github.com/charmbracelet/glamour"
|
"github.com/charmbracelet/glamour"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -43,7 +36,7 @@ func Markdown(content []byte, withColor bool) ([]byte, error) {
|
|||||||
var styleFunc glamour.TermRendererOption
|
var styleFunc glamour.TermRendererOption
|
||||||
|
|
||||||
if withColor {
|
if withColor {
|
||||||
style := os.Getenv(_c.EnvVarHelpStyle)
|
style := os.Getenv(env.HelpStyle)
|
||||||
switch style {
|
switch style {
|
||||||
case "dark":
|
case "dark":
|
||||||
styleFunc = glamour.WithStandardStyle("dark")
|
styleFunc = glamour.WithStandardStyle("dark")
|
||||||
@ -68,3 +61,27 @@ func Markdown(content []byte, withColor bool) ([]byte, error) {
|
|||||||
|
|
||||||
return renderer.RenderBytes(content)
|
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>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package render_test
|
package render_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -18,13 +8,13 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
"git.rob.mx/nidito/chinampa/pkg/env"
|
||||||
"git.rob.mx/nidito/chinampa/internal/render"
|
"git.rob.mx/nidito/chinampa/pkg/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMarkdownUnstyled(t *testing.T) {
|
func TestMarkdownUnstyled(t *testing.T) {
|
||||||
content := []byte("# hello")
|
content := []byte("# hello")
|
||||||
os.Setenv(_c.EnvVarHelpUnstyled, "true")
|
os.Setenv(env.HelpUnstyled, "true")
|
||||||
res, err := render.Markdown(content, false)
|
res, err := render.Markdown(content, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -38,7 +28,7 @@ func TestMarkdownUnstyled(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMarkdownNoColor(t *testing.T) {
|
func TestMarkdownNoColor(t *testing.T) {
|
||||||
os.Unsetenv(_c.EnvVarHelpUnstyled)
|
os.Unsetenv(env.HelpUnstyled)
|
||||||
content := []byte("# hello ﹅world﹅")
|
content := []byte("# hello ﹅world﹅")
|
||||||
res, err := render.Markdown(content, false)
|
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"
|
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) {
|
func TestMarkdownColor(t *testing.T) {
|
||||||
os.Unsetenv(_c.EnvVarHelpUnstyled)
|
os.Unsetenv(env.HelpUnstyled)
|
||||||
content := []byte("# hello")
|
content := []byte("# hello")
|
||||||
|
|
||||||
styles := map[string][]byte{
|
styles := map[string][]byte{
|
||||||
@ -72,7 +62,7 @@ func TestMarkdownColor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for style, expected := range styles {
|
for style, expected := range styles {
|
||||||
t.Run(fmt.Sprintf("style %s", style), func(t *testing.T) {
|
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)
|
res, err := render.Markdown(content, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
@ -1,22 +1,12 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
_c "git.rob.mx/nidito/chinampa/internal/constants"
|
"git.rob.mx/nidito/chinampa/pkg/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Executable = "chinampa"
|
var Executable = "chinampa"
|
||||||
@ -63,45 +53,60 @@ func isTrueIsh(val string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DebugEnabled() bool {
|
func DebugEnabled() bool {
|
||||||
return isTrueIsh(os.Getenv(_c.EnvVarDebug))
|
return isTrueIsh(os.Getenv(env.Debug))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidationEnabled() bool {
|
func ValidationEnabled() bool {
|
||||||
return isFalseIsh(os.Getenv(_c.EnvVarValidationDisabled))
|
return isFalseIsh(os.Getenv(env.ValidationDisabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerboseEnabled() bool {
|
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 {
|
func ColorEnabled() bool {
|
||||||
return isFalseIsh(os.Getenv(_c.EnvVarMilpaUnstyled)) && !UnstyledHelpEnabled()
|
return isFalseIsh(os.Getenv(env.NoColor)) && !UnstyledHelpEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnstyledHelpEnabled() bool {
|
func UnstyledHelpEnabled() bool {
|
||||||
return isTrueIsh(os.Getenv(_c.EnvVarHelpUnstyled))
|
return isTrueIsh(os.Getenv(env.HelpUnstyled))
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnvironmentMap returns the resolved environment map.
|
// EnvironmentMap returns the resolved environment map.
|
||||||
func EnvironmentMap() map[string]string {
|
func EnvironmentMap() map[string]string {
|
||||||
env := map[string]string{}
|
res := map[string]string{}
|
||||||
trueString := strconv.FormatBool(true)
|
trueString := strconv.FormatBool(true)
|
||||||
|
|
||||||
if !ColorEnabled() {
|
if !ColorEnabled() {
|
||||||
env[_c.EnvVarMilpaUnstyled] = trueString
|
res[env.NoColor] = trueString
|
||||||
} else if isTrueIsh(os.Getenv(_c.EnvVarMilpaForceColor)) {
|
} else if isTrueIsh(os.Getenv(env.ForceColor)) {
|
||||||
env[_c.EnvVarMilpaForceColor] = "always"
|
res[env.ForceColor] = "always"
|
||||||
}
|
}
|
||||||
|
|
||||||
if DebugEnabled() {
|
if DebugEnabled() {
|
||||||
env[_c.EnvVarDebug] = trueString
|
res[env.Debug] = trueString
|
||||||
}
|
}
|
||||||
|
|
||||||
if VerboseEnabled() {
|
if VerboseEnabled() {
|
||||||
env[_c.EnvVarMilpaVerbose] = trueString
|
res[env.Verbose] = trueString
|
||||||
} else if isTrueIsh(os.Getenv(_c.EnvVarMilpaSilent)) {
|
} else if SilenceEnabled() {
|
||||||
env[_c.EnvVarMilpaSilent] = trueString
|
res[env.Silent] = trueString
|
||||||
}
|
}
|
||||||
|
|
||||||
return env
|
return res
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
|
||||||
//
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
package runtime_test
|
package runtime_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,12 +10,12 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
_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/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEnabled(t *testing.T) {
|
func TestEnabled(t *testing.T) {
|
||||||
defer func() { os.Setenv(_c.EnvVarMilpaVerbose, "") }()
|
defer func() { os.Setenv(env.Verbose, "") }()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Name string
|
Name string
|
||||||
@ -33,29 +23,34 @@ func TestEnabled(t *testing.T) {
|
|||||||
Expects bool
|
Expects bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Name: _c.EnvVarMilpaVerbose,
|
Name: env.Verbose,
|
||||||
Func: VerboseEnabled,
|
Func: VerboseEnabled,
|
||||||
Expects: true,
|
Expects: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: _c.EnvVarValidationDisabled,
|
Name: env.Verbose,
|
||||||
|
Func: SilenceEnabled,
|
||||||
|
Expects: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: env.ValidationDisabled,
|
||||||
Func: ValidationEnabled,
|
Func: ValidationEnabled,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: _c.EnvVarMilpaUnstyled,
|
Name: env.NoColor,
|
||||||
Func: ColorEnabled,
|
Func: ColorEnabled,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: _c.EnvVarHelpUnstyled,
|
Name: env.HelpUnstyled,
|
||||||
Func: ColorEnabled,
|
Func: ColorEnabled,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: _c.EnvVarDebug,
|
Name: env.Debug,
|
||||||
Func: DebugEnabled,
|
Func: DebugEnabled,
|
||||||
Expects: true,
|
Expects: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: _c.EnvVarHelpUnstyled,
|
Name: env.HelpUnstyled,
|
||||||
Func: UnstyledHelpEnabled,
|
Func: UnstyledHelpEnabled,
|
||||||
Expects: true,
|
Expects: true,
|
||||||
},
|
},
|
||||||
@ -90,9 +85,9 @@ func TestEnabled(t *testing.T) {
|
|||||||
|
|
||||||
func TestEnvironmentMapEnabled(t *testing.T) {
|
func TestEnvironmentMapEnabled(t *testing.T) {
|
||||||
trueString := strconv.FormatBool(true)
|
trueString := strconv.FormatBool(true)
|
||||||
os.Setenv(_c.EnvVarMilpaForceColor, trueString)
|
os.Setenv(env.ForceColor, trueString)
|
||||||
os.Setenv(_c.EnvVarDebug, trueString)
|
os.Setenv(env.Debug, trueString)
|
||||||
os.Setenv(_c.EnvVarMilpaVerbose, trueString)
|
os.Setenv(env.Verbose, trueString)
|
||||||
|
|
||||||
res := EnvironmentMap()
|
res := EnvironmentMap()
|
||||||
if res == nil {
|
if res == nil {
|
||||||
@ -100,9 +95,9 @@ func TestEnvironmentMapEnabled(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
_c.EnvVarMilpaForceColor: "always",
|
env.ForceColor: "always",
|
||||||
_c.EnvVarDebug: trueString,
|
env.Debug: trueString,
|
||||||
_c.EnvVarMilpaVerbose: trueString,
|
env.Verbose: trueString,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(res, expected) {
|
if !reflect.DeepEqual(res, expected) {
|
||||||
@ -113,12 +108,12 @@ func TestEnvironmentMapEnabled(t *testing.T) {
|
|||||||
func TestEnvironmentMapDisabled(t *testing.T) {
|
func TestEnvironmentMapDisabled(t *testing.T) {
|
||||||
trueString := strconv.FormatBool(true)
|
trueString := strconv.FormatBool(true)
|
||||||
// clear COLOR
|
// clear COLOR
|
||||||
os.Unsetenv(_c.EnvVarMilpaForceColor)
|
os.Unsetenv(env.ForceColor)
|
||||||
// set NO_COLOR
|
// set NO_COLOR
|
||||||
os.Setenv(_c.EnvVarMilpaUnstyled, trueString)
|
os.Setenv(env.NoColor, trueString)
|
||||||
os.Unsetenv(_c.EnvVarDebug)
|
os.Unsetenv(env.Debug)
|
||||||
os.Unsetenv(_c.EnvVarMilpaVerbose)
|
os.Unsetenv(env.Verbose)
|
||||||
os.Setenv(_c.EnvVarMilpaSilent, trueString)
|
os.Setenv(env.Silent, trueString)
|
||||||
|
|
||||||
res := EnvironmentMap()
|
res := EnvironmentMap()
|
||||||
if res == nil {
|
if res == nil {
|
||||||
@ -126,8 +121,8 @@ func TestEnvironmentMapDisabled(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
_c.EnvVarMilpaUnstyled: trueString,
|
env.NoColor: trueString,
|
||||||
_c.EnvVarMilpaSilent: trueString,
|
env.Silent: trueString,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(res, expected) {
|
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