nf/nft/nfctl/internal/cmd/cmd.new.go

150 lines
4.0 KiB
Go
Raw Normal View History

2024-12-26 19:40:39 +08:00
package cmd
import (
"bufio"
"bytes"
2024-12-26 22:55:13 -08:00
"context"
2024-12-26 19:40:39 +08:00
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"text/template"
2024-12-26 22:55:13 -08:00
"github.com/loveuer/nf/nft/loading"
2024-12-26 19:40:39 +08:00
"github.com/loveuer/nf/nft/log"
"github.com/loveuer/nf/nft/nfctl/internal/opt"
"github.com/loveuer/nf/nft/tool"
"github.com/spf13/cobra"
)
var newCmd = &cobra.Command{
Use: "new",
Short: "new a nf project",
Example: "nfctl new <project> -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
}
2024-12-26 22:55:13 -08:00
func doNew(cmd *cobra.Command, args []string) (err error) {
2024-12-26 19:40:39 +08:00
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 名称不能以 . 开头")
}
2024-12-26 22:55:13 -08:00
return loading.Do(cmd.Context(), func(ctx context.Context, print func(msg string, types ...loading.Type)) error {
print("开始新建项目: "+args[0], loading.TypeInfo)
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
pwd, err := os.Getwd()
if err != nil {
return err
}
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
moduleName := args[0]
pwd = path.Join(filepath.ToSlash(pwd), base)
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
log.Debug("cmd.new: new project, pwd = %s, name = %s, template = %s", pwd, moduleName, opt.Cfg.New.Template)
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
print("开始下载模板: "+opt.Cfg.New.Template, loading.TypeProcessing)
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
repo := opt.Cfg.New.Template
if v, ok := opt.TemplateMap[repo]; ok {
repo = v
}
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
if err = tool.Clone(pwd, repo); err != nil {
2024-12-26 19:40:39 +08:00
return err
}
2024-12-26 22:55:13 -08:00
print("下载模板完成: "+opt.Cfg.New.Template, loading.TypeSuccess)
if err = os.RemoveAll(path.Join(pwd, ".git")); err != nil {
print(err.Error(), loading.TypeWarning)
2024-12-26 19:40:39 +08:00
}
2024-12-26 22:55:13 -08:00
print("开始初始化项目: "+args[0], loading.TypeProcessing)
if err = filepath.Walk(pwd, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
2024-12-26 19:40:39 +08:00
return nil
}
2024-12-26 22:55:13 -08:00
if strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "go.mod") {
var content []byte
if content, err = os.ReadFile(path); err != nil {
print("初始化文件失败: "+err.Error(), loading.TypeWarning)
print("开始初始化项目: "+args[0], 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
2024-12-26 19:40:39 +08:00
}
}
2024-12-26 22:55:13 -08:00
return nil
}); err != nil {
return err
}
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
var (
render *template.Template
rf *os.File
)
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
if render, err = template.New(base).Parse(opt.README); err != nil {
log.Debug("cmd.new: new text template err, err = %s", err.Error())
print("生成 readme 失败", loading.TypeWarning)
goto END
}
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
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())
print("生成 readme 失败", loading.TypeWarning)
goto END
}
defer rf.Close()
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
if err = render.Execute(rf, map[string]any{
"project_name": base,
}); err != nil {
log.Debug("cmd.new: template execute err, err = %s", err.Error())
print("生成 readme 失败", loading.TypeWarning)
}
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
END:
print(fmt.Sprintf("项目: %s 初始化成功", args[0]), loading.TypeSuccess)
2024-12-26 19:40:39 +08:00
2024-12-26 22:55:13 -08:00
return nil
})
2024-12-26 19:40:39 +08:00
}