From 49c3c77207ba20738cd39bcb9d25ec9df7de4f3b Mon Sep 17 00:00:00 2001 From: nxtcoder17 Date: Sat, 19 Oct 2024 22:01:50 +0530 Subject: [PATCH] feat: pre-rendered completions, for better testing and assertions [GA] for fish shell, rest are under-development --- .gitignore | 1 + Runfile | 2 +- cmd/run/completions.go | 46 ++++++++++++ cmd/run/completions/bash/run.bash | 35 +++++++++ cmd/run/completions/fish/run.fish | 26 ++++--- cmd/run/completions/ps/run.ps | Bin 0 -> 415 bytes cmd/run/completions/zsh/run.zsh | 20 ++++++ cmd/run/main.go | 113 +++++++++++++++++++++--------- examples/Runfile.yml | 13 +++- go.mod | 14 ++-- go.sum | 23 +++--- 11 files changed, 229 insertions(+), 64 deletions(-) create mode 100644 cmd/run/completions.go create mode 100644 cmd/run/completions/bash/run.bash create mode 100644 cmd/run/completions/ps/run.ps create mode 100644 cmd/run/completions/zsh/run.zsh diff --git a/.gitignore b/.gitignore index 59023bb..50d261c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .direnv bin/ .secrets/ +Taskfile.yml diff --git a/Runfile b/Runfile index 38097d4..f12eed6 100644 --- a/Runfile +++ b/Runfile @@ -7,7 +7,7 @@ tasks: cmd: - |+ echo "building ..." - go build -o bin/run -ldflags="-s -w" -tags urfave_cli_no_docs cmd/run/main.go + go build -o bin/run -ldflags="-s -w" -tags urfave_cli_no_docs ./cmd/run echo "DONE" example: diff --git a/cmd/run/completions.go b/cmd/run/completions.go new file mode 100644 index 0000000..e23df15 --- /dev/null +++ b/cmd/run/completions.go @@ -0,0 +1,46 @@ +package main + +import ( + "context" + "fmt" + "io" + "log/slog" + + "github.com/nxtcoder17/runfile/pkg/runfile" +) + +func generateShellCompletion(_ context.Context, writer io.Writer, rfpath string) error { + // if c.NArg() > 0 { + // return nil + // } + + // runfilePath, err := locateRunfile(c) + // if err != nil { + // slog.Error("locating runfile", "err", err) + // panic(err) + // } + + runfile, err := runfile.Parse(rfpath) + if err != nil { + slog.Error("parsing, got", "err", err) + panic(err) + } + + for k := range runfile.Tasks { + fmt.Fprintf(writer, "%s\n", k) + } + + m, err := runfile.ParseIncludes() + if err != nil { + slog.Error("parsing, got", "err", err) + panic(err) + } + + for k, v := range m { + for tn := range v.Runfile.Tasks { + fmt.Fprintf(writer, "%s:%s\n", k, tn) + } + } + + return nil +} diff --git a/cmd/run/completions/bash/run.bash b/cmd/run/completions/bash/run.bash new file mode 100644 index 0000000..32c4c8f --- /dev/null +++ b/cmd/run/completions/bash/run.bash @@ -0,0 +1,35 @@ +#! /bin/bash + +: ${PROG:=$(basename ${BASH_SOURCE})} + +# Macs have bash3 for which the bash-completion package doesn't include +# _init_completion. This is a minimal version of that function. +_cli_init_completion() { + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} + +_cli_bash_autocomplete() { + if [[ "${COMP_WORDS[0]}" != "source" ]]; then + local cur opts base words + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -n "=:" || return + else + _cli_init_completion -n "=:" || return + fi + words=("${words[@]:0:$cword}") + if [[ "$cur" == "-"* ]]; then + requestComp="${words[*]} ${cur} completion:gen" + else + requestComp="${words[*]} completion:gen" + fi + opts=$(eval "${requestComp}" 2>/dev/null) + COMPREPLY=($(compgen -W "${opts}" -- ${cur})) + return 0 + fi +} + +complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG +unset PROG diff --git a/cmd/run/completions/fish/run.fish b/cmd/run/completions/fish/run.fish index d9aa4af..a99ea9a 100644 --- a/cmd/run/completions/fish/run.fish +++ b/cmd/run/completions/fish/run.fish @@ -1,25 +1,35 @@ # run fish shell completion set PROGNAME run -function __fetch_runnable_tasks --description 'fetches all runnable tasks' +function __runfile_list_targets --description 'fetches all runnable tasks' for i in (commandline -opc) if contains -- $i help h return 1 end end - # Grab names and descriptions (if any) of the tasks - set -l output (run --generate-shell-completion | string split0) - echo "$output" > /tmp/test.txt + $PROGNAME --list 2>&1 | read -lz rawOutput + + # RETURN on non-zero exit code (in case of errors) + if test $status -ne 0 + return + end + + set -l output (echo $rawOutput | string split0) + if test $output + if test "$DEBUG" = "true" + echo "$output" > /tmp/test.txt + end echo $output end return 0 end -complete -c run -d "runs a task with given name" -xa "(__fish_run_no_subcommand)" -complete -c run -n '__fish_run_no_subcommand' -f -l help -s h -d 'show help' -complete -c run -n '__fish_run_no_subcommand' -f -l help -s h -d 'show help' -complete -r -c run -n '__fish_run_no_subcommand' -a 'help h' -d 'Shows a list of commands or help for one command' +complete -c $PROGNAME -d "runs target with given name" -xa "(__runfile_list_targets)" +complete -c $PROGNAME -rF -l file -s f -d 'runs targets from this runfile' +# complete -c $PROGNAME -n '__runfile_list_targets' -f -l help -s h -d 'show help' +# complete -c $PROGNAME -n '__runfile_list_targets' -f -l help -s h -d 'show help' +# complete -r -c $PROGNAME -n '__runfile_list_targets' -a 'help h' -d 'Shows a list of commands or help for one command' diff --git a/cmd/run/completions/ps/run.ps b/cmd/run/completions/ps/run.ps new file mode 100644 index 0000000000000000000000000000000000000000..a109ea04b05b8ba69cf948c51d7a4465e005325f GIT binary patch literal 415 zcmZ9I(M!WH5XRs8R~#kKPL{%63wxL{!H3SVi7zT6W@lH|Bqd2Vhx)&JN4pn8Lg4b< z@4heM!@?XyE~?9eJvpsn<7`n~?wm2o?u(T&I7x)1i*=-y9=ld)gfuU1Ca*;oW+I(J zCT3Z(!MX_%yVhJh=$+BaRiZJj>CcLKaR<(0{W9&E69 z3gYbi{>fdLRM5Q-&aYi)*!E;-6)qlV;2hF_4L~$@L(;NFn+|fG#?g-`mtUR75a=C7 z$Pe5*zf}4lAKyPw$E!VVqV=-9P+U_~%BmVUaIxs4Gs7>AMhUmU(6{leEG?dMu_Nbn WvNdPq&v{mDj~Q3T2+aOXX#WAiDTH1C literal 0 HcmV?d00001 diff --git a/cmd/run/completions/zsh/run.zsh b/cmd/run/completions/zsh/run.zsh new file mode 100644 index 0000000..a9c6535 --- /dev/null +++ b/cmd/run/completions/zsh/run.zsh @@ -0,0 +1,20 @@ +#compdef $PROG + +_cli_zsh_autocomplete() { + local -a opts + local cur + cur=${words[-1]} + if [[ "$cur" == "-"* ]]; then + opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} completion:gen)}") + else + opts=("${(@f)$(${words[@]:0:#words[@]-1} completion:gen)}") + fi + + if [[ "${opts[1]}" != "" ]]; then + _describe 'values' opts + else + _files + fi +} + +compdef _cli_zsh_autocomplete $PROG diff --git a/cmd/run/main.go b/cmd/run/main.go index 1437659..c6e1215 100644 --- a/cmd/run/main.go +++ b/cmd/run/main.go @@ -2,7 +2,9 @@ package main import ( "context" + _ "embed" "fmt" + "log/slog" "os" "os/signal" "path/filepath" @@ -11,7 +13,6 @@ import ( "github.com/nxtcoder17/runfile/pkg/logging" "github.com/nxtcoder17/runfile/pkg/runfile" - "github.com/nxtcoder17/runfile/pkg/runfile/errors" "github.com/urfave/cli/v3" ) @@ -23,6 +24,18 @@ var runfileNames []string = []string{ "Runfile.yaml", } +//go:embed completions/fish/run.fish +var shellCompletionFISH string + +//go:embed completions/bash/run.bash +var shellCompletionBASH string + +//go:embed completions/zsh/run.zsh +var shellCompletionZSH string + +//go:embed completions/ps/run.ps +var shellCompletionPS string + func main() { cmd := cli.Command{ Name: "run", @@ -30,9 +43,10 @@ func main() { Description: "A simple task runner", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "file", - Aliases: []string{"f"}, - Value: "", + Name: "file", + Aliases: []string{"f"}, + TakesFile: true, + Value: "", }, &cli.BoolFlag{ @@ -51,8 +65,16 @@ func main() { Name: "debug", Value: false, }, + + &cli.BoolFlag{ + Name: "list", + Value: false, + Aliases: []string{"ls"}, + }, }, + // ShellCompletionCommandName: "completion:shell", EnableShellCompletion: true, + // DefaultCommand: "help", ShellComplete: func(ctx context.Context, c *cli.Command) { if c.NArg() > 0 { return @@ -60,34 +82,27 @@ func main() { runfilePath, err := locateRunfile(c) if err != nil { + slog.Error("locating runfile", "err", err) panic(err) } - runfile, err := runfile.Parse(runfilePath) - if err != nil { - panic(err) - } - - for k := range runfile.Tasks { - fmt.Fprintf(c.Root().Writer, "%s\n", k) - } - - m, err := runfile.ParseIncludes() - if err != nil { - panic(err) - } - - for k, v := range m { - for tn := range v.Runfile.Tasks { - fmt.Fprintf(c.Root().Writer, "%s:%s\n", k, tn) - } - } + generateShellCompletion(ctx, c.Root().Writer, runfilePath) }, Action: func(ctx context.Context, c *cli.Command) error { parallel := c.Bool("parallel") watch := c.Bool("watch") debug := c.Bool("debug") + showList := c.Bool("list") + if showList { + runfilePath, err := locateRunfile(c) + if err != nil { + slog.Error("locating runfile, got", "err", err) + return err + } + return generateShellCompletion(ctx, c.Root().Writer, runfilePath) + } + if c.NArg() == 0 { c.Command("help").Run(ctx, nil) return nil @@ -95,12 +110,14 @@ func main() { runfilePath, err := locateRunfile(c) if err != nil { + slog.Error("locating runfile, got", "err", err) return err } - rf, err := runfile.Parse(runfilePath) - if err != nil { - panic(err) + rf, err2 := runfile.Parse(runfilePath) + if err2 != nil { + slog.Error("parsing runfile, got", "err", err2) + panic(err2) } kv := make(map[string]string) @@ -136,8 +153,8 @@ func main() { return fmt.Errorf("parallel and watch can't be set together") } - logger := logging.NewSlogLogger(logging.SlogOptions{ - ShowCaller: debug, + logger := logging.New(logging.Options{ + SlogKeyAsPrefix: "task", ShowDebugLogs: debug, SetAsDefaultLogger: true, }) @@ -150,23 +167,49 @@ func main() { KVs: kv, }) }, + Commands: []*cli.Command{ + { + Name: "shell:completion", + Action: func(ctx context.Context, c *cli.Command) error { + if c.NArg() != 2 { + return fmt.Errorf("needs argument one of [bash,zsh,fish,ps]") + } + + switch c.Args().Slice()[1] { + case "fish": + fmt.Fprint(c.Writer, shellCompletionFISH) + case "bash": + fmt.Fprint(c.Writer, shellCompletionBASH) + case "zsh": + fmt.Fprint(c.Writer, shellCompletionZSH) + case "ps": + fmt.Fprint(c.Writer, shellCompletionPS) + } + + return nil + }, + }, + }, } - ctx, cf := context.WithCancel(context.TODO()) + ctx, cf := signal.NotifyContext(context.TODO(), os.Interrupt, syscall.SIGTERM) + defer cf() - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { - <-c - fmt.Println("\n\rcanceling...") + <-ctx.Done() cf() os.Exit(1) }() if err := cmd.Run(ctx, os.Args); err != nil { - errm, ok := err.(errors.Message) + errm, ok := err.(*runfile.Error) + slog.Debug("got", "err", err) if ok { - errm.Log() + if errm != nil { + errm.Log() + } + } else { + slog.Error("got", "err", err) } os.Exit(1) } diff --git a/examples/Runfile.yml b/examples/Runfile.yml index 9ce57da..8c632d7 100644 --- a/examples/Runfile.yml +++ b/examples/Runfile.yml @@ -32,6 +32,7 @@ tasks: # - echo "value of k3 is '$k3'" # - echo "value of key_id (from .dotenv) is '$key_id', ${#key_id}" - echo "hello from cook" + - echo "k4 is $k4" - echo "k5 is $k5" clean: @@ -48,16 +49,23 @@ tasks: import os import time # print("key_id from env: ", os.environ['key_id']) - time.sleep(2) + # time.sleep(2) print("hello from clean") print(secrets.token_hex(32)) laundry: name: laundry shell: ["node", "-e"] + env: + k4: + default: + sh: |+ + console.log('1234' == '23344') cmd: - run: cook + - console.log(process.env.k4) - console.log("hello from laundry") + eat: name: eat env: @@ -73,4 +81,5 @@ tasks: code: name: code cmd: - - echo "code" + - echo "writing to stdout" + - echo "writing to stderr" 1>&2 diff --git a/go.mod b/go.mod index 2deb5de..c6a9d60 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,11 @@ module github.com/nxtcoder17/runfile go 1.22.7 require ( + github.com/charmbracelet/lipgloss v0.13.0 + github.com/charmbracelet/log v0.4.0 github.com/go-task/slim-sprig/v3 v3.0.0 github.com/joho/godotenv v1.5.1 - github.com/nxtcoder17/fwatcher v1.0.1 + github.com/phuslu/log v1.0.112 github.com/urfave/cli/v3 v3.0.0-alpha9 golang.org/x/sync v0.8.0 sigs.k8s.io/yaml v1.4.0 @@ -13,16 +15,14 @@ require ( require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/lipgloss v0.13.0 // indirect - github.com/charmbracelet/log v0.4.0 // indirect - github.com/charmbracelet/x/ansi v0.1.4 // indirect + github.com/charmbracelet/x/ansi v0.3.2 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/sys v0.19.0 // indirect + golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 // indirect + golang.org/x/sys v0.26.0 // indirect ) diff --git a/go.sum b/go.sum index 7fb069a..f67a20d 100644 --- a/go.sum +++ b/go.sum @@ -4,28 +4,29 @@ github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= -github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= -github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY= +github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= 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/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/nxtcoder17/fwatcher v1.0.1 h1:Rqy+7etcGv9L1KIoK8YGGpAhdXW/pkfkXQwdlJzL1a8= -github.com/nxtcoder17/fwatcher v1.0.1/go.mod h1:MNmSwXYOrqp7U1pUxh0GWB5skpjFTWTQXhAA0+sPJcU= +github.com/phuslu/log v1.0.112 h1:vQ0ZFd5O+in/0IQAcjuEl6wRkHiQPw7T0sqwmOjpL0U= +github.com/phuslu/log v1.0.112/go.mod h1:F8osGJADo5qLK/0F88djWwdyoZZ9xDJQL1HYRHFEkS0= 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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -37,13 +38,13 @@ github.com/urfave/cli/v3 v3.0.0-alpha9 h1:P0RMy5fQm1AslQS+XCmy9UknDXctOmG/q/FZkU github.com/urfave/cli/v3 v3.0.0-alpha9/go.mod h1:0kK/RUFHyh+yIKSfWxwheGndfnrvYSmYFVeKCh03ZUc= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw= +golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=