Compare commits

..

2 Commits

Author SHA1 Message Date
f63747e876 Merge branch 'master' of https://github.com/gogf/gf 2023-01-11 19:20:21 +08:00
2854daded6 v2.3.0 2023-01-09 14:39:42 +08:00
12 changed files with 78 additions and 382 deletions

View File

@ -2,7 +2,6 @@ package cmd
import (
"context"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/v2/errors/gerror"
@ -20,9 +19,8 @@ type cFix struct {
}
type cFixInput struct {
g.Meta `name:"fix"`
Path string `name:"path" short:"p" brief:"directory path, it uses current working directory in default"`
Version string `name:"version" short:"v" brief:"custom specified version to fix, leave it empty to auto detect"`
g.Meta `name:"fix"`
Path string `name:"path" brief:"directory path, it uses current working directory in default"`
}
type cFixOutput struct{}
@ -33,45 +31,38 @@ type cFixItem struct {
}
func (c cFix) Index(ctx context.Context, in cFixInput) (out *cFixOutput, err error) {
mlog.Print(`start auto fixing...`)
defer mlog.Print(`done!`)
if in.Path == "" {
in.Path = gfile.Pwd()
}
if in.Version == "" {
in.Version, err = c.autoDetectVersion(in)
if err != nil {
mlog.Fatal(err)
}
if in.Version == "" {
mlog.Print(`no GoFrame usage found, exit fixing`)
return
}
mlog.Debugf(`current GoFrame version auto detect "%s"`, in.Version)
}
if !gproc.IsChild() {
mlog.Printf(`start auto fixing directory path "%s"...`, in.Path)
defer mlog.Print(`done!`)
}
err = c.doFix(in)
return
}
func (c cFix) doFix(in cFixInput) (err error) {
version, err := c.getVersion(in)
if err != nil {
mlog.Fatal(err)
}
if version == "" {
mlog.Print(`no GoFrame usage found, exit fixing`)
return
}
mlog.Debugf(`current GoFrame version found "%s"`, version)
var items = []cFixItem{
{Version: "v2.3", Func: c.doFixV23},
}
for _, item := range items {
if gstr.CompareVersionGo(in.Version, item.Version) < 0 {
if gstr.CompareVersionGo(version, item.Version) < 0 {
mlog.Debugf(
`current GoFrame or contrib package version "%s" is lesser than "%s", nothing to do`,
in.Version, item.Version,
`current GoFrame version "%s" is lesser than "%s", nothing to do`,
version, item.Version,
)
continue
}
if err = item.Func(in.Version); err != nil {
if err = item.Func(version); err != nil {
return
}
}
@ -96,7 +87,7 @@ func (c cFix) doFixV23(version string) error {
return gfile.ReplaceDirFunc(replaceFunc, ".", "*.go", true)
}
func (c cFix) autoDetectVersion(in cFixInput) (string, error) {
func (c cFix) getVersion(in cFixInput) (string, error) {
var (
err error
path = gfile.Join(in.Path, "go.mod")

View File

@ -3,9 +3,6 @@ package cmd
import (
"context"
"fmt"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"github.com/gogf/gf/v2/container/gset"
"runtime"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/v2/frame/g"
@ -29,7 +26,7 @@ const (
gf up
gf up -a
gf up -c
gf up -cf
gf up -f -c
`
)
@ -42,8 +39,8 @@ func init() {
type cUpInput struct {
g.Meta `name:"up" config:"gfcli.up"`
All bool `name:"all" short:"a" brief:"upgrade both version and cli, auto fix codes" orphan:"true"`
Cli bool `name:"cli" short:"c" brief:"also upgrade CLI tool" orphan:"true"`
Fix bool `name:"fix" short:"f" brief:"auto fix codes(it only make sense if cli is to be upgraded)" orphan:"true"`
Fix bool `name:"fix" short:"f" brief:"auto fix codes" orphan:"true"`
Cli bool `name:"cli" short:"c" brief:"also upgrade CLI tool (not supported yet)" orphan:"true"`
}
type cUpOutput struct{}
@ -51,72 +48,42 @@ type cUpOutput struct{}
func (c cUp) Index(ctx context.Context, in cUpInput) (out *cUpOutput, err error) {
defer func() {
if err == nil {
mlog.Print()
mlog.Print(`👏congratulations! you've upgraded to the latest version of GoFrame! enjoy it!👏`)
mlog.Print()
mlog.Print(`done!`)
}
}()
var doUpgradeVersionOut *doUpgradeVersionOutput
if in.All {
in.Cli = true
in.Fix = true
}
if doUpgradeVersionOut, err = c.doUpgradeVersion(ctx, in); err != nil {
if err = c.doUpgradeVersion(ctx, in); err != nil {
return nil, err
}
if in.Cli {
if err = c.doUpgradeCLI(ctx); err != nil {
return nil, err
}
}
if in.Cli && in.Fix {
if doUpgradeVersionOut != nil && len(doUpgradeVersionOut.Items) > 0 {
upgradedPathSet := gset.NewStrSet()
for _, item := range doUpgradeVersionOut.Items {
if !upgradedPathSet.AddIfNotExist(item.DirPath) {
continue
}
if err = c.doAutoFixing(ctx, item.DirPath, item.Version); err != nil {
return nil, err
}
}
}
}
//if in.Cli {
// if err = c.doUpgradeCLI(ctx); err != nil {
// return nil, err
// }
//}
return
}
type doUpgradeVersionOutput struct {
Items []doUpgradeVersionOutputItem
}
type doUpgradeVersionOutputItem struct {
DirPath string
Version string
}
func (c cUp) doUpgradeVersion(ctx context.Context, in cUpInput) (out *doUpgradeVersionOutput, err error) {
func (c cUp) doUpgradeVersion(ctx context.Context, in cUpInput) (err error) {
mlog.Print(`start upgrading version...`)
out = &doUpgradeVersionOutput{
Items: make([]doUpgradeVersionOutputItem, 0),
}
type Package struct {
Name string
Version string
}
var (
temp string
dirPath = gfile.Pwd()
goModPath = gfile.Join(dirPath, "go.mod")
dir = gfile.Pwd()
temp string
path = gfile.Join(dir, "go.mod")
)
// It recursively upgrades the go.mod from sub folder to its parent folders.
for {
if gfile.Exists(goModPath) {
if gfile.Exists(path) {
var packages []Package
err = gfile.ReadLines(goModPath, func(line string) error {
err = gfile.ReadLines(path, func(line string) error {
line = gstr.Trim(line)
if gstr.HasPrefix(line, gfPackage) {
array := gstr.SplitAndTrim(line, " ")
@ -132,76 +99,38 @@ func (c cUp) doUpgradeVersion(ctx context.Context, in cUpInput) (out *doUpgradeV
}
for _, pkg := range packages {
mlog.Printf(`upgrading "%s" from "%s" to "latest"`, pkg.Name, pkg.Version)
// go get -u
command := fmt.Sprintf(`cd %s && go get -u %s@latest`, dirPath, pkg.Name)
command := fmt.Sprintf(`go get -u %s@latest`, pkg.Name)
if err = gproc.ShellRun(ctx, command); err != nil {
return
}
// go mod tidy
if err = utils.GoModTidy(ctx, dirPath); err != nil {
return nil, err
mlog.Print()
}
if in.Fix {
if err = c.doAutoFixing(ctx, dir); err != nil {
return err
}
out.Items = append(out.Items, doUpgradeVersionOutputItem{
DirPath: dirPath,
Version: pkg.Version,
})
mlog.Print()
}
return
}
temp = gfile.Dir(dirPath)
if temp == "" || temp == dirPath {
temp = gfile.Dir(dir)
if temp == "" || temp == dir {
return
}
dirPath = temp
goModPath = gfile.Join(dirPath, "go.mod")
dir = temp
path = gfile.Join(dir, "go.mod")
}
}
// doUpgradeCLI downloads the new version binary with process.
func (c cUp) doUpgradeCLI(ctx context.Context) (err error) {
mlog.Print(`start upgrading cli...`)
var (
downloadUrl = fmt.Sprintf(
`https://github.com/gogf/gf/releases/latest/download/gf_%s_%s`,
runtime.GOOS, runtime.GOARCH,
)
localSaveFilePath = gfile.SelfPath() + "~"
)
mlog.Printf(`start downloading "%s" to "%s", it may take some time`, downloadUrl, localSaveFilePath)
err = utils.HTTPDownloadFileWithPercent(downloadUrl, localSaveFilePath)
if err != nil {
return err
}
defer func() {
mlog.Printf(`new version cli binary is successfully installed to "%s"`, gfile.SelfPath())
mlog.Printf(`remove temporary buffer file "%s"`, localSaveFilePath)
_ = gfile.Remove(localSaveFilePath)
}()
// It fails if file not exist or its size is less than 1MB.
if !gfile.Exists(localSaveFilePath) || gfile.Size(localSaveFilePath) < 1024*1024 {
mlog.Fatalf(`download "%s" to "%s" failed`, downloadUrl, localSaveFilePath)
}
// It replaces self binary with new version cli binary.
switch runtime.GOOS {
case "windows":
if err := gfile.Rename(localSaveFilePath, gfile.SelfPath()); err != nil {
mlog.Fatalf(`install failed: %s`, err.Error())
}
default:
if err := gfile.PutBytes(gfile.SelfPath(), gfile.GetBytes(localSaveFilePath)); err != nil {
mlog.Fatalf(`install failed: %s`, err.Error())
}
}
return
}
func (c cUp) doAutoFixing(ctx context.Context, dirPath string, version string) (err error) {
mlog.Printf(`auto fixing directory path "%s" from version "%s" ...`, dirPath, version)
command := fmt.Sprintf(`gf fix -p %s`, dirPath)
_ = gproc.ShellRun(ctx, command)
func (c cUp) doAutoFixing(ctx context.Context, dirPath string) (err error) {
mlog.Printf(`auto fixing path "%s"...`, dirPath)
err = cFix{}.doFix(cFixInput{
Path: dirPath,
})
return
}

View File

@ -1,14 +1,12 @@
package utils
import (
"context"
"fmt"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/text/gstr"
"golang.org/x/tools/imports"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
)
// GoFmt formats the source file and adds or removes import statements as necessary.
@ -38,13 +36,6 @@ func GoFmt(path string) {
}
}
// GoModTidy executes `go mod tidy` at specified directory `dirPath`.
func GoModTidy(ctx context.Context, dirPath string) error {
command := fmt.Sprintf(`cd %s && go mod tidy`, dirPath)
err := gproc.ShellRun(ctx, command)
return err
}
// IsFileDoNotEdit checks and returns whether file contains `do not edit` key.
func IsFileDoNotEdit(filePath string) bool {
if !gfile.Exists(filePath) {

View File

@ -1,98 +0,0 @@
package utils
import (
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"io"
"net/http"
"os"
"strconv"
"time"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
)
// HTTPDownloadFileWithPercent downloads target url file to local path with percent process printing.
func HTTPDownloadFileWithPercent(url string, localSaveFilePath string) error {
start := time.Now()
out, err := os.Create(localSaveFilePath)
if err != nil {
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
}
defer out.Close()
headResp, err := http.Head(url)
if err != nil {
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
}
defer headResp.Body.Close()
size, err := strconv.Atoi(headResp.Header.Get("Content-Length"))
if err != nil {
return gerror.Wrap(err, "retrieve Content-Length failed")
}
doneCh := make(chan int64)
go doPrintDownloadPercent(doneCh, localSaveFilePath, int64(size))
resp, err := http.Get(url)
if err != nil {
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
}
defer resp.Body.Close()
wroteBytesCount, err := io.Copy(out, resp.Body)
if err != nil {
return gerror.Wrapf(err, `download "%s" to "%s" failed`, url, localSaveFilePath)
}
doneCh <- wroteBytesCount
elapsed := time.Since(start)
if elapsed > time.Minute {
mlog.Printf(`download completed in %.0fm`, float64(elapsed)/float64(time.Minute))
} else {
mlog.Printf(`download completed in %.0fs`, elapsed.Seconds())
}
return nil
}
func doPrintDownloadPercent(doneCh chan int64, localSaveFilePath string, total int64) {
var (
stop = false
lastPercentFmt string
)
for {
select {
case <-doneCh:
stop = true
default:
file, err := os.Open(localSaveFilePath)
if err != nil {
mlog.Fatal(err)
}
fi, err := file.Stat()
if err != nil {
mlog.Fatal(err)
}
size := fi.Size()
if size == 0 {
size = 1
}
var (
percent = float64(size) / float64(total) * 100
percentFmt = fmt.Sprintf(`%.0f`, percent) + "%"
)
if lastPercentFmt != percentFmt {
lastPercentFmt = percentFmt
mlog.Print(percentFmt)
}
}
if stop {
break
}
time.Sleep(time.Second)
}
}

View File

@ -9,11 +9,8 @@ package mysql_test
import (
"testing"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gmeta"
)
func Test_Model_Builder(t *testing.T) {
@ -62,66 +59,4 @@ func Test_Model_Builder(t *testing.T) {
t.AssertNil(err)
t.Assert(len(all), 6)
})
// Where with struct which has a field type of *gtime.Time
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
type Query struct {
Id interface{}
Nickname *gtime.Time
}
where, args := b.Where(&Query{Id: 1}).Build()
t.Assert(where, "`id`=? AND `nickname` IS NULL")
t.Assert(args, []interface{}{1})
})
// Where with struct which has a field type of *gjson.Json
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
type Query struct {
Id interface{}
Nickname *gjson.Json
}
where, args := b.Where(&Query{Id: 1}).Build()
t.Assert(where, "`id`=? AND `nickname` IS NULL")
t.Assert(args, []interface{}{1})
})
// Where with do struct which has a field type of *gtime.Time and generated by gf cli
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
type Query struct {
gmeta.Meta `orm:"do:true"`
Id interface{}
Nickname *gtime.Time
}
where, args := b.Where(&Query{Id: 1}).Build()
t.Assert(where, "`id`=?")
t.Assert(args, []interface{}{1})
})
// Where with do struct which has a field type of *gjson.Json and generated by gf cli
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
type Query struct {
gmeta.Meta `orm:"do:true"`
Id interface{}
Nickname *gjson.Json
}
where, args := b.Where(&Query{Id: 1}).Build()
t.Assert(where, "`id`=?")
t.Assert(args, []interface{}{1})
})
}

View File

@ -16,7 +16,6 @@ import (
"time"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/internal/utils"
@ -211,7 +210,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} {
m := gconv.Map(value, structTagPriority...)
for k, v := range m {
switch v.(type) {
case time.Time, *time.Time, gtime.Time, *gtime.Time, gjson.Json, *gjson.Json:
case time.Time, *time.Time, gtime.Time, *gtime.Time:
m[k] = v
default:

View File

@ -45,58 +45,56 @@ func TestSafeMutex(t *testing.T) {
go func() {
safeLock.Lock()
array.Append(1)
time.Sleep(1000 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
array.Append(1)
safeLock.Unlock()
}()
go func() {
time.Sleep(100 * time.Millisecond)
time.Sleep(10 * time.Millisecond)
safeLock.Lock()
array.Append(1)
time.Sleep(2000 * time.Millisecond)
time.Sleep(200 * time.Millisecond)
array.Append(1)
safeLock.Unlock()
}()
time.Sleep(500 * time.Millisecond)
time.Sleep(50 * time.Millisecond)
t.Assert(array.Len(), 1)
time.Sleep(800 * time.Millisecond)
time.Sleep(80 * time.Millisecond)
t.Assert(array.Len(), 3)
time.Sleep(1000 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
t.Assert(array.Len(), 3)
time.Sleep(1000 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
t.Assert(array.Len(), 4)
})
}
func TestUnsafeMutex(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
unsafeLock = mutex.New()
array = garray.New(true)
)
unsafeLock := mutex.New()
array := garray.New(true)
go func() {
unsafeLock.Lock()
array.Append(1)
time.Sleep(1000 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
array.Append(1)
unsafeLock.Unlock()
}()
go func() {
time.Sleep(100 * time.Millisecond)
time.Sleep(10 * time.Millisecond)
unsafeLock.Lock()
array.Append(1)
time.Sleep(2000 * time.Millisecond)
time.Sleep(200 * time.Millisecond)
array.Append(1)
unsafeLock.Unlock()
}()
time.Sleep(500 * time.Millisecond)
time.Sleep(50 * time.Millisecond)
t.Assert(array.Len(), 2)
time.Sleep(1000 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
t.Assert(array.Len(), 3)
time.Sleep(500 * time.Millisecond)
time.Sleep(50 * time.Millisecond)
t.Assert(array.Len(), 3)
time.Sleep(1000 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
t.Assert(array.Len(), 4)
})
}

View File

@ -407,7 +407,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
array[arrayIndex] = doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvAttrField.Index(arrayIndex).Interface(),
Value: rvAttrField.Index(arrayIndex),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Tags: in.Tags,
@ -463,7 +463,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
for i := 0; i < length; i++ {
array[i] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: reflectValue.Index(i).Interface(),
Value: reflectValue.Index(i),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Tags: in.Tags,

View File

@ -125,6 +125,9 @@ func Interfaces(any interface{}) []interface{} {
return slice
default:
if originValueAndKind.OriginValue.IsZero() {
return []interface{}{}
}
return []interface{}{any}
}
}

View File

@ -223,55 +223,3 @@ func Test_Issue2381(t *testing.T) {
t.Assert(a1.Flag.String(), a2.Flag.String())
})
}
// https://github.com/gogf/gf/issues/2391
func Test_Issue2391(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type Inherit struct {
Ids []int
Ids2 []int64
Flag *gjson.Json
Title string
}
type Test1 struct {
Inherit
}
type Test2 struct {
Inherit
}
var (
a1 Test1
a2 Test2
)
a1 = Test1{
Inherit{
Ids: []int{1, 2, 3},
Ids2: []int64{4, 5, 6},
Flag: gjson.New("[\"1\", \"2\"]"),
Title: "测试",
},
}
err := gconv.Scan(a1, &a2)
t.AssertNil(err)
t.Assert(a1.Ids, a2.Ids)
t.Assert(a1.Ids2, a2.Ids2)
t.Assert(a1.Title, a2.Title)
t.Assert(a1.Flag.String(), a2.Flag.String())
})
}
// https://github.com/gogf/gf/issues/2395
func Test_Issue2395(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type Test struct {
Num int
}
var ()
obj := Test{Num: 0}
t.Assert(gconv.Interfaces(obj), []interface{}{obj})
})
}

View File

@ -298,7 +298,7 @@ func Test_Slice_Empty(t *testing.T) {
t.Assert(gconv.Strings(nil), nil)
})
gtest.C(t, func(t *gtest.T) {
t.AssertEQ(gconv.SliceAny(""), []interface{}{""})
t.AssertEQ(gconv.SliceAny(""), []interface{}{})
t.Assert(gconv.SliceAny(nil), nil)
})
}

View File

@ -2,5 +2,5 @@ package gf
const (
// VERSION is the current GoFrame version.
VERSION = "v2.3.1"
VERSION = "v2.3.0"
)