Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
6add048755 | |||
ee3309164b | |||
d2bf98a710 | |||
576ef0d31e | |||
f71ab54a56 | |||
|
8235ef91cd | ||
|
bef276638f |
30
README.md
30
README.md
@ -1,3 +1,29 @@
|
|||||||
# hertz_scaffold
|
#### 简介
|
||||||
|
以golib.gaore.com/GaoreGo/hertz_demo为模板的hertz框架脚手架
|
||||||
|
|
||||||
|
#### 安装:
|
||||||
|
go install golib.gaore.com/GaoreGo/hertz_scaffold@latest
|
||||||
|
|
||||||
|
|
||||||
|
#### 命令行参数:
|
||||||
|
create 创建一个新项目
|
||||||
|
help Help about any command
|
||||||
|
update 更新项目
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
hertz_scaffold create [flags]
|
||||||
|
hertz_scaffold update [flags]
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for create
|
||||||
|
-p, --project string 项目名称
|
||||||
|
-t, --tag string 指定tag (不指定默认拉取master)
|
||||||
|
|
||||||
|
|
||||||
|
#### 创建项目:
|
||||||
|
hertz_scaffold create -p hertz.gaore.com
|
||||||
|
|
||||||
|
#### 更新项目(注意,更新项目会覆盖本地项目,请谨慎操作):
|
||||||
|
hertz_scaffold update -p hertz.gaore.com -t 1.0.0
|
||||||
|
|
||||||
|
|
||||||
hertz 脚 手架
|
|
40
cmd/cmd.go
Normal file
40
cmd/cmd.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
template = "git@golib-ssh.gaore.com:GaoreGo/hertz_demo.git"
|
||||||
|
project string
|
||||||
|
branch string
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "hertz_scaffold",
|
||||||
|
Short: "hertz框架脚手架",
|
||||||
|
Long: `以golib.gaore.com/GaoreGo/hertz_demo为模板的脚手架`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
cmd.Help()
|
||||||
|
} else {
|
||||||
|
fmt.Println("Invalid command")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute 执行命令
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(createCmd)
|
||||||
|
rootCmd.AddCommand(updateCmd)
|
||||||
|
}
|
44
cmd/create.go
Normal file
44
cmd/create.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var createCmd = &cobra.Command{
|
||||||
|
Use: "create",
|
||||||
|
Short: "创建一个新项目",
|
||||||
|
Long: `通过hertz_demo为模板创建一个新项目`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
handleCreateCommand(args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleCreateCommand(args []string) {
|
||||||
|
if projectExists(project) {
|
||||||
|
log.Printf("Project %s already exists, use update", project)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err := handleRemoteTemplate(template, branch, project)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error creating project: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("Project %s created successfully!\n", project)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initFlags(createCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// projectExists 检查项目路径是否已经存在
|
||||||
|
func projectExists(projectPath string) bool {
|
||||||
|
info, err := os.Stat(projectPath)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.IsDir()
|
||||||
|
}
|
142
cmd/helpers.go
Normal file
142
cmd/helpers.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getModuleName(goModPath string) (string, error) {
|
||||||
|
content, err := os.ReadFile(goModPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "module ") {
|
||||||
|
return strings.TrimSpace(strings.TrimPrefix(line, "module ")), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("module name not found in go.mod")
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRemoteTemplate(templateRepo, branch, projectPath string) (err error) {
|
||||||
|
// 创建临时目录
|
||||||
|
tempDir, err := os.MkdirTemp("", "template-*")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating temporary directory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理临时目录
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
|
// 克隆模板仓库
|
||||||
|
cloneCmd := exec.Command("git", "clone", "--branch", branch, templateRepo, tempDir)
|
||||||
|
cloneCmd.Stdout = io.Discard
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cloneCmd.Stderr = &stderr
|
||||||
|
|
||||||
|
if err = cloneCmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("error cloning template repository: %s\n%s", err, extractError(stderr.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyTemplate(tempDir, projectPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyTemplate(src, dist string) (err error) {
|
||||||
|
// 读取 go.mod 文件中的模块名称
|
||||||
|
oldModuleName, err := getModuleName(filepath.Join(src, "go.mod"))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error reading module name: %s", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
replacements := map[string]string{
|
||||||
|
oldModuleName: filepath.Base(dist),
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取相对路径
|
||||||
|
relPath, err := filepath.Rel(src, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标路径
|
||||||
|
targetPath := filepath.Join(dist, relPath)
|
||||||
|
|
||||||
|
if info.IsDir() && filepath.Base(path) == ".git" {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
// 创建目录
|
||||||
|
return os.MkdirAll(targetPath, info.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyAndReplaceFile(path, targetPath, info.Mode(), replacements)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyAndReplaceFile(src, dist string, mode os.FileMode, replacements map[string]string) (err error) {
|
||||||
|
|
||||||
|
// 读取源文件
|
||||||
|
sourceFile, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
content, err := io.ReadAll(sourceFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newContent := string(content)
|
||||||
|
for key, value := range replacements {
|
||||||
|
newContent = strings.ReplaceAll(newContent, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取目标文件
|
||||||
|
targetFile, err := os.OpenFile(dist, os.O_CREATE|os.O_RDWR|os.O_TRUNC, mode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer targetFile.Close()
|
||||||
|
|
||||||
|
_, err = targetFile.WriteString(newContent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractError 解析并提取实际的错误信息
|
||||||
|
func extractError(stderr string) string {
|
||||||
|
lines := strings.Split(stderr, "\n")
|
||||||
|
var errorLines []string
|
||||||
|
for _, line := range lines {
|
||||||
|
// 过滤掉非错误信息
|
||||||
|
if strings.HasPrefix(line, "fatal:") || strings.Contains(line, "error:") {
|
||||||
|
errorLines = append(errorLines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(errorLines, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// initFlags 初始化通用的命令行参数
|
||||||
|
func initFlags(cmd *cobra.Command) {
|
||||||
|
cmd.Flags().StringVarP(&project, "project", "p", "", "项目名称")
|
||||||
|
cmd.Flags().StringVarP(&branch, "tag", "t", "master", "指定tag")
|
||||||
|
cmd.MarkFlagRequired("project")
|
||||||
|
}
|
31
cmd/update.go
Normal file
31
cmd/update.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var updateCmd = &cobra.Command{
|
||||||
|
Use: "update",
|
||||||
|
Short: "更新项目",
|
||||||
|
Long: `Update the current project`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
handleUpdateCommand(args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initFlags(updateCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleUpdateCommand(args []string) {
|
||||||
|
err := handleRemoteTemplate(template, branch, project)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error update project: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("Project %s update successfully!\n", project)
|
||||||
|
return
|
||||||
|
}
|
4
go.mod
4
go.mod
@ -1,6 +1,6 @@
|
|||||||
module hertz_scaffold
|
module golib.gaore.com/GaoreGo/hertz_scaffold
|
||||||
|
|
||||||
go 1.19
|
go 1.20
|
||||||
|
|
||||||
require github.com/spf13/cobra v1.8.1
|
require github.com/spf13/cobra v1.8.1
|
||||||
|
|
||||||
|
16
main.go
16
main.go
@ -1,17 +1,9 @@
|
|||||||
|
// Code generated by hertz generator.
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "fmt"
|
import "golib.gaore.com/GaoreGo/hertz_scaffold/cmd"
|
||||||
import "github.com/spf13/cobra"
|
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
|
||||||
Use: "hello",
|
|
||||||
Short: "Hello, World!",
|
|
||||||
Long: `Hello, World!`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("Hello, World! corbra")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
rootCmd.Execute()
|
cmd.Execute()
|
||||||
}
|
}
|
||||||
|
19
vendor/github.com/spf13/pflag/flag.go
generated
vendored
19
vendor/github.com/spf13/pflag/flag.go
generated
vendored
@ -27,32 +27,23 @@ unaffected.
|
|||||||
Define flags using flag.String(), Bool(), Int(), etc.
|
Define flags using flag.String(), Bool(), Int(), etc.
|
||||||
|
|
||||||
This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
|
This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
|
||||||
|
|
||||||
var ip = flag.Int("flagname", 1234, "help message for flagname")
|
var ip = flag.Int("flagname", 1234, "help message for flagname")
|
||||||
|
|
||||||
If you like, you can bind the flag to a variable using the Var() functions.
|
If you like, you can bind the flag to a variable using the Var() functions.
|
||||||
|
|
||||||
var flagvar int
|
var flagvar int
|
||||||
func init() {
|
func init() {
|
||||||
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
|
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
|
||||||
}
|
}
|
||||||
|
|
||||||
Or you can create custom flags that satisfy the Value interface (with
|
Or you can create custom flags that satisfy the Value interface (with
|
||||||
pointer receivers) and couple them to flag parsing by
|
pointer receivers) and couple them to flag parsing by
|
||||||
|
|
||||||
flag.Var(&flagVal, "name", "help message for flagname")
|
flag.Var(&flagVal, "name", "help message for flagname")
|
||||||
|
|
||||||
For such flags, the default value is just the initial value of the variable.
|
For such flags, the default value is just the initial value of the variable.
|
||||||
|
|
||||||
After all flags are defined, call
|
After all flags are defined, call
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
to parse the command line into the defined flags.
|
to parse the command line into the defined flags.
|
||||||
|
|
||||||
Flags may then be used directly. If you're using the flags themselves,
|
Flags may then be used directly. If you're using the flags themselves,
|
||||||
they are all pointers; if you bind to variables, they're values.
|
they are all pointers; if you bind to variables, they're values.
|
||||||
|
|
||||||
fmt.Println("ip has value ", *ip)
|
fmt.Println("ip has value ", *ip)
|
||||||
fmt.Println("flagvar has value ", flagvar)
|
fmt.Println("flagvar has value ", flagvar)
|
||||||
|
|
||||||
@ -63,26 +54,22 @@ The arguments are indexed from 0 through flag.NArg()-1.
|
|||||||
The pflag package also defines some new functions that are not in flag,
|
The pflag package also defines some new functions that are not in flag,
|
||||||
that give one-letter shorthands for flags. You can use these by appending
|
that give one-letter shorthands for flags. You can use these by appending
|
||||||
'P' to the name of any function that defines a flag.
|
'P' to the name of any function that defines a flag.
|
||||||
|
|
||||||
var ip = flag.IntP("flagname", "f", 1234, "help message")
|
var ip = flag.IntP("flagname", "f", 1234, "help message")
|
||||||
var flagvar bool
|
var flagvar bool
|
||||||
func init() {
|
func init() {
|
||||||
flag.BoolVarP(&flagvar, "boolname", "b", true, "help message")
|
flag.BoolVarP(&flagvar, "boolname", "b", true, "help message")
|
||||||
}
|
}
|
||||||
flag.VarP(&flagval, "varname", "v", "help message")
|
flag.VarP(&flagval, "varname", "v", "help message")
|
||||||
|
|
||||||
Shorthand letters can be used with single dashes on the command line.
|
Shorthand letters can be used with single dashes on the command line.
|
||||||
Boolean shorthand flags can be combined with other shorthand flags.
|
Boolean shorthand flags can be combined with other shorthand flags.
|
||||||
|
|
||||||
Command line flag syntax:
|
Command line flag syntax:
|
||||||
|
|
||||||
--flag // boolean flags only
|
--flag // boolean flags only
|
||||||
--flag=x
|
--flag=x
|
||||||
|
|
||||||
Unlike the flag package, a single dash before an option means something
|
Unlike the flag package, a single dash before an option means something
|
||||||
different than a double dash. Single dashes signify a series of shorthand
|
different than a double dash. Single dashes signify a series of shorthand
|
||||||
letters for flags. All but the last shorthand letter must be boolean flags.
|
letters for flags. All but the last shorthand letter must be boolean flags.
|
||||||
|
|
||||||
// boolean flags
|
// boolean flags
|
||||||
-f
|
-f
|
||||||
-abc
|
-abc
|
||||||
@ -940,9 +927,9 @@ func (f *FlagSet) usage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --unknown (args will be empty)
|
//--unknown (args will be empty)
|
||||||
// --unknown --next-flag ... (args will be --next-flag ...)
|
//--unknown --next-flag ... (args will be --next-flag ...)
|
||||||
// --unknown arg ... (args will be arg ...)
|
//--unknown arg ... (args will be arg ...)
|
||||||
func stripUnknownFlagValue(args []string) []string {
|
func stripUnknownFlagValue(args []string) []string {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
//--unknown
|
//--unknown
|
||||||
|
12
vendor/github.com/spf13/pflag/string_slice.go
generated
vendored
12
vendor/github.com/spf13/pflag/string_slice.go
generated
vendored
@ -98,11 +98,8 @@ func (f *FlagSet) GetStringSlice(name string) ([]string, error) {
|
|||||||
// The argument p points to a []string variable in which to store the value of the flag.
|
// The argument p points to a []string variable in which to store the value of the flag.
|
||||||
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
|
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
|
||||||
// For example:
|
// For example:
|
||||||
//
|
|
||||||
// --ss="v1,v2" --ss="v3"
|
// --ss="v1,v2" --ss="v3"
|
||||||
//
|
|
||||||
// will result in
|
// will result in
|
||||||
//
|
|
||||||
// []string{"v1", "v2", "v3"}
|
// []string{"v1", "v2", "v3"}
|
||||||
func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) {
|
func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) {
|
||||||
f.VarP(newStringSliceValue(value, p), name, "", usage)
|
f.VarP(newStringSliceValue(value, p), name, "", usage)
|
||||||
@ -117,11 +114,8 @@ func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string, value []s
|
|||||||
// The argument p points to a []string variable in which to store the value of the flag.
|
// The argument p points to a []string variable in which to store the value of the flag.
|
||||||
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
|
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
|
||||||
// For example:
|
// For example:
|
||||||
//
|
|
||||||
// --ss="v1,v2" --ss="v3"
|
// --ss="v1,v2" --ss="v3"
|
||||||
//
|
|
||||||
// will result in
|
// will result in
|
||||||
//
|
|
||||||
// []string{"v1", "v2", "v3"}
|
// []string{"v1", "v2", "v3"}
|
||||||
func StringSliceVar(p *[]string, name string, value []string, usage string) {
|
func StringSliceVar(p *[]string, name string, value []string, usage string) {
|
||||||
CommandLine.VarP(newStringSliceValue(value, p), name, "", usage)
|
CommandLine.VarP(newStringSliceValue(value, p), name, "", usage)
|
||||||
@ -136,11 +130,8 @@ func StringSliceVarP(p *[]string, name, shorthand string, value []string, usage
|
|||||||
// The return value is the address of a []string variable that stores the value of the flag.
|
// The return value is the address of a []string variable that stores the value of the flag.
|
||||||
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
|
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
|
||||||
// For example:
|
// For example:
|
||||||
//
|
|
||||||
// --ss="v1,v2" --ss="v3"
|
// --ss="v1,v2" --ss="v3"
|
||||||
//
|
|
||||||
// will result in
|
// will result in
|
||||||
//
|
|
||||||
// []string{"v1", "v2", "v3"}
|
// []string{"v1", "v2", "v3"}
|
||||||
func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string {
|
func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string {
|
||||||
p := []string{}
|
p := []string{}
|
||||||
@ -159,11 +150,8 @@ func (f *FlagSet) StringSliceP(name, shorthand string, value []string, usage str
|
|||||||
// The return value is the address of a []string variable that stores the value of the flag.
|
// The return value is the address of a []string variable that stores the value of the flag.
|
||||||
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
|
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
|
||||||
// For example:
|
// For example:
|
||||||
//
|
|
||||||
// --ss="v1,v2" --ss="v3"
|
// --ss="v1,v2" --ss="v3"
|
||||||
//
|
|
||||||
// will result in
|
// will result in
|
||||||
//
|
|
||||||
// []string{"v1", "v2", "v3"}
|
// []string{"v1", "v2", "v3"}
|
||||||
func StringSlice(name string, value []string, usage string) *[]string {
|
func StringSlice(name string, value []string, usage string) *[]string {
|
||||||
return CommandLine.StringSliceP(name, "", value, usage)
|
return CommandLine.StringSliceP(name, "", value, usage)
|
||||||
|
Loading…
Reference in New Issue
Block a user