From 18c919efd2a5e1c1e47d3534e7d8b03923a592cb Mon Sep 17 00:00:00 2001 From: Morlay Date: Mon, 18 Dec 2023 14:29:41 +0800 Subject: [PATCH] feat: init --- .github/dependabot.yml | 11 + .github/workflows/ci.yml | 25 + .gitignore | 2 + LICENSE | 21 + Makefile | 21 + README.md | 9 + cmd/piper/do.go | 16 + cmd/piper/main.go | 18 + cmd/piper/zz_generated.runtimedoc.go | 36 + cue.mod/.gitignore | 3 + cue.mod/module.cue | 5 + cuepkg/github/release.cue | 148 ++++ cuepkg/golang/project.cue | 76 ++ cuepkg/pkg.go | 92 +++ go.mod | 116 +++ go.sum | 658 ++++++++++++++++++ install.sh | 48 ++ internal/cmd/tool/main.go | 32 + internal/version/version.go | 9 + piper.cue | 53 ++ pkg/cueflow/controller.go | 75 ++ pkg/cueflow/cueify.go | 1 + pkg/cueflow/cueify/cueify.go | 353 ++++++++++ pkg/cueflow/cueify/one_of.go | 5 + pkg/cueflow/cueify/zz_generated.runtimedoc.go | 73 ++ pkg/cueflow/flow.go | 19 + pkg/cueflow/runner.go | 263 +++++++ pkg/cueflow/scope.go | 8 + pkg/cueflow/task.go | 88 +++ pkg/cueflow/task_impl.go | 19 + pkg/cueflow/task_runner.go | 126 ++++ pkg/cueflow/task_runner_factory.go | 242 +++++++ pkg/cueflow/value.go | 113 +++ pkg/cueflow/zz_generated.runtimedoc.go | 19 + pkg/engine/logger.go | 200 ++++++ pkg/engine/pipleline.go | 84 +++ pkg/engine/project.go | 101 +++ pkg/engine/task/archive/tar.go | 107 +++ .../task/archive/zz_generated.runtimedoc.go | 47 ++ pkg/engine/task/client.go | 9 + pkg/engine/task/client/env.go | 99 +++ pkg/engine/task/client/read_secret.go | 34 + pkg/engine/task/client/rev_info.go | 32 + pkg/engine/task/client/secret.go | 26 + pkg/engine/task/client/secret_or_string.go | 34 + pkg/engine/task/client/string_or_bytes.go | 30 + pkg/engine/task/client/string_or_slice.go | 37 + .../task/client/zz_generated.runtimedoc.go | 110 +++ pkg/engine/task/exec/run.go | 173 +++++ .../task/exec/zz_generated.runtimedoc.go | 102 +++ pkg/engine/task/factory.go | 5 + pkg/engine/task/file/file.go | 85 +++ pkg/engine/task/file/write.go | 54 ++ .../task/file/zz_generated.runtimedoc.go | 80 +++ pkg/engine/task/flow/every.go | 69 ++ pkg/engine/task/flow/some.go | 65 ++ pkg/engine/task/flow/step.go | 58 ++ .../task/flow/zz_generated.runtimedoc.go | 99 +++ pkg/engine/task/http/do.go | 196 ++++++ .../task/http/zz_generated.runtimedoc.go | 94 +++ pkg/engine/task/log.go | 30 + pkg/engine/task/secret.go | 22 + pkg/engine/task/store.go | 24 + pkg/engine/task/task.go | 29 + pkg/engine/task/wd.go | 15 + pkg/engine/task/wd/local.go | 44 ++ pkg/engine/task/wd/ssh.go | 68 ++ pkg/engine/task/wd/su.go | 33 + pkg/engine/task/wd/sub.go | 34 + pkg/engine/task/wd/sys_info.go | 61 ++ pkg/engine/task/wd/work_dir.go | 53 ++ pkg/engine/task/wd/zz_generated.runtimedoc.go | 236 +++++++ pkg/engine/task/zz_generated.runtimedoc.go | 52 ++ pkg/engine/zz_generated.runtimedoc.go | 33 + pkg/sshutil/config.go | 79 +++ pkg/wd/fs.go | 171 +++++ pkg/wd/platform.go | 38 + pkg/wd/util.go | 47 ++ pkg/wd/wd.go | 199 ++++++ pkg/wd/wd_os.go | 36 + pkg/wd/wd_test.go | 46 ++ pkg/wd/zz_generated.runtimedoc.go | 60 ++ 82 files changed, 6243 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 cmd/piper/do.go create mode 100644 cmd/piper/main.go create mode 100644 cmd/piper/zz_generated.runtimedoc.go create mode 100755 cue.mod/.gitignore create mode 100755 cue.mod/module.cue create mode 100644 cuepkg/github/release.cue create mode 100644 cuepkg/golang/project.cue create mode 100644 cuepkg/pkg.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 install.sh create mode 100644 internal/cmd/tool/main.go create mode 100644 internal/version/version.go create mode 100644 piper.cue create mode 100644 pkg/cueflow/controller.go create mode 100644 pkg/cueflow/cueify.go create mode 100644 pkg/cueflow/cueify/cueify.go create mode 100644 pkg/cueflow/cueify/one_of.go create mode 100644 pkg/cueflow/cueify/zz_generated.runtimedoc.go create mode 100644 pkg/cueflow/flow.go create mode 100644 pkg/cueflow/runner.go create mode 100644 pkg/cueflow/scope.go create mode 100644 pkg/cueflow/task.go create mode 100644 pkg/cueflow/task_impl.go create mode 100644 pkg/cueflow/task_runner.go create mode 100644 pkg/cueflow/task_runner_factory.go create mode 100644 pkg/cueflow/value.go create mode 100644 pkg/cueflow/zz_generated.runtimedoc.go create mode 100644 pkg/engine/logger.go create mode 100644 pkg/engine/pipleline.go create mode 100644 pkg/engine/project.go create mode 100644 pkg/engine/task/archive/tar.go create mode 100644 pkg/engine/task/archive/zz_generated.runtimedoc.go create mode 100644 pkg/engine/task/client.go create mode 100644 pkg/engine/task/client/env.go create mode 100644 pkg/engine/task/client/read_secret.go create mode 100644 pkg/engine/task/client/rev_info.go create mode 100644 pkg/engine/task/client/secret.go create mode 100644 pkg/engine/task/client/secret_or_string.go create mode 100644 pkg/engine/task/client/string_or_bytes.go create mode 100644 pkg/engine/task/client/string_or_slice.go create mode 100644 pkg/engine/task/client/zz_generated.runtimedoc.go create mode 100644 pkg/engine/task/exec/run.go create mode 100644 pkg/engine/task/exec/zz_generated.runtimedoc.go create mode 100644 pkg/engine/task/factory.go create mode 100644 pkg/engine/task/file/file.go create mode 100644 pkg/engine/task/file/write.go create mode 100644 pkg/engine/task/file/zz_generated.runtimedoc.go create mode 100644 pkg/engine/task/flow/every.go create mode 100644 pkg/engine/task/flow/some.go create mode 100644 pkg/engine/task/flow/step.go create mode 100644 pkg/engine/task/flow/zz_generated.runtimedoc.go create mode 100644 pkg/engine/task/http/do.go create mode 100644 pkg/engine/task/http/zz_generated.runtimedoc.go create mode 100644 pkg/engine/task/log.go create mode 100644 pkg/engine/task/secret.go create mode 100644 pkg/engine/task/store.go create mode 100644 pkg/engine/task/task.go create mode 100644 pkg/engine/task/wd.go create mode 100644 pkg/engine/task/wd/local.go create mode 100644 pkg/engine/task/wd/ssh.go create mode 100644 pkg/engine/task/wd/su.go create mode 100644 pkg/engine/task/wd/sub.go create mode 100644 pkg/engine/task/wd/sys_info.go create mode 100644 pkg/engine/task/wd/work_dir.go create mode 100644 pkg/engine/task/wd/zz_generated.runtimedoc.go create mode 100644 pkg/engine/task/zz_generated.runtimedoc.go create mode 100644 pkg/engine/zz_generated.runtimedoc.go create mode 100644 pkg/sshutil/config.go create mode 100644 pkg/wd/fs.go create mode 100644 pkg/wd/platform.go create mode 100644 pkg/wd/util.go create mode 100644 pkg/wd/wd.go create mode 100644 pkg/wd/wd_os.go create mode 100644 pkg/wd/wd_test.go create mode 100644 pkg/wd/zz_generated.runtimedoc.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..03c48ef --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "daily" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c6b579c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: ci + +on: + push: + branches: + - "*" + tags: + - 'v*' + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v5 + with: + go-version: '^1.21' + + - run: go mod download -x + - run: make release + env: + GH_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..078a5ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +.build/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d07947c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 octohelm + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5e4bfa3 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +PIPER = go run ./cmd/piper + +DEBUG = 0 +ifeq ($(DEBUG),1) + PIPER := $(PIPER) --log-level=debug +endif + +build: + $(PIPER) do go build + +archive: + $(PIPER) do go archive + +release: + $(PIPER) do release + +gen: + go run ./internal/cmd/tool gen ./cmd/piper + +install: + go install ./cmd/piper diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef45ad3 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Piper + +Similar as [`cue/tool`](https://pkg.go.dev/cuelang.org/go/pkg/tool), but not just on current host. + +## Install + +```shell +curl -sSLf https://raw.githubusercontent.com/octohelm/piper/main/install.sh | sudo sh +``` diff --git a/cmd/piper/do.go b/cmd/piper/do.go new file mode 100644 index 0000000..c46404d --- /dev/null +++ b/cmd/piper/do.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/innoai-tech/infra/pkg/cli" + "github.com/octohelm/piper/pkg/engine" +) + +func init() { + cli.AddTo(App, &Do{}) +} + +type Do struct { + cli.C + engine.Logger + engine.Pipeline +} diff --git a/cmd/piper/main.go b/cmd/piper/main.go new file mode 100644 index 0000000..6cb4a50 --- /dev/null +++ b/cmd/piper/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "context" + "os" + + "github.com/innoai-tech/infra/pkg/cli" + + "github.com/octohelm/piper/internal/version" +) + +var App = cli.NewApp("piper", version.Version()) + +func main() { + if err := cli.Execute(context.Background(), App, os.Args[1:]); err != nil { + os.Exit(1) + } +} diff --git a/cmd/piper/zz_generated.runtimedoc.go b/cmd/piper/zz_generated.runtimedoc.go new file mode 100644 index 0000000..1a46a3f --- /dev/null +++ b/cmd/piper/zz_generated.runtimedoc.go @@ -0,0 +1,36 @@ +/* +Package main GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package main + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Do) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Logger": + return []string{}, true + case "Pipeline": + return []string{}, true + + } + if doc, ok := runtimeDoc(v.Logger, names...); ok { + return doc, ok + } + if doc, ok := runtimeDoc(v.Pipeline, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{}, true +} diff --git a/cue.mod/.gitignore b/cue.mod/.gitignore new file mode 100755 index 0000000..f5d0b6d --- /dev/null +++ b/cue.mod/.gitignore @@ -0,0 +1,3 @@ +gen/ +pkg/ +module.sum \ No newline at end of file diff --git a/cue.mod/module.cue b/cue.mod/module.cue new file mode 100755 index 0000000..f2f3442 --- /dev/null +++ b/cue.mod/module.cue @@ -0,0 +1,5 @@ +module: "github.com/octohelm/piper" + +require: { + "piper.octohelm.tech": "v0.0.0" +} diff --git a/cuepkg/github/release.cue b/cuepkg/github/release.cue new file mode 100644 index 0000000..8c19bcf --- /dev/null +++ b/cuepkg/github/release.cue @@ -0,0 +1,148 @@ +package github + +import ( + "path" + "strings" + "strconv" + "encoding/json" + + "piper.octohelm.tech/http" + "piper.octohelm.tech/file" + "piper.octohelm.tech/client" + "piper.octohelm.tech/flow" +) + +#GithubAPI: { + core: "https://api.github.com" + uploads: "https://uploads.github.com" +} + +#Release: { + token: client.#Secret + + owner: string + repo: string + + name: string | *"latest" + notes: string | *"" + prerelease: bool | *true + draft: bool | *false + + assets: [...file.#File] + + _client: #Client & {"token": token} + + _get_or_create_release: { + _ret: flow.#Some & { + steps: [ + _client.#Do & { + method: "GET" + url: "\(#GithubAPI.core)/repos/\(owner)/\(repo)/releases/tags/\(name)" + }, + _client.#Do & { + "method": "POST" + "url": "\(#GithubAPI.core)/repos/\(owner)/\(repo)/releases" + "header": { + "Content-Type": "application/json" + } + "body": json.Marshal({ + "tag_name": name + "name": name + "body": notes + "prerelease": prerelease + "draft": draft + }) + }, + ] + } + + id: _ret.result.data.id + } + + _list_assets: { + _req: _client.#Do & { + method: "GET" + url: "\(#GithubAPI.core)/repos/\(owner)/\(repo)/releases/\(_get_or_create_release.id)/assets" + } + + assets: { + for asset in _req.result.data { + "\(asset.name)": strconv.FormatFloat(asset.id, strings.ByteAt("f", 0), 0, 64) + } + } + } + + _upload_assets: flow.#Every & { + steps: [ + for f in assets { + let assetName = path.Base(f.filename) + + flow.#Every & { + steps: [ + // if asset name exists, delete first + if _list_assets.assets[assetName] != _|_ { + _client.#Do & { + "method": "DELETE" + "url": "\(#GithubAPI.core)/repos/\(owner)/\(repo)/releases/assets/\(_list_assets.assets[assetName])" + } + }, + // then upload + _client.#Do & { + "method": "POST" + "url": "\(#GithubAPI.uploads)/repos/\(owner)/\(repo)/releases/\(_get_or_create_release.id)/assets" + "header": { + "Content-Type": "application/octet-stream" + } + "query": { + "name": "\(assetName)" + } + "body": f + }, + ] + } + }, + ] + } + result: _upload_assets.result +} + +#Client: { + token: client.#Secret + + _token: client.#ReadSecret & { + secret: token + } + + #Do: { + method: string + url: string + body?: file.#StringOrFile + header: [Name=string]: string | [...string] + query: [Name=string]: string | [...string] + + _default_header: { + "Accept": "application/vnd.github+json" + "Authorization": "Bearer \(_token.value)" + "X-GitHub-Api-Version": "2022-11-28" + } + + _req: http.#Do & { + "method": method + "url": url + "header": { + for k, vv in header { + "\(k)": vv + } + for k, vv in _default_header if header[k] == _|_ { + "\(k)": vv + } + } + "query": query + if body != _|_ { + "body": body + } + } + + result: _req.result + } +} diff --git a/cuepkg/golang/project.cue b/cuepkg/golang/project.cue new file mode 100644 index 0000000..2b6f975 --- /dev/null +++ b/cuepkg/golang/project.cue @@ -0,0 +1,76 @@ +package golang + +import ( + "path" + "strings" + "strconv" + + "piper.octohelm.tech/wd" + "piper.octohelm.tech/file" + "piper.octohelm.tech/exec" + "piper.octohelm.tech/archive" +) + +#Project: { + cwd: wd.#WorkDir + main: string + os: [...string] | *["darwin", "linux"] + arch: [...string] | *["amd64", "arm64"] + ldflags: [...string] | *["-s", "-w"] + + bin: string | *path.Base(main) + + _buildDir: "./.build" + + build: { + for _os in os for _arch in arch { + "\(_os)/\(_arch)": { + _filename: "\(_buildDir)/\(bin)_\(_os)_\(_arch)/\(bin)" + + _run: exec.#Run & { + "cwd": cwd + env: { + CGO_ENABLED: "0" + GOOS: _os + GOARCH: _arch + } + cmd: [ + "go", "build", + "-ldflags", strconv.Quote(strings.Join(ldflags, " ")), + "-o", _filename, + "\(main)", + ] + } + + output: file.#File & { + cwd: _run.cwd + + if _run.result.ok { + filename: _filename + } + } + } + } + } + + "archive": { + for _os in os for _arch in arch { + "\(_os)/\(_arch)": { + _built: build["\(_os)/\(_arch)"] + + _dir: wd.#Sub & { + cwd: _built.output.cwd + dir: path.Dir(_built.output.filename) + } + + _tar: archive.#Tar & { + cwd: _built.output.cwd + filename: "\(_buildDir)/\(bin)_\(_os)_\(_arch).tar.gz" + dir: _dir.wd + } + + output: _tar.output + } + } + } +} diff --git a/cuepkg/pkg.go b/cuepkg/pkg.go new file mode 100644 index 0000000..2441f25 --- /dev/null +++ b/cuepkg/pkg.go @@ -0,0 +1,92 @@ +package cuepkg + +import ( + "context" + "github.com/octohelm/piper/pkg/engine/task" + "github.com/pkg/errors" + "io" + "io/fs" + "os" + "path/filepath" + + "github.com/octohelm/cuemod/pkg/cuemod/stdlib" + "github.com/octohelm/unifs/pkg/filesystem" +) + +var ( + PiperModule = "piper.octohelm.tech" +) + +func RegistryCueStdlibs() error { + source, err := task.Factory.Sources(context.Background()) + if err != nil { + return err + } + + module, err := createModule(filesystem.AsReadDirFS(source)) + if err != nil { + return err + } + + // ugly lock embed version + if err := registerStdlib(filesystem.AsReadDirFS(module), "v0.0.0", PiperModule); err != nil { + return err + } + + return nil +} + +func registerStdlib(fs fs.ReadDirFS, ver string, modules ...string) error { + stdlib.Register(fs, ver, modules...) + return nil +} + +func createModule(otherFs ...fs.ReadDirFS) (filesystem.FileSystem, error) { + mfs := filesystem.NewMemFS() + + ctx := context.Background() + + for _, f := range otherFs { + if err := listFile(f, ".", func(filename string) error { + src, err := f.Open(filename) + if err != nil { + return errors.Wrap(err, "open source file failed") + } + defer src.Close() + + if err := filesystem.MkdirAll(ctx, mfs, filepath.Dir(filename)); err != nil { + return err + } + dest, err := mfs.OpenFile(ctx, filename, os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + return errors.Wrap(err, "open dest file failed") + } + defer dest.Close() + + if _, err := io.Copy(dest, src); err != nil { + return err + } + return nil + }); err != nil { + return nil, err + } + } + + return mfs, nil +} + +func listFile(f fs.ReadDirFS, root string, each func(filename string) error) error { + return fs.WalkDir(f, root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + rel := path + if root != "" && root != "." { + rel, _ = filepath.Rel(root, path) + } + return each(rel) + }) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6d15d8a --- /dev/null +++ b/go.mod @@ -0,0 +1,116 @@ +module github.com/octohelm/piper + +go 1.21 + +replace github.com/k0sproject/rig => github.com/k0sproject/rig v0.15.2-0.20231214092155-eaf96adb3d48 + +require ( + cuelang.org/go v0.7.0 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc + github.com/fatih/color v1.16.0 + github.com/go-courier/logr v0.3.0 + github.com/innoai-tech/infra v0.0.0-20231124085701-81f94504013c + github.com/k0sproject/rig v0.15.2-0.20231219072317-afaa2d5ee397 + github.com/kevinburke/ssh_config v1.2.0 + github.com/mattn/go-isatty v0.0.20 + github.com/octohelm/cuemod v0.9.0 + github.com/octohelm/gengo v0.0.0-20230809023313-1339e47458a4 + github.com/octohelm/storage v0.0.0-20231213035828-4a91cdd3f879 + github.com/octohelm/unifs v0.0.0-20231219081842-bde4a9d9600a + github.com/octohelm/x v0.0.0-20231115103341-17be3238221d + github.com/opencontainers/go-digest v1.0.0 + github.com/opencontainers/image-spec v1.1.0-rc5 + github.com/pkg/errors v0.9.1 + github.com/vito/progrock v0.10.1 + golang.org/x/net v0.19.0 +) + +require ( + cuelabs.dev/go/oci/ociregistry v0.0.0-20231217163254-6feb86eb6e06 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect + github.com/alessio/shellescape v1.4.2 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/bodgit/ntlmssp v0.0.0-20231219172012-333c521e41ca // indirect + github.com/bodgit/windows v1.0.1 // indirect + github.com/charmbracelet/bubbles v0.17.1 // indirect + github.com/charmbracelet/bubbletea v0.25.0 // indirect + github.com/charmbracelet/harmonica v0.2.0 // indirect + github.com/charmbracelet/lipgloss v0.9.1 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/creasty/defaults v1.7.0 // indirect + github.com/davidmz/go-pageant v1.0.2 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/proto v1.13.0 // indirect + github.com/fogleman/ease v0.0.0-20170301025033-8da417bf1776 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/goidentity/v6 v6.0.1 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect + github.com/masterzen/winrm v0.0.0-20231222090117-f1fbea7700af // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/onsi/gomega v1.30.0 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect + github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde // indirect + github.com/vito/midterm v0.1.4 // indirect + github.com/zmb3/spotify/v2 v2.4.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.16.1 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/grpc v1.60.1 // indirect + google.golang.org/protobuf v1.32.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.29.0 // indirect + k8s.io/apimachinery v0.29.0 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/utils v0.0.0-20231127182322-b307cd553661 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..87d7133 --- /dev/null +++ b/go.sum @@ -0,0 +1,658 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cuelabs.dev/go/oci/ociregistry v0.0.0-20231217163254-6feb86eb6e06 h1:X2H+7Jw/dJ5omjq/92asjuszALGZWuGx6LwyBKXF+48= +cuelabs.dev/go/oci/ociregistry v0.0.0-20231217163254-6feb86eb6e06/go.mod h1:ApHceQLLwcOkCEXM1+DyCXTHEJhNGDpJ2kmV6axsx24= +cuelang.org/go v0.7.0 h1:gMztinxuKfJwMIxtboFsNc6s8AxwJGgsJV+3CuLffHI= +cuelang.org/go v0.7.0/go.mod h1:ix+3dM/bSpdG9xg6qpCgnJnpeLtciZu+O/rDbywoMII= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns= +github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= +github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/bodgit/ntlmssp v0.0.0-20231219172012-333c521e41ca h1:oh6Uj74PZO4PTg56YT5jT50n5MuqqibtuO/2kJm9CfM= +github.com/bodgit/ntlmssp v0.0.0-20231219172012-333c521e41ca/go.mod h1:y5dFiv2ZRv5sdsTtU5pse0EALqX4Ljlt1Oq/Wm8VnmM= +github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= +github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4= +github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= +github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= +github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= +github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/emicklei/proto v1.13.0 h1:YtC/om6EdkJ0me1JPw4h2g10k+ELITjYFb7tpzm8i8k= +github.com/emicklei/proto v1.13.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fogleman/ease v0.0.0-20170301025033-8da417bf1776 h1:VRIbnDWRmAh5yBdz+J6yFMF5vso1It6vn+WmM/5l7MA= +github.com/fogleman/ease v0.0.0-20170301025033-8da417bf1776/go.mod h1:9wvnDu3YOfxzWM9Cst40msBF1C2UdQgDv962oTxSuMs= +github.com/go-courier/logr v0.3.0 h1:0VEQB1b53EmYQ+ZehrIgD8l2IO+WX7TY+CqzlykIFmo= +github.com/go-courier/logr v0.3.0/go.mod h1:OI7f/JCFZ1ZMD5qG3bIJr5WMNnGzd24+II1D9D9w5x4= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/innoai-tech/infra v0.0.0-20231124085701-81f94504013c h1:Jw7BA/VwQRArnqrkRI/+CfbEs4f0sNWccOH06VaAkOg= +github.com/innoai-tech/infra v0.0.0-20231124085701-81f94504013c/go.mod h1:FJSk27gnNYlNG6+GT633/EFC5yi05njFeefSFeAhqdw= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/k0sproject/rig v0.15.2-0.20231214092155-eaf96adb3d48 h1:+ZAUH6fgyq4OYj8pMoWpwhoiWH+c54n1mQ2t/ZlrWlc= +github.com/k0sproject/rig v0.15.2-0.20231214092155-eaf96adb3d48/go.mod h1:RE1LUWtdogpfRAu0MS932EJ5bDEjEKK40fF3P68eQkY= +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 v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +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/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= +github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= +github.com/masterzen/winrm v0.0.0-20231222090117-f1fbea7700af h1:zoRxUX78B+0p3P8idEI5jZgiayAze391vXtqOlqERSI= +github.com/masterzen/winrm v0.0.0-20231222090117-f1fbea7700af/go.mod h1:qfAjztAGRm7J7Ci10OA9vrx8WRDM0mlhdsFu7gBtMK8= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +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/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto= +github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/octohelm/cuemod v0.9.0 h1:1pLT0KKALaGCyX9qEl9/RI2hoRhZBi+9aRI02we8YFg= +github.com/octohelm/cuemod v0.9.0/go.mod h1:tbJx/QnncW0rFFEg8mczmAOg7b+GcBK9UhVzNDfn2tc= +github.com/octohelm/gengo v0.0.0-20230809023313-1339e47458a4 h1:wmmT8Xn6kUwKhzJva3JlFRPVnCTMrdSyVZTp6K0NAtg= +github.com/octohelm/gengo v0.0.0-20230809023313-1339e47458a4/go.mod h1:FmonJDnRb/0KoFSDB/OgptioHK/RBm820MJ7cjeIMaE= +github.com/octohelm/storage v0.0.0-20231213035828-4a91cdd3f879 h1:jUnrUj80QMXGL/NYVd2jWS23qOK9c2STB5QHhzZsEKg= +github.com/octohelm/storage v0.0.0-20231213035828-4a91cdd3f879/go.mod h1:2ITEqPIAZVfceuRSjn/fzxhVdCQN9d+yHJ8TY5cutEs= +github.com/octohelm/unifs v0.0.0-20231219081842-bde4a9d9600a h1:jOwMFyHTfx3HuDZcLm3+SzGxARmm7flj4g63mqyafRM= +github.com/octohelm/unifs v0.0.0-20231219081842-bde4a9d9600a/go.mod h1:xdBavyPaqY7EPcFUHE7rIedDkZNcQCUDA5YfkbCRiSE= +github.com/octohelm/x v0.0.0-20231115103341-17be3238221d h1:lycsfOkujgyuJd1ImFAXDd2fhDgF0R9XpK9m5t2AJYY= +github.com/octohelm/x v0.0.0-20231115103341-17be3238221d/go.mod h1:9rwJtDb1mgLuBdLyG4qRdaJ+gceJ/9vMtTwZffujHqo= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf h1:014O62zIzQwvoD7Ekj3ePDF5bv9Xxy0w6AZk0qYbjUk= +github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.11.1-0.20231026093722-fa6a31e0812c h1:fPpdjePK1atuOg28PXfNSqgwf9I/qD1Hlo39JFwKBXk= +github.com/rogpeppe/go-internal v1.11.1-0.20231026093722-fa6a31e0812c/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= +github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +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/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde h1:AMNpJRc7P+GTwVbl8DkK2I9I8BBUzNiHuH/tlxrpan0= +github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde/go.mod h1:MvrEmduDUz4ST5pGZ7CABCnOU5f3ZiOAZzT6b1A6nX8= +github.com/vito/midterm v0.1.4 h1:SALq5mQ+AgzeZxjL4loB6Uk5TZc9JMyX2ELA/NVH65c= +github.com/vito/midterm v0.1.4/go.mod h1:Mm3u6lrpzo2EFSJbwksKOdottTJzYePK8c1KJy4aRbk= +github.com/vito/progrock v0.10.1 h1:4M/nwUhJKO2Hmo0D89dfor0XZKx5k4mqLkEHWLINSYo= +github.com/vito/progrock v0.10.1/go.mod h1:ysw2W2gc20Snmlc0a34JbWO45HPM0oXO8IC59hyFc3k= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zmb3/spotify/v2 v2.4.0 h1:ZHdhBx/Qyn7rtVDP+onk/oSvtL5uVyJtb+VBLrNDC7Y= +github.com/zmb3/spotify/v2 v2.4.0/go.mod h1:m6c3mHgZSt1rTF76UfSfdn1Gb2Kx/B/ClCcr+2V1Scw= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE= +golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= +k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= +k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= +k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= +k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..e572431 --- /dev/null +++ b/install.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +set -e + +_detect_os() { + os="$(uname)" + case "$os" in + Darwin) echo "darwin" ;; + Linux) echo "linux" ;; + *) + echo "Unsupported system: $os" 1>&2 + return 1 + ;; + esac + unset arch +} + +_detect_arch() { + arch="$(uname -m)" + case "$arch" in + amd64 | x86_64) echo "amd64" ;; + arm64 | aarch64) echo "arm64" ;; + *) + echo "Unsupported processor architecture: $arch" 1>&2 + return 1 + ;; + esac + unset arch +} + +_download_url() { + echo "https://github.com/octohelm/piper/releases/download/latest/piper_${OS}_${ARCH}.tar.gz" +} + +OS="$(_detect_os)" +ARCH="$(_detect_arch)" +DOWNLOAD_URL="$(_download_url)" +INSTALL_PATH=/usr/local/bin + +rm -rf /tmp/piper +mkdir -p /tmp/piper +echo "Downloading piper from ${DOWNLOAD_URL}" +wget -c "${DOWNLOAD_URL}" -O - | tar -xz -C "/tmp/piper" +chmod 755 /tmp/piper/piper + +mkdir -p -- "${INSTALL_PATH}" +mv -f /tmp/piper/piper "${INSTALL_PATH}/piper" +echo "$(piper --version) is now executable in ${INSTALL_PATH}" diff --git a/internal/cmd/tool/main.go b/internal/cmd/tool/main.go new file mode 100644 index 0000000..cb0d452 --- /dev/null +++ b/internal/cmd/tool/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "context" + "os" + + "github.com/go-courier/logr" + "github.com/go-courier/logr/slog" + "github.com/innoai-tech/infra/devpkg/gengo" + "github.com/innoai-tech/infra/pkg/cli" + + _ "github.com/octohelm/gengo/devpkg/deepcopygen" + _ "github.com/octohelm/gengo/devpkg/runtimedocgen" + _ "github.com/octohelm/storage/devpkg/enumgen" +) + +var App = cli.NewApp("gengo", "dev") + +func init() { + cli.AddTo(App, &struct { + cli.C `name:"gen"` + gengo.Gengo + }{}) +} + +func main() { + ctx := logr.WithLogger(context.Background(), slog.Logger(slog.Default())) + + if err := cli.Execute(ctx, App, os.Args[1:]); err != nil { + panic(err) + } +} diff --git a/internal/version/version.go b/internal/version/version.go new file mode 100644 index 0000000..aa2e1c7 --- /dev/null +++ b/internal/version/version.go @@ -0,0 +1,9 @@ +package version + +var ( + version = "v0.0.0" +) + +func Version() string { + return version +} diff --git a/piper.cue b/piper.cue new file mode 100644 index 0000000..4fcd8de --- /dev/null +++ b/piper.cue @@ -0,0 +1,53 @@ +package main + +import ( + "piper.octohelm.tech/wd" + "piper.octohelm.tech/client" + + "github.com/octohelm/piper/cuepkg/github" + "github.com/octohelm/piper/cuepkg/golang" +) + +hosts: { + local: wd.#Local & { + } +} + +ver: client.#RevInfo & { +} + +actions: go: golang.#Project & { + cwd: hosts.local.wd + main: "./cmd/piper" + os: [ + "darwin", + "linux", + "windows", + ] + arch: [ + "amd64", + "arm64", + ] + ldflags: [ + "-s", "-w", + "-X", "github.com/octohelm/piper/internal/version.version=\(ver.version)", + ] +} + +actions: release: { + _env: client.#Env & { + GH_PASSWORD: client.#Secret + } + + github.#Release & { + owner: "octohelm" + repo: "piper" + token: _env.GH_PASSWORD + prerelease: true + assets: [ + for f in actions.go.archive { + f.output + }, + ] + } +} diff --git a/pkg/cueflow/controller.go b/pkg/cueflow/controller.go new file mode 100644 index 0000000..897d415 --- /dev/null +++ b/pkg/cueflow/controller.go @@ -0,0 +1,75 @@ +package cueflow + +import ( + "context" + "cuelang.org/go/cue" + cueerrors "cuelang.org/go/cue/errors" + "cuelang.org/go/tools/flow" + "github.com/pkg/errors" +) + +type RunTaskOptionFunc func(c *taskController) + +func RunTasks(ctx context.Context, scope Scope, opts ...RunTaskOptionFunc) error { + tr := &taskController{ + taskRunnerResolver: TaskRunnerFactoryContext.From(ctx), + shouldRun: func(value cue.Value) bool { + return value.LookupPath(TaskPath).Exists() + }, + } + + tr.Build(opts...) + + if err := tr.Run(ctx, scope); err != nil { + return err + } + + return nil +} + +func WithShouldRunFunc(shouldRun func(value cue.Value) bool) RunTaskOptionFunc { + return func(c *taskController) { + c.shouldRun = shouldRun + } +} + +func WithPrefix(path cue.Path) RunTaskOptionFunc { + return func(c *taskController) { + c.prefix = path + } +} + +type taskController struct { + taskRunnerResolver TaskRunnerResolver + shouldRun func(value cue.Value) bool + prefix cue.Path +} + +func (fc *taskController) Build(optFns ...RunTaskOptionFunc) { + for _, optFn := range optFns { + optFn(fc) + } +} + +func (fc *taskController) Run(ctx context.Context, scope Scope) error { + return NewFlow(scope, func(cueValue cue.Value) (flow.Runner, error) { + if !(fc.shouldRun(cueValue)) { + return nil, nil + } + + return flow.RunnerFunc(func(t *flow.Task) (err error) { + tk := WrapTask(t, scope) + + tr, err := fc.taskRunnerResolver.ResolveTaskRunner(tk) + if err != nil { + return errors.Wrap(err, "resolve task failed") + } + + if err := tr.Run(ctx); err != nil { + return cueerrors.Wrapf(err, tk.Value().Pos(), "%s run failed", tk.Name()) + } + + return nil + }), nil + }).Run(ctx) +} diff --git a/pkg/cueflow/cueify.go b/pkg/cueflow/cueify.go new file mode 100644 index 0000000..ef53bae --- /dev/null +++ b/pkg/cueflow/cueify.go @@ -0,0 +1 @@ +package cueflow diff --git a/pkg/cueflow/cueify/cueify.go b/pkg/cueflow/cueify/cueify.go new file mode 100644 index 0000000..68eb4d9 --- /dev/null +++ b/pkg/cueflow/cueify/cueify.go @@ -0,0 +1,353 @@ +package cueify + +import ( + "bytes" + "encoding" + "fmt" + "github.com/octohelm/gengo/pkg/camelcase" + "go/ast" + "reflect" + "strings" +) + +type Decl struct { + PkgPath string + Name string + Doc []string + Source []byte + Imports map[string]string + Fields map[string]*Field +} + +func WithPkgPathReplaceFunc(replace func(pkgPath string) string) OptionFunc { + return func(s *scanner) { + s.pkgPathReplace = replace + } +} + +func WithRegister(register func(t reflect.Type)) OptionFunc { + return func(s *scanner) { + s.register = register + } +} + +type OptionFunc func(s *scanner) + +func FromType(tpe reflect.Type, optFns ...OptionFunc) *Decl { + for tpe.Kind() == reflect.Ptr { + tpe = tpe.Elem() + } + + s := &scanner{ + pkgPath: tpe.PkgPath(), + defs: map[reflect.Type]bool{}, + fieldInfos: map[reflect.Type]map[string]*Field{}, + imports: map[string]string{}, + pkgPathReplace: func(pkgPath string) string { + return pkgPath + }, + register: func(t reflect.Type) {}, + } + + for _, optFn := range optFns { + optFn(s) + } + + c := &Decl{ + Name: "#" + tpe.Name(), + PkgPath: s.pkgPathReplace(tpe.PkgPath()), + } + + c.Source = s.CueDecl(tpe, opt{ + naming: c.Name, + }) + + c.Imports = s.imports + c.Fields, _ = s.fieldInfos[tpe] + + v := reflect.New(tpe).Interface() + + c.Doc, _ = getRuntimeDoc(v) + + return c +} + +type scanner struct { + pkgPath string + defs map[reflect.Type]bool + imports map[string]string + pkgPathReplace func(pkgPath string) string + register func(t reflect.Type) + fieldInfos map[reflect.Type]map[string]*Field +} + +type opt struct { + naming string + embed string +} + +var oneOfType = reflect.TypeOf((*OneOfType)(nil)).Elem() +var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + +func (s *scanner) Named(name string, pkgPath string) string { + if pkgPath == s.pkgPath { + return "#" + name + } + + replaced := s.pkgPathReplace(pkgPath) + alias := camelcase.LowerSnakeCase(replaced) + s.imports[replaced] = alias + return alias + ".#" + name +} + +func (s *scanner) CueDecl(tpe reflect.Type, o opt) []byte { + if o.naming == "" && tpe.PkgPath() != "" { + if _, ok := s.defs[tpe]; !ok { + s.defs[tpe] = true + s.register(tpe) + } + + if o.embed != "" { + return []byte(fmt.Sprintf(`%s & { + %s +}`, s.Named(tpe.Name(), tpe.PkgPath()), o.embed)) + } + return []byte(s.Named(tpe.Name(), tpe.PkgPath())) + } + + if tpe.Implements(textMarshalerType) { + return []byte("string") + } + + if tpe.Implements(oneOfType) { + if ot, ok := reflect.New(tpe).Interface().(OneOfType); ok { + types := ot.OneOf() + b := bytes.NewBuffer(nil) + + for i := range types { + t := reflect.TypeOf(types[i]) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if i > 0 { + b.WriteString(" | ") + } + b.Write(s.CueDecl(t, opt{embed: o.embed})) + } + + return b.Bytes() + } + } + + switch tpe.Kind() { + case reflect.Ptr: + return []byte(fmt.Sprintf("%s | null", s.CueDecl(tpe.Elem(), opt{embed: o.embed}))) + case reflect.Map: + return []byte(fmt.Sprintf("[X=%s]: %s", s.CueDecl(tpe.Key(), opt{embed: o.embed}), s.CueDecl(tpe.Elem(), opt{embed: o.embed}))) + case reflect.Slice: + if tpe.Elem().Kind() == reflect.Uint8 { + return []byte("bytes") + } + return []byte(fmt.Sprintf("[...%s]", s.CueDecl(tpe.Elem(), opt{embed: o.embed}))) + case reflect.Struct: + b := bytes.NewBuffer(nil) + + fields := map[string]*Field{} + defer func() { + s.fieldInfos[tpe] = fields + }() + + _, _ = fmt.Fprintf(b, `{ +`) + + walkFields(tpe, func(field *Field) { + fields[field.Name] = field + + t := field.Type + + if field.Inline { + if t.Kind() == reflect.Map { + _, _ = fmt.Fprintf(b, `[!~"\\$\\$task"]: %s`, s.CueDecl(t.Elem(), opt{ + embed: field.Embed, + })) + } + return + } + + if len(field.Doc) > 0 { + for _, l := range field.Doc { + _, _ = fmt.Fprintf(b, `// %s +`, l) + } + } + + if field.Optional { + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + _, _ = fmt.Fprintf(b, "%s?: ", field.Name) + } else { + _, _ = fmt.Fprintf(b, "%s: ", field.Name) + } + + cueType := s.CueDecl(t, opt{ + embed: field.Embed, + }) + + if len(field.Enum) > 0 { + for i, e := range field.Enum { + if i > 0 { + _, _ = fmt.Fprint(b, " | ") + } + _, _ = fmt.Fprintf(b, `%q`, e) + } + } else { + _, _ = fmt.Fprintf(b, "%s", cueType) + } + + if field.DefaultValue != nil { + switch string(cueType) { + case "bytes": + _, _ = fmt.Fprintf(b, ` | *'%s'`, *field.DefaultValue) + case "string": + _, _ = fmt.Fprintf(b, ` | *%q`, *field.DefaultValue) + default: + _, _ = fmt.Fprintf(b, ` | *%v`, *field.DefaultValue) + } + } + + if field.AsOutput { + _, _ = fmt.Fprintf(b, " @generated()") + } + + _, _ = fmt.Fprint(b, "\n") + }) + + if strings.HasSuffix(o.naming, "Interface") { + _, _ = fmt.Fprintf(b, ` +... +`) + } + + _, _ = fmt.Fprintf(b, `}`) + + return b.Bytes() + case reflect.Interface: + return []byte("_") + default: + return []byte(tpe.Kind().String()) + } +} + +type Field struct { + Name string + Doc []string + Embed string + Idx int + Type reflect.Type + AsOutput bool + Optional bool + Inline bool + DefaultValue *string + Enum []string +} + +func (i *Field) EmptyDefaults() (string, bool) { + if i.Type.PkgPath() != "" { + return "", false + } + + switch i.Type.Kind() { + case reflect.Slice: + return "", false + case reflect.Map: + return "", false + case reflect.Interface: + return "", false + default: + return fmt.Sprintf("%v", reflect.New(i.Type).Elem()), true + } +} + +func walkFields(st reflect.Type, each func(info *Field)) { + if st.Kind() != reflect.Struct { + return + } + + v := reflect.New(st).Interface() + + for i := 0; i < st.NumField(); i++ { + f := st.Field(i) + + if !ast.IsExported(f.Name) { + continue + } + + info := &Field{} + info.Idx = i + info.Name = f.Name + info.Type = f.Type + if doc, ok := getRuntimeDoc(v, f.Name); ok { + info.Doc = doc + } + + jsonTag, hasJsonTag := f.Tag.Lookup("json") + if !hasJsonTag { + if f.Anonymous && f.Type.Kind() == reflect.Struct { + walkFields(f.Type, each) + } + continue + } + + if strings.Contains(jsonTag, ",omitempty") { + info.Optional = true + } + + if embed, hasEmbedTag := f.Tag.Lookup("embed"); hasEmbedTag { + info.Embed = embed + } + + taskTag, hasOutput := f.Tag.Lookup("output") + if jsonTag == "-" && !hasOutput { + continue + } + + if jsonName := strings.SplitN(jsonTag, ",", 2)[0]; jsonName != "" { + info.Name = jsonName + } + + if hasOutput { + attrs := strings.SplitN(taskTag, ",", 2) + + info.AsOutput = true + + if name := attrs[0]; name != "" { + info.Name = name + } + } + + if defaultValue, ok := f.Tag.Lookup("default"); ok { + info.DefaultValue = &defaultValue + } + + if enumValue, ok := f.Tag.Lookup("enum"); ok { + info.Enum = strings.Split(enumValue, ",") + } + + if strings.Contains(jsonTag, ",inline") { + info.Inline = true + info.Name = "" + } + + each(info) + } +} + +func getRuntimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} diff --git a/pkg/cueflow/cueify/one_of.go b/pkg/cueflow/cueify/one_of.go new file mode 100644 index 0000000..b736021 --- /dev/null +++ b/pkg/cueflow/cueify/one_of.go @@ -0,0 +1,5 @@ +package cueify + +type OneOfType interface { + OneOf() []any +} diff --git a/pkg/cueflow/cueify/zz_generated.runtimedoc.go b/pkg/cueflow/cueify/zz_generated.runtimedoc.go new file mode 100644 index 0000000..8c96aeb --- /dev/null +++ b/pkg/cueflow/cueify/zz_generated.runtimedoc.go @@ -0,0 +1,73 @@ +/* +Package cueify GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package cueify + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Decl) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "PkgPath": + return []string{}, true + case "Name": + return []string{}, true + case "Doc": + return []string{}, true + case "Source": + return []string{}, true + case "Imports": + return []string{}, true + case "Fields": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} + +func (v Field) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Name": + return []string{}, true + case "Doc": + return []string{}, true + case "Embed": + return []string{}, true + case "Idx": + return []string{}, true + case "Type": + return []string{}, true + case "AsOutput": + return []string{}, true + case "Optional": + return []string{}, true + case "Inline": + return []string{}, true + case "DefaultValue": + return []string{}, true + case "Enum": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} + +func (OptionFunc) RuntimeDoc(names ...string) ([]string, bool) { + return []string{}, true +} diff --git a/pkg/cueflow/flow.go b/pkg/cueflow/flow.go new file mode 100644 index 0000000..6887a6d --- /dev/null +++ b/pkg/cueflow/flow.go @@ -0,0 +1,19 @@ +package cueflow + +import ( + "cuelang.org/go/tools/flow" +) + +func NewFlow(v Scope, taskFunc flow.TaskFunc) *flow.Controller { + return flow.New(&flow.Config{ + FindHiddenTasks: true, + UpdateFunc: func(c *flow.Controller, t *flow.Task) error { + if t != nil { + // when task value changes + // need to put back value to root for using by child tasks + return v.Fill(t.Path(), WrapValue(t.Value())) + } + return nil + }, + }, CueValue(v.Value()), taskFunc) +} diff --git a/pkg/cueflow/runner.go b/pkg/cueflow/runner.go new file mode 100644 index 0000000..d70dd0e --- /dev/null +++ b/pkg/cueflow/runner.go @@ -0,0 +1,263 @@ +package cueflow + +import ( + "bytes" + "compress/zlib" + "context" + "encoding/base64" + "fmt" + "io" + "os" + "sort" + "strconv" + "strings" + "sync/atomic" + "text/tabwriter" + + "cuelang.org/go/cue" + "cuelang.org/go/cue/ast" + cueerrors "cuelang.org/go/cue/errors" + "cuelang.org/go/tools/flow" + "github.com/go-courier/logr" + "github.com/pkg/errors" +) + +func NewRunner(value Value) *Runner { + r := &Runner{} + r.root.Store(&scope{Value: value}) + return r +} + +type scope struct { + Value Value +} + +type Runner struct { + root atomic.Pointer[scope] + + target cue.Path + output string + + setups map[string][]string + targets map[string][]string +} + +func (r *Runner) printAllowedTasksTo(w io.Writer, tasks []*flow.Task) { + scope := r.target + + _, _ = fmt.Fprintf(w, ` +Undefined action: + +`) + printSelectors(w, scope.Selectors()[1:]...) + + _, _ = fmt.Fprintf(w, ` +Allowed action: + +`) + + taskSelectors := map[string][]cue.Selector{} + + for _, t := range tasks { + selectors := t.Path().Selectors() + + if selectors[0].String() == "actions" { + publicSelectors := make([]cue.Selector, 0, len(selectors)-1) + + func() { + for _, selector := range selectors[1:] { + if selector.String()[0] == '_' { + return + } + publicSelectors = append(publicSelectors, selector) + } + }() + + taskSelectors[cue.MakePath(publicSelectors...).String()] = publicSelectors + } + } + + keys := make([]string, 0, len(taskSelectors)) + for k := range taskSelectors { + keys = append(keys, k) + } + sort.Strings(keys) + + tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', tabwriter.TabIndent) + defer func() { + _ = tw.Flush() + }() + + for _, k := range keys { + printSelectors(tw, taskSelectors[k]...) + + taskValue := r.Value().(CueValueWrapper).CueValue().LookupPath(cue.ParsePath("actions." + k)) + + if n := taskValue.Source(); n != nil { + for _, c := range ast.Comments(n) { + _, _ = fmt.Fprintf(tw, "\t\t%s", strings.TrimSpace(c.Text())) + } + } + + _, _ = fmt.Fprintln(tw) + } +} + +func printSelectors(w io.Writer, selectors ...cue.Selector) { + for i, s := range selectors { + if i > 0 { + _, _ = fmt.Fprintf(w, ` `) + } + _, _ = fmt.Fprintf(w, `%s`, s.String()) + } +} + +func (r *Runner) resolveDependencies(t *flow.Task, collection map[string][]string) { + p := t.Path().String() + if _, ok := collection[p]; ok { + return + } + + // avoid cycle + collection[p] = make([]string, 0) + + deps := make([]string, 0) + for _, d := range t.Dependencies() { + deps = append(deps, d.Path().String()) + r.resolveDependencies(d, collection) + } + + collection[p] = deps +} + +func (r *Runner) Value() Value { + return r.root.Load().Value +} + +func (r *Runner) Fill(p cue.Path, v Value) error { + r.root.Store(&scope{Value: r.Value().FillPath(p, v)}) + return nil +} + +func (r *Runner) Run(ctx context.Context, action []string) error { + actions := append([]string{"actions"}, action...) + for i := range actions { + actions[i] = strconv.Quote(actions[i]) + } + + r.target = cue.ParsePath(strings.Join(actions, ".")) + + logr.FromContext(ctx).Debug("resolving tasks") + + f := NewFlow(r, noOpRunner) + + if err := r.prepareTasks(ctx, f.Tasks()); err != nil { + return err + } + + logr.FromContext(ctx).Info("running") + + if err := RunTasks(ctx, r, WithShouldRunFunc(func(value cue.Value) bool { + _, ok := r.setups[value.Path().String()] + return ok + })); err != nil { + return err + } + + if err := RunTasks(ctx, r, WithShouldRunFunc(func(value cue.Value) bool { + _, ok := r.targets[value.Path().String()] + return ok + })); err != nil { + return err + } + + if o := r.Value().LookupPath(r.target).LookupPath(cue.ParsePath("result")); o.Exists() { + if ok := o.LookupPath(cue.ParsePath("ok")); ok.Exists() { + ok, _ := CueValue(ok).Bool() + if ok { + logr.FromContext(ctx).WithValues("result", CueLogValue(o)).Info("success.") + } else { + logr.FromContext(ctx).WithValues("result", CueLogValue(o)).Error(errors.New("failed.")) + } + } else { + logr.FromContext(ctx).WithValues("result", CueLogValue(o)).Info("done.") + } + } + + return nil +} + +func (r *Runner) prepareTasks(ctx context.Context, tasks []*flow.Task) error { + taskRunnerFactory := TaskRunnerFactoryContext.From(ctx) + + r.setups = map[string][]string{} + r.targets = map[string][]string{} + + for i := range tasks { + tk := WrapTask(tasks[i], r) + + t, err := taskRunnerFactory.ResolveTaskRunner(tk) + if err != nil { + return cueerrors.Wrapf(err, tk.Value().Pos(), "resolve task failed") + } + + if _, ok := t.Underlying().(interface{ Setup() bool }); ok { + r.resolveDependencies(tasks[i], r.setups) + } + + if strings.HasPrefix(tk.Path().String(), r.target.String()) { + r.resolveDependencies(tasks[i], r.targets) + } + } + + if r.target.String() != "actions" && len(r.targets) > 0 { + if os.Getenv("GRAPH") != "" { + fmt.Println(printGraph(r.targets)) + } + return nil + } + + buf := bytes.NewBuffer(nil) + r.printAllowedTasksTo(buf, tasks) + return errors.New(buf.String()) +} + +func noOpRunner(cueValue cue.Value) (flow.Runner, error) { + v := cueValue.LookupPath(TaskPath) + + if !v.Exists() { + return nil, nil + } + + // task in slice not be valid task + for _, s := range v.Path().Selectors() { + if s.Type() == cue.IndexLabel { + return nil, nil + } + } + + return flow.RunnerFunc(func(t *flow.Task) error { + return nil + }), nil +} + +func printGraph(targets map[string][]string) (string, error) { + buffer := bytes.NewBuffer(nil) + + w, err := zlib.NewWriterLevel(buffer, 9) + if err != nil { + return "", errors.Wrap(err, "fail to create the w") + } + + _, _ = fmt.Fprintf(w, "direction: right\n") + for name, deps := range targets { + for _, d := range deps { + _, _ = fmt.Fprintf(w, "%q -> %q\n", d, name) + } + } + _ = w.Close() + if err != nil { + return "", errors.Wrap(err, "fail to create the payload") + } + return fmt.Sprintf("https://kroki.io/d2/svg/%s?theme=101", base64.URLEncoding.EncodeToString(buffer.Bytes())), nil +} diff --git a/pkg/cueflow/scope.go b/pkg/cueflow/scope.go new file mode 100644 index 0000000..21a572b --- /dev/null +++ b/pkg/cueflow/scope.go @@ -0,0 +1,8 @@ +package cueflow + +import "cuelang.org/go/cue" + +type Scope interface { + Value() Value + Fill(path cue.Path, value Value) error +} diff --git a/pkg/cueflow/task.go b/pkg/cueflow/task.go new file mode 100644 index 0000000..b500f60 --- /dev/null +++ b/pkg/cueflow/task.go @@ -0,0 +1,88 @@ +package cueflow + +import ( + "fmt" + "os" + "strings" + + "cuelang.org/go/cue" + "cuelang.org/go/tools/flow" +) + +var TaskPath = cue.ParsePath("$$task.name") + +type Task interface { + fmt.Stringer + Name() string + Path() cue.Path + Scope() Scope + Value() Value + Decode(inputs any) error + Fill(values map[string]any) error +} + +type Group interface { + SetParent(t Task) + Parent() Task +} + +func WrapTask(t *flow.Task, scope Scope) Task { + name, _ := t.Value().LookupPath(TaskPath).String() + + return &task{ + name: name, + scope: scope, + task: t, + } +} + +type task struct { + name string + scope Scope + task *flow.Task +} + +func (t *task) Path() cue.Path { + return t.task.Path() +} + +func (t *task) Scope() Scope { + return t.scope +} + +func (t *task) Decode(input any) error { + if lv, ok := input.(Group); ok { + lv.SetParent(t) + return nil + } + + if err := t.Value().Decode(input); err != nil { + _, _ = fmt.Fprint(os.Stdout, t.Value().Source()) + _, _ = fmt.Fprintln(os.Stdout) + return err + } + + return nil +} + +func (t *task) Name() string { + return t.name +} + +func (t *task) Value() Value { + // always pick value from root + return t.scope.Value().LookupPath(t.task.Path()) +} + +func (t *task) String() string { + return fmt.Sprintf("%s %s", t.Path(), shortName(t.Name())) +} + +func (t *task) Fill(values map[string]any) error { + return t.task.Fill(values) +} + +func shortName(name string) string { + parts := strings.Split(name, "/") + return parts[len(parts)-1] +} diff --git a/pkg/cueflow/task_impl.go b/pkg/cueflow/task_impl.go new file mode 100644 index 0000000..8311e4e --- /dev/null +++ b/pkg/cueflow/task_impl.go @@ -0,0 +1,19 @@ +package cueflow + +type FlowTask interface { + flowTask() +} + +type TaskImpl struct { +} + +func (TaskImpl) flowTask() { +} + +type TaskImplRegister interface { + Register(t any) +} + +type Result interface { + Success() bool +} diff --git a/pkg/cueflow/task_runner.go b/pkg/cueflow/task_runner.go new file mode 100644 index 0000000..c7a0ff4 --- /dev/null +++ b/pkg/cueflow/task_runner.go @@ -0,0 +1,126 @@ +package cueflow + +import ( + "context" + "cuelang.org/go/cue" + "github.com/go-courier/logr" + contextx "github.com/octohelm/x/context" + "github.com/pkg/errors" + "reflect" +) + +var TaskRunnerFactoryContext = contextx.New[TaskRunnerResolver]() + +type TaskRunnerResolver interface { + ResolveTaskRunner(task Task) (TaskRunner, error) +} + +type TaskRunner interface { + Path() cue.Path + Underlying() any + Run(ctx context.Context) error +} + +type StepRunner interface { + Do(ctx context.Context) error +} + +type WithScopeName interface { + ScopeName(ctx context.Context) string +} + +type taskRunner struct { + task Task + inputTaskRunner reflect.Value + outputFields map[string]int +} + +func (t *taskRunner) Underlying() any { + return t.inputTaskRunner.Interface() +} + +func (t *taskRunner) Path() cue.Path { + return t.task.Path() +} + +func (t *taskRunner) Task() Task { + return t.task +} + +func (t *taskRunner) resultValues() map[string]any { + values := map[string]any{} + + rv := t.inputTaskRunner + + if rv.Kind() == reflect.Ptr { + rv = rv.Elem() + } + + for name, i := range t.outputFields { + if name == "" { + f := rv.Field(i) + if f.Kind() == reflect.Map { + for _, k := range f.MapKeys() { + key := k.String() + if key == "$$task" { + continue + } + values[key] = f.MapIndex(k).Interface() + } + } + continue + } + values[name] = rv.Field(i).Interface() + } + + return values +} + +func (t *taskRunner) Run(ctx context.Context) (err error) { + taskValue := t.inputTaskRunner.Interface() + + inputStepRunner := taskValue.(StepRunner) + l := logr.FromContext(ctx) + + if err := t.task.Decode(inputStepRunner); err != nil { + return errors.Wrapf(err, "decode failed") + } + + if n, ok := inputStepRunner.(WithScopeName); ok { + l = l.WithValues("scope", n.ScopeName(ctx)) + } + + ctx, l = l.Start(ctx, t.task.String()) + defer l.End() + + l.WithValues("inputs", CueLogValue(inputStepRunner)).Debug("started.") + + if err := inputStepRunner.Do(ctx); err != nil { + + return errors.Wrapf(err, "%T do failed", inputStepRunner) + } + + values := t.resultValues() + + defer func() { + if err != nil { + l.Error(err) + } else { + if result, ok := values["result"].(Result); ok { + if result.Success() { + l.WithValues("result", CueLogValue(result)).Debug("success.") + } else { + l.WithValues("result", CueLogValue(result)).Debug("failed.") + } + } else if output, ok := values["output"]; ok { + l.WithValues("output", CueLogValue(output)).Debug("done.") + } + } + }() + + if err := t.task.Fill(values); err != nil { + return errors.Wrap(err, "fill result failed") + } + + return nil +} diff --git a/pkg/cueflow/task_runner_factory.go b/pkg/cueflow/task_runner_factory.go new file mode 100644 index 0000000..352bf05 --- /dev/null +++ b/pkg/cueflow/task_runner_factory.go @@ -0,0 +1,242 @@ +package cueflow + +import ( + "bytes" + "context" + "fmt" + "github.com/octohelm/unifs/pkg/filesystem" + "github.com/pkg/errors" + "io" + "os" + "path" + "path/filepath" + "reflect" + "sort" + + cueformat "cuelang.org/go/cue/format" + "github.com/octohelm/piper/pkg/cueflow/cueify" +) + +type TaskRunnerFactory interface { + New(task Task) (TaskRunner, error) +} + +type TaskFactory interface { + TaskRunnerResolver + TaskImplRegister + Sources(ctx context.Context) (filesystem.FileSystem, error) +} + +func NewTaskFactory(domain string) TaskFactory { + return &factory{ + domain: domain, + named: map[string]*namedType{}, + } +} + +type factory struct { + domain string + named map[string]*namedType +} + +func (f *factory) Register(t any) { + tpe := reflect.TypeOf(t) + for tpe.Kind() == reflect.Ptr { + tpe = tpe.Elem() + } + f.register(tpe) +} + +func (f *factory) register(tpe reflect.Type) { + block := cueify.FromType(tpe, + cueify.WithPkgPathReplaceFunc(func(pkgPath string) string { + return fmt.Sprintf("%s/%s", f.domain, filepath.Base(pkgPath)) + }), + cueify.WithRegister(f.register), + ) + + pt := &namedType{ + tpe: tpe, + outputFields: map[string]int{}, + decl: block, + } + + if _, ok := reflect.New(tpe).Interface().(FlowTask); ok { + pt.flowTask = true + } + + for _, info := range pt.decl.Fields { + if info.AsOutput { + pt.outputFields[info.Name] = info.Idx + } + } + + f.named[pt.FullName()] = pt +} + +func (f *factory) ResolveTaskRunner(task Task) (TaskRunner, error) { + if found, ok := f.named[task.Name()]; ok { + return found.New(task) + } + return nil, fmt.Errorf("unknown task `%s`", task) +} + +type source struct { + pkgName string + imports map[string]string + bytes.Buffer +} + +func (s *source) WriteDecl(named *namedType) { + for k, v := range named.decl.Imports { + s.imports[k] = v + } + + s.WriteString("\n") + + if named.flowTask { + _, _ = fmt.Fprintf(s, `%s: $$task: name: %q +`, named.decl.Name, named.FullName()) + } + + _, _ = fmt.Fprintf(s, `%s: %s +`, named.decl.Name, named.decl.Source) +} + +func (s *source) Source() ([]byte, error) { + b := bytes.NewBufferString("package " + s.pkgName) + + if len(s.imports) > 0 { + _, _ = fmt.Fprintf(b, ` + +import ( +`) + + for e := range SortedIter(context.Background(), s.imports) { + + _, _ = fmt.Fprintf(b, `%s %q +`, e.Value, e.Key) + } + + _, _ = fmt.Fprintf(b, `) +`) + } + + _, _ = io.Copy(b, s) + + data, err := cueformat.Source(b.Bytes(), cueformat.Simplify()) + if err != nil { + return nil, errors.Wrapf(err, `format invalid: + +%s`, b.Bytes()) + } + return data, nil +} + +func (f *factory) Sources(ctx context.Context) (filesystem.FileSystem, error) { + sources := map[string]*source{} + + for nt := range SortedIter(ctx, f.named) { + s, ok := sources[nt.Value.decl.PkgPath] + if !ok { + s = &source{ + pkgName: filepath.Base(nt.Value.decl.PkgPath), + imports: map[string]string{}, + } + sources[nt.Value.decl.PkgPath] = s + } + + s.WriteDecl(nt.Value) + } + + fs := filesystem.NewMemFS() + + for pathPath, s := range sources { + code, err := s.Source() + if err != nil { + return nil, err + } + + if err := WriteFile(ctx, fs, path.Join(pathPath, s.pkgName+".cue"), code); err != nil { + return nil, err + } + } + + return fs, nil +} + +func WriteFile(ctx context.Context, fs filesystem.FileSystem, filename string, data []byte) error { + if err := filesystem.MkdirAll(ctx, fs, filepath.Dir(filename)); err != nil { + return err + } + file, err := fs.OpenFile(ctx, filename, os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + return err + } + defer file.Close() + if _, err := file.Write(data); err != nil { + return err + } + return nil +} + +type namedType struct { + tpe reflect.Type + flowTask bool + outputFields map[string]int + decl *cueify.Decl +} + +func (nt *namedType) New(planTask Task) (TaskRunner, error) { + r := &taskRunner{ + task: planTask, + inputTaskRunner: reflect.New(nt.tpe), + outputFields: map[string]int{}, + } + + for f, i := range nt.outputFields { + r.outputFields[f] = i + } + + return r, nil +} + +func (nt *namedType) FullName() string { + return fmt.Sprintf("%s.%s", nt.decl.PkgPath, nt.decl.Name) +} + +func SortedIter[V any](ctx context.Context, m map[string]V) <-chan *Element[V] { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + + ch := make(chan *Element[V]) + + go func() { + defer func() { + close(ch) + }() + + for _, key := range keys { + select { + case <-ctx.Done(): + return + default: + ch <- &Element[V]{ + Key: key, + Value: m[key], + } + } + } + }() + + return ch +} + +// +gengo:runtimedoc=false +type Element[V any] struct { + Key string + Value V +} diff --git a/pkg/cueflow/value.go b/pkg/cueflow/value.go new file mode 100644 index 0000000..9b7c0c9 --- /dev/null +++ b/pkg/cueflow/value.go @@ -0,0 +1,113 @@ +package cueflow + +import ( + "log/slog" + + "cuelang.org/go/cue" + cueformat "cuelang.org/go/cue/format" + "cuelang.org/go/cue/token" + "encoding/json" +) + +type Value interface { + Path() cue.Path + Pos() token.Pos + + Exists() bool + LookupPath(p cue.Path) Value + FillPath(p cue.Path, v any) Value + + Decode(target any) error + Source(opts ...cue.Option) string +} + +func CueValue(v Value) cue.Value { + if w, ok := v.(CueValueWrapper); ok { + return w.CueValue() + } + return cue.Value{} +} + +type CueValueWrapper interface { + CueValue() cue.Value +} + +func WrapValue(cueValue cue.Value) Value { + return &value{cueValue: cueValue} +} + +type value struct { + cueValue cue.Value +} + +func (val *value) CueValue() cue.Value { + return val.cueValue +} + +func (val *value) Path() cue.Path { + return val.cueValue.Path() +} + +func (val *value) Pos() token.Pos { + return val.cueValue.Pos() +} + +func (val *value) Decode(target any) error { + return val.cueValue.Decode(target) +} + +func (val *value) Source(opts ...cue.Option) string { + syn := val.cueValue.Syntax( + append(opts, + cue.Concrete(false), // allow incomplete values + cue.DisallowCycles(true), + cue.Docs(true), + cue.All(), + )..., + ) + data, _ := cueformat.Node(syn, cueformat.Simplify()) + return string(data) +} + +func (val *value) Exists() bool { + return val.cueValue.Exists() +} + +func (val *value) LookupPath(p cue.Path) Value { + return WrapValue(val.cueValue.LookupPath(p)) +} + +func (val *value) FillPath(p cue.Path, v any) Value { + switch x := v.(type) { + case CueValueWrapper: + return WrapValue(val.cueValue.FillPath(p, x.CueValue())) + default: + return WrapValue(val.cueValue.FillPath(p, x)) + } +} + +func CueLogValue(v any) slog.LogValuer { + return &logValue{v: v} +} + +type logValue struct { + v any +} + +func (c *logValue) LogValue() slog.Value { + switch x := c.v.(type) { + case Value: + return slog.AnyValue(x.Source(cue.Final())) + default: + data, err := json.MarshalIndent(c.v, "", " ") + if err != nil { + panic(err) + } + data, err = cueformat.Source(data, cueformat.Simplify()) + if err != nil { + panic(err) + } + return slog.AnyValue(data) + } + +} diff --git a/pkg/cueflow/zz_generated.runtimedoc.go b/pkg/cueflow/zz_generated.runtimedoc.go new file mode 100644 index 0000000..c3c7634 --- /dev/null +++ b/pkg/cueflow/zz_generated.runtimedoc.go @@ -0,0 +1,19 @@ +/* +Package cueflow GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package cueflow + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (RunTaskOptionFunc) RuntimeDoc(names ...string) ([]string, bool) { + return []string{}, true +} diff --git a/pkg/engine/logger.go b/pkg/engine/logger.go new file mode 100644 index 0000000..014ed0b --- /dev/null +++ b/pkg/engine/logger.go @@ -0,0 +1,200 @@ +package engine + +import ( + "context" + "fmt" + "github.com/fatih/color" + "io" + "log/slog" + "strings" + + "github.com/go-courier/logr" + "github.com/innoai-tech/infra/pkg/configuration" + "github.com/opencontainers/go-digest" + "github.com/vito/progrock" +) + +// +gengo:enum +type LogLevel string + +const ( + ErrorLevel LogLevel = "error" + WarnLevel LogLevel = "warn" + InfoLevel LogLevel = "info" + DebugLevel LogLevel = "debug" +) + +type Logger struct { + // Log level + LogLevel LogLevel `flag:",omitempty"` + + logger logr.Logger +} + +func (l *Logger) SetDefaults() { + if l.LogLevel == "" { + l.LogLevel = InfoLevel + } +} + +func (l *Logger) Init(ctx context.Context) error { + if l.logger == nil { + lvl, _ := logr.ParseLevel(string(l.LogLevel)) + l.logger = &logger{ + lvl: lvl, + vtx: progrock.FromContext(ctx).Vertex(digest.FromString("piper"), "piper"), + } + } + return nil +} + +func (l *Logger) InjectContext(ctx context.Context) context.Context { + return configuration.InjectContext( + ctx, + configuration.InjectContextFunc(logr.WithLogger, l.logger), + ) +} + +type logger struct { + lvl logr.Level + vtx *progrock.VertexRecorder + attrs []slog.Attr +} + +func (d *logger) WithValues(keyAndValues ...any) logr.Logger { + return &logger{ + lvl: d.lvl, + vtx: d.vtx, + attrs: append(d.attrs, argsToAttrSlice(keyAndValues)...), + } +} + +func (d *logger) Start(ctx context.Context, name string, keyAndValues ...any) (context.Context, logr.Logger) { + display := name + + for _, attr := range d.attrs { + if attr.Key == "scope" { + display += color.BlueString(" %s", attr.Value) + break + } + } + + vtx := progrock.FromContext(ctx).Vertex( + digest.FromString(name), + color.MagentaString(display), + progrock.Internal(), + ) + + l := &logger{ + lvl: d.lvl, + vtx: vtx, + attrs: append(d.attrs, argsToAttrSlice(keyAndValues)...), + } + + return logr.WithLogger(ctx, l), l +} + +func (d *logger) End() { + d.vtx.Done(nil) + d.attrs = nil +} + +func (d *logger) Enabled(lvl logr.Level) bool { + return lvl <= d.lvl +} + +func (d *logger) Debug(format string, args ...any) { + if !d.Enabled(logr.DebugLevel) { + return + } + + if len(args) > 0 { + d.printf(d.vtx.Stdout(), color.WhiteString(strings.ToUpper(logr.DebugLevel.String()[:4])), fmt.Sprintf(format, args...)) + } else { + d.printf(d.vtx.Stdout(), color.WhiteString(strings.ToUpper(logr.DebugLevel.String()[:4])), format) + } +} + +func (d *logger) Info(format string, args ...any) { + if !d.Enabled(logr.InfoLevel) { + return + } + + if len(args) > 0 { + d.printf(d.vtx.Stdout(), color.GreenString(strings.ToUpper(logr.InfoLevel.String()[:4])), fmt.Sprintf(format, args...)) + } else { + d.printf(d.vtx.Stdout(), color.GreenString(strings.ToUpper(logr.InfoLevel.String()[:4])), format) + } +} + +func (d *logger) Warn(err error) { + if err == nil || !d.Enabled(logr.WarnLevel) { + return + } + + d.printf(d.vtx.Stderr(), color.YellowString(strings.ToUpper(logr.InfoLevel.String()[:4])), err.Error()) +} + +func (d *logger) Error(err error) { + if err == nil || !d.Enabled(logr.ErrorLevel) { + return + } + d.printf(d.vtx.Stderr(), color.RedString(strings.ToUpper(logr.ErrorLevel.String()[:4])), err.Error()) +} + +func (d *logger) printf(w io.Writer, prefix string, msg string) { + _, _ = fmt.Fprint(w, prefix) + _, _ = fmt.Fprint(w, " ") + _, _ = fmt.Fprint(w, msg) + + for _, attr := range d.attrs { + if attr.Key != "scope" { + switch attr.Value.Kind() { + case slog.KindString: + _, _ = fmt.Fprintf(w, color.WhiteString(" %s=%q", attr.Key, attr.Value)) + default: + logValue := attr.Value.Any() + if valuer, ok := logValue.(slog.LogValuer); ok { + logValue = valuer.LogValue().Any() + } + + switch x := logValue.(type) { + case []byte: + _, _ = fmt.Fprint(w, color.WhiteString(" %s=%v", attr.Key, string(x))) + default: + _, _ = fmt.Fprint(w, color.WhiteString(" %s=%v", attr.Key, x)) + } + } + } + } + + _, _ = fmt.Fprintln(w) +} + +func argsToAttrSlice(args []any) []slog.Attr { + var ( + attr slog.Attr + attrs []slog.Attr + ) + for len(args) > 0 { + attr, args = argsToAttr(args) + attrs = append(attrs, attr) + } + return attrs +} + +func argsToAttr(args []any) (slog.Attr, []any) { + switch x := args[0].(type) { + case string: + if len(args) == 1 { + return slog.String(badKey, x), nil + } + return slog.Any(x, args[1]), args[2:] + case slog.Attr: + return x, args[1:] + default: + return slog.Any(badKey, x), args[1:] + } +} + +const badKey = "!BADKEY" diff --git a/pkg/engine/pipleline.go b/pkg/engine/pipleline.go new file mode 100644 index 0000000..8c49ed3 --- /dev/null +++ b/pkg/engine/pipleline.go @@ -0,0 +1,84 @@ +package engine + +import ( + "bytes" + "github.com/mattn/go-isatty" + "github.com/vito/progrock/console" + "os" + "sync" + + cueerrors "cuelang.org/go/cue/errors" + "github.com/pkg/errors" + "github.com/vito/progrock" + "golang.org/x/net/context" +) + +type Pipeline struct { + Action []string `arg:""` + // plan root file + // and the dir of the root file will be the cwd for all cue files + Project string `flag:",omitempty" alias:"p"` +} + +func (pipeline *Pipeline) SetDefaults() { + if pipeline.Project == "" { + pipeline.Project = "./piper.cue" + } +} + +func (pipeline *Pipeline) Run(ctx context.Context) error { + p, err := New(ctx, WithProject(pipeline.Project)) + if err != nil { + return err + } + + return runWith(ctx, func(ctx context.Context) error { + if err := p.Run(ctx, pipeline.Action...); err != nil { + if errList := cueerrors.Errors(err); len(errList) > 0 { + buf := bytes.NewBuffer(nil) + for i := range errList { + cueerrors.Print(buf, errList[i], nil) + } + return errors.New(buf.String()) + } + return err + } + return nil + }) +} + +var isTTY = sync.OnceValue(func() bool { + // ugly to make as non-tty for Run of intellij + if os.Getenv("_INTELLIJ_FORCE_PREPEND_PATH") != "" { + return false + } + + for _, f := range []*os.File{os.Stdin, os.Stdout, os.Stderr} { + if isatty.IsTerminal(f.Fd()) { + return true + } + } + return false +}) + +func runWith(ctx context.Context, fn func(ctx context.Context) error) error { + if isTTY() { + tape := progrock.NewTape() + tape.ShowInternal(true) + tape.ShowAllOutput(true) + + ctx = progrock.ToContext(ctx, progrock.NewRecorder(tape)) + + return progrock.DefaultUI().Run(ctx, tape, func(ctx context.Context, client progrock.UIClient) error { + return fn(ctx) + }) + } + + rootRec := progrock.NewRecorder( + console.NewWriter(os.Stdout, + console.ShowInternal(true), + ), + ) + + return fn(progrock.ToContext(ctx, rootRec)) +} diff --git a/pkg/engine/project.go b/pkg/engine/project.go new file mode 100644 index 0000000..0a3e568 --- /dev/null +++ b/pkg/engine/project.go @@ -0,0 +1,101 @@ +package engine + +import ( + "os" + "path" + "strings" + + "cuelang.org/go/cue/build" + "cuelang.org/go/cue/cuecontext" + cueload "cuelang.org/go/cue/load" + "github.com/octohelm/cuemod/pkg/cuemod" + "github.com/pkg/errors" + "golang.org/x/net/context" + + "github.com/octohelm/piper/cuepkg" + "github.com/octohelm/piper/pkg/cueflow" + "github.com/octohelm/piper/pkg/engine/task" + + _ "github.com/octohelm/piper/pkg/engine/task/archive" + _ "github.com/octohelm/piper/pkg/engine/task/client" + _ "github.com/octohelm/piper/pkg/engine/task/exec" + _ "github.com/octohelm/piper/pkg/engine/task/file" + _ "github.com/octohelm/piper/pkg/engine/task/http" + _ "github.com/octohelm/piper/pkg/engine/task/wd" +) + +func init() { + if err := cuepkg.RegistryCueStdlibs(); err != nil { + panic(err) + } +} + +type Project interface { + Run(ctx context.Context, action ...string) error +} + +type option struct { + entryFile string +} + +type OptFunc = func(o *option) + +func WithProject(root string) OptFunc { + return func(o *option) { + o.entryFile = root + } +} + +func New(ctx context.Context, opts ...OptFunc) (Project, error) { + c := &project{} + for i := range opts { + opts[i](&c.opt) + } + + cwd, _ := os.Getwd() + sourceRoot := path.Join(cwd, c.opt.entryFile) + + if strings.HasSuffix(sourceRoot, ".cue") { + sourceRoot = path.Dir(sourceRoot) + } + + c.sourceRoot = sourceRoot + + buildConfig := cuemod.ContextFor(cwd).BuildConfig(ctx) + + instances := cueload.Instances([]string{c.opt.entryFile}, buildConfig) + if len(instances) != 1 { + return nil, errors.New("only one package is supported at a time") + } + c.instance = instances[0] + + if err := c.instance.Err; err != nil { + return nil, err + } + + return c, nil +} + +type project struct { + opt option + sourceRoot string + instance *build.Instance +} + +func (p *project) Root() string { + return p.sourceRoot +} + +func (p *project) Run(ctx context.Context, action ...string) error { + val := cuecontext.New().BuildInstance(p.instance) + if err := val.Err(); err != nil { + return err + } + + runner := cueflow.NewRunner(cueflow.WrapValue(val)) + + ctx = cueflow.TaskRunnerFactoryContext.Inject(ctx, task.Factory) + ctx = task.ClientContext.Inject(ctx, p) + + return runner.Run(ctx, action) +} diff --git a/pkg/engine/task/archive/tar.go b/pkg/engine/task/archive/tar.go new file mode 100644 index 0000000..c608f6d --- /dev/null +++ b/pkg/engine/task/archive/tar.go @@ -0,0 +1,107 @@ +package archive + +import ( + "archive/tar" + "compress/gzip" + "context" + "fmt" + "github.com/go-courier/logr" + "io" + "os" + "path/filepath" + "strings" + + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/engine/task/file" + taskwd "github.com/octohelm/piper/pkg/engine/task/wd" + "github.com/octohelm/piper/pkg/wd" + "github.com/octohelm/unifs/pkg/filesystem" +) + +func init() { + task.Factory.Register(&Tar{}) +} + +// Tar +// make a tar archive file of specified dir +type Tar struct { + task.Task + + taskwd.CurrentWorkDir + + // final tar filename base on the current work dir + Filename string `json:"filename"` + + // specified dir for tar + Dir taskwd.WorkDir `json:"dir"` + + // output tar file when created + // just group cwd and filename + Output file.File `json:"-" output:"output"` +} + +func (e *Tar) Do(ctx context.Context) error { + return e.Cwd.Do(ctx, func(ctx context.Context, cwd wd.WorkDir) (err error) { + if err := filesystem.MkdirAll(ctx, cwd, filepath.Dir(e.Filename)); err != nil { + return err + } + + tarFile, err := cwd.OpenFile(ctx, e.Filename, os.O_TRUNC|os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + return err + } + defer tarFile.Close() + defer func() { + if err == nil { + e.Output = file.File{ + Cwd: e.Cwd, + Filename: e.Filename, + } + + logr.FromContext(ctx).Info(fmt.Sprintf("%s created.", e.Output.Filename)) + } + }() + + return e.Dir.Do(ctx, func(ctx context.Context, contents wd.WorkDir) error { + var w io.WriteCloser = tarFile + + if strings.HasSuffix(e.Filename, ".gz") { + w = gzip.NewWriter(w) + defer func() { + _ = w.Close() + }() + } + + tw := tar.NewWriter(w) + defer func() { + _ = tw.Close() + }() + + err := wd.ListFile(contents, ".", func(filename string) error { + s, err := contents.Stat(ctx, filename) + if err != nil { + return err + } + if err := tw.WriteHeader(&tar.Header{ + Name: filename, + Size: s.Size(), + Mode: int64(s.Mode()), + ModTime: s.ModTime(), + }); err != nil { + return err + } + + f, err := contents.OpenFile(ctx, filename, os.O_RDONLY, os.ModePerm) + if err != nil { + return err + } + _, err = io.Copy(tw, f) + return err + }) + if err != nil { + return err + } + return nil + }) + }) +} diff --git a/pkg/engine/task/archive/zz_generated.runtimedoc.go b/pkg/engine/task/archive/zz_generated.runtimedoc.go new file mode 100644 index 0000000..a4372c3 --- /dev/null +++ b/pkg/engine/task/archive/zz_generated.runtimedoc.go @@ -0,0 +1,47 @@ +/* +Package archive GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package archive + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Tar) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "CurrentWorkDir": + return []string{}, true + case "Filename": + return []string{ + "final tar filename base on the current work dir", + }, true + case "Dir": + return []string{ + "specified dir for tar", + }, true + case "Output": + return []string{ + "output tar file when created", + "just group cwd and filename", + }, true + + } + if doc, ok := runtimeDoc(v.CurrentWorkDir, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "Tar", + "make a tar archive file of specified dir", + }, true +} diff --git a/pkg/engine/task/client.go b/pkg/engine/task/client.go new file mode 100644 index 0000000..f874be3 --- /dev/null +++ b/pkg/engine/task/client.go @@ -0,0 +1,9 @@ +package task + +import contextx "github.com/octohelm/x/context" + +var ClientContext = contextx.New[Client]() + +type Client interface { + Root() string +} diff --git a/pkg/engine/task/client/env.go b/pkg/engine/task/client/env.go new file mode 100644 index 0000000..73c2265 --- /dev/null +++ b/pkg/engine/task/client/env.go @@ -0,0 +1,99 @@ +package client + +import ( + "context" + "encoding/json" + "os" + "strings" + "sync" + + "github.com/pkg/errors" + + "github.com/octohelm/piper/pkg/cueflow" + "github.com/octohelm/piper/pkg/engine/task" +) + +func init() { + task.Factory.Register(&Env{}) +} + +// Env of client +type Env struct { + cueflow.TaskImpl + + // pick the requested env vars + Env map[string]SecretOrString `json:",inline" output:"env"` +} + +func (ce *Env) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &ce.Env); err != nil { + return err + } + + for k := range ce.Env { + if strings.HasPrefix(k, "$$") { + delete(ce.Env, k) + } + } + + return nil +} + +func (ce *Env) MarshalJSON() ([]byte, error) { + + return json.Marshal(ce.Env) +} + +func (ce *Env) Do(ctx context.Context) error { + secretStore := task.SecretContext.From(ctx) + + clientEnvs := getClientEnvs() + + env := map[string]SecretOrString{} + + for key := range ce.Env { + e := ce.Env[key] + + if envVar, ok := clientEnvs[key]; ok { + if secret := e.Secret; secret != nil { + id := secretStore.Set(task.Secret{ + Key: key, + Value: envVar, + }) + + env[key] = SecretOrString{ + Secret: SecretOfID(id), + } + } else { + env[key] = SecretOrString{ + Value: envVar, + } + } + } else { + if secret := e.Secret; secret != nil { + return errors.Errorf("EnvVar %s is not defined.", key) + } + + env[key] = e + } + } + + ce.Env = env + + return nil +} + +var getClientEnvs = sync.OnceValue(func() map[string]string { + clientEnvs := map[string]string{} + + for _, i := range os.Environ() { + parts := strings.SplitN(i, "=", 2) + if len(parts) == 2 { + clientEnvs[parts[0]] = parts[1] + } else { + clientEnvs[parts[0]] = "" + } + } + + return clientEnvs +}) diff --git a/pkg/engine/task/client/read_secret.go b/pkg/engine/task/client/read_secret.go new file mode 100644 index 0000000..8f1d2ba --- /dev/null +++ b/pkg/engine/task/client/read_secret.go @@ -0,0 +1,34 @@ +package client + +import ( + "context" + + "github.com/octohelm/piper/pkg/cueflow" + "github.com/octohelm/piper/pkg/engine/task" + "github.com/pkg/errors" +) + +func init() { + task.Factory.Register(&ReadSecret{}) +} + +// ReadSecret +// read secret value for the secret +type ReadSecret struct { + cueflow.TaskImpl + + // secret ref + Secret Secret `json:"secret"` + + // secret value + Value string `json:"-" output:"value"` +} + +func (e *ReadSecret) Do(ctx context.Context) error { + s, ok := e.Secret.Value(ctx) + if ok { + e.Value = s.Value + return nil + } + return errors.New("secret not found") +} diff --git a/pkg/engine/task/client/rev_info.go b/pkg/engine/task/client/rev_info.go new file mode 100644 index 0000000..266e7b0 --- /dev/null +++ b/pkg/engine/task/client/rev_info.go @@ -0,0 +1,32 @@ +package client + +import ( + "context" + + "github.com/octohelm/cuemod/pkg/modutil" + "github.com/octohelm/piper/pkg/engine/task" +) + +func init() { + task.Factory.Register(&RevInfo{}) +} + +type RevInfo struct { + task.SetupTask + + // get pseudo version same of go mod + // like + // v0.0.0-20231222030512-c093d5e89975 + // v0.0.0-dirty.0.20231222022414-5f9d1d44dacc + Version string `json:"-" output:"version"` +} + +func (i *RevInfo) Do(ctx context.Context) error { + planRoot := task.ClientContext.From(ctx).Root() + r, err := modutil.RevInfoFromDir(context.Background(), planRoot) + if err != nil { + return err + } + i.Version = r.Version + return nil +} diff --git a/pkg/engine/task/client/secret.go b/pkg/engine/task/client/secret.go new file mode 100644 index 0000000..faf0272 --- /dev/null +++ b/pkg/engine/task/client/secret.go @@ -0,0 +1,26 @@ +package client + +import ( + "context" + "github.com/octohelm/piper/pkg/engine/task" +) + +func init() { + task.Factory.Register(&Secret{}) +} + +func SecretOfID(id string) *Secret { + s := &Secret{} + s.Ref.ID = id + return s +} + +type Secret struct { + Ref struct { + ID string `json:"id,omitempty"` + } `json:"$$secret"` +} + +func (s *Secret) Value(ctx context.Context) (task.Secret, bool) { + return task.SecretContext.From(ctx).Get(s.Ref.ID) +} diff --git a/pkg/engine/task/client/secret_or_string.go b/pkg/engine/task/client/secret_or_string.go new file mode 100644 index 0000000..6824782 --- /dev/null +++ b/pkg/engine/task/client/secret_or_string.go @@ -0,0 +1,34 @@ +package client + +import "encoding/json" + +type SecretOrString struct { + Secret *Secret + Value string +} + +func (SecretOrString) OneOf() []any { + return []any{ + "", + &Secret{}, + } +} + +func (s *SecretOrString) UnmarshalJSON(data []byte) error { + if len(data) > 0 && data[0] == '{' { + se := &Secret{} + if err := json.Unmarshal(data, se); err != nil { + return err + } + s.Secret = se + return nil + } + return json.Unmarshal(data, &s.Value) +} + +func (s SecretOrString) MarshalJSON() ([]byte, error) { + if s.Secret != nil { + return json.Marshal(s.Secret) + } + return json.Marshal(s.Value) +} diff --git a/pkg/engine/task/client/string_or_bytes.go b/pkg/engine/task/client/string_or_bytes.go new file mode 100644 index 0000000..1830c96 --- /dev/null +++ b/pkg/engine/task/client/string_or_bytes.go @@ -0,0 +1,30 @@ +package client + +import "encoding/json" + +type StringOrBytes []byte + +func (StringOrBytes) OneOf() []any { + return []any{ + "", + []byte{}, + } +} + +func (s *StringOrBytes) UnmarshalJSON(data []byte) error { + if len(data) > 0 && data[0] == '"' { + b := "" + if err := json.Unmarshal(data, &b); err != nil { + return err + } + *s = []byte(b) + + return nil + } + *s = data + return nil +} + +func (s StringOrBytes) MarshalJSON() ([]byte, error) { + return s, nil +} diff --git a/pkg/engine/task/client/string_or_slice.go b/pkg/engine/task/client/string_or_slice.go new file mode 100644 index 0000000..3580cc3 --- /dev/null +++ b/pkg/engine/task/client/string_or_slice.go @@ -0,0 +1,37 @@ +package client + +import "encoding/json" + +type StringOrSlice []string + +func (StringOrSlice) OneOf() []any { + return []any{ + "", + []string{}, + } +} + +func (s *StringOrSlice) UnmarshalJSON(data []byte) error { + if len(data) > 0 && data[0] == '"' { + b := "" + if err := json.Unmarshal(data, &b); err != nil { + return err + } + *s = []string{b} + return nil + } + + var list []string + + if err := json.Unmarshal(data, &list); err != nil { + return err + } + + *s = list + + return nil +} + +func (s StringOrSlice) MarshalJSON() ([]byte, error) { + return json.Marshal([]string(s)) +} diff --git a/pkg/engine/task/client/zz_generated.runtimedoc.go b/pkg/engine/task/client/zz_generated.runtimedoc.go new file mode 100644 index 0000000..23e83c8 --- /dev/null +++ b/pkg/engine/task/client/zz_generated.runtimedoc.go @@ -0,0 +1,110 @@ +/* +Package client GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package client + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Env) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Env": + return []string{ + "pick the requested env vars", + }, true + + } + + return nil, false + } + return []string{ + "Env of client", + }, true +} + +func (v ReadSecret) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Secret": + return []string{ + "secret ref", + }, true + case "Value": + return []string{ + "secret value", + }, true + + } + + return nil, false + } + return []string{ + "ReadSecret", + "read secret value for the secret", + }, true +} + +func (v RevInfo) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "SetupTask": + return []string{}, true + case "Version": + return []string{ + "get pseudo version same of go mod", + "like", + "v0.0.0-20231222030512-c093d5e89975", + "v0.0.0-dirty.0.20231222022414-5f9d1d44dacc", + }, true + + } + if doc, ok := runtimeDoc(v.SetupTask, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{}, true +} + +func (v Secret) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + + } + + return nil, false + } + return []string{}, true +} + +func (v SecretOrString) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Secret": + return []string{}, true + case "Value": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} + +func (StringOrBytes) RuntimeDoc(names ...string) ([]string, bool) { + return []string{}, true +} +func (StringOrSlice) RuntimeDoc(names ...string) ([]string, bool) { + return []string{}, true +} diff --git a/pkg/engine/task/exec/run.go b/pkg/engine/task/exec/run.go new file mode 100644 index 0000000..a30cd2d --- /dev/null +++ b/pkg/engine/task/exec/run.go @@ -0,0 +1,173 @@ +package exec + +import ( + "bufio" + "bytes" + "context" + "github.com/go-courier/logr" + "github.com/octohelm/x/ptr" + "github.com/pkg/errors" + "io" + "strings" + + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/engine/task/client" + "github.com/octohelm/piper/pkg/engine/task/flow" + taskwd "github.com/octohelm/piper/pkg/engine/task/wd" + "github.com/octohelm/piper/pkg/wd" +) + +func init() { + task.Factory.Register(&Run{}) +} + +// Run some cmd +type Run struct { + task.Task + + taskwd.CurrentWorkDir + + // cmd for executing + Command client.StringOrSlice `json:"cmd"` + // env vars + Env map[string]client.SecretOrString `json:"env,omitempty"` + // executing user + User string `json:"user,omitempty"` + + // other setting + With With `json:"with,omitempty"` + + // result + Result RunResult `json:"-" output:"result"` +} + +type With struct { + // when enabled + // once executed failed, will break whole pipeline + // otherwise, just set result + Failfast bool `json:"failfast,omitempty" default:"true"` + + // when enabled + // `result.stdout` should be with the string value + // otherwise, just log stdout + Stdout bool `json:"stdout,omitempty" default:"false"` + + // when enabled + // `result.ok` will not set be false if empty stdout + StdoutOmitempty bool `json:"stdoutOmitempty,omitempty" default:"false"` + + // when enabled + // `result.stderr` should be with the string value + // otherwise, just log stderr + Stderr bool `json:"stderr,omitempty" default:"false"` +} + +var _ flow.Result = RunResult{} + +type RunResult struct { + // result of the executing + Ok bool `json:"ok"` + // exists when `with.stdout` enabled + Stdout *string `json:"stdout,omitempty"` + // exists when `with.stdout` enabled + Stderr *string `json:"stderr,omitempty"` +} + +func (r RunResult) Success() bool { + return r.Ok +} + +func (e *Run) Do(ctx context.Context) error { + return e.Cwd.Do(ctx, func(ctx context.Context, cwd wd.WorkDir) error { + cmd := strings.Join(e.Command, " ") + + env := map[string]string{} + + for k, v := range e.Env { + if s := v.Secret; s != nil { + if secret, ok := s.Value(ctx); ok { + env[k] = secret.Value + } else { + return errors.Errorf("not found secret for %s", k) + } + } else { + env[k] = v.Value + } + } + + stdout := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) + + opts := []wd.OptionFunc{ + wd.WithEnv(env), + wd.WithUser(e.User), + } + + if e.With.Stdout { + opts = append(opts, wd.WithStdout(stdout)) + } else { + w := (&forward{l: logr.FromContext(ctx)}).NewWriter() + defer w.Close() + opts = append(opts, wd.WithStdout(w)) + } + + if e.With.Stderr { + opts = append(opts, wd.WithStderr(stderr)) + } else { + w := (&forward{l: logr.FromContext(ctx), stderr: true}).NewWriter() + defer w.Close() + opts = append(opts, wd.WithStderr(w)) + } + + err := cwd.Exec(ctx, cmd, opts...) + + if err != nil { + if e.With.Failfast { + return err + } + } else { + e.Result.Ok = true + } + + if e.With.Stdout { + e.Result.Stdout = ptr.Ptr(stdout.String()) + + if !e.With.StdoutOmitempty { + e.Result.Ok = stdout.Len() > 0 + + if !e.Result.Ok && e.With.Failfast { + return errors.New("empty stdout") + } + } + } + + if e.With.Stdout { + e.Result.Stderr = ptr.Ptr(stderr.String()) + } + + return nil + }) +} + +type forward struct { + l logr.Logger + stderr bool +} + +func (f *forward) NewWriter() io.WriteCloser { + r, w := io.Pipe() + + go func() { + s := bufio.NewScanner(r) + + for s.Scan() { + if f.stderr { + f.l.Warn(errors.New(s.Text())) + } else { + f.l.Info(s.Text()) + } + } + }() + + return w +} diff --git a/pkg/engine/task/exec/zz_generated.runtimedoc.go b/pkg/engine/task/exec/zz_generated.runtimedoc.go new file mode 100644 index 0000000..3ffe21a --- /dev/null +++ b/pkg/engine/task/exec/zz_generated.runtimedoc.go @@ -0,0 +1,102 @@ +/* +Package exec GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package exec + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Run) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "CurrentWorkDir": + return []string{}, true + case "Command": + return []string{ + "cmd for executing", + }, true + case "Env": + return []string{ + "env vars", + }, true + case "User": + return []string{ + "executing user", + }, true + case "With": + return []string{ + "other setting", + }, true + case "Result": + return []string{ + "result", + }, true + + } + if doc, ok := runtimeDoc(v.CurrentWorkDir, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "Run some cmd", + }, true +} + +func (v RunResult) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Ok": + return []string{ + "result of the executing", + }, true + case "Stdout": + return []string{ + "exists when `with.stdout` enabled", + }, true + case "Stderr": + return []string{ + "exists when `with.stdout` enabled", + }, true + + } + + return nil, false + } + return []string{}, true +} + +func (v With) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Failfast": + return []string{ + "enabled for will break when executed failed", + "otherwise, just print", + }, true + case "Stdout": + return []string{ + "enabled for getting stdout as string", + "otherwise, just print", + }, true + case "Stderr": + return []string{ + "enabled for getting stderr as string", + "otherwise, just print", + }, true + + } + + return nil, false + } + return []string{}, true +} diff --git a/pkg/engine/task/factory.go b/pkg/engine/task/factory.go new file mode 100644 index 0000000..7ebf25b --- /dev/null +++ b/pkg/engine/task/factory.go @@ -0,0 +1,5 @@ +package task + +import "github.com/octohelm/piper/pkg/cueflow" + +var Factory = cueflow.NewTaskFactory("piper.octohelm.tech") diff --git a/pkg/engine/task/file/file.go b/pkg/engine/task/file/file.go new file mode 100644 index 0000000..8d4ae31 --- /dev/null +++ b/pkg/engine/task/file/file.go @@ -0,0 +1,85 @@ +package file + +import ( + "bytes" + "context" + "encoding/json" + "github.com/octohelm/piper/pkg/engine/task/wd" + "io" + "os" +) + +type File struct { + // current work dir + Cwd wd.WorkDir `json:"cwd"` + // filename related from current work dir + Filename string `json:"filename"` +} + +type StringOrFile struct { + File *File + String string +} + +func (StringOrFile) OneOf() []any { + return []any{ + "", + &File{}, + } +} + +func (s *StringOrFile) UnmarshalJSON(data []byte) error { + if len(data) > 0 && data[0] == '"' { + b := "" + if err := json.Unmarshal(data, &b); err != nil { + return err + } + *s = StringOrFile{ + String: b, + } + return nil + } + + *s = StringOrFile{ + File: &File{}, + } + return json.Unmarshal(data, s.File) +} + +func (s StringOrFile) MarshalJSON() ([]byte, error) { + if s.File != nil { + return json.Marshal(s.File) + } + return json.Marshal(s.String) +} + +func (s *StringOrFile) Size(ctx context.Context) (int64, error) { + if s.File != nil { + cwd, err := s.File.Cwd.Get(ctx) + if err != nil { + return -1, err + } + info, err := cwd.Stat(ctx, s.File.Filename) + if err != nil { + return -1, err + } + return info.Size(), nil + } + return int64(len(s.String)), nil +} + +func (s *StringOrFile) Open(ctx context.Context) (io.ReadCloser, error) { + if s.File != nil { + cwd, err := s.File.Cwd.Get(ctx) + if err != nil { + return nil, err + } + return cwd.OpenFile(ctx, s.File.Filename, os.O_RDONLY, os.ModePerm) + } + + if len(s.String) == 0 { + return nil, nil + } + + return io.NopCloser(bytes.NewBufferString(s.String)), nil +} diff --git a/pkg/engine/task/file/write.go b/pkg/engine/task/file/write.go new file mode 100644 index 0000000..359298c --- /dev/null +++ b/pkg/engine/task/file/write.go @@ -0,0 +1,54 @@ +package file + +import ( + "context" + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/engine/task/client" + taskwd "github.com/octohelm/piper/pkg/engine/task/wd" + "github.com/octohelm/piper/pkg/wd" + "os" + "path/filepath" +) + +func init() { + task.Factory.Register(&Write{}) +} + +// Write file with contents +type Write struct { + task.Task + + taskwd.CurrentWorkDir + + // filename + Filename string `json:"filename"` + // file contents + Contents client.StringOrBytes `json:"contents"` + + // the written file + // just group cwd and filename + Output File `json:"-" output:"output"` +} + +func (t *Write) Do(ctx context.Context) error { + return t.Cwd.Do(ctx, func(ctx context.Context, cwd wd.WorkDir) error { + if err := cwd.Mkdir(ctx, filepath.Dir(t.Filename), os.ModeDir); err != nil { + return err + } + + f, err := cwd.OpenFile(ctx, t.Filename, os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + return err + } + defer f.Close() + + if _, err = f.Write(t.Contents); err != nil { + return err + } + + t.Output.Cwd = t.Cwd + t.Output.Filename = t.Filename + + return nil + }) +} diff --git a/pkg/engine/task/file/zz_generated.runtimedoc.go b/pkg/engine/task/file/zz_generated.runtimedoc.go new file mode 100644 index 0000000..04e3b14 --- /dev/null +++ b/pkg/engine/task/file/zz_generated.runtimedoc.go @@ -0,0 +1,80 @@ +/* +Package file GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package file + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v File) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Cwd": + return []string{ + "current work dir", + }, true + case "Filename": + return []string{ + "filename related from current work dir", + }, true + + } + + return nil, false + } + return []string{}, true +} + +func (v StringOrFile) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "File": + return []string{}, true + case "String": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} + +func (v Write) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "CurrentWorkDir": + return []string{}, true + case "Filename": + return []string{ + "filename", + }, true + case "Contents": + return []string{ + "file contents", + }, true + case "Output": + return []string{ + "the written file", + "just group cwd and filename", + }, true + + } + if doc, ok := runtimeDoc(v.CurrentWorkDir, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "Write file with contents", + }, true +} diff --git a/pkg/engine/task/flow/every.go b/pkg/engine/task/flow/every.go new file mode 100644 index 0000000..0a13ab2 --- /dev/null +++ b/pkg/engine/task/flow/every.go @@ -0,0 +1,69 @@ +package flow + +import ( + "context" + "strings" + + "cuelang.org/go/cue" + "github.com/pkg/errors" + + "github.com/octohelm/piper/pkg/cueflow" + "github.com/octohelm/piper/pkg/engine/task" +) + +func init() { + task.Factory.Register(&Every{}) +} + +// Every task group +type Every struct { + task.Group + + // do the step one by one + Steps []StepInterface `json:"steps"` + + // util every step ok + Result ResultInterface `json:"-" output:"result"` +} + +func (e *Every) Do(ctx context.Context) error { + p := e.Parent() + + scope := cueflow.CueValue(p.Value().LookupPath(cue.ParsePath("steps"))) + + list, err := scope.List() + if err != nil { + return err + } + + for idx := 0; list.Next(); idx++ { + itemValue := list.Value() + + if err := cueflow.RunTasks(ctx, p.Scope(), + cueflow.WithShouldRunFunc(func(value cue.Value) bool { + return value.LookupPath(cueflow.TaskPath).Exists() && strings.HasPrefix(value.Path().String(), itemValue.Path().String()) + }), + cueflow.WithPrefix(e.Parent().Path()), + ); err != nil { + return errors.Wrapf(err, "steps[%d]", idx) + } + + resultValue := p.Scope().Value().LookupPath(itemValue.Path()) + + ti := &StepInterface{} + + if err := resultValue.Decode(ti); err != nil { + + return err + } + + if !ti.Result.Success() { + e.Result = ti.Result + return nil + } + } + + e.Result.SetSuccess(true) + + return nil +} diff --git a/pkg/engine/task/flow/some.go b/pkg/engine/task/flow/some.go new file mode 100644 index 0000000..5af8548 --- /dev/null +++ b/pkg/engine/task/flow/some.go @@ -0,0 +1,65 @@ +package flow + +import ( + "context" + "strings" + + "cuelang.org/go/cue" + "github.com/pkg/errors" + + "github.com/octohelm/piper/pkg/cueflow" + "github.com/octohelm/piper/pkg/engine/task" +) + +func init() { + task.Factory.Register(&Some{}) +} + +// Some task group +type Some struct { + task.Group + + // do the step one by one + Steps []StepInterface `json:"steps"` + + // util some step ok, and remain steps will not execute. + Result ResultInterface `json:"-" output:"result"` +} + +func (e *Some) Do(ctx context.Context) error { + p := e.Parent() + + scope := cueflow.CueValue(p.Value().LookupPath(cue.ParsePath("steps"))) + + list, err := scope.List() + if err != nil { + return err + } + + for idx := 0; list.Next(); idx++ { + valuePath := list.Value().Path() + + if err := cueflow.RunTasks(ctx, p.Scope(), + cueflow.WithShouldRunFunc(func(value cue.Value) bool { + return value.LookupPath(cueflow.TaskPath).Exists() && strings.HasPrefix(value.Path().String(), valuePath.String()) + }), + cueflow.WithPrefix(e.Parent().Path()), + ); err != nil { + return errors.Wrapf(err, "steps[%d]", idx) + } + + resultValue := p.Scope().Value().LookupPath(valuePath) + + ti := &StepInterface{} + if err := resultValue.Decode(ti); err != nil { + return err + } + + if ti.Result.Success() { + e.Result = ti.Result + return nil + } + } + + return nil +} diff --git a/pkg/engine/task/flow/step.go b/pkg/engine/task/flow/step.go new file mode 100644 index 0000000..39b2f2e --- /dev/null +++ b/pkg/engine/task/flow/step.go @@ -0,0 +1,58 @@ +package flow + +import ( + "encoding/json" + "github.com/octohelm/piper/pkg/cueflow" +) + +// StepInterface +// support additional fields but must the `result: ResultInterface` as result checking +type StepInterface struct { + Result ResultInterface `json:"result"` +} + +type Result = cueflow.Result + +var _ Result = ResultInterface{} + +// ResultInterface +// support additional fields but the `ok: bool` +type ResultInterface struct { + Ok bool `json:"ok"` + + values map[string]any +} + +func (r *ResultInterface) SetSuccess(ok bool) { + r.Ok = ok +} + +func (r ResultInterface) Success() bool { + return r.Ok +} + +func (r ResultInterface) MarshalJSON() ([]byte, error) { + values := map[string]any{} + + for k, v := range r.values { + values[k] = v + } + + values["ok"] = r.Ok + + return json.Marshal(values) +} + +func (r *ResultInterface) UnmarshalJSON(bytes []byte) error { + rr := &ResultInterface{ + values: map[string]any{}, + } + + if err := json.Unmarshal(bytes, &rr.values); err != nil { + return err + } + + rr.Ok = rr.values["ok"].(bool) + *r = *rr + return nil +} diff --git a/pkg/engine/task/flow/zz_generated.runtimedoc.go b/pkg/engine/task/flow/zz_generated.runtimedoc.go new file mode 100644 index 0000000..b9fda4f --- /dev/null +++ b/pkg/engine/task/flow/zz_generated.runtimedoc.go @@ -0,0 +1,99 @@ +/* +Package flow GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package flow + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Every) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Group": + return []string{}, true + case "Steps": + return []string{ + "do the step one by one", + }, true + case "Result": + return []string{ + "util every step ok", + }, true + + } + if doc, ok := runtimeDoc(v.Group, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "Every task group", + }, true +} + +func (v ResultInterface) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Ok": + return []string{}, true + + } + + return nil, false + } + return []string{ + "ResultInterface", + "support additional fields but the `ok: bool`", + }, true +} + +func (v Some) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Group": + return []string{}, true + case "Steps": + return []string{ + "do the step one by one", + }, true + case "Result": + return []string{ + "util some step ok, and remain steps will not execute.", + }, true + + } + if doc, ok := runtimeDoc(v.Group, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "Some task group", + }, true +} + +func (v StepInterface) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Result": + return []string{}, true + + } + + return nil, false + } + return []string{ + "StepInterface", + "support additional fields but must the `result: ResultInterface` as result checking", + }, true +} diff --git a/pkg/engine/task/http/do.go b/pkg/engine/task/http/do.go new file mode 100644 index 0000000..a956840 --- /dev/null +++ b/pkg/engine/task/http/do.go @@ -0,0 +1,196 @@ +package http + +import ( + "context" + "encoding/json" + "fmt" + "github.com/octohelm/piper/pkg/engine/task/client" + "io" + "net/http" + "strings" + "time" + + "github.com/go-courier/logr" + + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/engine/task/file" + "github.com/octohelm/piper/pkg/engine/task/flow" +) + +func init() { + task.Factory.Register(&Do{}) +} + +// Do http request +type Do struct { + task.Task + + // http method + Method string `json:"method"` + // http request url + Url string `json:"url"` + // http headers + Header map[string]client.StringOrSlice `json:"header,omitempty"` + // http query + Query map[string]client.StringOrSlice `json:"query,omitempty"` + // http request body + RequestBody file.StringOrFile `json:"body,omitempty"` + + With With `json:"with,omitempty"` + Result ResponseResult `json:"-" output:"result"` +} + +var _ flow.Result = ResponseResult{} + +type With struct { + // header keys for result + Header []string `json:"header"` +} + +type ResponseResult struct { + // ok when status code >= 200 < 300 + Ok bool `json:"ok"` + // status code + Status int `json:"status,omitempty"` + // response header, only pick headers requests by `with.header` + Header map[string]client.StringOrSlice `json:"header,omitempty"` + // auto unmarshal based on content-type + Data any `json:"data,omitempty"` +} + +func (r ResponseResult) Success() bool { + return r.Ok +} + +type processingRecoder struct { + written int64 + size int64 +} + +func (r *processingRecoder) Write(p []byte) (n int, err error) { + n = len(p) + r.written += int64(n) + return +} + +func (r *processingRecoder) percent() float64 { + return float64(r.written) / float64(r.size) * float64(100) +} + +func (r *processingRecoder) Processing(ctx context.Context) <-chan float64 { + percentCh := make(chan float64) + t := time.NewTicker(1 * time.Second) + + go func() { + defer func() { + t.Stop() + close(percentCh) + }() + + if r.size == 0 { + return + } + + for range t.C { + select { + case <-ctx.Done(): + return + default: + percent := r.percent() + percentCh <- percent + if percent >= 100 { + return + } + } + } + }() + + return percentCh +} + +func (r *Do) Do(ctx context.Context) error { + size, err := r.RequestBody.Size(ctx) + if err != nil { + return err + } + + var reader io.Reader + + if size > 0 { + pr := &processingRecoder{ + size: size, + } + + f, err := r.RequestBody.Open(ctx) + if err != nil { + return err + } + defer f.Close() + + go func() { + for p := range pr.Processing(ctx) { + logr.FromContext(ctx).Info(fmt.Sprintf("uploading %00.1f", p) + "%") + } + }() + + reader = io.TeeReader(f, pr) + } + + req, err := http.NewRequestWithContext(ctx, r.Method, r.Url, reader) + if err != nil { + return err + } + + if len(r.Query) > 0 { + q := req.URL.Query() + for k, vv := range r.Query { + q[k] = vv + } + req.URL.RawQuery = q.Encode() + } + + for k, vv := range r.Header { + req.Header[k] = vv + } + + if size > 0 { + req.ContentLength = size + } + + logr.FromContext(ctx).Info(fmt.Sprintf("%s %s", req.Method, req.URL.String())) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + r.Result.Ok = resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices + r.Result.Status = resp.StatusCode + + if contentType := resp.Header.Get("Content-Type"); strings.Contains(contentType, "json") { + if err := json.NewDecoder(resp.Body).Decode(&r.Result.Data); err != nil { + return err + } + } else if strings.HasPrefix(contentType, "text/") { + data, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + r.Result.Data = string(data) + } else { + data, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + r.Result.Data = data + } + + r.Result.Header = map[string]client.StringOrSlice{} + + for _, k := range r.With.Header { + r.Result.Header[k] = resp.Header.Values(k) + } + + return nil +} diff --git a/pkg/engine/task/http/zz_generated.runtimedoc.go b/pkg/engine/task/http/zz_generated.runtimedoc.go new file mode 100644 index 0000000..e7dff7e --- /dev/null +++ b/pkg/engine/task/http/zz_generated.runtimedoc.go @@ -0,0 +1,94 @@ +/* +Package http GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package http + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Do) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Method": + return []string{ + "http method", + }, true + case "Url": + return []string{ + "http request url", + }, true + case "Header": + return []string{ + "http headers", + }, true + case "Query": + return []string{ + "http query", + }, true + case "RequestBody": + return []string{ + "http request body", + }, true + case "With": + return []string{}, true + case "Result": + return []string{}, true + + } + + return nil, false + } + return []string{ + "Do http request", + }, true +} + +func (v ResponseResult) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Ok": + return []string{ + "ok when status code >= 200 < 300", + }, true + case "Status": + return []string{ + "status code", + }, true + case "Header": + return []string{ + "response header, only pick headers requests by `with.header`", + }, true + case "Data": + return []string{ + "auto unmarshal based on content-type", + }, true + + } + + return nil, false + } + return []string{}, true +} + +func (v With) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Header": + return []string{ + "header keys for result", + }, true + + } + + return nil, false + } + return []string{}, true +} diff --git a/pkg/engine/task/log.go b/pkg/engine/task/log.go new file mode 100644 index 0000000..a4c861f --- /dev/null +++ b/pkg/engine/task/log.go @@ -0,0 +1,30 @@ +package task + +import ( + "github.com/k0sproject/rig" + "os" +) + +func init() { + if os.Getenv("ENABLE_RIG_LOG") != "1" { + rig.SetLogger(&discord{}) + } +} + +type discord struct { +} + +func (discord) Tracef(s string, i ...interface{}) { +} + +func (discord) Debugf(s string, i ...interface{}) { +} + +func (discord) Infof(s string, i ...interface{}) { +} + +func (discord) Warnf(s string, i ...interface{}) { +} + +func (discord) Errorf(s string, i ...interface{}) { +} diff --git a/pkg/engine/task/secret.go b/pkg/engine/task/secret.go new file mode 100644 index 0000000..1dad566 --- /dev/null +++ b/pkg/engine/task/secret.go @@ -0,0 +1,22 @@ +package task + +import ( + contextx "github.com/octohelm/x/context" +) + +var SecretStore = &Store[string, Secret]{} + +var SecretContext = contextx.New[*Store[string, Secret]]( + contextx.WithDefaultsFunc[*Store[string, Secret]](func() *Store[string, Secret] { + return SecretStore + }), +) + +type Secret struct { + Key string + Value string +} + +func (s Secret) String() string { + return s.Key +} diff --git a/pkg/engine/task/store.go b/pkg/engine/task/store.go new file mode 100644 index 0000000..4480e74 --- /dev/null +++ b/pkg/engine/task/store.go @@ -0,0 +1,24 @@ +package task + +import ( + "fmt" + "github.com/opencontainers/go-digest" + "sync" +) + +type Store[K comparable, V fmt.Stringer] struct { + m sync.Map +} + +func (s *Store[K, V]) Get(k K) (V, bool) { + if v, ok := s.m.Load(k); ok { + return v.(V), true + } + return *(new(V)), false +} + +func (s *Store[K, V]) Set(v V) string { + dget := digest.FromString(v.String()).String() + s.m.Store(dget, v) + return dget +} diff --git a/pkg/engine/task/task.go b/pkg/engine/task/task.go new file mode 100644 index 0000000..2632d4f --- /dev/null +++ b/pkg/engine/task/task.go @@ -0,0 +1,29 @@ +package task + +import "github.com/octohelm/piper/pkg/cueflow" + +type Task = cueflow.TaskImpl + +type SetupTask struct { + Task +} + +func (v *SetupTask) Setup() bool { + return true +} + +var _ cueflow.Group = &Group{} + +type Group struct { + Task + + parent cueflow.Task +} + +func (v *Group) SetParent(t cueflow.Task) { + v.parent = t +} + +func (v *Group) Parent() cueflow.Task { + return v.parent +} diff --git a/pkg/engine/task/wd.go b/pkg/engine/task/wd.go new file mode 100644 index 0000000..ce2fa46 --- /dev/null +++ b/pkg/engine/task/wd.go @@ -0,0 +1,15 @@ +package task + +import ( + contextx "github.com/octohelm/x/context" + + "github.com/octohelm/piper/pkg/wd" +) + +var WorkDirStore = &Store[string, wd.WorkDir]{} + +var WorkDirContext = contextx.New[*Store[string, wd.WorkDir]]( + contextx.WithDefaultsFunc[*Store[string, wd.WorkDir]](func() *Store[string, wd.WorkDir] { + return WorkDirStore + }), +) diff --git a/pkg/engine/task/wd/local.go b/pkg/engine/task/wd/local.go new file mode 100644 index 0000000..873e30f --- /dev/null +++ b/pkg/engine/task/wd/local.go @@ -0,0 +1,44 @@ +package wd + +import ( + "context" + "github.com/k0sproject/rig" + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/wd" +) + +func init() { + task.Factory.Register(&Local{}) +} + +// Local +// create a local work dir +type Local struct { + task.SetupTask + + // related dir on the root of project + Dir string `json:"dir" default:"."` + + WorkDir WorkDir `json:"-" output:"wd"` +} + +func (c *Local) Do(ctx context.Context) error { + planRoot := wd.Dir(task.ClientContext.From(ctx).Root()) + + cwd, err := wd.Wrap( + &rig.Connection{ + Localhost: &rig.Localhost{ + Enabled: true, + }, + }, + wd.WithDir(planRoot.String()), + ) + if err != nil { + return err + } + + id := task.WorkDirContext.From(ctx).Set(cwd) + c.WorkDir.Ref.ID = id + + return nil +} diff --git a/pkg/engine/task/wd/ssh.go b/pkg/engine/task/wd/ssh.go new file mode 100644 index 0000000..547d0e7 --- /dev/null +++ b/pkg/engine/task/wd/ssh.go @@ -0,0 +1,68 @@ +package wd + +import ( + "context" + "github.com/k0sproject/rig" + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/sshutil" + "github.com/octohelm/piper/pkg/wd" +) + +func init() { + task.Factory.Register(&SSH{}) +} + +// SSH +// create ssh work dir for remote executing +type SSH struct { + task.SetupTask + + // path to ssh config + Config string `json:"config,omitempty"` + // host key of ssh config + HostKey string `json:"hostKey,omitempty"` + + // custom setting + // ssh address + Address string `json:"address,omitempty"` + // ssh port + Port int `json:"port,omitempty" default:"22"` + // ssh user + User string `json:"user,omitempty" default:"root"` + // ssh identity file + IdentityFile string `json:"identityFile,omitempty"` + + WorkDir WorkDir `json:"-" output:"wd"` +} + +func (c *SSH) Do(ctx context.Context) error { + conn := &rig.Connection{} + + if c.Config != "" { + ssh, err := sshutil.Load(c.Config, c.HostKey) + if err != nil { + return err + } + conn.SSH = ssh + } else { + conn.SSH = &rig.SSH{ + Address: c.Address, + Port: c.Port, + User: c.User, + KeyPath: &c.IdentityFile, + } + } + + cwd, err := wd.Wrap( + conn, + wd.WithUser(conn.SSH.User), + ) + + if err != nil { + return err + } + + c.WorkDir.Ref.ID = task.WorkDirContext.From(ctx).Set(cwd) + + return nil +} diff --git a/pkg/engine/task/wd/su.go b/pkg/engine/task/wd/su.go new file mode 100644 index 0000000..55efb3b --- /dev/null +++ b/pkg/engine/task/wd/su.go @@ -0,0 +1,33 @@ +package wd + +import ( + "context" + + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/wd" +) + +func init() { + task.Factory.Register(&Su{}) +} + +// Su +// switch user +type Su struct { + task.Task + + CurrentWorkDir + + // new user + User string `json:"user"` + + // new work dir + WorkDir WorkDir `json:"-" output:"wd"` +} + +func (e *Su) Do(ctx context.Context) error { + return e.Cwd.Do(ctx, func(ctx context.Context, cwd wd.WorkDir) error { + e.WorkDir.SetBy(ctx, cwd) + return nil + }, wd.WithUser(e.User)) +} diff --git a/pkg/engine/task/wd/sub.go b/pkg/engine/task/wd/sub.go new file mode 100644 index 0000000..912c517 --- /dev/null +++ b/pkg/engine/task/wd/sub.go @@ -0,0 +1,34 @@ +package wd + +import ( + "context" + + "github.com/octohelm/piper/pkg/engine/task" + + "github.com/octohelm/piper/pkg/wd" +) + +func init() { + task.Factory.Register(&Sub{}) +} + +// Sub +// create new work dir base on current work dir +type Sub struct { + task.Task + + CurrentWorkDir + + // related path from current work dir + Dir string `json:"dir"` + + // new work dir + WorkDir WorkDir `json:"-" output:"wd"` +} + +func (e *Sub) Do(ctx context.Context) error { + return e.Cwd.Do(ctx, func(ctx context.Context, cwd wd.WorkDir) error { + e.WorkDir.SetBy(ctx, cwd) + return nil + }, wd.WithDir(e.Dir)) +} diff --git a/pkg/engine/task/wd/sys_info.go b/pkg/engine/task/wd/sys_info.go new file mode 100644 index 0000000..8546b79 --- /dev/null +++ b/pkg/engine/task/wd/sys_info.go @@ -0,0 +1,61 @@ +package wd + +import ( + "context" + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/wd" +) + +func init() { + task.Factory.Register(&SysInfo{}) +} + +// SysInfo +// get sys info of current work dir +type SysInfo struct { + task.Task + + CurrentWorkDir + + // os release info + Release Release `json:"-" output:"release"` + // os platform + Platform Platform `json:"-" output:"platform"` +} + +type Platform struct { + OS string `json:"os"` + Architecture string `json:"architecture"` +} + +type Release struct { + // OS name + Name string `json:"name"` + // OS Version + Version string `json:"version,omitempty"` + // OS id, like `ubuntu` `windows` + ID string `json:"id"` + // if os is based on some upstream + // like debian when id is `ubuntu` + IDLike string `json:"id_like,omitempty"` +} + +func (e *SysInfo) Do(ctx context.Context) error { + return e.Cwd.Do(ctx, func(ctx context.Context, cwd wd.WorkDir) error { + if can, ok := cwd.(wd.CanOSInfo); ok { + info, err := can.OSInfo(ctx) + if err != nil { + return err + } + + e.Release.Name = info.Name + e.Release.ID = info.ID + e.Release.IDLike = info.IDLike + e.Release.Version = info.Version + + e.Platform.OS = info.Platform.OS + e.Platform.Architecture = info.Platform.Architecture + } + return nil + }) +} diff --git a/pkg/engine/task/wd/work_dir.go b/pkg/engine/task/wd/work_dir.go new file mode 100644 index 0000000..25f498e --- /dev/null +++ b/pkg/engine/task/wd/work_dir.go @@ -0,0 +1,53 @@ +package wd + +import ( + "context" + "github.com/octohelm/piper/pkg/cueflow" + "github.com/octohelm/piper/pkg/engine/task" + "github.com/octohelm/piper/pkg/wd" + "github.com/pkg/errors" +) + +func init() { + task.Factory.Register(&WorkDir{}) +} + +type WorkDir struct { + Ref struct { + ID string `json:"id"` + } `json:"$$wd"` +} + +func (w *WorkDir) Get(ctx context.Context, optFns ...wd.OptionFunc) (wd.WorkDir, error) { + if found, ok := task.WorkDirContext.From(ctx).Get(w.Ref.ID); ok { + return wd.With(found, optFns...) + } + return nil, errors.Errorf("workdir %s is not found", w.Ref.ID) +} + +func (w *WorkDir) Do(ctx context.Context, action func(ctx context.Context, wd wd.WorkDir) error, optFns ...wd.OptionFunc) error { + cwd, err := w.Get(ctx, optFns...) + if err != nil { + return err + } + return action(ctx, cwd) +} + +func (w *WorkDir) SetBy(ctx context.Context, workdir wd.WorkDir) { + w.Ref.ID = task.WorkDirContext.From(ctx).Set(workdir) +} + +type CurrentWorkDir struct { + // current word dir + Cwd WorkDir `json:"cwd"` +} + +var _ cueflow.WithScopeName = CurrentWorkDir{} + +func (w CurrentWorkDir) ScopeName(ctx context.Context) string { + cwd, err := w.Cwd.Get(ctx) + if err != nil { + panic(err) + } + return cwd.String() +} diff --git a/pkg/engine/task/wd/zz_generated.runtimedoc.go b/pkg/engine/task/wd/zz_generated.runtimedoc.go new file mode 100644 index 0000000..d0230f0 --- /dev/null +++ b/pkg/engine/task/wd/zz_generated.runtimedoc.go @@ -0,0 +1,236 @@ +/* +Package wd GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package wd + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (w CurrentWorkDir) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Cwd": + return []string{ + "current word dir", + }, true + + } + + return nil, false + } + return []string{}, true +} + +func (v Local) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "SetupTask": + return []string{}, true + case "Dir": + return []string{ + "related dir on the root of project", + }, true + case "WorkDir": + return []string{}, true + + } + if doc, ok := runtimeDoc(v.SetupTask, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "Local", + "create a local work dir", + }, true +} + +func (v Platform) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "OS": + return []string{}, true + case "Architecture": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} + +func (v Release) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Name": + return []string{ + "OS name", + }, true + case "Version": + return []string{ + "OS Version", + }, true + case "ID": + return []string{ + "OS id, like `ubuntu` `windows`", + }, true + case "IDLike": + return []string{ + "if os is based on some upstream", + "like debian when id is `ubuntu`", + }, true + + } + + return nil, false + } + return []string{}, true +} + +func (v SSH) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "SetupTask": + return []string{}, true + case "Config": + return []string{ + "path to ssh config", + }, true + case "HostKey": + return []string{ + "host key of ssh config", + }, true + case "Address": + return []string{ + "custom setting", + "ssh address", + }, true + case "Port": + return []string{ + "ssh port", + }, true + case "User": + return []string{ + "ssh user", + }, true + case "IdentityFile": + return []string{ + "ssh identity file", + }, true + case "WorkDir": + return []string{}, true + + } + if doc, ok := runtimeDoc(v.SetupTask, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "SSH", + "create ssh work dir for remote executing", + }, true +} + +func (v Su) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "CurrentWorkDir": + return []string{}, true + case "User": + return []string{ + "new user", + }, true + case "WorkDir": + return []string{ + "new work dir", + }, true + + } + if doc, ok := runtimeDoc(v.CurrentWorkDir, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "Su", + "switch user", + }, true +} + +func (v Sub) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "CurrentWorkDir": + return []string{}, true + case "Dir": + return []string{ + "related path from current work dir", + }, true + case "WorkDir": + return []string{ + "new work dir", + }, true + + } + if doc, ok := runtimeDoc(v.CurrentWorkDir, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "Sub", + "create new work dir base on current work dir", + }, true +} + +func (v SysInfo) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "CurrentWorkDir": + return []string{}, true + case "Release": + return []string{ + "os release info", + }, true + case "Platform": + return []string{ + "os platform", + }, true + + } + if doc, ok := runtimeDoc(v.CurrentWorkDir, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{ + "SysInfo", + "get sys info of current work dir", + }, true +} + +func (v WorkDir) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + + } + + return nil, false + } + return []string{}, true +} diff --git a/pkg/engine/task/zz_generated.runtimedoc.go b/pkg/engine/task/zz_generated.runtimedoc.go new file mode 100644 index 0000000..5a8851e --- /dev/null +++ b/pkg/engine/task/zz_generated.runtimedoc.go @@ -0,0 +1,52 @@ +/* +Package task GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package task + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (v Group) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + + } + + return nil, false + } + return []string{}, true +} + +func (v Secret) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Key": + return []string{}, true + case "Value": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} + +func (v SetupTask) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + + } + + return nil, false + } + return []string{}, true +} diff --git a/pkg/engine/zz_generated.runtimedoc.go b/pkg/engine/zz_generated.runtimedoc.go new file mode 100644 index 0000000..84bb845 --- /dev/null +++ b/pkg/engine/zz_generated.runtimedoc.go @@ -0,0 +1,33 @@ +/* +Package engine GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package engine + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (pipeline Pipeline) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "Action": + return []string{}, true + case "Project": + return []string{ + "plan root file", + "and the dir of the root file will be the cwd for all cue files", + }, true + + } + + return nil, false + } + return []string{}, true +} diff --git a/pkg/sshutil/config.go b/pkg/sshutil/config.go new file mode 100644 index 0000000..ebf0177 --- /dev/null +++ b/pkg/sshutil/config.go @@ -0,0 +1,79 @@ +package sshutil + +import ( + "github.com/octohelm/x/ptr" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/k0sproject/rig" + "github.com/kevinburke/ssh_config" + "github.com/pkg/errors" +) + +func Load(configPath string, hostKey string) (*rig.SSH, error) { + if configPath == "" { + configPath = filepath.Join(os.Getenv("HOME"), ".ssh", "config") + } + + if strings.HasPrefix(configPath, "~/") { + configPath = filepath.Join(os.Getenv("HOME"), configPath[2:]) + } + + f, err := os.Open(configPath) + if err != nil { + return nil, err + } + + cfg, err := ssh_config.Decode(f) + if err != nil { + return nil, err + } + + for _, host := range cfg.Hosts { + if len(host.Patterns) > 0 && host.Patterns[0].String() != "*" { + if host.Matches(hostKey) { + ssh := &rig.SSH{} + + for _, node := range host.Nodes { + switch x := node.(type) { + case *ssh_config.KV: + switch strings.ToLower(x.Key) { + case "hostname": + ssh.Address = x.Value + case "port": + ssh.Port, _ = strconv.Atoi(x.Value) + case "user": + ssh.User = x.Value + case "identityfile": + ssh.KeyPath = ptr.Ptr(mayUnquote(x.Value)) + } + } + } + return ssh, nil + } + } + } + + for _, host := range cfg.Hosts { + if host.Matches("*") { + for _, node := range host.Nodes { + switch x := node.(type) { + case *ssh_config.Include: + return Load(x.String()[len("Include "):], hostKey) + } + } + } + } + + return nil, errors.Errorf("not found %s", hostKey) +} + +func mayUnquote(s string) string { + if s != "" && s[0] == '"' { + v, _ := strconv.Unquote(s) + return v + } + return s +} diff --git a/pkg/wd/fs.go b/pkg/wd/fs.go new file mode 100644 index 0000000..79ad7d9 --- /dev/null +++ b/pkg/wd/fs.go @@ -0,0 +1,171 @@ +package wd + +import ( + "context" + "io" + "io/fs" + "os" + "path/filepath" + + "github.com/k0sproject/rig/pkg/rigfs" + "github.com/octohelm/unifs/pkg/filesystem" + "golang.org/x/net/webdav" +) + +func WrapRigFS(fsys rigfs.Fsys) filesystem.FileSystem { + return &rigfsWrapper{fsys: fsys} +} + +type rigfsWrapper struct { + fsys rigfs.Fsys +} + +func (r *rigfsWrapper) Mkdir(ctx context.Context, name string, perm os.FileMode) error { + return r.fsys.MkDirAll(name, perm) +} + +func (r *rigfsWrapper) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) { + if perm.IsDir() { + info, err := r.fsys.Stat(name) + if err != nil { + return nil, err + } + + return &wrapper{ + fsys: r.fsys, + path: name, + info: info, + }, err + } + + f, err := r.fsys.OpenFile(name, flag, perm) + if err != nil { + return nil, err + } + + return &wrapper{ + fsys: r.fsys, + path: name, + file: f, + }, err +} + +var _ fs.ReadDirFile = &wrapper{} + +type wrapper struct { + fsys rigfs.Fsys + path string + info fs.FileInfo + file fs.File +} + +func (f *wrapper) Close() error { + if f.file != nil { + return f.file.Close() + } + return nil +} + +func (f *wrapper) Stat() (fs.FileInfo, error) { + if f.info != nil { + return f.info, nil + } + return f.file.Stat() +} + +func (f *wrapper) Read(bytes []byte) (int, error) { + if seeker, ok := f.file.(io.Reader); ok { + return seeker.Read(bytes) + } + return -1, &fs.PathError{ + Op: "read", + Path: f.path, + Err: fs.ErrInvalid, + } +} + +func (f *wrapper) Seek(offset int64, whence int) (int64, error) { + if seeker, ok := f.file.(io.Seeker); ok { + return seeker.Seek(offset, whence) + } + return -1, &fs.PathError{ + Op: "seek", + Path: f.path, + Err: fs.ErrInvalid, + } +} + +func (f *wrapper) Write(p []byte) (n int, err error) { + if writer, ok := f.file.(io.Writer); ok { + return writer.Write(p) + } + return -1, &fs.PathError{ + Op: "write", + Path: f.path, + Err: fs.ErrPermission, + } +} + +func (f *wrapper) ReadDir(n int) ([]fs.DirEntry, error) { + if ff, ok := f.file.(fs.ReadDirFile); ok { + if n < 0 { + n = 0 + } + return ff.ReadDir(n) + } + return fs.ReadDir(f.fsys, f.path) +} + +func (f *wrapper) Readdir(count int) ([]fs.FileInfo, error) { + list, err := f.ReadDir(count) + if err != nil { + return nil, err + } + + infos := make([]fs.FileInfo, len(list)) + for i := range list { + info, err := list[i].Info() + if err != nil { + return nil, err + } + infos[i] = info + } + + return infos, nil +} + +func (r *rigfsWrapper) RemoveAll(ctx context.Context, name string) error { + return r.fsys.RemoveAll(name) +} + +func (r *rigfsWrapper) Stat(ctx context.Context, name string) (os.FileInfo, error) { + return r.fsys.Stat(name) +} + +func (r *rigfsWrapper) Rename(ctx context.Context, oldName, newName string) (err error) { + oldFile, err := r.OpenFile(ctx, oldName, os.O_RDONLY, os.ModePerm) + if err != nil { + return err + } + defer func() { + _ = oldFile.Close() + if err != nil { + _ = r.RemoveAll(ctx, oldName) + } + }() + + if err := r.Mkdir(ctx, filepath.Dir(newName), os.ModeDir); err != nil { + return err + } + + newFile, err := r.OpenFile(ctx, oldName, os.O_CREATE, os.ModePerm) + if err != nil { + return err + } + + defer newFile.Close() + if _, err := io.Copy(newFile, oldFile); err != nil { + return err + } + return nil +} diff --git a/pkg/wd/platform.go b/pkg/wd/platform.go new file mode 100644 index 0000000..ce8e2ac --- /dev/null +++ b/pkg/wd/platform.go @@ -0,0 +1,38 @@ +package wd + +func GoOS(id string) (string, bool) { + if id == "darwin" || id == "windows" { + return id, true + } + return "linux", true +} + +func GoArch(os string, unameM string) (string, bool) { + switch os { + case "windows": + v, ok := windowsArches[unameM] + return v, ok + case "linux": + v, ok := linuxArches[unameM] + return v, ok + case "darwin": + v, ok := darwinArches[unameM] + return v, ok + } + return "", false +} + +var windowsArches = map[string]string{ + "x86_64": "amd64", + "aarch64": "arm64", +} + +var linuxArches = map[string]string{ + "x86_64": "amd64", + "aarch64": "arm64", +} + +var darwinArches = map[string]string{ + "x86_64": "amd64", + "arm64": "arm64", +} diff --git a/pkg/wd/util.go b/pkg/wd/util.go new file mode 100644 index 0000000..633bdab --- /dev/null +++ b/pkg/wd/util.go @@ -0,0 +1,47 @@ +package wd + +import ( + "context" + "io/fs" + "path/filepath" + "strings" + + "github.com/octohelm/unifs/pkg/filesystem" +) + +func ListFile(f filesystem.FileSystem, root string, each func(filename string) error) error { + return filesystem.WalkDir(context.Background(), f, root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + rel := path + if root != "" && root != "." { + rel, _ = filepath.Rel(root, path) + } + return each(rel) + }) +} + +type Dir string + +func (d Dir) String() string { + return string(d) +} + +func (d Dir) With(dir string) Dir { + if dir != "" { + if d == "" || strings.HasPrefix(dir, "/") { + return Dir(dir) + } else { + prefix := string(d) + if prefix == "" { + prefix = "/" + } + return Dir(filepath.Join(prefix, dir)) + } + } + return d +} diff --git a/pkg/wd/wd.go b/pkg/wd/wd.go new file mode 100644 index 0000000..891216e --- /dev/null +++ b/pkg/wd/wd.go @@ -0,0 +1,199 @@ +package wd + +import ( + "context" + "fmt" + "github.com/octohelm/unifs/pkg/filesystem/local" + "io" + "os" + "strings" + + "github.com/go-courier/logr" + "github.com/k0sproject/rig" + "github.com/k0sproject/rig/exec" + "github.com/octohelm/unifs/pkg/filesystem" +) + +type WorkDir interface { + fmt.Stringer + + filesystem.FileSystem + + Options() Options + + Exec(ctx context.Context, cmd string, optFns ...OptionFunc) error +} + +type WorkDirConnection interface { + Connection() *rig.Connection +} + +type OptionFunc func(opt *Options) + +type Options struct { + BasePath Dir + Env map[string]string + User string + Stdout io.Writer + Stderr io.Writer +} + +func (o *Options) Build(optionFuncs ...OptionFunc) { + for _, fn := range optionFuncs { + fn(o) + } +} + +func WithDir(dir string) OptionFunc { + return func(opt *Options) { + opt.BasePath = opt.BasePath.With(dir) + } +} + +func WithUser(user string) OptionFunc { + return func(opt *Options) { + if user != "" { + opt.User = user + } + } +} + +func WithStdout(stdout io.Writer) OptionFunc { + return func(opt *Options) { + if stdout != nil { + opt.Stdout = stdout + } + } +} + +func WithStderr(stderr io.Writer) OptionFunc { + return func(opt *Options) { + if stderr != nil { + opt.Stderr = stderr + } + } +} + +func WithEnv(env map[string]string) OptionFunc { + return func(opt *Options) { + opt.Env = env + } +} + +func Wrap(c *rig.Connection, optionFuncs ...OptionFunc) (WorkDir, error) { + w := &wd{} + w.connection = c + w.opt.User = "root" + w.opt.BasePath = "/" + + w.opt.Build(optionFuncs...) + + if err := w.init(); err != nil { + return nil, err + } + + return w, nil +} + +func With(source WorkDir, optionFuncs ...OptionFunc) (WorkDir, error) { + if len(optionFuncs) == 0 { + return source, nil + } + + w := &wd{} + w.connection = source.(WorkDirConnection).Connection() + w.opt = source.Options() + + w.opt.Build(optionFuncs...) + + if err := w.init(); err != nil { + return nil, err + } + + return w, nil +} + +type wd struct { + opt Options + filesystem.FileSystem + connection *rig.Connection +} + +func (w *wd) Connection() *rig.Connection { + return w.connection +} + +func (w *wd) init() error { + if !w.connection.IsConnected() { + if err := w.connection.Connect(); err != nil { + return err + } + } + + if w.connection.Localhost != nil { + w.FileSystem = local.NewLocalFS(w.opt.BasePath.String()) + } else { + if w.opt.User == "root" { + w.FileSystem = filesystem.Sub(WrapRigFS(w.connection.SudoFsys()), w.opt.BasePath.String()) + } else { + w.FileSystem = filesystem.Sub(WrapRigFS(w.connection.Fsys()), w.opt.BasePath.String()) + } + } + return nil +} + +func (w *wd) Options() Options { + return w.opt +} + +func (w *wd) String() string { + switch w.connection.Protocol() { + case "Local": + return fmt.Sprintf("%s@local:%s", w.opt.User, w.opt.BasePath) + default: + return fmt.Sprintf("%s,%s@%s:%s", strings.ToLower(w.connection.Protocol()), w.opt.User, w.connection.Address(), w.opt.BasePath) + } +} + +func (w *wd) Exec(ctx context.Context, cmd string, optFns ...OptionFunc) error { + logr.FromContext(ctx).Info(cmd) + + o, b, opts := w.normalizeExecArgs(cmd, optFns...) + + waiter, err := w.connection.ExecStreams(b.String(), nil, o.Stdout, o.Stderr, opts...) + if err != nil { + return err + } + return waiter.Wait() +} + +func (w *wd) normalizeExecArgs(cmd string, optFns ...OptionFunc) (opt *Options, b *strings.Builder, execOptions []exec.Option) { + b = &strings.Builder{} + + opt = &Options{ + BasePath: w.opt.BasePath, + User: w.opt.User, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + + for _, optFn := range optFns { + optFn(opt) + } + + if w.connection.Localhost == nil && opt.User == "root" { + execOptions = append(execOptions, exec.Sudo(w.connection)) + } + + if opt.BasePath != "" && opt.BasePath != "/" { + _, _ = fmt.Fprintf(b, "cd %s; ", opt.BasePath) + } + + for k, v := range opt.Env { + _, _ = fmt.Fprintf(b, "%s=%s ", k, v) + } + + b.WriteString(cmd) + + return +} diff --git a/pkg/wd/wd_os.go b/pkg/wd/wd_os.go new file mode 100644 index 0000000..dbe0b17 --- /dev/null +++ b/pkg/wd/wd_os.go @@ -0,0 +1,36 @@ +package wd + +import ( + "context" + "github.com/k0sproject/rig" + v1 "github.com/opencontainers/image-spec/specs-go/v1" +) + +type CanOSInfo interface { + OSInfo(ctx context.Context) (*OSInfo, error) +} + +type OSInfo struct { + rig.OSVersion + + Platform v1.Platform +} + +var _ CanOSInfo = &wd{} + +func (w *wd) OSInfo(ctx context.Context) (*OSInfo, error) { + gnuarch, err := w.connection.ExecOutput("uname -m") + if err != nil { + return nil, err + } + os, _ := GoOS(w.connection.OSVersion.ID) + arch, _ := GoArch(os, gnuarch) + + return &OSInfo{ + Platform: v1.Platform{ + OS: os, + Architecture: arch, + }, + OSVersion: *w.connection.OSVersion, + }, nil +} diff --git a/pkg/wd/wd_test.go b/pkg/wd/wd_test.go new file mode 100644 index 0000000..185e90c --- /dev/null +++ b/pkg/wd/wd_test.go @@ -0,0 +1,46 @@ +package wd + +import ( + "github.com/k0sproject/rig" + testingx "github.com/octohelm/x/testing" + "golang.org/x/net/context" + "os" + "testing" +) + +func TestFS(t *testing.T) { + dir := t.TempDir() + t.Cleanup(func() { + _ = os.RemoveAll(dir) + }) + + local, err := Wrap( + &rig.Connection{ + Localhost: &rig.Localhost{ + Enabled: true, + }, + }, + WithDir(dir), + WithUser("root"), + ) + testingx.Expect(t, err, testingx.Be[error](nil)) + + t.Run("touch file", func(t *testing.T) { + f, err := local.OpenFile(context.Background(), "1.txt", os.O_RDWR|os.O_CREATE, os.ModePerm) + testingx.Expect(t, err, testingx.Be[error](nil)) + f.Close() + + t.Run("ls", func(t *testing.T) { + files := make([]string, 0) + + err = ListFile(local, ".", func(filename string) error { + files = append(files, filename) + return nil + }) + testingx.Expect(t, err, testingx.Be[error](nil)) + testingx.Expect(t, files, testingx.Equal([]string{ + "1.txt", + })) + }) + }) +} diff --git a/pkg/wd/zz_generated.runtimedoc.go b/pkg/wd/zz_generated.runtimedoc.go new file mode 100644 index 0000000..27d8b9d --- /dev/null +++ b/pkg/wd/zz_generated.runtimedoc.go @@ -0,0 +1,60 @@ +/* +Package wd GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package wd + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} + +func (Dir) RuntimeDoc(names ...string) ([]string, bool) { + return []string{}, true +} +func (v OSInfo) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "OSVersion": + return []string{}, true + case "Platform": + return []string{}, true + + } + if doc, ok := runtimeDoc(v.OSVersion, names...); ok { + return doc, ok + } + + return nil, false + } + return []string{}, true +} + +func (OptionFunc) RuntimeDoc(names ...string) ([]string, bool) { + return []string{}, true +} +func (v Options) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "BasePath": + return []string{}, true + case "Env": + return []string{}, true + case "User": + return []string{}, true + case "Stdout": + return []string{}, true + case "Stderr": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +}