From e814260ccc0186e29aeb58381975d1f465f9b3a1 Mon Sep 17 00:00:00 2001 From: Mohd Bilal Date: Fri, 28 Oct 2022 15:01:03 +0530 Subject: [PATCH] cli: refactor `util` package to use `internal/errors` PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6508 Co-authored-by: Aravind K P <8335904+scriptonist@users.noreply.github.com> GitOrigin-RevId: 41f4842593a7226966c53ffd6314809adbd72266 --- cli/util/copy.go | 86 +++++++++++++++++++++--------------------- cli/util/filesystem.go | 30 +++++++++------ cli/util/flags.go | 5 ++- cli/util/git.go | 40 ++++++++++++++------ cli/util/prompt.go | 38 ++++++++++++++++--- cli/util/server.go | 8 ++-- cli/util/zip.go | 15 +++++--- 7 files changed, 141 insertions(+), 81 deletions(-) diff --git a/cli/util/copy.go b/cli/util/copy.go index e9d4b1b68dd..07dfe010903 100644 --- a/cli/util/copy.go +++ b/cli/util/copy.go @@ -8,6 +8,8 @@ import ( "path/filepath" "github.com/spf13/afero" + + "github.com/hasura/graphql-engine/cli/v2/internal/errors" ) // from https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 @@ -17,76 +19,77 @@ import ( // destination file exists, all it's contents will be replaced by the contents // of the source file. The file mode will be copied from the source and // the copied data is synced/flushed to stable storage. -func CopyFile(src, dst string) (err error) { +func CopyFile(src, dst string) error { + var op errors.Op = "util.CopyFile" in, err := os.Open(src) if err != nil { - return + return errors.E(op, err) } defer in.Close() out, err := os.Create(dst) if err != nil { - return + return errors.E(op, err) } defer func() { if e := out.Close(); e != nil { - err = e + err = errors.E(op, e) } }() _, err = io.Copy(out, in) if err != nil { - return + return errors.E(op, err) } err = out.Sync() if err != nil { - return + return errors.E(op, err) } si, err := os.Stat(src) if err != nil { - return + return errors.E(op, err) } err = os.Chmod(dst, si.Mode()) if err != nil { - return + return errors.E(op, err) } - - return + return nil } // CopyDir recursively copies a directory tree, attempting to preserve permissions. // Source directory must exist, destination directory must *not* exist. // Symlinks are ignored and skipped. -func CopyDir(src string, dst string) (err error) { +func CopyDir(src string, dst string) error { + var op errors.Op = "util.CopyDir" src = filepath.Clean(src) dst = filepath.Clean(dst) si, err := os.Stat(src) if err != nil { - return err + return errors.E(op, err) } if !si.IsDir() { - return fmt.Errorf("source is not a directory") + return errors.E(op, fmt.Errorf("source is not a directory")) } _, err = os.Stat(dst) if err != nil && !os.IsNotExist(err) { - return + return errors.E(op, err) } if err == nil { - return fmt.Errorf("destination already exists") + return errors.E(op, fmt.Errorf("destination already exists")) } err = os.MkdirAll(dst, si.Mode()) if err != nil { - return + return errors.E(op, err) } entries, err := ioutil.ReadDir(src) if err != nil { - return + return errors.E(op, err) } for _, entry := range entries { @@ -96,7 +99,7 @@ func CopyDir(src string, dst string) (err error) { if entry.IsDir() { err = CopyDir(srcPath, dstPath) if err != nil { - return + return errors.E(op, err) } } else { // Skip symlinks. @@ -106,83 +109,83 @@ func CopyDir(src string, dst string) (err error) { err = CopyFile(srcPath, dstPath) if err != nil { - return + return errors.E(op, err) } } } - - return + return nil } // CopyFile but with Afero -func CopyFileAfero(fs afero.Fs, src, dst string) (err error) { +func CopyFileAfero(fs afero.Fs, src, dst string) error { + var op errors.Op = "util.CopyFileAfero" in, err := fs.Open(src) if err != nil { - return + return errors.E(op, err) } defer in.Close() out, err := fs.Create(dst) if err != nil { - return + return errors.E(op, err) } defer func() { if e := out.Close(); e != nil { - err = e + err = errors.E(op, e) } }() _, err = io.Copy(out, in) if err != nil { - return + return errors.E(op, err) } err = out.Sync() if err != nil { - return + return errors.E(op, err) } si, err := fs.Stat(src) if err != nil { - return + return errors.E(op, err) } err = fs.Chmod(dst, si.Mode()) if err != nil { - return + return errors.E(op, err) } - - return + return nil } // CopyDir but with afero -func CopyDirAfero(fs afero.Fs, src string, dst string) (err error) { +func CopyDirAfero(fs afero.Fs, src string, dst string) error { + var op errors.Op = "util.CopyDirAfero" src = filepath.Clean(src) dst = filepath.Clean(dst) si, err := fs.Stat(src) if err != nil { - return err + return errors.E(op, err) } if !si.IsDir() { - return fmt.Errorf("source is not a directory") + return errors.E(op, fmt.Errorf("source is not a directory")) } _, err = fs.Stat(dst) if err != nil && !os.IsNotExist(err) { - return + return errors.E(op, err) } if err == nil { - return fmt.Errorf("destination already exists") + return errors.E(op, fmt.Errorf("destination already exists")) } err = fs.MkdirAll(dst, si.Mode()) if err != nil { - return + return errors.E(op, err) } entries, err := afero.ReadDir(fs, src) if err != nil { - return + return errors.E(op, err) } for _, entry := range entries { @@ -192,7 +195,7 @@ func CopyDirAfero(fs afero.Fs, src string, dst string) (err error) { if entry.IsDir() { err = CopyDirAfero(fs, srcPath, dstPath) if err != nil { - return + return errors.E(op, err) } } else { // Skip symlinks. @@ -202,10 +205,9 @@ func CopyDirAfero(fs afero.Fs, src string, dst string) (err error) { err = CopyFileAfero(fs, srcPath, dstPath) if err != nil { - return + return errors.E(op, err) } } } - - return + return nil } diff --git a/cli/util/filesystem.go b/cli/util/filesystem.go index 6a9ddb4cd62..1b84249c662 100644 --- a/cli/util/filesystem.go +++ b/cli/util/filesystem.go @@ -1,65 +1,73 @@ package util import ( - "errors" "fmt" "io" "io/ioutil" "os" "path" + + "github.com/hasura/graphql-engine/cli/v2/internal/errors" ) func FSCheckIfDirPathExists(path string) error { + var op errors.Op = "util.FSCheckIfDirPathExists" stat, err := os.Lstat(path) if err != nil { - return err + return errors.E(op, err) } if !stat.IsDir() { - err = errors.New("no such directory") + err = errors.E(op, "no such directory") } return err } func FSCopyFile(src, dst string) error { + var op errors.Op = "util.FSCopyFile" var err error var srcfd *os.File var dstfd *os.File var srcinfo os.FileInfo if srcfd, err = os.Open(src); err != nil { - return err + return errors.E(op, err) } defer srcfd.Close() if dstfd, err = os.Create(dst); err != nil { - return err + return errors.E(op, err) } defer dstfd.Close() if _, err = io.Copy(dstfd, srcfd); err != nil { - return err + return errors.E(op, err) } if srcinfo, err = os.Stat(src); err != nil { - return err + return errors.E(op, err) } - return os.Chmod(dst, srcinfo.Mode()) + err = os.Chmod(dst, srcinfo.Mode()) + if err != nil { + return errors.E(op, err) + } + return nil } func FSCopyDir(src string, dst string) error { + var op errors.Op = "util.FSCopyDir" var err error var fds []os.FileInfo var srcinfo os.FileInfo if srcinfo, err = os.Stat(src); err != nil { - return err + return errors.E(op, err) } if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil { - return err + return errors.E(op, err) } if fds, err = ioutil.ReadDir(src); err != nil { - return err + return errors.E(op, err) } for _, fd := range fds { srcfp := path.Join(src, fd.Name()) diff --git a/cli/util/flags.go b/cli/util/flags.go index ac969ccfb35..0efbc61619c 100644 --- a/cli/util/flags.go +++ b/cli/util/flags.go @@ -2,6 +2,8 @@ package util import ( "github.com/Masterminds/semver" + + "github.com/hasura/graphql-engine/cli/v2/internal/errors" ) type VersionFlag struct { @@ -15,9 +17,10 @@ func NewVersionFlagValue(p *VersionFlag) *VersionFlag { // Set sets the value of the named command-line flag. func (c *VersionFlag) Set(s string) error { + var op errors.Op = "util.VersionFlag.Set" v, err := semver.NewVersion(s) if err != nil { - return err + return errors.E(op, err) } c.Version = v return nil diff --git a/cli/util/git.go b/cli/util/git.go index 7b014ea710d..a1879d30356 100644 --- a/cli/util/git.go +++ b/cli/util/git.go @@ -8,6 +8,8 @@ import ( "gopkg.in/src-d/go-git.v4" "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/plumbing" + + "github.com/hasura/graphql-engine/cli/v2/internal/errors" ) // Default Codegen Assets constants @@ -40,79 +42,93 @@ func NewGitUtil(uri string, path string, refName string) *GitUtil { } func (g *GitUtil) EnsureCloned() error { + var op errors.Op = "util.GitUtil.EnsureCloned" if g.DisableCloneOrUpdate { g.Logger.Debugf("skipping clone/update for %s", g.URI) return nil } if ok, err := g.IsGitCloned(); err != nil { - return err + return errors.E(op, err) } else if !ok { _, err := git.PlainClone(g.Path, false, &git.CloneOptions{ URL: g.URI, ReferenceName: g.ReferenceName, }) if err != nil && err != git.ErrRepositoryAlreadyExists { - return err + return errors.E(op, err) } } return nil } func (g *GitUtil) IsGitCloned() (bool, error) { + var op errors.Op = "util.GitUtil.IsGitCloned" f, err := os.Stat(filepath.Join(g.Path, ".git")) if os.IsNotExist(err) { return false, nil } - return err == nil && f.IsDir(), err + if err != nil { + return false, errors.E(op, err) + } + return f.IsDir(), nil } // EnsureUpdated will ensure the destination path exists and is up to date. func (g *GitUtil) EnsureUpdated() error { + var op errors.Op = "util.GitUtil.EnsureUpdated" if g.DisableCloneOrUpdate { g.Logger.Debugf("skipping clone/update for %s", g.URI) return nil } if err := g.EnsureCloned(); err != nil { - return err + return errors.E(op, err) } - return g.updateAndCleanUntracked() + if err := g.updateAndCleanUntracked(); err != nil { + return errors.E(op, err) + } + return nil } func (g *GitUtil) updateAndCleanUntracked() error { + var op errors.Op = "util.GitUtil.updateAndCleanUntracked" repo, err := git.PlainOpen(g.Path) if err != nil { - return err + return errors.E(op, err) } err = repo.Fetch(&git.FetchOptions{ RefSpecs: []config.RefSpec{"refs/*:refs/*"}, }) if err != nil && err != git.NoErrAlreadyUpToDate { - return err + return errors.E(op, err) } wt, err := repo.Worktree() if err != nil { - return err + return errors.E(op, err) } err = wt.Checkout(&git.CheckoutOptions{ Branch: g.ReferenceName, }) if err != nil { - return err + return errors.E(op, err) } err = wt.Pull(&git.PullOptions{ ReferenceName: g.ReferenceName, Force: true, }) if err != nil && err != git.NoErrAlreadyUpToDate { - return err + return errors.E(op, err) } err = wt.Reset(&git.ResetOptions{ Mode: git.HardReset, }) if err != nil { - return err + return errors.E(op, err) } - return wt.Clean(&git.CleanOptions{ + err = wt.Clean(&git.CleanOptions{ Dir: true, }) + if err != nil { + return errors.E(op, err) + } + return nil } diff --git a/cli/util/prompt.go b/cli/util/prompt.go index 566da71a2c9..777a41f0783 100644 --- a/cli/util/prompt.go +++ b/cli/util/prompt.go @@ -2,53 +2,79 @@ package util import ( "github.com/AlecAivazis/survey/v2" + + "github.com/hasura/graphql-engine/cli/v2/internal/errors" ) func GetYesNoPrompt(message string) (promptResp bool, err error) { + var op errors.Op = "util.GetYesNoPrompt" prompt := &survey.Confirm{ Message: message, Default: true, } err = survey.AskOne(prompt, &promptResp) - return promptResp, err + if err != nil { + return promptResp, errors.E(op, err) + } + return promptResp, nil } func GetSelectPrompt(message string, options []string) (selection string, err error) { + var op errors.Op = "util.GetSelectPrompt" prompt := &survey.Select{ Message: message, Options: options, } err = survey.AskOne(prompt, &selection) - return selection, err + if err != nil { + return selection, errors.E(op, err) + } + return selection, nil } func GetInputPrompt(message string) (input string, err error) { + var op errors.Op = "util.GetInputPrompt" prompt := &survey.Input{ Message: message, } err = survey.AskOne(prompt, &input) - return input, err + if err != nil { + return input, errors.E(op, err) + } + return input, nil } func GetInputPromptWithDefault(message string, def string) (input string, err error) { + var op errors.Op = "util.GetInputPromptWithDefault" prompt := &survey.Input{ Message: message, Default: def, } err = survey.AskOne(prompt, &input) - return input, err + if err != nil { + return input, errors.E(op, err) + } + return input, nil } func validateDirPath(a interface{}) error { + var op errors.Op = "util.validateDirPath" err := FSCheckIfDirPathExists(a.(string)) - return err + if err != nil { + return errors.E(op, err) + } + return nil } func GetFSPathPrompt(message string, def string) (input string, err error) { + var op errors.Op = "util.GetFSPathPrompt" prompt := &survey.Input{ Message: message, Default: def, } err = survey.AskOne(prompt, &input, survey.WithValidator(validateDirPath)) - return input, err + if err != nil { + return input, errors.E(op, err) + } + return input, nil } diff --git a/cli/util/server.go b/cli/util/server.go index 8caa62056b0..f0f88db0b4b 100644 --- a/cli/util/server.go +++ b/cli/util/server.go @@ -8,6 +8,7 @@ import ( "net/http" "github.com/hasura/graphql-engine/cli/v2/internal/httpc" + "github.com/hasura/graphql-engine/cli/v2/internal/errors" "github.com/sirupsen/logrus" ) @@ -99,17 +100,18 @@ func GetServerState(client *httpc.Client, endpoint string, hasMetadataV3 bool, l } func GetServerStatus(versionEndpoint string, httpClient *httpc.Client) (err error) { + var op errors.Op = "util.GetServerStatus" req, err := http.NewRequest("GET", versionEndpoint, nil) if err != nil { - return fmt.Errorf("failed to create GET request to %s: %w", versionEndpoint, err) + return errors.E(op, fmt.Errorf("failed to create GET request to %s: %w", versionEndpoint, err)) } var responseBs bytes.Buffer resp, err := httpClient.Do(context.Background(), req, &responseBs) if err != nil { - return fmt.Errorf("making http request failed: %w", err) + return errors.E(op, fmt.Errorf("making http request failed: %w", err)) } if resp.StatusCode != http.StatusOK { - return fmt.Errorf("request failed: url: %s status code: %v status: %s \n%s", versionEndpoint, resp.StatusCode, resp.Status, responseBs.String()) + return errors.E(op, fmt.Errorf("request failed: url: %s status code: %v status: %s \n%s", versionEndpoint, resp.StatusCode, resp.Status, responseBs.String())) } return nil } diff --git a/cli/util/zip.go b/cli/util/zip.go index 39a5913a152..918bcc87e0a 100644 --- a/cli/util/zip.go +++ b/cli/util/zip.go @@ -6,6 +6,8 @@ import ( "io" "os" "path/filepath" + + "github.com/hasura/graphql-engine/cli/v2/internal/errors" ) // from https://gist.github.com/svett/424e6784facc0ba907ae @@ -13,20 +15,21 @@ import ( // Unzip unzips the archive to target. Both archive and target should be paths // in the filesystem. target is created if it doesn't exist already. func Unzip(archive, target string) error { + var op errors.Op = "util.Unzip" reader, err := zip.OpenReader(archive) if err != nil { - return err + return errors.E(op, err) } if err := os.MkdirAll(target, 0755); err != nil { - return err + return errors.E(op, err) } for _, file := range reader.File { path := filepath.Join(target, file.Name) if file.FileInfo().IsDir() { if err = os.MkdirAll(path, file.Mode()); err != nil { - return fmt.Errorf("error while creating directory and it's parent directories: %w", err) + return errors.E(op, fmt.Errorf("error while creating directory and it's parent directories: %w", err)) } continue } @@ -38,7 +41,7 @@ func Unzip(archive, target string) error { fileReader.Close() } - return err + return errors.E(op, err) } targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) @@ -49,14 +52,14 @@ func Unzip(archive, target string) error { targetFile.Close() } - return err + return errors.E(op, err) } if _, err := io.Copy(targetFile, fileReader); err != nil { fileReader.Close() targetFile.Close() - return err + return errors.E(op, err) } fileReader.Close()