refactor file|path (#1409)
Co-authored-by: anqiansong <anqiansong@bytedance.com>
This commit is contained in:
223
tools/goctl/util/pathx/file.go
Normal file
223
tools/goctl/util/pathx/file.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package pathx
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/internal/version"
|
||||
)
|
||||
|
||||
// NL defines a new line
|
||||
const (
|
||||
NL = "\n"
|
||||
goctlDir = ".goctl"
|
||||
gitDir = ".git"
|
||||
)
|
||||
|
||||
var goctlHome string
|
||||
|
||||
// RegisterGoctlHome register goctl home path
|
||||
func RegisterGoctlHome(home string) {
|
||||
goctlHome = home
|
||||
}
|
||||
|
||||
// CreateIfNotExist creates a file if it is not exists
|
||||
func CreateIfNotExist(file string) (*os.File, error) {
|
||||
_, err := os.Stat(file)
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("%s already exist", file)
|
||||
}
|
||||
|
||||
return os.Create(file)
|
||||
}
|
||||
|
||||
// RemoveIfExist deletes the specified file if it is exists
|
||||
func RemoveIfExist(filename string) error {
|
||||
if !FileExists(filename) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.Remove(filename)
|
||||
}
|
||||
|
||||
// RemoveOrQuit deletes the specified file if read a permit command from stdin
|
||||
func RemoveOrQuit(filename string) error {
|
||||
if !FileExists(filename) {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("%s exists, overwrite it?\nEnter to overwrite or Ctrl-C to cancel...",
|
||||
aurora.BgRed(aurora.Bold(filename)))
|
||||
bufio.NewReader(os.Stdin).ReadBytes('\n')
|
||||
|
||||
return os.Remove(filename)
|
||||
}
|
||||
|
||||
// FileExists returns true if the specified file is exists
|
||||
func FileExists(file string) bool {
|
||||
_, err := os.Stat(file)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// FileNameWithoutExt returns a file name without suffix
|
||||
func FileNameWithoutExt(file string) string {
|
||||
return strings.TrimSuffix(file, filepath.Ext(file))
|
||||
}
|
||||
|
||||
// GetGoctlHome returns the path value of the goctl home where Join $HOME with .goctl
|
||||
func GetGoctlHome() (string, error) {
|
||||
if len(goctlHome) != 0 {
|
||||
return goctlHome, nil
|
||||
}
|
||||
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, goctlDir), nil
|
||||
}
|
||||
|
||||
// GetGitHome returns the git home of goctl.
|
||||
func GetGitHome() (string, error) {
|
||||
goctlH, err := GetGoctlHome()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(goctlH, gitDir), nil
|
||||
}
|
||||
|
||||
// GetTemplateDir returns the category path value in GoctlHome where could get it by GetGoctlHome
|
||||
func GetTemplateDir(category string) (string, error) {
|
||||
home, err := GetGoctlHome()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if home == goctlHome {
|
||||
// backward compatible, it will be removed in the feature
|
||||
// backward compatible start
|
||||
beforeTemplateDir := filepath.Join(home, version.GetGoctlVersion(), category)
|
||||
fs, _ := ioutil.ReadDir(beforeTemplateDir)
|
||||
var hasContent bool
|
||||
for _, e := range fs {
|
||||
if e.Size() > 0 {
|
||||
hasContent = true
|
||||
}
|
||||
}
|
||||
if hasContent {
|
||||
return beforeTemplateDir, nil
|
||||
}
|
||||
// backward compatible end
|
||||
|
||||
return filepath.Join(home, category), nil
|
||||
}
|
||||
|
||||
return filepath.Join(home, version.GetGoctlVersion(), category), nil
|
||||
}
|
||||
|
||||
// InitTemplates creates template files GoctlHome where could get it by GetGoctlHome
|
||||
func InitTemplates(category string, templates map[string]string) error {
|
||||
dir, err := GetTemplateDir(category)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := MkdirIfNotExist(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for k, v := range templates {
|
||||
if err := createTemplate(filepath.Join(dir, k), v, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTemplate writes template into file even it is exists
|
||||
func CreateTemplate(category, name, content string) error {
|
||||
dir, err := GetTemplateDir(category)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return createTemplate(filepath.Join(dir, name), content, true)
|
||||
}
|
||||
|
||||
// Clean deletes all templates and removes the parent directory
|
||||
func Clean(category string) error {
|
||||
dir, err := GetTemplateDir(category)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
// LoadTemplate gets template content by the specified file
|
||||
func LoadTemplate(category, file, builtin string) (string, error) {
|
||||
dir, err := GetTemplateDir(category)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
file = filepath.Join(dir, file)
|
||||
if !FileExists(file) {
|
||||
return builtin, nil
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
// SameFile compares the between path if the same path,
|
||||
// it maybe the same path in case case-ignore, such as:
|
||||
// /Users/go_zero and /Users/Go_zero, as far as we know,
|
||||
// this case maybe appear on macOS and Windows.
|
||||
func SameFile(path1, path2 string) (bool, error) {
|
||||
stat1, err := os.Stat(path1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
stat2, err := os.Stat(path2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return os.SameFile(stat1, stat2), nil
|
||||
}
|
||||
|
||||
func createTemplate(file, content string, force bool) error {
|
||||
if FileExists(file) && !force {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.WriteString(content)
|
||||
return err
|
||||
}
|
||||
|
||||
// MustTempDir creates a temporary directory
|
||||
func MustTempDir() string {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
76
tools/goctl/util/pathx/file_test.go
Normal file
76
tools/goctl/util/pathx/file_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package pathx
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/tools/goctl/internal/version"
|
||||
)
|
||||
|
||||
func TestGetTemplateDir(t *testing.T) {
|
||||
category := "foo"
|
||||
t.Run("before_have_templates", func(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
RegisterGoctlHome("")
|
||||
RegisterGoctlHome(home)
|
||||
v := version.GetGoctlVersion()
|
||||
dir := filepath.Join(home, v, category)
|
||||
err := MkdirIfNotExist(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tempFile := filepath.Join(dir, "bar.txt")
|
||||
err = ioutil.WriteFile(tempFile, []byte("foo"), os.ModePerm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
templateDir, err := GetTemplateDir(category)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, dir, templateDir)
|
||||
RegisterGoctlHome("")
|
||||
})
|
||||
|
||||
t.Run("before_has_no_template", func(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
RegisterGoctlHome("")
|
||||
RegisterGoctlHome(home)
|
||||
dir := filepath.Join(home, category)
|
||||
err := MkdirIfNotExist(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
templateDir, err := GetTemplateDir(category)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, dir, templateDir)
|
||||
})
|
||||
|
||||
t.Run("default", func(t *testing.T) {
|
||||
RegisterGoctlHome("")
|
||||
dir, err := GetTemplateDir(category)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assert.Contains(t, dir, version.BuildVersion)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetGitHome(t *testing.T) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
actual, err := GetGitHome()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
expected := filepath.Join(homeDir, goctlDir, gitDir)
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
123
tools/goctl/util/pathx/path.go
Normal file
123
tools/goctl/util/pathx/path.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package pathx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
const (
|
||||
pkgSep = "/"
|
||||
goModeIdentifier = "go.mod"
|
||||
)
|
||||
|
||||
// JoinPackages calls strings.Join and returns
|
||||
func JoinPackages(pkgs ...string) string {
|
||||
return strings.Join(pkgs, pkgSep)
|
||||
}
|
||||
|
||||
// MkdirIfNotExist makes directories if the input path is not exists
|
||||
func MkdirIfNotExist(dir string) error {
|
||||
if len(dir) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return os.MkdirAll(dir, os.ModePerm)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PathFromGoSrc returns the path without slash where has been trim the prefix $GOPATH
|
||||
func PathFromGoSrc() (string, error) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
gopath := os.Getenv("GOPATH")
|
||||
parent := path.Join(gopath, "src", vars.ProjectName)
|
||||
pos := strings.Index(dir, parent)
|
||||
if pos < 0 {
|
||||
return "", fmt.Errorf("%s is not in GOPATH", dir)
|
||||
}
|
||||
|
||||
// skip slash
|
||||
return dir[len(parent)+1:], nil
|
||||
}
|
||||
|
||||
// FindGoModPath returns the path in project where has file go.mod, it maybe return empty string if
|
||||
// there is no go.mod file in project
|
||||
func FindGoModPath(dir string) (string, bool) {
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
absDir = strings.ReplaceAll(absDir, `\`, `/`)
|
||||
var rootPath string
|
||||
tempPath := absDir
|
||||
hasGoMod := false
|
||||
for {
|
||||
if FileExists(filepath.Join(tempPath, goModeIdentifier)) {
|
||||
rootPath = strings.TrimPrefix(absDir[len(tempPath):], "/")
|
||||
hasGoMod = true
|
||||
break
|
||||
}
|
||||
|
||||
if tempPath == filepath.Dir(tempPath) {
|
||||
break
|
||||
}
|
||||
|
||||
tempPath = filepath.Dir(tempPath)
|
||||
if tempPath == string(filepath.Separator) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasGoMod {
|
||||
return rootPath, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// FindProjectPath returns the parent directory where has file go.mod in project
|
||||
func FindProjectPath(loc string) (string, bool) {
|
||||
var dir string
|
||||
if strings.IndexByte(loc, '/') == 0 {
|
||||
dir = loc
|
||||
} else {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
dir = filepath.Join(wd, loc)
|
||||
}
|
||||
|
||||
for {
|
||||
if FileExists(filepath.Join(dir, goModeIdentifier)) {
|
||||
return dir, true
|
||||
}
|
||||
|
||||
dir = filepath.Dir(dir)
|
||||
if dir == "/" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func isLink(name string) (bool, error) {
|
||||
fi, err := os.Lstat(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return fi.Mode()&os.ModeSymlink != 0, nil
|
||||
}
|
||||
39
tools/goctl/util/pathx/path_test.go
Normal file
39
tools/goctl/util/pathx/path_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package pathx
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReadLink(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "go-zero")
|
||||
assert.Nil(t, err)
|
||||
symLink := filepath.Join(dir, "test")
|
||||
pwd, err := os.Getwd()
|
||||
assertError(err, t)
|
||||
|
||||
err = os.Symlink(pwd, symLink)
|
||||
assertError(err, t)
|
||||
|
||||
t.Run("linked", func(t *testing.T) {
|
||||
ret, err := ReadLink(symLink)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, pwd, ret)
|
||||
})
|
||||
|
||||
t.Run("unlink", func(t *testing.T) {
|
||||
ret, err := ReadLink(pwd)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, pwd, ret)
|
||||
})
|
||||
}
|
||||
|
||||
func assertError(err error, t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
8
tools/goctl/util/pathx/readlink+polyfill.go
Normal file
8
tools/goctl/util/pathx/readlink+polyfill.go
Normal file
@@ -0,0 +1,8 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package pathx
|
||||
|
||||
func ReadLink(name string) (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
56
tools/goctl/util/pathx/readlink.go
Normal file
56
tools/goctl/util/pathx/readlink.go
Normal file
@@ -0,0 +1,56 @@
|
||||
//go:build linux || darwin
|
||||
// +build linux darwin
|
||||
|
||||
package pathx
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// ReadLink returns the destination of the named symbolic link recursively.
|
||||
func ReadLink(name string) (string, error) {
|
||||
name, err := filepath.Abs(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := os.Lstat(name); err != nil {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// uncheck condition: ignore file path /var, maybe be temporary file path
|
||||
if name == "/" || name == "/var" {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
isLink, err := isLink(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !isLink {
|
||||
dir, base := filepath.Split(name)
|
||||
dir = filepath.Clean(dir)
|
||||
dir, err := ReadLink(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(dir, base), nil
|
||||
}
|
||||
|
||||
link, err := os.Readlink(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
dir, base := filepath.Split(link)
|
||||
dir = filepath.Dir(dir)
|
||||
dir, err = ReadLink(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(dir, base), nil
|
||||
}
|
||||
Reference in New Issue
Block a user