feat: fun new 项目脚手架——一条命令生成可运行 hello world
- fun new <名字|模块路径>:go.mod + HelloSvc.Say 示例 + README + .gitignore - -local 写 replace 指向本地 fun 源码(离线/未发布版本也能跑) - -port 指定示例端口;flag 手动解析可放任意位置 - 自动 go mod tidy,失败给出手动提示 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
+154
@@ -0,0 +1,154 @@
|
||||
package main
|
||||
|
||||
// fun new:脚手架——一条命令生成可运行的 fun 项目骨架
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var reModuleName = regexp.MustCompile(`^[a-zA-Z0-9][\w.\-/]*$`)
|
||||
|
||||
func cmdNew(args []string) {
|
||||
// 手动解析:允许 flag 放任意位置(go flag 遇位置参数即停,不友好)
|
||||
port, local, module := 18080, "", ""
|
||||
for i := 0; i < len(args); i++ {
|
||||
a := args[i]
|
||||
if v, ok := strings.CutPrefix(a, "-port="); ok {
|
||||
fmt.Sscanf(v, "%d", &port)
|
||||
continue
|
||||
}
|
||||
if v, ok := strings.CutPrefix(a, "-local="); ok {
|
||||
local = v
|
||||
continue
|
||||
}
|
||||
if (a == "-port" || a == "-local") && i+1 < len(args) {
|
||||
i++
|
||||
if a == "-port" {
|
||||
fmt.Sscanf(args[i], "%d", &port)
|
||||
} else {
|
||||
local = args[i]
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(a, "-") {
|
||||
fmt.Fprintln(os.Stderr, "fun: 未知参数:", a)
|
||||
os.Exit(2)
|
||||
}
|
||||
if module != "" {
|
||||
fmt.Fprintln(os.Stderr, "用法: fun new <项目名|模块路径> [-port 端口] [-local fun源码路径]")
|
||||
os.Exit(2)
|
||||
}
|
||||
module = a
|
||||
}
|
||||
if module == "" {
|
||||
fmt.Fprintln(os.Stderr, "用法: fun new <项目名|模块路径> [-port 端口] [-local fun源码路径]")
|
||||
fmt.Fprintln(os.Stderr, "示例: fun new myapp fun new github.com/me/myapp")
|
||||
os.Exit(2)
|
||||
}
|
||||
if !reModuleName.MatchString(module) || strings.Contains(module, "..") {
|
||||
fmt.Fprintln(os.Stderr, "fun: 非法项目名:", module)
|
||||
os.Exit(1)
|
||||
}
|
||||
// 模块路径形如 github.com/me/app 时,目录取最后一段
|
||||
dir := module
|
||||
if i := strings.LastIndex(module, "/"); i >= 0 {
|
||||
dir = module[i+1:]
|
||||
}
|
||||
if _, err := os.Stat(dir); err == nil {
|
||||
fmt.Fprintf(os.Stderr, "fun: 目录 %s 已存在\n", dir)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "fun: 创建目录失败:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
gomod := fmt.Sprintf("module %s\n\ngo 1.22\n\nrequire github.com/cyi-cc/fun v1.4.1\n", module)
|
||||
if local != "" {
|
||||
abs, err := filepath.Abs(local)
|
||||
if err != nil {
|
||||
abs = local
|
||||
}
|
||||
gomod += fmt.Sprintf("\nreplace github.com/cyi-cc/fun => %s\n", filepath.ToSlash(abs))
|
||||
}
|
||||
files := map[string]string{
|
||||
"go.mod": gomod,
|
||||
"main.go": fmt.Sprintf(`package main
|
||||
|
||||
import "github.com/cyi-cc/fun"
|
||||
|
||||
type HelloDto struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type HelloSvc struct {
|
||||
fun.Ctx
|
||||
}
|
||||
|
||||
// hello world:最小可调用的服务方法
|
||||
func (s *HelloSvc) Say(dto HelloDto) (string, error) {
|
||||
return "hello, " + dto.Name + "!", nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := fun.New()
|
||||
f.BindService(&HelloSvc{})
|
||||
f.Start(%d)
|
||||
}
|
||||
`, port),
|
||||
".gitignore": "/gen\n/log\nfun-docs-edits.json\n",
|
||||
"README.md": fmt.Sprintf(`# %s
|
||||
|
||||
fun 框架项目。[文档](https://fungo.ink)
|
||||
|
||||
## 启动
|
||||
|
||||
`+"```bash"+`
|
||||
go run .
|
||||
`+"```"+`
|
||||
|
||||
## 工具链(fun-cli)
|
||||
|
||||
`+"```bash"+`
|
||||
fun docs # API 文档站 + 在线调测(热更新)
|
||||
fun run # 热更新开发循环
|
||||
fun gen ts # 生成 TypeScript 客户端
|
||||
`+"```"+`
|
||||
`, module),
|
||||
}
|
||||
for name, body := range files {
|
||||
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fun: 写入 %s 失败: %v\n", name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
tidy := exec.Command("go", "mod", "tidy")
|
||||
tidy.Dir = dir
|
||||
tidy.Stdout, tidy.Stderr = os.Stderr, os.Stderr
|
||||
tidied := tidy.Run() == nil
|
||||
|
||||
next := "go run ."
|
||||
if !tidied {
|
||||
next = "go mod tidy && go run ."
|
||||
}
|
||||
fmt.Printf(`
|
||||
已创建 %s(module %s)
|
||||
─────────────────────────────────────────
|
||||
%s/go.mod 依赖 github.com/cyi-cc/fun
|
||||
%s/main.go HelloSvc.Say 示例服务
|
||||
─────────────────────────────────────────
|
||||
下一步:
|
||||
cd %s
|
||||
%s # 启动服务(:%d)
|
||||
fun docs # 文档站在线调测
|
||||
`, dir, module, dir, dir, dir, next, port)
|
||||
if !tidied {
|
||||
fmt.Println("\n ⚠ go mod tidy 失败(可能离线),进入目录后手动执行一次即可")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user