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
This commit is contained in:
Mohd Bilal 2022-10-28 15:01:03 +05:30 committed by hasura-bot
parent 3ca779797d
commit e814260ccc
7 changed files with 141 additions and 81 deletions

View File

@ -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
}

View File

@ -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())

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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()