commit ecdea80122294afb0804c37d0e3bd5c6ea1a3b07 Author: xuyang Date: Wed Aug 7 15:46:09 2024 +0800 first commit diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7531224 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/scaffold.iml b/.idea/scaffold.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/scaffold.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cd15f9b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/xuyang404/scaffold + +go 1.20 diff --git a/main.go b/main.go new file mode 100644 index 0000000..f62a702 --- /dev/null +++ b/main.go @@ -0,0 +1,170 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "io/fs" + "log" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func main() { + + var ( + localTemplatePath string + remoteTemplatePath string + projectName string + branch string + ) + + flag.StringVar(&localTemplatePath, "local", "", "本地模板路径") + flag.StringVar(&remoteTemplatePath, "remote", "", "远程仓库url") + flag.StringVar(&projectName, "name", "", "项目名称") + flag.StringVar(&branch, "branch", "main", "要使用的分支(仅当模板是远程仓库时)") + + flag.Parse() + if localTemplatePath == "" && remoteTemplatePath == "" { + log.Println("请指定本地模板路径或远程仓库url") + os.Exit(1) + } + + if projectName == "" { + log.Println("请指定项目名称") + os.Exit(1) + } + + templatePath := "" + if localTemplatePath != "" { + templatePath = localTemplatePath + } + + if remoteTemplatePath != "" { + templatePath = remoteTemplatePath + } + + replacements := getReplacements() + if _, ok := replacements["{{PROJECT_NAME}}"]; !ok { + replacements["{{PROJECT_NAME}}"] = projectName + } + + var err error + + if remoteTemplatePath != "" { + err = handleRemoteTemplate(templatePath, branch, projectName, replacements) + } else { + err = copyTemplate(templatePath, projectName, replacements) + } + + if err != nil { + log.Printf("Error creating project: %s\n", err) + os.Exit(1) + } + + fmt.Printf("Project %s created successfully!\n", projectName) +} + +func handleRemoteTemplate(templateRepo, branch, projectName string, replacements map[string]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", "-b", branch, templateRepo, tempDir) + cloneCmd.Stdout = os.Stdout + cloneCmd.Stderr = os.Stderr + + if err = cloneCmd.Run(); err != nil { + return fmt.Errorf("error cloning template repository: %s", err) + } + + return copyTemplate(tempDir, projectName, replacements) +} + +func getReplacements() map[string]string { + scanner := bufio.NewScanner(os.Stdin) + replacements := make(map[string]string) + fmt.Println("输入替换值(key=value),空行结束: ") + for { + fmt.Print("> ") + scanner.Scan() + line := scanner.Text() + if line == "" { + break + } + + splits := strings.Split(line, "=") + if len(splits) != 2 { + fmt.Println("无效输入,请以key=value格式输入") + continue + } + + replacements[splits[0]] = splits[1] + } + + return replacements +} + +func copyTemplate(src, dist string, replacements map[string]string) (err error) { + 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() { + // 创建目录 + 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 +}