This is an automated email from the ASF dual-hosted git repository.
zhongxjian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git
The following commit(s) were added to refs/heads/master by this push:
new d794962a [dubboctl] Update create logic (#540)
d794962a is described below
commit d794962aba7d561d3de4a94fc5e077d4d52032c6
Author: Jian Zhong <[email protected]>
AuthorDate: Tue Jan 14 20:03:31 2025 +0800
[dubboctl] Update create logic (#540)
---
dubboctl/cmd/create.go | 49 ++-
dubboctl/pkg/filesystem/filesystem.go | 376 +++++++++++++++++++++
dubboctl/pkg/sdk/client.go | 220 ++----------
dubboctl/pkg/sdk/dubbo/config.go | 63 ++++
dubboctl/pkg/sdk/template.go | 20 ++
dubboctl/pkg/sdk/templates.go | 5 +
.../pkg/template/go/{common => sample}/.gitignore | 0
.../template/go/{common => sample}/api/api.pb.go | 0
.../template/go/{common => sample}/api/api.proto | 0
.../go/{common => sample}/api/api_triple.pb.go | 0
.../pkg/template/go/{common => sample}/cmd/app.go | 0
.../go/{common => sample}/conf/dubbogo.yaml | 0
dubboctl/pkg/template/go/{common => sample}/go.mod | 0
dubboctl/pkg/template/go/{common => sample}/go.sum | 0
.../go/{common => sample}/pkg/service/service.go | 0
.../template/java/{common => sample}/.gitignore | 0
.../pkg/template/java/{common => sample}/pom.xml | 0
.../java/com/example/demo/DemoApplication.java | 0
.../example/demo/demos/web/BasicController.java | 0
.../demo/demos/web/PathVariableController.java | 0
.../main/java/com/example/demo/demos/web/User.java | 0
.../com/example/demo/dubbo/api/DemoService.java | 0
.../com/example/demo/dubbo/consumer/Consumer.java | 0
.../demo/dubbo/service/DemoServiceImpl.java | 0
.../src/main/resources/application.yml | 0
.../src/main/resources/log4j.properties | 0
.../src/main/resources/static/index.html | 0
.../com/example/demo/DemoApplicationTests.java | 0
go.mod | 13 +-
go.sum | 13 +
30 files changed, 552 insertions(+), 207 deletions(-)
diff --git a/dubboctl/cmd/create.go b/dubboctl/cmd/create.go
index 32883f06..11e135b7 100644
--- a/dubboctl/cmd/create.go
+++ b/dubboctl/cmd/create.go
@@ -3,9 +3,12 @@ package cmd
import (
"fmt"
"github.com/apache/dubbo-kubernetes/dubboctl/pkg/cli"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/sdk"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/sdk/dubbo"
"github.com/apache/dubbo-kubernetes/operator/cmd/cluster"
"github.com/apache/dubbo-kubernetes/operator/pkg/util/clog"
"github.com/apache/dubbo-kubernetes/pkg/kube"
+ "github.com/ory/viper"
"github.com/spf13/cobra"
"os"
"path/filepath"
@@ -75,16 +78,54 @@ func sdkGenerateCmd(ctx cli.Context, _ *cluster.RootArgs,
tempArgs *templateArgs
}
type createArgs struct {
- dirname string
- path string
- create string
+ Path string
+ Runtime string
+ Template string
+ Name string
+ Initialzed bool
}
func runCreate(kc kube.CLIClient, tempArgs *templateArgs, cl clog.Logger)
error {
+ dcfg, err := newCreate(kc, tempArgs, cl)
+ if err != nil {
+ return err
+ }
+ var newClient sdk.ClientFactory
+ var cmd *cobra.Command
+ client, cancel := newClient()
+ defer cancel()
+ _, err = client.Initialize(&dubbo.DubboConfig{
+ Root: dcfg.Path,
+ Name: dcfg.Name,
+ Runtime: dcfg.Runtime,
+ Template: dcfg.Template,
+ }, dcfg.Initialzed, cmd)
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(cmd.OutOrStderr(), "Created %v dubbo application in %v\n",
dcfg.Runtime, dcfg.Path)
return nil
}
-func newCreate(kc kube.CLIClient, tempArgs *templateArgs, cl clog.Logger)
(createArgs, error) {
+func newCreate(kc kube.CLIClient, tempArgs *templateArgs, cl clog.Logger)
(dcfg createArgs, err error) {
+ var (
+ path string
+ dirName string
+ absolutePath string
+ )
+ dirName, absolutePath = deriveNameAndAbsolutePathFromPath(path)
+
+ dcfg = createArgs{
+ Path: absolutePath,
+ Runtime: viper.GetString("language"),
+ Template: viper.GetString("template"),
+ Name: dirName,
+ }
+
+ fmt.Printf("Path: %v\n", dcfg.Path)
+ fmt.Printf("Language: %v\n", dcfg.Runtime)
+ fmt.Printf("Template: %v\n", dcfg.Template)
+
return createArgs{}, nil
}
diff --git a/dubboctl/pkg/filesystem/filesystem.go
b/dubboctl/pkg/filesystem/filesystem.go
new file mode 100644
index 00000000..38172059
--- /dev/null
+++ b/dubboctl/pkg/filesystem/filesystem.go
@@ -0,0 +1,376 @@
+package filesystem
+
+import (
+ "archive/zip"
+ "embed"
+ "errors"
+ "fmt"
+ "io"
+ "io/fs"
+ "net/url"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+)
+
+import (
+ billy "github.com/go-git/go-billy/v5"
+)
+
+// Filesystems
+// Wrap the implementations of FS with their subtle differences into the
+// common interface for accessing template files defined herein.
+// os: standard for on-disk extensible template repositories.
+// zip: embedded filesystem backed by the byte array representing zipfile.
+// billy: go-git library's filesystem used for remote git template repos.
+type Filesystem interface {
+ fs.ReadDirFS
+ fs.StatFS
+ Readlink(link string) (string, error)
+}
+
+type zipFS struct {
+ archive *zip.Reader
+}
+
+func NewZipFS(archive *zip.Reader) zipFS {
+ return zipFS{archive: archive}
+}
+
+func (z zipFS) Open(name string) (fs.File, error) {
+ return z.archive.Open(name)
+}
+
+func (z zipFS) ReadDir(name string) ([]fs.DirEntry, error) {
+ var dirEntries []fs.DirEntry
+ for _, file := range z.archive.File {
+ cleanName := strings.TrimRight(file.Name, "/")
+ if path.Dir(cleanName) == name {
+ f, err := z.archive.Open(cleanName)
+ if err != nil {
+ return nil, err
+ }
+ fi, err := f.Stat()
+ if err != nil {
+ return nil, err
+ }
+ dirEntries = append(dirEntries, dirEntry{FileInfo: fi})
+ }
+ }
+ return dirEntries, nil
+}
+
+func (z zipFS) Readlink(link string) (string, error) {
+ f, err := z.archive.Open(link)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+
+ fi, err := f.Stat()
+ if err != nil {
+ return "", err
+ }
+
+ if fi.Mode()&fs.ModeSymlink == 0 {
+ return "", &fs.PathError{Op: "readlink", Path: link, Err:
fs.ErrInvalid}
+ }
+
+ bs, err := io.ReadAll(f)
+ if err != nil {
+ return "", err
+ }
+
+ return string(bs), nil
+}
+
+func (z zipFS) Stat(name string) (fs.FileInfo, error) {
+ f, err := z.archive.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ return f.Stat()
+}
+
+// BillyFilesystem is a template file accessor backed by a billy FS
+type BillyFilesystem struct{ fs billy.Filesystem }
+
+func NewBillyFilesystem(fs billy.Filesystem) BillyFilesystem {
+ return BillyFilesystem{fs: fs}
+}
+
+func (b BillyFilesystem) Readlink(link string) (string, error) {
+ return b.fs.Readlink(link)
+}
+
+type bfsFile struct {
+ billy.File
+ stats func() (fs.FileInfo, error)
+}
+
+func (b bfsFile) Stat() (fs.FileInfo, error) {
+ return b.stats()
+}
+
+func (b BillyFilesystem) Open(name string) (fs.File, error) {
+ f, err := b.fs.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ return bfsFile{
+ File: f,
+ stats: func() (fs.FileInfo, error) {
+ return b.fs.Stat(name)
+ },
+ }, nil
+}
+
+type dirEntry struct {
+ fs.FileInfo
+}
+
+func (d dirEntry) Type() fs.FileMode {
+ return d.Mode().Type()
+}
+
+func (d dirEntry) Info() (fs.FileInfo, error) {
+ return d, nil
+}
+
+func (b BillyFilesystem) ReadDir(name string) ([]fs.DirEntry, error) {
+ fis, err := b.fs.ReadDir(name)
+ if err != nil {
+ return nil, err
+ }
+ des := make([]fs.DirEntry, len(fis))
+ for i, fi := range fis {
+ des[i] = dirEntry{fi}
+ }
+ return des, nil
+}
+
+func (b BillyFilesystem) Stat(name string) (fs.FileInfo, error) {
+ return b.fs.Lstat(name)
+}
+
+// osFilesystem is a template file accessor backed by the os.
+type osFilesystem struct{ root string }
+
+func NewOsFilesystem(root string) osFilesystem {
+ return osFilesystem{root: root}
+}
+
+func (o osFilesystem) Open(name string) (fs.File, error) {
+ name = filepath.FromSlash(name)
+ return os.Open(filepath.Join(o.root, name))
+}
+
+func (o osFilesystem) ReadDir(name string) ([]fs.DirEntry, error) {
+ name = filepath.FromSlash(name)
+ return os.ReadDir(filepath.Join(o.root, name))
+}
+
+func (o osFilesystem) Stat(name string) (fs.FileInfo, error) {
+ name = filepath.FromSlash(name)
+ return os.Lstat(filepath.Join(o.root, name))
+}
+
+func (o osFilesystem) Readlink(link string) (string, error) {
+ link = filepath.FromSlash(link)
+ return os.Readlink(filepath.Join(o.root, link))
+}
+
+// subFS exposes subdirectory of underlying FS, this is similar to `chroot`.
+type subFS struct {
+ root string
+ fs Filesystem
+}
+
+func NewSubFS(root string, fs Filesystem) subFS {
+ return subFS{root: root, fs: fs}
+}
+
+func (o subFS) Open(name string) (fs.File, error) {
+ return o.fs.Open(path.Join(o.root, name))
+}
+
+func (o subFS) ReadDir(name string) ([]fs.DirEntry, error) {
+ return o.fs.ReadDir(path.Join(o.root, name))
+}
+
+func (o subFS) Stat(name string) (fs.FileInfo, error) {
+ return o.fs.Stat(path.Join(o.root, name))
+}
+
+func (o subFS) Readlink(link string) (string, error) {
+ return o.fs.Readlink(path.Join(o.root, link))
+}
+
+type maskingFS struct {
+ masked func(path string) bool
+ fs Filesystem
+}
+
+func NewMaskingFS(masked func(path string) bool, fs Filesystem) maskingFS {
+ return maskingFS{masked: masked, fs: fs}
+}
+
+func (m maskingFS) Open(name string) (fs.File, error) {
+ if m.masked(name) {
+ return nil, &fs.PathError{Op: "open", Path: name, Err:
fs.ErrNotExist}
+ }
+ return m.fs.Open(name)
+}
+
+func (m maskingFS) ReadDir(name string) ([]fs.DirEntry, error) {
+ if m.masked(name) {
+ return nil, &fs.PathError{Op: "readdir", Path: name, Err:
fs.ErrNotExist}
+ }
+ des, err := m.fs.ReadDir(name)
+ if err != nil {
+ return nil, err
+ }
+ result := make([]fs.DirEntry, 0, len(des))
+ for _, de := range des {
+ if !m.masked(path.Join(name, de.Name())) {
+ result = append(result, de)
+ }
+ }
+ return result, nil
+}
+
+func (m maskingFS) Stat(name string) (fs.FileInfo, error) {
+ if m.masked(name) {
+ return nil, &fs.PathError{Op: "stat", Path: name, Err:
fs.ErrNotExist}
+ }
+ return m.fs.Stat(name)
+}
+
+func (m maskingFS) Readlink(link string) (string, error) {
+ if m.masked(link) {
+ return "", &fs.PathError{Op: "readlink", Path: link, Err:
fs.ErrNotExist}
+ }
+ return m.fs.Readlink(link)
+}
+
+const (
+ EmbedSchema = "embed"
+)
+
+// UnionFS is an os.FS and embed.FS union fs.
+// Files in embed.FS has the header "embed://", and files in os.FS don't have
this header.
+type UnionFS struct {
+ embedFS embed.FS
+}
+
+func NewUnionFS(embedFS embed.FS) UnionFS {
+ return UnionFS{
+ embedFS: embedFS,
+ }
+}
+
+func (u UnionFS) ReadFile(name string) ([]byte, error) {
+ uri, _ := url.Parse(name)
+ if uri != nil && uri.Scheme == EmbedSchema {
+ name = strings.TrimLeft(uri.Opaque, "/")
+ return u.embedFS.ReadFile(name)
+ }
+
+ return os.ReadFile(name)
+}
+
+func (u UnionFS) Open(name string) (fs.File, error) {
+ uri, _ := url.Parse(name)
+ if uri != nil && uri.Scheme == EmbedSchema {
+ name = strings.TrimLeft(uri.Opaque, "/")
+ return u.embedFS.Open(name)
+ }
+
+ return os.Open(name)
+}
+
+func (u UnionFS) ReadDir(name string) ([]fs.DirEntry, error) {
+ uri, _ := url.Parse(name)
+ if uri != nil && uri.Scheme == EmbedSchema {
+ name = strings.TrimLeft(uri.Opaque, "/")
+ return u.embedFS.ReadDir(name)
+ }
+
+ return os.ReadDir(name)
+}
+
+func (u UnionFS) Stat(name string) (fs.FileInfo, error) {
+ f, err := u.Open(name)
+ if err != nil {
+ return nil, err
+ }
+
+ return f.Stat()
+}
+
+func (u UnionFS) Readlink(link string) (string, error) {
+ uri, _ := url.Parse(link)
+ if uri != nil && uri.Scheme == EmbedSchema {
+ return "", errors.New("embed FS not support read link")
+ }
+
+ return os.Readlink(link)
+}
+
+// CopyFromFS copies files from the `src` dir on the accessor Filesystem to
local filesystem into `dest` dir.
+// The src path uses slashes as their separator.
+// The dest path uses OS specific separator.
+func CopyFromFS(root, dest string, fsys Filesystem) (err error) {
+ // Walks the filesystem rooted at root.
+ return fs.WalkDir(fsys, root, func(path string, de fs.DirEntry, err
error) error {
+ if err != nil {
+ return err
+ }
+
+ p, err := filepath.Rel(filepath.FromSlash(root),
filepath.FromSlash(path))
+ if err != nil {
+ return err
+ }
+
+ dest := filepath.Join(dest, p)
+
+ switch {
+ case de.IsDir():
+ // Ideally we should use the file mode of the src node
+ // but it seems the git module is reporting directories
+ // as 0644 instead of 0755. For now, just do it this
way.
+ // See https://github.com/go-git/go-git/issues/364
+ // Upon resolution, return accessor.Stat(src).Mode()
+ return os.MkdirAll(dest, 0o755)
+ case de.Type()&fs.ModeSymlink != 0:
+ var symlinkTarget string
+ symlinkTarget, err = fsys.Readlink(path)
+ if err != nil {
+ return err
+ }
+ return os.Symlink(symlinkTarget, dest)
+ case de.Type().IsRegular():
+ fi, err := de.Info()
+ if err != nil {
+ return err
+ }
+ destFile, err := os.OpenFile(dest,
os.O_RDWR|os.O_CREATE|os.O_TRUNC, fi.Mode())
+ if err != nil {
+ return err
+ }
+ defer destFile.Close()
+
+ srcFile, err := fsys.Open(path)
+ if err != nil {
+ return err
+ }
+ defer srcFile.Close()
+
+ _, err = io.Copy(destFile, srcFile)
+ return err
+ default:
+ return fmt.Errorf("unsuported file type: %s",
de.Type().String())
+ }
+ })
+}
diff --git a/dubboctl/pkg/sdk/client.go b/dubboctl/pkg/sdk/client.go
index ead010a6..f2968576 100644
--- a/dubboctl/pkg/sdk/client.go
+++ b/dubboctl/pkg/sdk/client.go
@@ -2,132 +2,38 @@ package sdk
import (
"bufio"
- "context"
"fmt"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/sdk/dubbo"
"github.com/pkg/errors"
"github.com/spf13/cobra"
- "gopkg.in/yaml.v3"
"os"
"path/filepath"
+ "sigs.k8s.io/yaml"
"strings"
"time"
)
-const (
- DubboFile = "dubbo.yaml"
- RunDataDir = ".dubbo"
- DefaultTemplate = "common"
-)
-
type Client struct {
templates *Templates
}
-type Option func(*Client)
-
-func New(options ...Option) *Client {
- c := &Client{}
- for _, o := range options {
- o(c)
- }
-
- c.templates = newTemplates(c)
-
- return c
-}
-
-func newTemplates(client *Client) *Templates {
- return &Templates{client: client}
-}
-
-type Templates struct {
- client *Client
-}
-
-type DubboConfig struct {
- Root string `yaml:"-"`
- Name string `yaml:"name,omitempty"
jsonschema:"pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"`
- Runtime string `yaml:"runtime,omitempty"`
- Template string `yaml:"template,omitempty"`
- Created time.Time `yaml:"created,omitempty"`
-}
-
-func (f *DubboConfig) Validate() error {
- if f.Root == "" {
- return errors.New("dubbo root path is required")
- }
-
- var ctr int
- errs := [][]string{
- validateOptions(),
- }
-
- var b strings.Builder
- b.WriteString(fmt.Sprintf("'%v' contains errors:", DubboFile))
-
- for _, ee := range errs {
- if len(ee) > 0 {
- b.WriteString("\n") // Precede each group of errors
with a linebreak
- }
- for _, e := range ee {
- ctr++
- b.WriteString("\t" + e)
- }
- }
- if ctr == 0 {
- return nil // Return nil if there were no validation errors.
- }
+type Option func(*Client)
- return errors.New(b.String())
-}
-
-func validateOptions() []string {
- return nil
-}
+type ClientFactory func(...Option) (*Client, func())
func (c *Client) Templates() *Templates {
return c.templates
}
-func (f *DubboConfig) Write() (err error) {
- if err = f.Validate(); err != nil {
- return
- }
- dubboyamlpath := filepath.Join(f.Root, DubboFile)
- var dubbobytes []byte
- if dubbobytes, err = yaml.Marshal(f); err != nil {
- return
- }
- if err = os.WriteFile(dubboyamlpath, dubbobytes, 0644); err != nil {
- return
- }
- return
-}
-
-type Template interface {
- Name() string
- Runtime() string
- Repository() string
- Fullname() string
- Write(ctx context.Context, f *DubboConfig) error
-}
-
-type template struct {
- name string
- runtime string
-}
-
-func (t template) Name() string {
- return t.name
+func (c *Client) Runtimes() ([]string, error) {
+ // TODO
+ return nil, nil
}
-func (t template) Runtime() string {
- return t.runtime
-}
-
-func (c *Client) Create(dcfg *DubboConfig, initial bool, cmd *cobra.Command)
(*DubboConfig, error) {
+func (c *Client) Initialize(dcfg *dubbo.DubboConfig, initialized bool, _
*cobra.Command) (*dubbo.DubboConfig, error) {
var err error
oldRoot := dcfg.Root
+
dcfg.Root, err = filepath.Abs(dcfg.Root)
if err != nil {
return dcfg, err
@@ -137,12 +43,12 @@ func (c *Client) Create(dcfg *DubboConfig, initial bool,
cmd *cobra.Command) (*D
return dcfg, err
}
- has, err := hasInitializedApplication(dcfg.Root)
+ has, err := hasInitialized(dcfg.Root)
if err != nil {
return dcfg, err
}
if has {
- return dcfg, fmt.Errorf("application at '%v' already
initialized", dcfg.Root)
+ return dcfg, fmt.Errorf("%v already initialized", dcfg.Root)
}
if dcfg.Root == "" {
@@ -150,90 +56,40 @@ func (c *Client) Create(dcfg *DubboConfig, initial bool,
cmd *cobra.Command) (*D
return dcfg, err
}
}
-
if dcfg.Name == "" {
dcfg.Name = nameFromPath(dcfg.Root)
}
- if !initial {
+ if !initialized {
if err := assertEmptyRoot(dcfg.Root); err != nil {
return dcfg, err
}
}
- f := NewDubboWithTemplate(dcfg, initial)
-
+ f := dubbo.NewDubboConfigWithTemplate(dcfg, initialized)
if err = EnsureRunDataDir(f.Root); err != nil {
return f, err
}
f.Created = time.Now()
- err = f.Write()
- if err != nil {
- return f, err
- }
-
- return NewDubbo(oldRoot)
-}
-
-func NewDubbo(path string) (*DubboConfig, error) {
- var err error
- f := &DubboConfig{}
- // Path defaults to current working directory, and if provided
explicitly
- // Path must exist and be a directory
- if path == "" {
- if path, err = os.Getwd(); err != nil {
- return f, err
- }
- }
- f.Root = path // path is not persisted, as this is the purview of the FS
-
- // Path must exist and be a directory
- fd, err := os.Stat(path)
- if err != nil {
- return f, err
- }
- if !fd.IsDir() {
- return f, fmt.Errorf("function path must be a directory")
- }
-
- // If no dubbo.yaml in directory, return the default function which will
- // have f.Initialized() == false
- filename := filepath.Join(path, DubboFile)
- if _, err = os.Stat(filename); err != nil {
- if os.IsNotExist(err) {
- err = nil
- }
- return f, err
- }
-
- // Path is valid and dubbo.yaml exists: load it
- bb, err := os.ReadFile(filename)
- if err != nil {
- return f, err
- }
- err = yaml.Unmarshal(bb, f)
- if err != nil {
- return f, err
- }
- return f, nil
+ return dubbo.NewDubboConfig(oldRoot)
}
-func hasInitializedApplication(path string) (bool, error) {
+func hasInitialized(path string) (bool, error) {
var err error
- filename := filepath.Join(path, DubboFile)
+ filename := filepath.Join(path, dubbo.DubboFile)
if _, err = os.Stat(filename); err != nil {
if os.IsNotExist(err) {
return false, nil
}
- return false, err
+ return false, err // invalid path or access error
}
bb, err := os.ReadFile(filename)
if err != nil {
return false, err
}
- f := DubboConfig{}
+ f := dubbo.DubboConfig{}
if err = yaml.Unmarshal(bb, &f); err != nil {
return false, err
}
@@ -241,10 +97,6 @@ func hasInitializedApplication(path string) (bool, error) {
return f.Initialized(), nil
}
-func (f *DubboConfig) Initialized() bool {
- return !f.Created.IsZero()
-}
-
func nameFromPath(path string) string {
pathParts := strings.Split(strings.TrimRight(path,
string(os.PathSeparator)), string(os.PathSeparator))
return pathParts[len(pathParts)-1]
@@ -261,15 +113,12 @@ func nameFromPath(path string) string {
}
func assertEmptyRoot(path string) (err error) {
- // If there exists contentious files (config files for instance), this
function may have already been initialized.
files, err := contentiousFilesIn(path)
if err != nil {
return
} else if len(files) > 0 {
return fmt.Errorf("the chosen directory '%v' contains
contentious files: %v. Has the Service function already been created? Try
either using a different directory, deleting the function if it exists, or
manually removing the files", path, files)
}
-
- // Ensure there are no non-hidden files, and again none of the
aforementioned contentious files.
empty, err := isEffectivelyEmpty(path)
if err != nil {
return
@@ -281,11 +130,10 @@ func assertEmptyRoot(path string) (err error) {
}
var contentiousFiles = []string{
- DubboFile,
+ dubbo.DubboFile,
".gitignore",
}
-// contentiousFilesIn the given directory
func contentiousFilesIn(dir string) (contentious []string, err error) {
files, err := os.ReadDir(dir)
for _, file := range files {
@@ -313,18 +161,9 @@ func isEffectivelyEmpty(dir string) (bool, error) {
}
func EnsureRunDataDir(root string) error {
- // Ensure the runtime directory exists
- if err := os.MkdirAll(filepath.Join(root, RunDataDir), os.ModePerm);
err != nil {
+ if err := os.MkdirAll(filepath.Join(root, dubbo.DataDir), os.ModePerm);
err != nil {
return err
}
-
- // Update .gitignore
- //
- // Ensure .dubbo is added to .gitignore unless the user explicitly
- // commented out the ignore line for some awful reason.
- // Also creates the .gitignore in the application's root directory if
it does
- // not already exist (note that this may not be in the root of the repo
- // if the function is at a subpart of a monorepo)
filePath := filepath.Join(root, ".gitignore")
roFile, err := os.Open(filePath)
if err != nil && !os.IsNotExist(err) {
@@ -334,19 +173,17 @@ func EnsureRunDataDir(root string) error {
if !os.IsNotExist(err) { // if no error openeing it
s := bufio.NewScanner(roFile) // create a scanner
for s.Scan() { // scan each line
- if strings.HasPrefix(s.Text(), "# /"+RunDataDir) { //
if it was commented
+ if strings.HasPrefix(s.Text(), "# /"+dubbo.DataDir) {
// if it was commented
return nil // user wants it
}
- if strings.HasPrefix(s.Text(), "#/"+RunDataDir) {
+ if strings.HasPrefix(s.Text(), "#/"+dubbo.DataDir) {
return nil // user wants it
}
- if strings.HasPrefix(s.Text(), "/"+RunDataDir) { // if
it is there
+ if strings.HasPrefix(s.Text(), "/"+dubbo.DataDir) { //
if it is there
return nil // we're done
}
}
}
- // Either .gitignore does not exist or it does not have the ignore
- // directive for .func yet.
roFile.Close()
rwFile, err := os.OpenFile(filePath,
os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
@@ -369,14 +206,3 @@ func EnsureRunDataDir(root string) error {
}
return nil
}
-
-func NewDubboWithTemplate(defaults *DubboConfig, init bool) *DubboConfig {
- if !init {
- if defaults.Template == "" {
- defaults.Template = DefaultTemplate
- }
- } else {
- defaults.Template = "init"
- }
- return defaults
-}
diff --git a/dubboctl/pkg/sdk/dubbo/config.go b/dubboctl/pkg/sdk/dubbo/config.go
new file mode 100644
index 00000000..9ce9dd45
--- /dev/null
+++ b/dubboctl/pkg/sdk/dubbo/config.go
@@ -0,0 +1,63 @@
+package dubbo
+
+import (
+ "gopkg.in/yaml.v3"
+ "os"
+ "path/filepath"
+ "time"
+)
+
+const (
+ DubboFile = "dubbo.yaml"
+ DataDir = ".dubbo"
+)
+
+type DubboConfig struct {
+ Root string `yaml:"-"`
+ Name string `yaml:"name,omitempty"
jsonschema:"pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"`
+ Runtime string `yaml:"runtime,omitempty"`
+ Template string `yaml:"template,omitempty"`
+ Created time.Time `yaml:"created,omitempty"`
+}
+
+func NewDubboConfig(path string) (*DubboConfig, error) {
+ var err error
+
+ fd, err := os.Stat(path)
+ if err != nil {
+ return nil, err
+ }
+ if !fd.IsDir() {
+ return nil, nil
+ }
+ filename := filepath.Join(path, DubboFile)
+ if _, err = os.Stat(filename); err != nil {
+ if os.IsNotExist(err) {
+ err = nil
+ }
+ return nil, err
+ }
+
+ bb, err := os.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ err = yaml.Unmarshal(bb, nil)
+ if err != nil {
+ return nil, err
+ }
+ return nil, nil
+}
+
+func NewDubboConfigWithTemplate(defaults *DubboConfig, initialized bool)
*DubboConfig {
+ if !initialized {
+ if defaults.Template == "" {
+ defaults.Template = "sample"
+ }
+ }
+ return defaults
+}
+
+func (dc *DubboConfig) Initialized() bool {
+ return !dc.Created.IsZero()
+}
diff --git a/dubboctl/pkg/sdk/template.go b/dubboctl/pkg/sdk/template.go
new file mode 100644
index 00000000..7ca369c4
--- /dev/null
+++ b/dubboctl/pkg/sdk/template.go
@@ -0,0 +1,20 @@
+package sdk
+
+import (
+ "context"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/filesystem"
+ "github.com/apache/dubbo-kubernetes/dubboctl/pkg/sdk/dubbo"
+)
+
+type template struct {
+ name string
+ runtime string
+ fs filesystem.Filesystem
+}
+
+type Template interface {
+ Name() string
+ Fullname() string
+ Runtime() string
+ Write(ctx context.Context, f *dubbo.DubboConfig) error
+}
diff --git a/dubboctl/pkg/sdk/templates.go b/dubboctl/pkg/sdk/templates.go
new file mode 100644
index 00000000..9c0759ac
--- /dev/null
+++ b/dubboctl/pkg/sdk/templates.go
@@ -0,0 +1,5 @@
+package sdk
+
+type Templates struct {
+ client *Client
+}
diff --git a/dubboctl/pkg/template/go/common/.gitignore
b/dubboctl/pkg/template/go/sample/.gitignore
similarity index 100%
rename from dubboctl/pkg/template/go/common/.gitignore
rename to dubboctl/pkg/template/go/sample/.gitignore
diff --git a/dubboctl/pkg/template/go/common/api/api.pb.go
b/dubboctl/pkg/template/go/sample/api/api.pb.go
similarity index 100%
rename from dubboctl/pkg/template/go/common/api/api.pb.go
rename to dubboctl/pkg/template/go/sample/api/api.pb.go
diff --git a/dubboctl/pkg/template/go/common/api/api.proto
b/dubboctl/pkg/template/go/sample/api/api.proto
similarity index 100%
rename from dubboctl/pkg/template/go/common/api/api.proto
rename to dubboctl/pkg/template/go/sample/api/api.proto
diff --git a/dubboctl/pkg/template/go/common/api/api_triple.pb.go
b/dubboctl/pkg/template/go/sample/api/api_triple.pb.go
similarity index 100%
rename from dubboctl/pkg/template/go/common/api/api_triple.pb.go
rename to dubboctl/pkg/template/go/sample/api/api_triple.pb.go
diff --git a/dubboctl/pkg/template/go/common/cmd/app.go
b/dubboctl/pkg/template/go/sample/cmd/app.go
similarity index 100%
rename from dubboctl/pkg/template/go/common/cmd/app.go
rename to dubboctl/pkg/template/go/sample/cmd/app.go
diff --git a/dubboctl/pkg/template/go/common/conf/dubbogo.yaml
b/dubboctl/pkg/template/go/sample/conf/dubbogo.yaml
similarity index 100%
rename from dubboctl/pkg/template/go/common/conf/dubbogo.yaml
rename to dubboctl/pkg/template/go/sample/conf/dubbogo.yaml
diff --git a/dubboctl/pkg/template/go/common/go.mod
b/dubboctl/pkg/template/go/sample/go.mod
similarity index 100%
rename from dubboctl/pkg/template/go/common/go.mod
rename to dubboctl/pkg/template/go/sample/go.mod
diff --git a/dubboctl/pkg/template/go/common/go.sum
b/dubboctl/pkg/template/go/sample/go.sum
similarity index 100%
rename from dubboctl/pkg/template/go/common/go.sum
rename to dubboctl/pkg/template/go/sample/go.sum
diff --git a/dubboctl/pkg/template/go/common/pkg/service/service.go
b/dubboctl/pkg/template/go/sample/pkg/service/service.go
similarity index 100%
rename from dubboctl/pkg/template/go/common/pkg/service/service.go
rename to dubboctl/pkg/template/go/sample/pkg/service/service.go
diff --git a/dubboctl/pkg/template/java/common/.gitignore
b/dubboctl/pkg/template/java/sample/.gitignore
similarity index 100%
rename from dubboctl/pkg/template/java/common/.gitignore
rename to dubboctl/pkg/template/java/sample/.gitignore
diff --git a/dubboctl/pkg/template/java/common/pom.xml
b/dubboctl/pkg/template/java/sample/pom.xml
similarity index 100%
rename from dubboctl/pkg/template/java/common/pom.xml
rename to dubboctl/pkg/template/java/sample/pom.xml
diff --git
a/dubboctl/pkg/template/java/common/src/main/java/com/example/demo/DemoApplication.java
b/dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/DemoApplication.java
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/java/com/example/demo/DemoApplication.java
rename to
dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/DemoApplication.java
diff --git
a/dubboctl/pkg/template/java/common/src/main/java/com/example/demo/demos/web/BasicController.java
b/dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/demos/web/BasicController.java
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/java/com/example/demo/demos/web/BasicController.java
rename to
dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/demos/web/BasicController.java
diff --git
a/dubboctl/pkg/template/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java
b/dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/demos/web/PathVariableController.java
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/java/com/example/demo/demos/web/PathVariableController.java
rename to
dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/demos/web/PathVariableController.java
diff --git
a/dubboctl/pkg/template/java/common/src/main/java/com/example/demo/demos/web/User.java
b/dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/demos/web/User.java
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/java/com/example/demo/demos/web/User.java
rename to
dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/demos/web/User.java
diff --git
a/dubboctl/pkg/template/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java
b/dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/dubbo/api/DemoService.java
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/java/com/example/demo/dubbo/api/DemoService.java
rename to
dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/dubbo/api/DemoService.java
diff --git
a/dubboctl/pkg/template/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
b/dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
rename to
dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/dubbo/consumer/Consumer.java
diff --git
a/dubboctl/pkg/template/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
b/dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
rename to
dubboctl/pkg/template/java/sample/src/main/java/com/example/demo/dubbo/service/DemoServiceImpl.java
diff --git
a/dubboctl/pkg/template/java/common/src/main/resources/application.yml
b/dubboctl/pkg/template/java/sample/src/main/resources/application.yml
similarity index 100%
rename from dubboctl/pkg/template/java/common/src/main/resources/application.yml
rename to dubboctl/pkg/template/java/sample/src/main/resources/application.yml
diff --git
a/dubboctl/pkg/template/java/common/src/main/resources/log4j.properties
b/dubboctl/pkg/template/java/sample/src/main/resources/log4j.properties
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/resources/log4j.properties
rename to dubboctl/pkg/template/java/sample/src/main/resources/log4j.properties
diff --git
a/dubboctl/pkg/template/java/common/src/main/resources/static/index.html
b/dubboctl/pkg/template/java/sample/src/main/resources/static/index.html
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/main/resources/static/index.html
rename to dubboctl/pkg/template/java/sample/src/main/resources/static/index.html
diff --git
a/dubboctl/pkg/template/java/common/src/test/java/com/example/demo/DemoApplicationTests.java
b/dubboctl/pkg/template/java/sample/src/test/java/com/example/demo/DemoApplicationTests.java
similarity index 100%
rename from
dubboctl/pkg/template/java/common/src/test/java/com/example/demo/DemoApplicationTests.java
rename to
dubboctl/pkg/template/java/sample/src/test/java/com/example/demo/DemoApplicationTests.java
diff --git a/go.mod b/go.mod
index 5473dfe5..14eb4d83 100644
--- a/go.mod
+++ b/go.mod
@@ -58,15 +58,15 @@ require (
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spiffe/go-spiffe/v2 v2.1.7
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
go.opentelemetry.io/otel/trace v1.28.0
go.uber.org/atomic v1.10.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
- golang.org/x/net v0.33.0
+ golang.org/x/net v0.34.0
golang.org/x/sync v0.10.0
- golang.org/x/sys v0.28.0
+ golang.org/x/sys v0.29.0
golang.org/x/text v0.21.0
google.golang.org/genproto/googleapis/rpc
v0.0.0-20240826202546-f6391c0de4c7
google.golang.org/grpc v1.65.0
@@ -120,7 +120,7 @@ require (
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/creasty/defaults v1.5.2 // indirect
- github.com/cyphar/filepath-securejoin v0.2.4 // indirect
+ github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc //
indirect
github.com/dgraph-io/ristretto v0.0.1 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
@@ -135,6 +135,7 @@ require (
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
+ github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
@@ -245,10 +246,10 @@ require (
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
golang.org/x/arch v0.3.0 // indirect
- golang.org/x/crypto v0.31.0 // indirect
+ golang.org/x/crypto v0.32.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
- golang.org/x/term v0.27.0 // indirect
+ golang.org/x/term v0.28.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.28.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
diff --git a/go.sum b/go.sum
index 666216d4..abe27870 100644
--- a/go.sum
+++ b/go.sum
@@ -201,6 +201,8 @@ github.com/creasty/defaults v1.5.2
h1:/VfB6uxpyp6h0fr7SPp7n8WJBoV8jfxQXPCnkVSjyl
github.com/creasty/defaults v1.5.2/go.mod
h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY=
github.com/cyphar/filepath-securejoin v0.2.4
h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod
h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
+github.com/cyphar/filepath-securejoin v0.3.6
h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
+github.com/cyphar/filepath-securejoin v0.3.6/go.mod
h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
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=
@@ -302,6 +304,8 @@ github.com/go-co-op/gocron v1.9.0/go.mod
h1:DbJm9kdgr1sEvWpHCA7dFFs/PGHPMil9/97E
github.com/go-errors/errors v1.0.1/go.mod
h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.4.2
h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod
h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-git/go-billy/v5 v5.6.2
h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
+github.com/go-git/go-billy/v5 v5.6.2/go.mod
h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
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=
@@ -950,6 +954,7 @@ github.com/stretchr/testify v1.8.2/go.mod
h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.4/go.mod
h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0
h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0/go.mod
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod
h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0
h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod
h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
@@ -1117,6 +1122,8 @@ golang.org/x/crypto
v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.3.0/go.mod
h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod
h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod
h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1212,6 +1219,8 @@ golang.org/x/net
v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
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=
@@ -1321,11 +1330,15 @@ golang.org/x/sys v0.2.0/go.mod
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod
h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
+golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
+golang.org/x/term v0.28.0/go.mod
h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
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=