diff --git a/go.mod b/go.mod index 62d8f70..2c39cc2 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/fatih/color v1.17.0 github.com/go-git/go-git/v5 v5.12.0 github.com/google/uuid v1.6.0 - github.com/savioxavier/termlink v1.3.0 github.com/spf13/cobra v1.8.1 ) @@ -19,6 +18,7 @@ require ( github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-resty/resty/v2 v2.16.2 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -30,10 +30,10 @@ require ( github.com/skeema/knownhosts v1.2.2 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/tools v0.13.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index c86c7df..b47aa27 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgF github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= +github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -60,8 +62,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/savioxavier/termlink v1.3.0 h1:3Gl4FzQjUyiHzmoEDfmWEhgIwDiJY4poOQHP+k8ReA4= -github.com/savioxavier/termlink v1.3.0/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -83,8 +83,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= @@ -96,8 +96,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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -116,14 +116,14 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -131,7 +131,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/middleware.go b/middleware.go index 181e951..b18e04b 100644 --- a/middleware.go +++ b/middleware.go @@ -2,10 +2,11 @@ package nf import ( "fmt" - "github.com/loveuer/nf/nft/log" "os" "runtime/debug" "time" + + "github.com/loveuer/nf/nft/log" ) func NewRecover(enableStackTrace bool) HandlerFunc { @@ -18,7 +19,7 @@ func NewRecover(enableStackTrace bool) HandlerFunc { os.Stderr.WriteString(fmt.Sprintf("recovered from panic: %v\n", r)) } - //serveError(c, 500, []byte(fmt.Sprint(r))) + // serveError(c, 500, []byte(fmt.Sprint(r))) _ = c.Status(500).SendString(fmt.Sprint(r)) } }() @@ -28,7 +29,6 @@ func NewRecover(enableStackTrace bool) HandlerFunc { } func NewLogger() HandlerFunc { - return func(c *Ctx) error { var ( now = time.Now() diff --git a/nft/nfctl/cmd/check.go b/nft/nfctl/cmd/check.go deleted file mode 100644 index e06c7a4..0000000 --- a/nft/nfctl/cmd/check.go +++ /dev/null @@ -1,16 +0,0 @@ -package cmd - -import ( - "github.com/loveuer/nf/nft/nfctl/version" - "github.com/spf13/cobra" -) - -var ( - checkCmd = &cobra.Command{ - Use: "check", - Short: "nfctl new version check", - Run: func(cmd *cobra.Command, args []string) { - version.Check(true, true, 30) - }, - } -) diff --git a/nft/nfctl/cmd/cmd.go b/nft/nfctl/cmd/cmd.go deleted file mode 100644 index 68fb2dc..0000000 --- a/nft/nfctl/cmd/cmd.go +++ /dev/null @@ -1,30 +0,0 @@ -package cmd - -import ( - "github.com/loveuer/nf/nft/log" - "github.com/loveuer/nf/nft/nfctl/opt" - "github.com/spf13/cobra" -) - -var ( - Root = &cobra.Command{ - Use: "nfctl", - Short: "nfctl: easy start your nf backend work", - PersistentPreRun: func(cmd *cobra.Command, args []string) { - if opt.Debug == true { - log.SetLogLevel(log.LogLevelDebug) - } - }, - } -) - -func init() { - initNew() - Root.PersistentFlags().BoolVar(&opt.Debug, "debug", false, "debug mode") - - Root.AddCommand( - versionCmd, - checkCmd, - cmdNew, - ) -} diff --git a/nft/nfctl/cmd/new.go b/nft/nfctl/cmd/new.go deleted file mode 100644 index 506a716..0000000 --- a/nft/nfctl/cmd/new.go +++ /dev/null @@ -1,135 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "net/url" - "os" - "path" - - "github.com/loveuer/nf/nft/log" - "github.com/loveuer/nf/nft/nfctl/clone" - "github.com/loveuer/nf/nft/nfctl/opt" - "github.com/loveuer/nf/nft/nfctl/tp" - "github.com/loveuer/nf/nft/nfctl/version" - "github.com/spf13/cobra" -) - -var ( - cmdNew = &cobra.Command{ - Use: "new", - Short: "nfctl new: start new project", - Example: `nfctl new {project} -t ultone [recommend] -nfctl new {project} -t https://github.com/loveuer/ultone.git -nfctl new {project} --template http://username:token@my.gitlab.com/my-zone/my-repo.git -`, - SilenceUsage: true, - } - - template string - disableInit bool - - preTemplateMap = map[string]string{ - "ultone": "https://gitea.loveuer.com/loveuer/ultone.git", - } -) - -func initNew() { - cmdNew.Flags().StringVarP(&template, "template", "t", "ultone", "template name/url[example:ultone, https://github.com/xxx/yyy.git]") - cmdNew.Flags().BoolVar(&disableInit, "without-init", false, "don't run template init script") - - cmdNew.RunE = func(cmd *cobra.Command, args []string) error { - version.Check(true, false, 5) - - var ( - err error - urlIns *url.URL - pwd string - moduleName string - projectDir string - initBs []byte - renderBs []byte - scripts []tp.Cmd - ) - - if len(args) == 0 { - return fmt.Errorf("project name required") - } - - if pwd, err = os.Getwd(); err != nil { - return fmt.Errorf("get work dir err") - } - - moduleName = args[0] - projectDir = path.Join(pwd, path.Base(args[0])) - - if _, err = os.Stat(projectDir); !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("project folder already exist") - } - - if err = os.MkdirAll(projectDir, 0o750); err != nil { - return fmt.Errorf("create project dir err: %v", err) - } - - defer func() { - if err != nil { - _ = os.RemoveAll(projectDir) - } - }() - - if template == "" { - // todo no template new project - return fmt.Errorf("😥create basic project(without template) comming soon...") - } - - cloneUrl := template - if ptUrl, ok := preTemplateMap[cloneUrl]; ok { - cloneUrl = ptUrl - } - - if urlIns, err = url.Parse(cloneUrl); err != nil { - return fmt.Errorf("invalid clone url: %v", err) - } - - if err = clone.Clone(projectDir, urlIns); err != nil { - return fmt.Errorf("clone template err: %v", err) - } - - if initBs, err = os.ReadFile(path.Join(projectDir, ".nfctl")); err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil - } - - return fmt.Errorf("read nfctl script file err: %v", err) - } - - if renderBs, err = tp.RenderVar(initBs, map[string]any{ - "PROJECT_NAME": projectDir, - "MODULE_NAME": moduleName, - }); err != nil { - return fmt.Errorf("render template init script err: %v", err) - } - - if scripts, err = tp.ParseCmd(projectDir, renderBs); err != nil { - return fmt.Errorf("parse template init script err: %v", err) - } - - for _, script := range scripts { - if opt.Debug { - log.Debug("start script:\n%s\n", script.String()) - } - - if err = script.Execute(); err != nil { - return fmt.Errorf("execute template init script err: %v", err) - } - } - - if err = os.RemoveAll(path.Join(projectDir, ".git")); err != nil { - log.Warn("remove .git folder err: %s", err.Error()) - } - - log.Info("🎉 create project [%s] 成功!!!", args[0]) - - return nil - } -} diff --git a/nft/nfctl/cmd/version.go b/nft/nfctl/cmd/version.go deleted file mode 100644 index 3869263..0000000 --- a/nft/nfctl/cmd/version.go +++ /dev/null @@ -1,18 +0,0 @@ -package cmd - -import ( - "github.com/fatih/color" - "github.com/loveuer/nf/nft/nfctl/version" - "github.com/spf13/cobra" -) - -var ( - versionCmd = &cobra.Command{ - Use: "version", - Short: "print nfctl version and exit", - Run: func(cmd *cobra.Command, args []string) { - color.Cyan("nfctl - version: %s", version.Version) - version.Check(true, false, 5) - }, - } -) diff --git a/nft/nfctl/internal/cmd/cmd.new.go b/nft/nfctl/internal/cmd/cmd.new.go new file mode 100644 index 0000000..0636396 --- /dev/null +++ b/nft/nfctl/internal/cmd/cmd.new.go @@ -0,0 +1,153 @@ +package cmd + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "os" + "path" + "path/filepath" + "strings" + "text/template" + + "github.com/loveuer/nf/nft/log" + "github.com/loveuer/nf/nft/nfctl/internal/opt" + "github.com/loveuer/nf/nft/nfctl/pkg/loading" + "github.com/loveuer/nf/nft/tool" + "github.com/spf13/cobra" +) + +var newCmd = &cobra.Command{ + Use: "new", + Short: "new a nf project", + Example: "nfctl new -t ultone [options]", + RunE: doNew, + SilenceErrors: true, +} + +func initNew() *cobra.Command { + newCmd.Flags().StringVarP(&opt.Cfg.New.Template, "template", "t", "ultone", "template name/url[example:ultone, https://gitea.loveuer.com/loveuer/ultone.git]") + newCmd.Flags().BoolVar(&opt.Cfg.New.DisableInitScript, "disable-init-script", false, "disable init script(.nfctl)") + return newCmd +} + +func doNew(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("必须提供 project 名称") + } + + if strings.HasSuffix(args[0], "/") { + return errors.New("project 名称不能以 / 结尾") + } + + base := path.Base(args[0]) + if strings.HasPrefix(base, ".") { + return errors.New("project 名称不能以 . 开头") + } + + ch := make(chan *loading.Loading) + defer close(ch) + + go loading.Print(cmd.Context(), ch) + ch <- &loading.Loading{Content: "开始新建项目: " + args[0], Type: loading.TypeInfo} + + pwd, err := os.Getwd() + if err != nil { + ch <- &loading.Loading{Content: err.Error(), Type: loading.TypeError} + return err + } + + moduleName := args[0] + pwd = path.Join(filepath.ToSlash(pwd), base) + + log.Debug("cmd.new: new project, pwd = %s, name = %s, template = %s", pwd, moduleName, opt.Cfg.New.Template) + + ch <- &loading.Loading{Content: "开始下载模板: " + opt.Cfg.New.Template, Type: loading.TypeProcessing} + + repo := opt.Cfg.New.Template + if v, ok := opt.TemplateMap[repo]; ok { + repo = v + } + + if err = tool.Clone(pwd, repo); err != nil { + ch <- &loading.Loading{Content: err.Error(), Type: loading.TypeError} + return err + } + + ch <- &loading.Loading{Content: "下载模板完成: " + opt.Cfg.New.Template, Type: loading.TypeSuccess} + + if err = os.RemoveAll(path.Join(pwd, ".git")); err != nil { + ch <- &loading.Loading{Content: err.Error(), Type: loading.TypeWarning} + } + + ch <- &loading.Loading{Content: "开始初始化项目: " + args[0], Type: loading.TypeProcessing} + + if err = filepath.Walk(pwd, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + if strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "go.mod") { + var content []byte + if content, err = os.ReadFile(path); err != nil { + ch <- &loading.Loading{Content: "初始化文件失败: " + err.Error(), Type: loading.TypeWarning} + ch <- &loading.Loading{Content: "开始初始化项目: " + args[0], Type: loading.TypeProcessing} + return nil + } + + scanner := bufio.NewScanner(bytes.NewReader(content)) + replaced := make([]string, 0, 16) + for scanner.Scan() { + line := scanner.Text() + // 操作 go.mod 文件时, 忽略 toolchain 行, 以更好的兼容 go1.20 + if strings.HasSuffix(path, "go.mod") && strings.HasPrefix(line, "toolchain") { + continue + } + replaced = append(replaced, strings.ReplaceAll(line, opt.Cfg.New.Template, moduleName)) + } + if err = os.WriteFile(path, []byte(strings.Join(replaced, "\n")), 0o644); err != nil { + return err + } + } + + return nil + }); err != nil { + ch <- &loading.Loading{Content: "初始化文件失败: " + err.Error(), Type: loading.TypeWarning} + return err + } + + var ( + render *template.Template + rf *os.File + ) + + if render, err = template.New(base).Parse(opt.README); err != nil { + log.Debug("cmd.new: new text template err, err = %s", err.Error()) + ch <- &loading.Loading{Content: "生成 readme 失败", Type: loading.TypeWarning} + goto END + } + + if rf, err = os.OpenFile(path.Join(pwd, "readme.md"), os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o644); err != nil { + log.Debug("cmd.new: new readme file err, err = %s", err.Error()) + ch <- &loading.Loading{Content: "生成 readme 失败", Type: loading.TypeWarning} + goto END + } + defer rf.Close() + + if err = render.Execute(rf, map[string]any{ + "project_name": base, + }); err != nil { + log.Debug("cmd.new: template execute err, err = %s", err.Error()) + ch <- &loading.Loading{Content: "生成 readme 失败", Type: loading.TypeWarning} + } + +END: + ch <- &loading.Loading{Content: fmt.Sprintf("项目: %s 初始化成功", args[0]), Type: loading.TypeSuccess} + + return nil +} diff --git a/nft/nfctl/internal/cmd/cmd.root.go b/nft/nfctl/internal/cmd/cmd.root.go new file mode 100644 index 0000000..fc61e6c --- /dev/null +++ b/nft/nfctl/internal/cmd/cmd.root.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/loveuer/nf/nft/log" + "github.com/loveuer/nf/nft/nfctl/internal/opt" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "nfctl", + Short: "nfctl is a tool for quick start a nf projects", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if opt.Cfg.Debug { + log.SetLogLevel(log.LogLevelDebug) + } + + if !opt.Cfg.DisableUpdate { + doUpdate(cmd.Context()) + } + + return nil + }, + DisableSuggestions: true, + SilenceUsage: true, + + Run: func(cmd *cobra.Command, args []string) {}, +} + +func initRoot(cmds ...*cobra.Command) { + rootCmd.PersistentFlags().BoolVar(&opt.Cfg.Debug, "debug", false, "debug mode") + rootCmd.PersistentFlags().BoolVar(&opt.Cfg.DisableUpdate, "disable-update", false, "disable self update") + rootCmd.AddCommand(cmds...) +} diff --git a/nft/nfctl/internal/cmd/cmd.update.go b/nft/nfctl/internal/cmd/cmd.update.go new file mode 100644 index 0000000..8734585 --- /dev/null +++ b/nft/nfctl/internal/cmd/cmd.update.go @@ -0,0 +1,86 @@ +package cmd + +import ( + "context" + "crypto/tls" + "fmt" + "regexp" + "strings" + "time" + + resty "github.com/go-resty/resty/v2" + "github.com/loveuer/nf/nft/log" + "github.com/loveuer/nf/nft/nfctl/internal/opt" + "github.com/loveuer/nf/nft/nfctl/pkg/loading" + "github.com/spf13/cobra" +) + +var updateCmd = &cobra.Command{ + Use: "update", + Short: "update nfctl self", + RunE: func(cmd *cobra.Command, args []string) error { return nil }, +} + +func initUpdate() *cobra.Command { + return updateCmd +} + +func doUpdate(ctx context.Context) (err error) { + ch := make(chan *loading.Loading) + defer close(ch) + + go func() { + loading.Print(ctx, ch) + }() + + ch <- &loading.Loading{Content: "正在检查更新...", Type: loading.TypeProcessing} + tip := "❗ 请尝试手动更新: go install github.com/loveuer/nf/nft/nfctl@latest" + version := "" + + var rr *resty.Response + if rr, err = resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).R(). + SetContext(ctx). + Get(opt.VersionURL); err != nil { + err = fmt.Errorf("检查更新失败: %s\n%s", err.Error(), tip) + ch <- &loading.Loading{Content: err.Error(), Type: loading.TypeError} + return err + } + + log.Debug("cmd.update: url = %s, raw_response = %s", opt.VersionURL, rr.String()) + + if rr.StatusCode() != 200 { + err = fmt.Errorf("检查更新失败: %s\n%s", rr.Status(), tip) + ch <- &loading.Loading{Content: err.Error(), Type: loading.TypeError} + return + } + + reg := regexp.MustCompile(`const Version = "v\d{2}\.\d{2}\.\d{2}-r\d{1,2}"`) + for _, line := range strings.Split(rr.String(), "\n") { + if reg.MatchString(line) { + version = strings.TrimSpace(strings.TrimPrefix(line, "const Version = ")) + version = version[1 : len(version)-1] + break + } + } + + if version == "" { + err = fmt.Errorf("检查更新失败: 未找到版本信息\n%s", tip) + ch <- &loading.Loading{Content: err.Error(), Type: loading.TypeError} + return err + } + + log.Debug("cmd.update: find version = %s, now_version = %s", version, opt.Version) + + if version <= opt.Version { + ch <- &loading.Loading{Content: fmt.Sprintf("已是最新版本: %s", opt.Version), Type: loading.TypeSuccess} + return nil + } + + ch <- &loading.Loading{Content: fmt.Sprintf("发现新版本: %s", version), Type: loading.TypeInfo} + + ch <- &loading.Loading{Content: fmt.Sprintf("正在更新到 %s ...", version)} + + time.Sleep(2 * time.Second) + ch <- &loading.Loading{Content: "暂时无法自动更新, 请尝试手动更新: go install github.com/loveuer/nf/nft/nfctl@latest", Type: loading.TypeWarning} + return nil +} diff --git a/nft/nfctl/internal/cmd/init.go b/nft/nfctl/internal/cmd/init.go new file mode 100644 index 0000000..bd6ca9f --- /dev/null +++ b/nft/nfctl/internal/cmd/init.go @@ -0,0 +1,25 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "time" +) + +func Init() { + initRoot( + initUpdate(), + initNew(), + ) +} + +func Run(ctx context.Context) { + if err := rootCmd.ExecuteContext(ctx); err != nil { + fmt.Printf("❌ %s\n", err.Error()) + time.Sleep(300 * time.Millisecond) + os.Exit(1) + } + + time.Sleep(300 * time.Millisecond) +} diff --git a/nft/nfctl/internal/opt/opt.go b/nft/nfctl/internal/opt/opt.go new file mode 100644 index 0000000..d5e2d4c --- /dev/null +++ b/nft/nfctl/internal/opt/opt.go @@ -0,0 +1,20 @@ +package opt + +type _new struct { + Template string + DisableInitScript bool +} + +type config struct { + Debug bool + DisableUpdate bool + New _new +} + +var Cfg = &config{} + +var TemplateMap = map[string]string{ + "ultone": "https://gitea.loveuer.com/loveuer/ultone.git", +} + +const README = "# {{ .project_name }}\n\n### 启动\n- `go run . --help`\n- `go run .`\n\n### 构建\n- `go build -ldflags '-s -w' -o dist/{{ .project_name}}_app .`\n- `docker build -t -f Dockerfile .`" diff --git a/nft/nfctl/internal/opt/version.go b/nft/nfctl/internal/opt/version.go new file mode 100644 index 0000000..518247e --- /dev/null +++ b/nft/nfctl/internal/opt/version.go @@ -0,0 +1,7 @@ +package opt + +const Version = "v24.12.27-r01" + +// const VersionURL = "https://github.com/loveuer/nf/nft/nfctl/internal/opt/version.go" + +const VersionURL = "https://raw.githubusercontent.com/loveuer/nf/refs/heads/master/nft/nfctl/internal/opt/version.go" diff --git a/nft/nfctl/main.go b/nft/nfctl/main.go index 656ddd1..dd7d542 100644 --- a/nft/nfctl/main.go +++ b/nft/nfctl/main.go @@ -4,13 +4,19 @@ import ( "context" "os/signal" "syscall" + "time" - "github.com/loveuer/nf/nft/nfctl/cmd" + "github.com/loveuer/nf/nft/nfctl/internal/cmd" ) +func init() { + time.Local = time.FixedZone("CST", 8*3600) + cmd.Init() +} + func main() { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) defer cancel() - _ = cmd.Root.ExecuteContext(ctx) + cmd.Run(ctx) } diff --git a/nft/nfctl/opt/var.go b/nft/nfctl/opt/var.go deleted file mode 100644 index a3e7ac4..0000000 --- a/nft/nfctl/opt/var.go +++ /dev/null @@ -1,5 +0,0 @@ -package opt - -var ( - Debug bool -) diff --git a/nft/nfctl/pkg/loading/loading.go b/nft/nfctl/pkg/loading/loading.go new file mode 100644 index 0000000..0557353 --- /dev/null +++ b/nft/nfctl/pkg/loading/loading.go @@ -0,0 +1,81 @@ +package loading + +import ( + "context" + "fmt" + "time" +) + +type Type int + +const ( + TypeProcessing Type = iota + TypeInfo + TypeSuccess + TypeWarning + TypeError +) + +func (t Type) Symbol() string { + switch t { + case TypeSuccess: + return "✔️ " + case TypeWarning: + return "❗ " + case TypeError: + return "❌ " + case TypeInfo: + return "❕ " + default: + return "" + } +} + +type Loading struct { + Content string + Type Type +} + +func Print(ctx context.Context, ch <-chan *Loading) { + var ( + ok bool + frames = []string{"|", "/", "-", "\\"} + start = time.Now() + loading = &Loading{} + ) + + for { + for _, frame := range frames { + select { + case <-ctx.Done(): + return + case loading, ok = <-ch: + if !ok || loading == nil { + return + } + + if loading.Content == "" { + time.Sleep(100 * time.Millisecond) + continue + } + + switch loading.Type { + case TypeInfo, + TypeSuccess, + TypeWarning, + TypeError: + // Clear the loading animation + fmt.Printf("\r\033[K") + fmt.Printf("%s%s\n", loading.Type.Symbol(), loading.Content) + loading.Content = "" + } + default: + elapsed := time.Since(start).Seconds() + if loading.Content != "" { + fmt.Printf("\r\033[K%s %s (%.2fs)", frame, loading.Content, elapsed) + } + time.Sleep(100 * time.Millisecond) + } + } + } +} diff --git a/nft/nfctl/pkg/loading/loading_test.go b/nft/nfctl/pkg/loading/loading_test.go new file mode 100644 index 0000000..2c036a5 --- /dev/null +++ b/nft/nfctl/pkg/loading/loading_test.go @@ -0,0 +1,27 @@ +package loading + +import ( + "context" + "testing" + "time" +) + +func TestLoadingPrint(t *testing.T) { + ch := make(chan *Loading) + + Print(context.TODO(), ch) + ch <- &Loading{Content: "处理中(1)..."} + + time.Sleep(3 * time.Second) + + ch <- &Loading{Content: "处理完成(1)", Type: TypeSuccess} + + ch <- &Loading{Content: "处理中(2)..."} + + time.Sleep(4 * time.Second) + + ch <- &Loading{Content: "处理失败(2)", Type: TypeError} + + time.Sleep(2 * time.Second) + close(ch) +} diff --git a/nft/nfctl/readme.md b/nft/nfctl/readme.md deleted file mode 100644 index e39ff0d..0000000 --- a/nft/nfctl/readme.md +++ /dev/null @@ -1,23 +0,0 @@ -# nfctl - -# 通过 nfctl 快速开启后台项目 - -### 1. Installation - -- ① `go install github.com/loveuer/nf/nft/nfctl@latest` -- ② download prebuild binary [release](https://github.com/loveuer/nf/releases) - -### 2. Usage - -- `nfctl new {project}` -- `nfctl new project -t ultone` -- `nfctl new project -t https://github.com/xxx/yyy.git` -- `nfctl new project --template https://gitcode/loveuer/ultone.git` -- `nfctl new project --template https://{username}:{password/token}@my.gitlab.com/name/project.git` - -### 3. nfctl init script - -- `为方便模版的初始化, 可以采用 nfctl init script, 当 nfctl new project -t xxx 从模版开始项目时会自动执行` -- `具体的编写规则如下:` - * [init 脚本规则](https://github.com/loveuer/nf/nft/nfctl/script.md) 或者 - * [国内](https://gitcode.com/loveuer/nf/nft/nfctl/script.md) \ No newline at end of file diff --git a/nft/nfctl/tp/parse.go b/nft/nfctl/tp/parse.go deleted file mode 100644 index 1536357..0000000 --- a/nft/nfctl/tp/parse.go +++ /dev/null @@ -1,80 +0,0 @@ -package tp - -import ( - "bufio" - "bytes" - "fmt" - "strings" -) - -func ParseCmd(pwd string, content []byte) ([]Cmd, error) { - var ( - err error - cmds = make([]Cmd, 0) - start = false - ) - - scanner := bufio.NewScanner(bytes.NewReader(content)) - scanner.Buffer(make([]byte, 1024), 1024*1024*10) - - record := make([]string, 0) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if len(line) == 0 { - continue - } - - if !start && strings.HasPrefix(line, "#") { - continue - } - - if strings.HasPrefix(line, "!") { - if start { - return nil, fmt.Errorf("invalid content: unEOF cmd block found") - } - - start = true - record = append(record, line) - continue - } - - if strings.HasPrefix(line, "EOF") { - start = false - if len(record) == 0 { - continue - } - - var cmd Cmd - if cmd, err = ParseBlock(pwd, record); err != nil { - return nil, err - } - - cmds = append(cmds, cmd) - record = record[:0] - continue - } - - if start { - record = append(record, line) - } - } - - if err = scanner.Err(); err != nil { - return nil, err - } - - return cmds, err -} - -func ParseBlock(pwd string, lines []string) (Cmd, error) { - switch lines[0] { - case "!replace content": - return newReplaceContent(pwd, lines[1:]) - case "!replace name": - return newReplaceName(pwd, lines[1:]) - case "!generate": - return newGenerate(pwd, lines[1:]) - } - - return nil, fmt.Errorf("invalid cmd block: unknown type: %s", lines[0]) -} diff --git a/nft/nfctl/tp/parse_test.go b/nft/nfctl/tp/parse_test.go deleted file mode 100644 index 7843f17..0000000 --- a/nft/nfctl/tp/parse_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package tp - -import ( - "github.com/loveuer/nf/nft/log" - "os" - "testing" -) - -func TestParseInitFile(t *testing.T) { - bs, err := os.ReadFile("xtest") - if err != nil { - log.Fatal(err.Error()) - } - - data := map[string]any{ - "PROJECT_NAME": "myproject", - } - - result, err := RenderVar(bs, data) - if err != nil { - log.Fatal(err.Error()) - } - - pwd, _ := os.Getwd() - - cmds, err := ParseCmd(pwd, result) - if err != nil { - log.Fatal(err.Error()) - } - - for _, item := range cmds { - log.Info("one cmd => %s\n\n", item.String()) - if err = item.Execute(); err != nil { - log.Fatal(err.Error()) - } - } -} diff --git a/nft/nfctl/tp/render.go b/nft/nfctl/tp/render.go deleted file mode 100644 index b8b7059..0000000 --- a/nft/nfctl/tp/render.go +++ /dev/null @@ -1,29 +0,0 @@ -package tp - -import ( - "bytes" - "text/template" -) - -var ( - _t *template.Template -) - -func init() { - _t = template.New("tp") -} - -func RenderVar(t []byte, data map[string]any) ([]byte, error) { - tr, err := _t.Parse(string(t)) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - - if err = tr.Execute(&buf, data); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} diff --git a/nft/nfctl/tp/tp.go b/nft/nfctl/tp/tp.go deleted file mode 100644 index 433a460..0000000 --- a/nft/nfctl/tp/tp.go +++ /dev/null @@ -1,316 +0,0 @@ -package tp - -import ( - "bufio" - "bytes" - "fmt" - "github.com/loveuer/nf/nft/log" - "io/fs" - "os" - "path" - "path/filepath" - "regexp" - "strings" -) - -type Cmd interface { - String() string - Execute() error -} - -var ( - _ Cmd = (*Generate)(nil) - _ Cmd = (*ReplaceContent)(nil) - _ Cmd = (*ReplaceName)(nil) -) - -type Generate struct { - pwd string - filename string - content []string -} - -func (t *Generate) String() string { - return fmt.Sprintf("!generate\n%s\n%s\n", t.filename, strings.Join(t.content, "\n")) -} - -func (t *Generate) Execute() error { - var ( - err error - location = t.filename - input *os.File - ) - - log.Debug("[Generate] generate[%s]", t.filename) - - if !path.IsAbs(t.filename) { - location = path.Join(t.pwd, t.filename) - } - - if err = os.MkdirAll(path.Dir(location), 0644); err != nil { - return err - } - - if !strings.HasSuffix(location, "/") { - if input, err = os.OpenFile(location, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0744); err != nil { - return err - } - - if len(t.content) > 0 { - content := strings.Join(t.content, "\n") - _, err = input.WriteString(content) - return err - } - } - - return nil -} - -func newGenerate(pwd string, lines []string) (*Generate, error) { - if len(lines) == 0 { - return nil, fmt.Errorf("generate cmd require file/folder name") - } - - return &Generate{ - pwd: pwd, - filename: lines[0], - content: lines[1:], - }, nil -} - -type replaceNameMatchType int - -const ( - replaceNameMatchReg replaceNameMatchType = iota + 1 - replaceNameMatchExact - replaceNameMatchPrefix - replaceNameMatchSuffix -) - -func (rm replaceNameMatchType) Label() string { - switch rm { - case replaceNameMatchReg: - return "reg" - case replaceNameMatchExact: - return "exact" - case replaceNameMatchPrefix: - return "prefix" - case replaceNameMatchSuffix: - return "suffix" - } - - log.Panic("unknown replace match type: %v", rm) - return "" -} - -type ReplaceContent struct { - pwd string - name string - content string - - targetName string - matchType replaceNameMatchType - fromContent string - targetEmpty bool - targetContent string -} - -func (t *ReplaceContent) String() string { - return fmt.Sprintf("!replace content\n%s\n%s\n", t.name, t.content) -} - -func (t *ReplaceContent) Execute() error { - var ( - fn filepath.WalkFunc - - handler = func(location string) error { - bs, err := os.ReadFile(location) - if err != nil { - return err - } - - log.Debug("[ReplaceContent] handle[%s] replace [%s] => [%s]", location, t.fromContent, t.targetContent) - newbs, err := t.executeFile(bs) - if err != nil { - return err - } - - return os.WriteFile(location, newbs, 0644) - } - ) - - switch t.matchType { - case replaceNameMatchExact: - fn = func(location string, info fs.FileInfo, err error) error { - if location == path.Join(t.pwd, t.targetName) { - log.Debug("[ReplaceContent] exact match: %s", location) - return handler(location) - } - - return nil - } - case replaceNameMatchPrefix: - fn = func(location string, info fs.FileInfo, err error) error { - if strings.HasPrefix(path.Base(location), t.targetName) { - log.Debug("[ReplaceContent] prefix match: %s", location) - return handler(location) - } - - return nil - } - case replaceNameMatchSuffix: - fn = func(location string, info fs.FileInfo, err error) error { - if strings.HasSuffix(location, t.targetName) { - log.Debug("[ReplaceContent] suffix match: %s", location) - return handler(location) - } - - return nil - } - case replaceNameMatchReg: - fn = func(location string, info fs.FileInfo, err error) error { - if match, err := regexp.MatchString(t.targetName, location); err == nil && match { - log.Debug("[ReplaceContent] reg match: %s", location) - return handler(location) - } - - return nil - } - } - - return filepath.Walk(t.pwd, fn) -} - -func (t *ReplaceContent) executeFile(raw []byte) ([]byte, error) { - scanner := bufio.NewScanner(bytes.NewReader(raw)) - scanner.Buffer(make([]byte, 1024), 1024*1024) - - lines := make([]string, 0) - for scanner.Scan() { - line := scanner.Text() - lines = append( - lines, - strings.ReplaceAll(line, t.fromContent, t.targetContent), - ) - } - - return []byte(strings.Join(lines, "\n")), nil -} - -func newReplaceContent(pwd string, lines []string) (*ReplaceContent, error) { - if len(lines) != 2 { - return nil, fmt.Errorf("invalid replace_content cmd: required 2 lines params") - } - - var ( - name = lines[0] - content = lines[1] - matchType replaceNameMatchType - ) - - names := strings.SplitN(name, " ", 2) - if len(names) != 2 { - return nil, fmt.Errorf("invalid replace_content cmd: name line, required: [reg/exact/prefix/shuffix] {filename}") - } - - switch names[0] { - case "exact": - matchType = replaceNameMatchExact - case "reg": - matchType = replaceNameMatchReg - case "prefix": - matchType = replaceNameMatchPrefix - case "suffix": - matchType = replaceNameMatchSuffix - default: - return nil, fmt.Errorf("invalid replace_content name match type, example: [reg *.go] [exact go.mod]") - } - - var ( - targetName string = names[1] - targetEmpty = false - targetContent string - ) - contents := strings.SplitN(content, "=>", 2) - fromContent := strings.TrimSpace(contents[0]) - if len(contents) == 1 { - targetEmpty = true - } else { - if targetContent = strings.TrimSpace(contents[1]); targetContent == "" || targetContent == `""` || targetContent == `''` { - targetEmpty = true - } - } - - return &ReplaceContent{ - pwd: pwd, - name: name, - content: content, - - matchType: matchType, - targetName: targetName, - fromContent: fromContent, - targetEmpty: targetEmpty, - targetContent: targetContent, - }, nil -} - -type ReplaceName struct { - pwd string - line string - - targetEmpty bool - fromContent string - targetContent string -} - -func (t *ReplaceName) String() string { - return fmt.Sprintf("!replace name\n%s\n", t.line) -} - -func (t *ReplaceName) Execute() error { - fullpath := path.Join(t.pwd, t.fromContent) - if t.targetEmpty { - return os.RemoveAll(fullpath) - } - - ftpath := path.Join(t.pwd, t.targetContent) - return os.Rename(fullpath, ftpath) -} - -func newReplaceName(pwd string, lines []string) (*ReplaceName, error) { - if len(lines) != 1 { - return nil, fmt.Errorf("replace_name need one line param, for example: mian.go => main.go") - } - - var ( - content = lines[0] - targetEmpty = false - fromContent string - targetContent string - ) - - contents := strings.SplitN(content, "=>", 2) - fromContent = strings.TrimSpace(contents[0]) - if len(contents) == 1 { - targetEmpty = true - } else { - if targetContent = strings.TrimSpace(contents[1]); targetContent == "" || targetContent == `""` || targetContent == `''` { - targetEmpty = true - } - } - - if !targetEmpty { - if (strings.HasPrefix(targetContent, `"`) && strings.HasSuffix(targetContent, `"`)) || (strings.HasPrefix(targetContent, `'`) && strings.HasSuffix(targetContent, `'`)) { - targetContent = targetContent[1 : len(targetContent)-1] - } - } - - return &ReplaceName{ - pwd: pwd, - line: content, - - targetEmpty: targetEmpty, - fromContent: fromContent, - targetContent: targetContent, - }, nil -} diff --git a/nft/nfctl/version/var.go b/nft/nfctl/version/var.go deleted file mode 100644 index ce234af..0000000 --- a/nft/nfctl/version/var.go +++ /dev/null @@ -1,3 +0,0 @@ -package version - -const Version = "v24.07.14-r3" diff --git a/nft/nfctl/version/version.go b/nft/nfctl/version/version.go deleted file mode 100644 index ac2e741..0000000 --- a/nft/nfctl/version/version.go +++ /dev/null @@ -1,95 +0,0 @@ -package version - -import ( - "crypto/tls" - "fmt" - "github.com/fatih/color" - "github.com/loveuer/nf/nft/log" - "github.com/savioxavier/termlink" - "io" - "net/http" - "strings" - "time" -) - -var ( - uri = "https://raw.gitcode.com/loveuer/nf/raw/master/nft/nfctl/version/var.go" - prefix = "const Version = " -) - -func UpgradePrint(newVersion string) { - fmt.Printf(`+----------------------------------------------------------------------+ -| 🎉 🎉 🎉 %s 🎉 🎉 🎉 | -| %s | -| Or Download by: | -| %s | -| %s | -+----------------------------------------------------------------------+ -`, - color.GreenString("New Version Found: %s", newVersion), - color.CyanString("Upgrade it with: [go install github.com/loveuer/nf/nft/nfctl@master]"), - color.CyanString(termlink.Link("Releases", "https://github.com/loveuer/nf/releases")), - color.CyanString(termlink.Link("Releases", "https://gitcode.com/loveuer/nf/releases")), - ) -} - -func Check(printUpgradable bool, printNoNeedUpgrade bool, timeouts ...int) string { - var ( - v string - ) - - defer func() { - if printUpgradable { - if v > Version { - UpgradePrint(v) - } - } - - if printNoNeedUpgrade { - if v == Version { - color.Cyan("Your Version: %s is Newest", Version) - } - } - }() - - timeout := time.Duration(30) * time.Second - if len(timeouts) > 0 && timeouts[0] > 0 { - timeout = time.Duration(timeouts[0]) * time.Second - } - - req, _ := http.NewRequest(http.MethodGet, uri, nil) - resp, err := (&http.Client{ - Timeout: timeout, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - }, - }).Do(req) - if err != nil { - log.Debug("[Check] http get[%s] err: %v", uri, err.Error()) - return "" - } - defer resp.Body.Close() - - content, err := io.ReadAll(resp.Body) - if err != nil { - log.Debug("[Check] http read all body err: %v", err) - } - - log.Debug("[Check] http get[%s] body:\n%s", uri, string(content)) - - for _, line := range strings.Split(string(content), "\n") { - log.Debug("[Check] version.go line: %s", line) - if strings.HasPrefix(line, prefix) { - may := strings.TrimPrefix(line, prefix) - if len(may) > 2 { - v = may[1 : len(may)-1] - } - - return v - } - } - - return "" -} diff --git a/nft/nfctl/version/version_test.go b/nft/nfctl/version/version_test.go deleted file mode 100644 index f7552ef..0000000 --- a/nft/nfctl/version/version_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package version - -import ( - "github.com/loveuer/nf/nft/log" - "testing" -) - -func TestUpgradePrint(t *testing.T) { - log.SetLogLevel(log.LogLevelDebug) - UpgradePrint("v24.07.14-r5") -} - -func TestCheck(t *testing.T) { - log.SetLogLevel(log.LogLevelDebug) - v := Check(true, true, 1) - t.Logf("got version: %s", v) -} diff --git a/nft/nfctl/clone/clone.go b/nft/tool/clone.go similarity index 71% rename from nft/nfctl/clone/clone.go rename to nft/tool/clone.go index c975945..a421d26 100644 --- a/nft/nfctl/clone/clone.go +++ b/nft/tool/clone.go @@ -1,16 +1,21 @@ -package clone +package tool import ( "fmt" - "github.com/go-git/go-git/v5" - _ "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/loveuer/nf/nft/log" "net/url" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/transport/http" ) -func Clone(pwd string, ins *url.URL) error { +func Clone(projectDir string, repoURL string) error { + ins, err := url.Parse(repoURL) + if err != nil { + return err + } + uri := fmt.Sprintf("%s://%s%s", ins.Scheme, ins.Host, ins.Path) + opt := &git.CloneOptions{ URL: uri, Depth: 1, @@ -26,8 +31,7 @@ func Clone(pwd string, ins *url.URL) error { } } - log.Info("start clone %s", uri) - _, err := git.PlainClone(pwd, false, opt) + _, err = git.PlainClone(projectDir, false, opt) if err != nil { return err } diff --git a/nft/tool/ctx.go b/nft/tool/ctx.go new file mode 100644 index 0000000..02dea0e --- /dev/null +++ b/nft/tool/ctx.go @@ -0,0 +1,34 @@ +package tool + +import ( + "context" + "time" +) + +func Timeout(seconds ...int) (ctx context.Context) { + var duration time.Duration + + if len(seconds) > 0 && seconds[0] > 0 { + duration = time.Duration(seconds[0]) * time.Second + } else { + duration = time.Duration(30) * time.Second + } + + ctx, _ = context.WithTimeout(context.Background(), duration) + + return +} + +func TimeoutCtx(ctx context.Context, seconds ...int) context.Context { + var duration time.Duration + + if len(seconds) > 0 && seconds[0] > 0 { + duration = time.Duration(seconds[0]) * time.Second + } else { + duration = time.Duration(30) * time.Second + } + + nctx, _ := context.WithTimeout(ctx, duration) + + return nctx +}