From 07ccb515849ca1a50462bf2e5059af23c14a516a Mon Sep 17 00:00:00 2001 From: jolheiser Date: Tue, 14 Jun 2022 19:59:53 +0000 Subject: [PATCH] Major changes (#22) 1. Switch from TOML to YAML (oof) 2. Add a JSON schema for templates (anti-oof) 3. Add functionality for prompt defaults to be based on earlier prompt answers 4. Add env commands and setting/loading for tmpl-specific env 5. Use a more helpful testing library Co-authored-by: jolheiser Reviewed-on: https://git.jojodev.com/jolheiser/tmpl/pulls/22 --- CLI.md | 12 ++ DOCS.md => FAQ.md | 58 +++++---- README.md | 2 +- docs.go => cli.go | 2 +- cmd/app.go | 10 +- cmd/download.go | 9 +- cmd/env.go | 69 ++++++++++- cmd/flags/flags.go | 6 - cmd/init.go | 17 +-- cmd/list.go | 3 +- cmd/remove.go | 3 +- cmd/restore.go | 3 +- cmd/save.go | 3 +- cmd/source.go | 7 +- cmd/test.go | 28 ++++- cmd/update.go | 3 +- cmd/use.go | 12 +- config/config.go | 41 +++++++ env/env.go | 50 ++++++++ go.mod | 58 +++++++-- go.sum | 242 ++++++++++++++++++++++---------------- registry/error.go | 5 - registry/helper.go | 9 +- registry/prompt.go | 134 +++++++++------------ registry/registry.go | 19 ++- registry/registry_test.go | 32 ++--- registry/source.go | 4 +- registry/source_test.go | 10 +- registry/template.go | 17 ++- registry/template_test.go | 107 +++++++---------- schema/convert.go | 46 ++++++++ schema/schema.go | 49 ++++++++ schema/tmpl.json | 51 ++++++++ 33 files changed, 732 insertions(+), 389 deletions(-) rename DOCS.md => FAQ.md (53%) rename docs.go => cli.go (95%) delete mode 100644 cmd/flags/flags.go create mode 100644 config/config.go create mode 100644 env/env.go create mode 100644 schema/convert.go create mode 100644 schema/schema.go create mode 100644 schema/tmpl.json diff --git a/CLI.md b/CLI.md index c7c5a8d..b9d8647 100644 --- a/CLI.md +++ b/CLI.md @@ -11,6 +11,10 @@ tmpl [--source|-s]=[value] ``` +# DESCRIPTION + +Template automation + **Usage**: ``` @@ -36,6 +40,14 @@ Download a template Show tmpl environment variables +### set, add + +Set a tmpl environment variable (stored plaintext) + +### unset, delete + +Unsets a tmpl environment variable + ## init Initialize a template diff --git a/DOCS.md b/FAQ.md similarity index 53% rename from DOCS.md rename to FAQ.md index 005df0b..b6d813a 100644 --- a/DOCS.md +++ b/FAQ.md @@ -6,34 +6,28 @@ This documentation aims to cover FAQs and setup. A "valid" tmpl template only requires two things -1. A `template.toml` file in the root directory. +1. A `tmpl.yaml` file in the root directory. 2. A `template` directory that serves as the "root" of the template. -## template.toml +## tmpl.yaml -**NOTE:** The template.toml file will be expanded, though not with the full power of the template itself. -The template.toml file will only expand environment variables with syntax `$USER` or `${USER}`. +**NOTE:** The tmpl.yaml file will be expanded, though not with the full power of the template itself. +The tmpl.yaml file will only expand environment variables with syntax `$USER` or `${USER}`. For full documentation on the syntax, see [os.ExpandEnv](https://golang.org/pkg/os/#ExpandEnv). When using the `--defaults` flag, no prompts will be shown and only default values will be used. As another alternative, any environment variable that matches a key will bypass the prompt. For example, `author` would have the corresponding environment variable `TMPL_VAR_AUTHOR`. -```toml -# Key-value pairs can be simple -# The user will receive a basic prompt asking them to fill out the variable -project = "my-project" +```yaml +# tmpl.yaml +# Write any template args here to prompt the user for, giving any defaults/options as applicable -# Extended properties MUST be added after any simple key-value pairs (due to how TOML works) - -# The "key" is enclosed in braces -[author] -# prompt is what will be shown to prompt the user -prompt = "The name of the author of this project" -# help would be extra information (generally seen by giving '?' to a prompt) -help = "Who will be primarily writing this project" -# default is the "value" part of the simple pair. This could be a suggested value -default = "$USER" +prompts: + - id: project # The unique ID for the prompt + label: Project Name # The prompt message/label + help: The name to use in the project # Optional help message for the prompt + default: tmpl # Prompt default ``` ## template directory @@ -44,7 +38,7 @@ Everything in this directory (including paths and file names!) will be executed See the [documentation](https://golang.org/pkg/text/template/) for every available possibility, but some basic examples are... -* A variable defined in template.toml (tmpl allows for keys to be called as a func or variable, whichever you prefer!) +* An id defined in `tmpl.yaml` (tmpl allows for keys to be called as a func or variable, whichever you prefer!) * `{{project}}` or `{{.project}}` * `{{author}}` or `{{.author}}` * Conditionally including something @@ -54,18 +48,20 @@ See the [documentation](https://golang.org/pkg/text/template/) for every availab For a full list, see [helper.go](registry/helper.go) -|Helper|Example|Output| -|-----|-----|-----| -|upper|`{{upper project}}`|`MY-PROJECT`| -|lower|`{{lower project}}`|`my-project`| -|title|`{{title project}}`|`My-Project`| -|snake|`{{snake project}}`|`my_project`| -|kebab|`{{kebab project}}`|`my-project`| -|pascal|`{{pascal project}}`|`MyProject`| -|camel|`{{camel project}}`|`myProject`| -|env|`{{env "USER"}}`|The current user| -|sep|`{{sep}}`|Filepath separator for current OS| -|time}|`{{time "01/02/2006"}}`|`11/21/2020` - The time according to the given [format](https://flaviocopes.com/go-date-time-format/)| +| Helper | Example | Output | +|-------------|----------------------------------|-------------------------------------------------------------------------------------------------------| +| upper | `{{upper project}}` | `MY-PROJECT` | +| lower | `{{lower project}}` | `my-project` | +| title | `{{title project}}` | `My-Project` | +| snake | `{{snake project}}` | `my_project` | +| kebab | `{{kebab project}}` | `my-project` | +| pascal | `{{pascal project}}` | `MyProject` | +| camel | `{{camel project}}` | `myProject` | +| env | `{{env "USER"}}` | The current user | +| sep | `{{sep}}` | Filepath separator for current OS | +| time | `{{time "01/02/2006"}}` | `11/21/2020` - The time according to the given [format](https://flaviocopes.com/go-date-time-format/) | +| trim_prefix | `{{trim_prefix "foobar" "foo"}}` | `bar` | +| trim_suffix | `{{trim_suffix "foobar" "bar"}}` | `foo` | ## Sources diff --git a/README.md b/README.md index c417315..70e91f8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ The two projects share many similarities, however other than general layout/stru [CLI Docs](CLI.md) -[Project Docs/FAQs](DOCS.md) +[Project Docs/FAQs](FAQ.md) ## Examples diff --git a/docs.go b/cli.go similarity index 95% rename from docs.go rename to cli.go index 00deb1f..d7d5988 100644 --- a/docs.go +++ b/cli.go @@ -11,7 +11,7 @@ import ( "go.jolheiser.com/tmpl/cmd" ) -//go:generate go run docs.go +//go:generate go run cli.go func main() { app := cmd.NewApp() diff --git a/cmd/app.go b/cmd/app.go index 1befa0c..a5a9d9c 100644 --- a/cmd/app.go +++ b/cmd/app.go @@ -4,8 +4,6 @@ import ( "os" "path/filepath" - "go.jolheiser.com/tmpl/cmd/flags" - "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" ) @@ -13,12 +11,16 @@ import ( var ( Version = "develop" defaultDir string + + registryFlag string + sourceFlag string ) func init() { home, err := os.UserHomeDir() if err != nil { log.Error().Msg("could not locate user's home directory, tmpl will use temp dir for registry") + defaultDir = filepath.Join(os.TempDir(), ".tmpl") return } defaultDir = filepath.Join(home, ".tmpl") @@ -37,14 +39,14 @@ func NewApp() *cli.App { Usage: "Registry directory of tmpl", Value: defaultDir, DefaultText: "~/.tmpl", - Destination: &flags.Registry, + Destination: ®istryFlag, EnvVars: []string{"TMPL_REGISTRY"}, }, &cli.StringFlag{ Name: "source", Aliases: []string{"s"}, Usage: "Short-name source to use", - Destination: &flags.Source, + Destination: &sourceFlag, EnvVars: []string{"TMPL_SOURCE"}, }, } diff --git a/cmd/download.go b/cmd/download.go index dac63bb..90469dd 100644 --- a/cmd/download.go +++ b/cmd/download.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "go.jolheiser.com/tmpl/cmd/flags" "go.jolheiser.com/tmpl/registry" "github.com/rs/zerolog/log" @@ -33,21 +32,21 @@ func runDownload(ctx *cli.Context) error { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } var source *registry.Source - if flags.Source != "" { + if sourceFlag != "" { for _, s := range reg.Sources { - if strings.EqualFold(s.Name, flags.Source) { + if strings.EqualFold(s.Name, sourceFlag) { source = s break } } if source == nil { - return fmt.Errorf("could not find source for %s", flags.Source) + return fmt.Errorf("could not find source for %s", sourceFlag) } } diff --git a/cmd/env.go b/cmd/env.go index 703efbf..de252d2 100644 --- a/cmd/env.go +++ b/cmd/env.go @@ -3,6 +3,8 @@ package cmd import ( "os" + "go.jolheiser.com/tmpl/env" + "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" ) @@ -12,20 +14,79 @@ var Env = &cli.Command{ Usage: "Show tmpl environment variables", Description: "Show tmpl environment variables and their configuration", Action: runEnv, + Subcommands: []*cli.Command{ + { + Name: "set", + Aliases: []string{"add"}, + Usage: "Set a tmpl environment variable (stored plaintext)", + Description: "Set an environment variable that will be loaded specifically when running tmpl (stored plaintext)", + ArgsUsage: "[key] [value]", + Action: runEnvSet, + }, + { + Name: "unset", + Aliases: []string{"delete"}, + Usage: "Unsets a tmpl environment variable", + Description: "Unsets an environment variable previously set for tmpl", + ArgsUsage: "[key]", + Action: runEnvUnset, + }, + }, } func runEnv(_ *cli.Context) error { - // Source - log.Info().Str("TMPL_SOURCE", os.Getenv("TMPL_SOURCE")).Msg("") + log.Info().Str("TMPL_SOURCE", os.Getenv("TMPL_SOURCE")).Msg("system") // Registry Path - log.Info().Str("TMPL_REGISTRY", os.Getenv("TMPL_REGISTRY")).Msg("") + log.Info().Str("TMPL_REGISTRY", os.Getenv("TMPL_REGISTRY")).Msg("system") // Branch - log.Info().Str("TMPL_BRANCH", os.Getenv("TMPL_BRANCH")).Msg("") + log.Info().Str("TMPL_BRANCH", os.Getenv("TMPL_BRANCH")).Msg("system") + + // Custom + e, err := env.Load(registryFlag) + if err != nil { + return err + } + for key, val := range e { + log.Info().Str(key, val).Msg("env.json") + } return nil } +func runEnvSet(ctx *cli.Context) error { + if ctx.NArg() < 2 { + return cli.ShowCommandHelp(ctx, ctx.Command.Name) + } + e, err := env.Load(registryFlag) + if err != nil { + return err + } + key, val := ctx.Args().Get(0), ctx.Args().Get(1) + e[key] = val + if err := env.Save(registryFlag, e); err != nil { + return err + } + log.Info().Str(key, val).Msg("Successfully saved tmpl environment variable!") + return nil +} +func runEnvUnset(ctx *cli.Context) error { + if ctx.NArg() < 1 { + return cli.ShowCommandHelp(ctx, ctx.Command.Name) + } + e, err := env.Load(registryFlag) + if err != nil { + return err + } + key := ctx.Args().First() + val := e[key] + delete(e, key) + if err := env.Save(registryFlag, e); err != nil { + return err + } + log.Info().Str(key, val).Msg("Successfully unset tmpl environment variable!") + return nil +} diff --git a/cmd/flags/flags.go b/cmd/flags/flags.go deleted file mode 100644 index 8bc7cac..0000000 --- a/cmd/flags/flags.go +++ /dev/null @@ -1,6 +0,0 @@ -package flags - -var ( - Registry string - Source string -) diff --git a/cmd/init.go b/cmd/init.go index 08ba88c..61c916c 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -16,11 +16,11 @@ var Init = &cli.Command{ } func runInit(_ *cli.Context) error { - if _, err := os.Lstat("template.toml"); !os.IsNotExist(err) { + if _, err := os.Lstat("tmpl.yaml"); !os.IsNotExist(err) { if err != nil { return err } - return errors.New("template.toml already detected, aborting initialization") + return errors.New("tmpl.yaml already detected, aborting initialization") } if fi, err := os.Lstat("template"); !os.IsNotExist(err) { if err != nil { @@ -32,7 +32,7 @@ func runInit(_ *cli.Context) error { return errors.New("template directory already detected, aborting initialization") } - fi, err := os.Create("template.toml") + fi, err := os.Create("tmpl.yaml") if err != nil { return err } @@ -46,11 +46,12 @@ func runInit(_ *cli.Context) error { return fi.Close() } -var comments = `# template.toml +var comments = `# tmpl.yaml # Write any template args here to prompt the user for, giving any defaults/options as applicable -[name] -prompt = "Project Name" -help = "The name to use in the project" -default = "tmpl" +prompts: + - id: name # The unique ID for the prompt + label: Project Name # The prompt message/label + help: The name to use in the project # Optional help message for the prompt + default: tmpl # Prompt default ` diff --git a/cmd/list.go b/cmd/list.go index 4f7143d..32002be 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -5,7 +5,6 @@ import ( "os" "text/tabwriter" - "go.jolheiser.com/tmpl/cmd/flags" "go.jolheiser.com/tmpl/registry" "github.com/urfave/cli/v2" @@ -19,7 +18,7 @@ var List = &cli.Command{ } func runList(_ *cli.Context) error { - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } diff --git a/cmd/remove.go b/cmd/remove.go index 9800118..c424517 100644 --- a/cmd/remove.go +++ b/cmd/remove.go @@ -1,7 +1,6 @@ package cmd import ( - "go.jolheiser.com/tmpl/cmd/flags" "go.jolheiser.com/tmpl/registry" "github.com/rs/zerolog/log" @@ -21,7 +20,7 @@ func runRemove(ctx *cli.Context) error { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } diff --git a/cmd/restore.go b/cmd/restore.go index cf96172..c88dc25 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -3,7 +3,6 @@ package cmd import ( "os" - "go.jolheiser.com/tmpl/cmd/flags" "go.jolheiser.com/tmpl/registry" "github.com/rs/zerolog/log" @@ -18,7 +17,7 @@ var Restore = &cli.Command{ } func runRestore(_ *cli.Context) error { - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } diff --git a/cmd/save.go b/cmd/save.go index 8b8ccfc..508f683 100644 --- a/cmd/save.go +++ b/cmd/save.go @@ -3,7 +3,6 @@ package cmd import ( "path/filepath" - "go.jolheiser.com/tmpl/cmd/flags" "go.jolheiser.com/tmpl/registry" "github.com/rs/zerolog/log" @@ -23,7 +22,7 @@ func runSave(ctx *cli.Context) error { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } diff --git a/cmd/source.go b/cmd/source.go index 355038e..1a5f820 100644 --- a/cmd/source.go +++ b/cmd/source.go @@ -5,7 +5,6 @@ import ( "os" "text/tabwriter" - "go.jolheiser.com/tmpl/cmd/flags" "go.jolheiser.com/tmpl/registry" "github.com/rs/zerolog/log" @@ -50,7 +49,7 @@ var ( ) func runSourceList(_ *cli.Context) error { - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } @@ -72,7 +71,7 @@ func runSourceAdd(ctx *cli.Context) error { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } @@ -91,7 +90,7 @@ func runSourceRemove(ctx *cli.Context) error { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } diff --git a/cmd/test.go b/cmd/test.go index 4450223..1cb327f 100644 --- a/cmd/test.go +++ b/cmd/test.go @@ -1,9 +1,13 @@ package cmd import ( + "errors" + "fmt" "os" "path/filepath" + "go.jolheiser.com/tmpl/schema" + "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" ) @@ -23,15 +27,28 @@ func runTest(ctx *cli.Context) error { } var errs []string - if _, err := os.Lstat(filepath.Join(testPath, "template.toml")); err != nil { - errs = append(errs, "could not find template.toml") + + fi, err := os.Open(filepath.Join(testPath, "tmpl.yaml")) + if err != nil { + errs = append(errs, fmt.Sprintf("could not open tmpl.yaml: %v", err)) + } + defer fi.Close() + if err := schema.Lint(fi); err != nil { + var rerr schema.ResultErrors + if errors.As(err, &rerr) { + for _, re := range rerr { + errs = append(errs, fmt.Sprintf("%s: %s", re.Field(), re.Description())) + } + } else { + errs = append(errs, fmt.Sprintf("could not lint tmpl.yaml: %v", err)) + } } - fi, err := os.Lstat(filepath.Join(testPath, "template")) + fstat, err := os.Lstat(filepath.Join(testPath, "template")) if err != nil { errs = append(errs, "no template directory found") } - if err == nil && !fi.IsDir() { + if err == nil && !fstat.IsDir() { errs = append(errs, "template path is a file, not a directory") } @@ -41,6 +58,7 @@ func runTest(ctx *cli.Context) error { } return nil } - log.Info().Msg("this is a valid tmpl template") + + log.Info().Msg("This is a valid tmpl template!") return nil } diff --git a/cmd/update.go b/cmd/update.go index d621bc6..f71efe3 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -1,7 +1,6 @@ package cmd import ( - "go.jolheiser.com/tmpl/cmd/flags" "go.jolheiser.com/tmpl/registry" "github.com/rs/zerolog/log" @@ -21,7 +20,7 @@ func runUpdate(ctx *cli.Context) error { return cli.ShowCommandHelp(ctx, ctx.Command.Name) } - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } diff --git a/cmd/use.go b/cmd/use.go index 74fa3d6..dfe0359 100644 --- a/cmd/use.go +++ b/cmd/use.go @@ -1,7 +1,7 @@ package cmd import ( - "go.jolheiser.com/tmpl/cmd/flags" + "go.jolheiser.com/tmpl/env" "go.jolheiser.com/tmpl/registry" "github.com/rs/zerolog/log" @@ -36,11 +36,19 @@ func runUse(ctx *cli.Context) error { dest = ctx.Args().Get(1) } - reg, err := registry.Open(flags.Registry) + reg, err := registry.Open(registryFlag) if err != nil { return err } + e, err := env.Load(registryFlag) + if err != nil { + return err + } + if err := e.Set(); err != nil { + return err + } + tmpl, err := reg.GetTemplate(ctx.Args().First()) if err != nil { return err diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..c306743 --- /dev/null +++ b/config/config.go @@ -0,0 +1,41 @@ +package config + +import ( + "fmt" + "io" + "os" + "strings" + + "gopkg.in/yaml.v3" +) + +// Config is a tmpl config +type Config struct { + Prompts []Prompt `yaml:"prompts"` +} + +// Prompt is a tmpl prompt +type Prompt struct { + ID string `yaml:"id"` + Label string `yaml:"label"` + Help string `yaml:"help"` + Default any `yaml:"default"` +} + +// Load loads a tmpl config +func Load(r io.Reader) (*Config, error) { + configBytes, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + configContents := os.Expand(string(configBytes), func(s string) string { + if strings.HasPrefix(s, "TMPL_PROMPT") { + return fmt.Sprintf("${%s}", s) + } + return os.Getenv(s) + }) + + var c Config + return &c, yaml.Unmarshal([]byte(configContents), &c) +} diff --git a/env/env.go b/env/env.go new file mode 100644 index 0000000..6c091ec --- /dev/null +++ b/env/env.go @@ -0,0 +1,50 @@ +package env + +import ( + "encoding/json" + "errors" + "os" + "path/filepath" +) + +// Env is tmpl environment variables +type Env map[string]string + +// Set sets all environment variables from an Env +func (e Env) Set() error { + for key, val := range e { + if err := os.Setenv(key, val); err != nil { + return err + } + } + return nil +} + +// Load loads an env from /env.json +func Load(path string) (Env, error) { + p := filepath.Join(path, "env.json") + fi, err := os.Open(p) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return Env{}, nil + } + return nil, err + } + defer fi.Close() + + var e Env + if err := json.NewDecoder(fi).Decode(&e); err != nil { + return nil, err + } + return e, nil +} + +// Save saves an Env to /env.json +func Save(path string, e Env) error { + p := filepath.Join(path, "env.json") + fi, err := os.Create(p) + if err != nil { + return err + } + return json.NewEncoder(fi).Encode(e) +} diff --git a/go.mod b/go.mod index 63613e5..e0e0591 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,55 @@ module go.jolheiser.com/tmpl -go 1.15 +go 1.18 require ( - github.com/AlecAivazis/survey/v2 v2.2.2 - github.com/go-git/go-git/v5 v5.2.0 + github.com/AlecAivazis/survey/v2 v2.3.5 + github.com/go-git/go-git/v5 v5.4.2 github.com/huandu/xstrings v1.3.2 - github.com/mattn/go-isatty v0.0.12 // indirect - github.com/mholt/archiver/v3 v3.5.0 - github.com/pelletier/go-toml v1.8.1 - github.com/rs/zerolog v1.26.0 - github.com/urfave/cli/v2 v2.3.0 - golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 // indirect + github.com/matryer/is v1.4.0 + github.com/mholt/archiver/v3 v3.5.1 + github.com/rs/zerolog v1.27.0 + github.com/urfave/cli/v2 v2.8.1 + github.com/xeipuuv/gojsonschema v1.2.0 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/Microsoft/go-winio v0.5.2 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b // indirect + github.com/acomagu/bufpipe v1.0.3 // indirect + github.com/andybalholm/brotli v1.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-git/gcfg v1.5.0 // indirect + github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.15.6 // indirect + github.com/klauspost/pgzip v1.2.5 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/nwaples/rardecode v1.1.3 // indirect + github.com/pierrec/lz4/v4 v4.1.14 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sergi/go-diff v1.2.0 // indirect + github.com/stretchr/testify v1.7.1 // indirect + github.com/ulikunitz/xz v0.5.10 // indirect + github.com/xanzy/ssh-agent v0.3.1 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect + golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect + golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect + golang.org/x/text v0.3.7 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 2901739..5be3a0d 100644 --- a/go.sum +++ b/go.sum @@ -1,162 +1,202 @@ -github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY= -github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= -github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= -github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ= +github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b h1:lcbBNuQhppsc7A5gjdHmdlqUqJfgGMylBdGyDs0j7G8= +github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= +github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= -github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= +github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= -github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= -github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= -github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI= -github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= -github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= -github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I= -github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.15.6 h1:6D9PcO8QWu0JyaQ2zUMmu16T1T+zjjEpP91guRsvDfY= +github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A= -github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= -github.com/kr/pty v1.1.4/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/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE= -github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= +github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pierrec/lz4/v4 v4.0.3 h1:vNQKSVZNYUEAvRY9FaUXAF1XPbSOHJtDTiP41kzDz2E= -github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= +github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= +github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= -github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= +github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4= -github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= +github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= +github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/registry/error.go b/registry/error.go index f7950f4..168ed92 100644 --- a/registry/error.go +++ b/registry/error.go @@ -23,11 +23,6 @@ func (e ErrTemplateNotFound) Error() string { return fmt.Sprintf("template not found for %s", e.Name) } -func IsErrTemplateNotFound(err error) bool { - _, ok := err.(ErrTemplateNotFound) - return ok -} - type ErrSourceNotFound struct { Name string } diff --git a/registry/helper.go b/registry/helper.go index 2ce93cb..a9bb49d 100644 --- a/registry/helper.go +++ b/registry/helper.go @@ -9,8 +9,7 @@ import ( "github.com/huandu/xstrings" ) -var funcMap = map[string]interface{}{ - +var funcMap = map[string]any{ // String conversions "upper": strings.ToUpper, "lower": strings.ToLower, @@ -21,6 +20,12 @@ var funcMap = map[string]interface{}{ "camel": func(in string) string { return xstrings.FirstRuneToLower(xstrings.ToCamelCase(in)) }, + "trim_prefix": func(in, trim string) string { + return strings.TrimPrefix(in, trim) + }, + "trim_suffix": func(in, trim string) string { + return strings.TrimSuffix(in, trim) + }, // Other "env": os.Getenv, diff --git a/registry/prompt.go b/registry/prompt.go index d0d34cf..e12adce 100644 --- a/registry/prompt.go +++ b/registry/prompt.go @@ -2,112 +2,97 @@ package registry import ( "fmt" - "io/ioutil" "os" "path/filepath" - "sort" "strings" "text/template" + "go.jolheiser.com/tmpl/config" + "github.com/AlecAivazis/survey/v2" - "github.com/pelletier/go-toml" ) -type templatePrompt struct { - Key string `toml:"-"` - Value interface{} `toml:"-"` - Message string `toml:"prompt"` - Help string `toml:"help"` - Default interface{} `toml:"default"` -} - func prompt(dir string, defaults bool) (templatePrompts, error) { - templatePath := filepath.Join(dir, "template.toml") - if _, err := os.Lstat(templatePath); err != nil { + templatePath := filepath.Join(dir, "tmpl.yaml") + fi, err := os.Open(templatePath) + if err != nil { return nil, err } + defer fi.Close() - templateBytes, err := ioutil.ReadFile(templatePath) + cfg, err := config.Load(fi) if err != nil { return nil, err } - // Expand the template with environment variables - templateContents := os.ExpandEnv(string(templateBytes)) - - tree, err := toml.Load(templateContents) - if err != nil { - return nil, err + prompts := make(templatePrompts, 0, len(cfg.Prompts)) + for _, prompt := range cfg.Prompts { + tp := templatePrompt{ + Prompt: prompt, + } + if tp.Label == "" { + tp.Label = tp.ID + } + if tp.Default == nil { + tp.Default = "" + } + prompts = append(prompts, tp) } - prompts := make(templatePrompts, len(tree.Keys())) - for idx, k := range tree.Keys() { - v := tree.Get(k) - - obj, ok := v.(*toml.Tree) - if !ok { - prompts[idx] = templatePrompt{ - Key: k, - Message: k, - Default: v, - } - continue - } - - var p templatePrompt - if err := obj.Unmarshal(&p); err != nil { - return nil, err - } - p.Key = k - if p.Message == "" { - p.Message = p.Key - } - if p.Default == nil { - p.Default = "" - } - prompts[idx] = p - } - - // Sort the prompts so they are consistent - sort.Sort(prompts) - for idx, prompt := range prompts { // Check for env variable - if e, ok := os.LookupEnv(fmt.Sprintf("TMPL_VAR_%s", strings.ToUpper(prompt.Key))); ok { + envKey := strings.ToUpper(prompt.ID) + if e, ok := os.LookupEnv(fmt.Sprintf("TMPL_VAR_%s", envKey)); ok { prompts[idx].Value = e + os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), e) continue } // Check if we are using defaults if defaults { - prompts[idx].Value = prompt.Default + val := prompt.Default + switch t := prompt.Default.(type) { + case []string: + for idy, s := range t { + t[idy] = os.ExpandEnv(s) + } + val = t + case string: + val = os.ExpandEnv(t) + } + s := fmt.Sprint(val) + prompts[idx].Value = s + os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), s) continue } var p survey.Prompt switch t := prompt.Default.(type) { case []string: + for idy, s := range t { + t[idy] = os.ExpandEnv(s) + } p = &survey.Select{ - Message: prompt.Message, + Message: prompt.Label, Options: t, Help: prompt.Help, } case bool: p = &survey.Confirm{ - Message: prompt.Message, + Message: prompt.Label, Default: t, Help: prompt.Help, } case string: p = &survey.Input{ - Message: prompt.Message, - Default: t, + Message: prompt.Label, + Default: os.ExpandEnv(t), Help: prompt.Help, } default: p = &survey.Input{ - Message: prompt.Message, - Default: fmt.Sprintf("%v", t), + Message: prompt.Label, + Default: fmt.Sprint(t), Help: prompt.Help, } } @@ -116,45 +101,36 @@ func prompt(dir string, defaults bool) (templatePrompts, error) { return nil, err } prompts[idx].Value = a + os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), a) } return prompts, nil } +type templatePrompt struct { + config.Prompt + Value string +} + type templatePrompts []templatePrompt // ToMap converts a slice to templatePrompt into a suitable template context -func (t templatePrompts) ToMap() map[string]interface{} { - m := make(map[string]interface{}) +func (t templatePrompts) ToMap() map[string]any { + m := make(map[string]any) for _, p := range t { - m[p.Key] = p.Value + m[p.ID] = p.Value } return m } // ToFuncMap converts a slice of templatePrompt into a suitable template.FuncMap func (t templatePrompts) ToFuncMap() template.FuncMap { - m := make(map[string]interface{}) + m := make(map[string]any) for k, v := range t.ToMap() { vv := v // Enclosure - m[k] = func() interface{} { + m[k] = func() any { return vv } } return m } - -// Len is for sort.Sort -func (t templatePrompts) Len() int { - return len(t) -} - -// Less is for sort.Sort -func (t templatePrompts) Less(i, j int) bool { - return t[i].Key > t[j].Key -} - -// Swap is for sort.Sort -func (t templatePrompts) Swap(i, j int) { - t[i], t[j] = t[j], t[i] -} diff --git a/registry/registry.go b/registry/registry.go index d2ddf01..a0f40ae 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -11,14 +11,14 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/mholt/archiver/v3" - "github.com/pelletier/go-toml" + "gopkg.in/yaml.v3" ) // Registry is a collection of Template type Registry struct { dir string - Sources []*Source `toml:"sources"` - Templates []*Template `toml:"templates"` + Sources []*Source `yaml:"sources"` + Templates []*Template `yaml:"templates"` } func (r *Registry) save() error { @@ -26,7 +26,7 @@ func (r *Registry) save() error { if err != nil { return err } - if err := toml.NewEncoder(fi).Encode(r); err != nil { + if err := yaml.NewEncoder(fi).Encode(r); err != nil { return err } return fi.Close() @@ -34,7 +34,7 @@ func (r *Registry) save() error { // MetaFilePath is the path to the Registry meta-file func (r *Registry) MetaFilePath() string { - return filepath.Join(r.dir, "registry.toml") + return filepath.Join(r.dir, "registry.yaml") } // GetTemplate retrieves a Template from the Registry @@ -111,7 +111,7 @@ func (r *Registry) RemoveTemplate(name string) error { return nil } -// RemoveTemplate updates the Template on disk and in meta +// UpdateTemplate updates the Template on disk and in meta func (r *Registry) UpdateTemplate(name string) error { _, err := r.GetTemplate(name) if err != nil { @@ -195,12 +195,12 @@ func Open(dir string) (*Registry, error) { } } - tree, err := toml.LoadFile(reg.MetaFilePath()) + contents, err := os.ReadFile(reg.MetaFilePath()) if err != nil { return nil, err } - if err := tree.Unmarshal(®); err != nil { + if err := yaml.Unmarshal(contents, ®); err != nil { return nil, err } @@ -254,9 +254,8 @@ func download(cloneURL, branch, dest string) error { } func save(source, dest string) error { - // Make sure it's a valid template - if _, err := os.Lstat(filepath.Join(source, "template.toml")); err != nil { + if _, err := os.Lstat(filepath.Join(source, "tmpl.yaml")); err != nil { return err } fi, err := os.Lstat(filepath.Join(source, "template")) diff --git a/registry/registry_test.go b/registry/registry_test.go index 628e29d..eb99ce1 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -1,26 +1,24 @@ package registry import ( + "errors" "fmt" "io/ioutil" "os" "path/filepath" "testing" + + "github.com/matryer/is" ) var ( tmplDir string regDir string - destDir string reg *Registry ) func TestMain(m *testing.M) { var err error - destDir, err = ioutil.TempDir(os.TempDir(), "tmpl-dest") - if err != nil { - panic(err) - } // Set up template setupTemplate() @@ -30,9 +28,6 @@ func TestMain(m *testing.M) { status := m.Run() - if err = os.RemoveAll(destDir); err != nil { - fmt.Printf("could not clean up temp directory %s\n", destDir) - } if err = os.RemoveAll(tmplDir); err != nil { fmt.Printf("could not clean up temp directory %s\n", tmplDir) } @@ -51,25 +46,22 @@ func TestTemplate(t *testing.T) { } func testSave(t *testing.T) { - if _, err := reg.SaveTemplate("test", tmplDir); err != nil { - t.Log("could not save template") - t.FailNow() - } + assert := is.New(t) + _, err := reg.SaveTemplate("test", tmplDir) + assert.NoErr(err) // Should save template } func testGet(t *testing.T) { + assert := is.New(t) _, err := reg.GetTemplate("test") - if err != nil { - t.Logf("could not get template") - t.FailNow() - } + assert.NoErr(err) // Should get template } func testGetFail(t *testing.T) { + assert := is.New(t) _, err := reg.GetTemplate("fail") - if !IsErrTemplateNotFound(err) { - t.Logf("template should not exist") - t.FailNow() + if !errors.As(err, &ErrTemplateNotFound{}) { + assert.Fail() // Template should not exist } } @@ -81,7 +73,7 @@ func setupTemplate() { } // Template config - fi, err := os.Create(filepath.Join(tmplDir, "template.toml")) + fi, err := os.Create(filepath.Join(tmplDir, "tmpl.yaml")) if err != nil { panic(err) } diff --git a/registry/source.go b/registry/source.go index 7770538..7e90290 100644 --- a/registry/source.go +++ b/registry/source.go @@ -5,8 +5,8 @@ import "fmt" // Source is a quick way to specify a git source // e.g. Gitea, GitHub, etc. type Source struct { - Name string `toml:"name"` - URL string `toml:"url"` + Name string `yaml:"name"` + URL string `yaml:"url"` } // CloneURL constructs a URL suitable for cloning a repository diff --git a/registry/source_test.go b/registry/source_test.go index 2613441..950e068 100644 --- a/registry/source_test.go +++ b/registry/source_test.go @@ -1,11 +1,14 @@ package registry import ( - "strings" "testing" + + "github.com/matryer/is" ) func TestSource(t *testing.T) { + assert := is.New(t) + tt := []struct { Name string Source *Source @@ -38,10 +41,7 @@ func TestSource(t *testing.T) { for _, tc := range tt { t.Run(tc.Name, func(t *testing.T) { cloneURL := tc.Source.CloneURL(namespace) - if !strings.EqualFold(tc.CloneURL, cloneURL) { - t.Logf("incorrect clone URL:\n\tExpected: %s\n\tGot: %s\n", tc.CloneURL, cloneURL) - t.Fail() - } + assert.Equal(tc.CloneURL, cloneURL) // Clone URLs should match }) } } diff --git a/registry/template.go b/registry/template.go index 76bf84c..c60e03a 100644 --- a/registry/template.go +++ b/registry/template.go @@ -15,12 +15,12 @@ import ( // Template is a tmpl project type Template struct { - reg *Registry `toml:"-"` - Name string `toml:"name"` - Path string `toml:"path"` - Repository string `toml:"repository"` - Branch string `toml:"branch"` - LastUpdate time.Time `toml:"last_update"` + reg *Registry `yaml:"-"` + Name string `yaml:"name"` + Path string `yaml:"path"` + Repository string `yaml:"repository"` + Branch string `yaml:"branch"` + LastUpdate time.Time `yaml:"last_update"` } // ArchiveName is the name given to the archive for this Template @@ -51,7 +51,6 @@ func (t *Template) Execute(dest string, defaults, overwrite bool) error { } funcs := mergeMaps(funcMap, prompts.ToFuncMap()) - base := filepath.Join(tmp, "template") return filepath.Walk(base, func(walkPath string, walkInfo os.FileInfo, walkErr error) error { if walkErr != nil { @@ -117,8 +116,8 @@ func (t *Template) Execute(dest string, defaults, overwrite bool) error { }) } -func mergeMaps(maps ...map[string]interface{}) map[string]interface{} { - m := make(map[string]interface{}) +func mergeMaps(maps ...map[string]any) map[string]any { + m := make(map[string]any) for _, mm := range maps { for k, v := range mm { m[k] = v diff --git a/registry/template_test.go b/registry/template_test.go index a51717b..138d84d 100644 --- a/registry/template_test.go +++ b/registry/template_test.go @@ -5,99 +5,74 @@ import ( "os" "path/filepath" "testing" + + "github.com/matryer/is" ) var ( - tmplContents = `{{title name}} (@{{username}}) {{if .bool}}{{.year}}{{end}}` + tmplContents = `{{title name}} (@{{username}}) {{if .bool}}{{.year}}{{end}} {{org}}` tmplTemplate = ` -name = "john olheiser" - -[year] -default = ${TMPL_TEST} # 2020 - -[package] -default = "pkg" - -[bool] -default = true - -[username] -default = "username" +prompts: + - id: name + default: john olheiser + - id: year + default: ${TMPL_TEST} # 2020 + - id: package + default: pkg + - id: bool + default: true + - id: username + default: username + - id: org + default: ${TMPL_PROMPT_USERNAME}/org ` - tmplGold = "John Olheiser (@jolheiser) 2020" + tmplGold = "John Olheiser (@jolheiser) 2020 jolheiser/org" tmplNewGold = "DO NOT OVERWRITE!" ) func testExecute(t *testing.T) { + assert := is.New(t) + destDir := t.TempDir() + // Set environment variable - if err := os.Setenv("TMPL_TEST", "2020"); err != nil { - t.Logf("could not set environment: %v", err) - t.FailNow() - } - if err := os.Setenv("TMPL_VAR_USERNAME", "jolheiser"); err != nil { - t.Logf("could not set environment: %v", err) - t.FailNow() - } + err := os.Setenv("TMPL_TEST", "2020") + assert.NoErr(err) // Should set TMPL_TEST env + + err = os.Setenv("TMPL_VAR_USERNAME", "jolheiser") + assert.NoErr(err) // Should set TMPL_VAR_USERNAME env // Get template tmpl, err := reg.GetTemplate("test") - if err != nil { - t.Logf("could not get template") - t.FailNow() - } + assert.NoErr(err) // Should get template // Execute template - if err := tmpl.Execute(destDir, true, true); err != nil { - t.Logf("could not execute template: %v\n", err) - t.FailNow() - } + err = tmpl.Execute(destDir, true, true) + assert.NoErr(err) // Should execute template // Check contents of file testPath := filepath.Join(destDir, "TEST") contents, err := ioutil.ReadFile(testPath) - if err != nil { - t.Logf("could not read file: %v\n", err) - t.FailNow() - } - - if string(contents) != tmplGold { - t.Logf("contents did not match:\n\tExpected: %s\n\tGot: %s", tmplGold, string(contents)) - t.FailNow() - } + assert.NoErr(err) // Should be able to read TEST file + assert.Equal(string(contents), tmplGold) // Template should match golden file // Check if directory was created pkgPath := filepath.Join(destDir, "PKG") - if _, err := os.Lstat(pkgPath); err != nil { - t.Logf("expected a directory at %s: %v\n", pkgPath, err) - t.FailNow() - } + _, err = os.Lstat(pkgPath) + assert.NoErr(err) // PKG directory should exist // Check for .tmplkeep tmplKeep := filepath.Join(pkgPath, ".tmplkeep") - if _, err := os.Lstat(tmplKeep); err == nil { - t.Logf(".tmplkeep files should NOT be retained upon execution: %s\n", tmplKeep) - t.FailNow() - } + _, err = os.Lstat(tmplKeep) + assert.True(err != nil) // .tmplkeep file should NOT be retained // Change file to test non-overwrite - if err := ioutil.WriteFile(testPath, []byte(tmplNewGold), os.ModePerm); err != nil { - t.Logf("could not write file: %v\n", err) - t.FailNow() - } + err = ioutil.WriteFile(testPath, []byte(tmplNewGold), os.ModePerm) + assert.NoErr(err) // Writing file should succeed - if err := tmpl.Execute(destDir, true, false); err != nil { - t.Logf("could not execute template: %v\n", err) - t.FailNow() - } + err = tmpl.Execute(destDir, true, false) + assert.NoErr(err) // Should execute template - contents, err = ioutil.ReadFile(testPath) - if err != nil { - t.Logf("could not read file: %v\n", err) - t.FailNow() - } - - if string(contents) != tmplNewGold { - t.Logf("contents did not match:\n\tExpected: %s\n\tGot: %s", tmplNewGold, string(contents)) - t.FailNow() - } + contents, err = os.ReadFile(testPath) + assert.NoErr(err) // Should be able to read file + assert.Equal(string(contents), tmplNewGold) // Template should match new golden file } diff --git a/schema/convert.go b/schema/convert.go new file mode 100644 index 0000000..03ff44c --- /dev/null +++ b/schema/convert.go @@ -0,0 +1,46 @@ +package schema + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +// Unmarshal YAML to map[string]any instead of map[any]any. +func Unmarshal(in []byte, out any) error { + var res any + + if err := yaml.Unmarshal(in, &res); err != nil { + return err + } + *out.(*any) = mapValue(res) + + return nil +} + +func mapSlice(in []any) []any { + res := make([]any, len(in)) + for i, v := range in { + res[i] = mapValue(v) + } + return res +} + +func mapMap(in map[any]any) map[string]any { + res := make(map[string]any) + for k, v := range in { + res[fmt.Sprintf("%v", k)] = mapValue(v) + } + return res +} + +func mapValue(v any) any { + switch v := v.(type) { + case []any: + return mapSlice(v) + case map[any]any: + return mapMap(v) + default: + return v + } +} diff --git a/schema/schema.go b/schema/schema.go new file mode 100644 index 0000000..7f925d9 --- /dev/null +++ b/schema/schema.go @@ -0,0 +1,49 @@ +package schema + +import ( + _ "embed" + "fmt" + "io" + "strings" + + "github.com/xeipuuv/gojsonschema" +) + +var ( + //go:embed tmpl.json + schema []byte + schemaLoader = gojsonschema.NewBytesLoader(schema) +) + +// Lint is for linting a recipe against the schema +func Lint(r io.Reader) error { + data, err := io.ReadAll(r) + if err != nil { + return err + } + var m any + if err := Unmarshal(data, &m); err != nil { + return err + } + sourceLoader := gojsonschema.NewGoLoader(m) + result, err := gojsonschema.Validate(schemaLoader, sourceLoader) + if err != nil { + return err + } + if len(result.Errors()) > 0 { + return ResultErrors(result.Errors()) + } + return nil +} + +// ResultErrors is a slice of gojsonschema.ResultError that implements error +type ResultErrors []gojsonschema.ResultError + +// Error implements error +func (r ResultErrors) Error() string { + errs := make([]string, 0, len(r)) + for _, re := range r { + errs = append(errs, fmt.Sprintf("%s: %s", re.Field(), re.Description())) + } + return strings.Join(errs, " | ") +} diff --git a/schema/tmpl.json b/schema/tmpl.json new file mode 100644 index 0000000..b8acf98 --- /dev/null +++ b/schema/tmpl.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://git.jojodev.com/jolheiser/tmpl/src/branch/main/schema/tmpl.json", + "title": "tmpl template", + "description": "A template for tmpl", + "type": "object", + "required": [ + "prompts" + ], + "additionalProperties": false, + "properties": { + "prompts": { + "description": "Template prompts", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "id" + ], + "additionalProperties": false, + "properties": { + "id": { + "description": "The unique prompt ID", + "type": "string" + }, + "label": { + "description": "A label to show instead of the ID when prompting", + "type": "string" + }, + "default": { + "description": "A default value for the prompt", + "type": "string" + }, + "help": { + "description": "A help message for more information on a prompt", + "type": "string" + }, + "depends_on": { + "description": "A list of prompt IDs that this prompt depends on", + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } + } + } + } + } +}