Compare commits

...

7 Commits

Author SHA1 Message Date
6add048755 更新readme 2024-08-09 15:11:53 +08:00
ee3309164b 更新readme 2024-08-09 15:03:09 +08:00
d2bf98a710 实现更新功能 2024-08-09 14:57:41 +08:00
576ef0d31e 优化命令参数提示 2024-08-08 21:44:39 +08:00
f71ab54a56 create完成 2024-08-08 21:29:58 +08:00
liangzy
8235ef91cd feat(readme): add installation command for hertz_scaffold
Add a installation shell command in the README to facilitate users in
getting started with the hertz_scaffold by directly installing it usingthe provided go command.
2024-07-18 17:43:10 +08:00
liangzy
bef276638f feat(readme): add installation command for hertz_scaffold
Add a installation shell command in the README to facilitate users in
getting started with the hertz_scaffold by directly installing it usingthe provided go command.
2024-07-18 17:40:49 +08:00
9 changed files with 302 additions and 52 deletions

View File

@ -1,3 +1,29 @@
# hertz_scaffold
#### 简介
以golib.gaore.com/GaoreGo/hertz_demo为模板的hertz框架脚手架
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

40
cmd/cmd.go Normal file
View 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
View 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
View 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
View 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
View File

@ -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

16
main.go
View File

@ -1,17 +1,9 @@
// Code generated by hertz generator.
package main
import "fmt"
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")
},
}
import "golib.gaore.com/GaoreGo/hertz_scaffold/cmd"
func main() {
rootCmd.Execute()
cmd.Execute()
}

View File

@ -27,32 +27,23 @@ unaffected.
Define flags using flag.String(), Bool(), Int(), etc.
This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
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.
var flagvar int
func init() {
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
}
Or you can create custom flags that satisfy the Value interface (with
pointer receivers) and couple them to flag parsing by
flag.Var(&flagVal, "name", "help message for flagname")
For such flags, the default value is just the initial value of the variable.
After all flags are defined, call
flag.Parse()
to parse the command line into the defined flags.
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.
fmt.Println("ip has value ", *ip)
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,
that give one-letter shorthands for flags. You can use these by appending
'P' to the name of any function that defines a flag.
var ip = flag.IntP("flagname", "f", 1234, "help message")
var flagvar bool
func init() {
flag.BoolVarP(&flagvar, "boolname", "b", true, "help message")
}
flag.VarP(&flagval, "varname", "v", "help message")
Shorthand letters can be used with single dashes on the command line.
Boolean shorthand flags can be combined with other shorthand flags.
Command line flag syntax:
--flag // boolean flags only
--flag=x
Unlike the flag package, a single dash before an option means something
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.
// boolean flags
-f
-abc
@ -940,9 +927,9 @@ func (f *FlagSet) usage() {
}
}
// --unknown (args will be empty)
// --unknown --next-flag ... (args will be --next-flag ...)
// --unknown arg ... (args will be arg ...)
//--unknown (args will be empty)
//--unknown --next-flag ... (args will be --next-flag ...)
//--unknown arg ... (args will be arg ...)
func stripUnknownFlagValue(args []string) []string {
if len(args) == 0 {
//--unknown

View File

@ -98,12 +98,9 @@ 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.
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
// For example:
//
// --ss="v1,v2" --ss="v3"
//
// --ss="v1,v2" --ss="v3"
// will result in
//
// []string{"v1", "v2", "v3"}
// []string{"v1", "v2", "v3"}
func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) {
f.VarP(newStringSliceValue(value, p), name, "", usage)
}
@ -117,12 +114,9 @@ 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.
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
// For example:
//
// --ss="v1,v2" --ss="v3"
//
// --ss="v1,v2" --ss="v3"
// will result in
//
// []string{"v1", "v2", "v3"}
// []string{"v1", "v2", "v3"}
func StringSliceVar(p *[]string, name string, value []string, usage string) {
CommandLine.VarP(newStringSliceValue(value, p), name, "", usage)
}
@ -136,12 +130,9 @@ 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.
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
// For example:
//
// --ss="v1,v2" --ss="v3"
//
// --ss="v1,v2" --ss="v3"
// will result in
//
// []string{"v1", "v2", "v3"}
// []string{"v1", "v2", "v3"}
func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string {
p := []string{}
f.StringSliceVarP(&p, name, "", value, usage)
@ -159,12 +150,9 @@ 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.
// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
// For example:
//
// --ss="v1,v2" --ss="v3"
//
// --ss="v1,v2" --ss="v3"
// will result in
//
// []string{"v1", "v2", "v3"}
// []string{"v1", "v2", "v3"}
func StringSlice(name string, value []string, usage string) *[]string {
return CommandLine.StringSliceP(name, "", value, usage)
}