package main import ( "archive/tar" "bytes" "compress/gzip" "errors" "flag" "fmt" "git.nobla.cn/golang/kos/util/env" "io" "net/http" "os" "path" "runtime" "strconv" "strings" "time" ) var ( packageNameFlag = flag.String("package-name", "", "应用程序包名称") versionFlag = flag.String("version", env.Get("MOTO_VERSION", "v0.0.3"), "模板版本号,可以使用环境变量 MOTO_VERSION") ) var ( oldPackageName = "git.nobla.cn/golang/moto" ) func writeFile(dstFile string, r io.Reader) (err error) { var ( fp *os.File ) if fp, err = os.Create(dstFile); err != nil { return } defer fp.Close() _, err = io.Copy(fp, r) return } func extractFile(file string, dirname string) (err error) { var ( prefix string fp *os.File gzipReader *gzip.Reader ) // 清理路径字符串 dirname = path.Clean(dirname) // 打开压缩文件 if fp, err = os.Open(file); err != nil { return } defer fp.Close() // 执行解压操作 if gzipReader, err = gzip.NewReader(fp); err != nil { return } defer gzipReader.Close() tarReader := tar.NewReader(gzipReader) for { header, errRead := tarReader.Next() if errRead != nil { if errors.Is(errRead, io.EOF) { break } err = errRead break } if header.Typeflag == tar.TypeXGlobalHeader { continue } if prefix == "" && header.Typeflag == tar.TypeDir { prefix = header.Name continue } filename := path.Join(dirname, strings.TrimPrefix(header.Name, prefix)) if header.Typeflag == tar.TypeDir { if err = os.MkdirAll(filename, 0755); err != nil { return } } else { if err = writeFile(filename, tarReader); err != nil { return err } if runtime.GOOS != "windows" { os.Chmod(filename, 0644) } } } return nil } func downloadPackage(version string, dirname string) (err error) { var ( uri string res *http.Response ) uri = "https://git.nobla.cn/golang/moto/archive/" + version + ".tar.gz" if res, err = http.Get(uri); err != nil { return } defer func() { res.Body.Close() }() if res.StatusCode != http.StatusOK { err = errors.New(res.Status) return } filename := path.Join(os.TempDir(), strconv.FormatInt(time.Now().UnixMilli(), 10)+".tar.gz") if err = writeFile(filename, res.Body); err != nil { return } err = extractFile(filename, dirname) return } func replaceFiles(dirname string, source, replace string) (err error) { var ( buf []byte files []os.DirEntry ) if files, err = os.ReadDir(dirname); err != nil { if errors.Is(err, io.EOF) { return nil } return err } sb := []byte(source) rb := []byte(replace) for _, file := range files { if file.Name() == "." || file.Name() == ".." { continue } filename := path.Join(dirname, file.Name()) if file.IsDir() { err = replaceFiles(filename, source, replace) } else { if buf, err = os.ReadFile(filename); err == nil { buf = bytes.ReplaceAll(buf, sb, rb) os.WriteFile(filename, buf, 064) } } } return } func main() { var ( pos int err error dirname string appname string packageName string ) flag.Parse() packageName = *packageNameFlag if packageName == "" { if len(os.Args) > 1 { packageName = os.Args[1] } } pos = strings.LastIndexByte(packageName, '/') if pos == -1 { fmt.Println("应用包名称是无效的") os.Exit(1) } appname = packageName[pos:] if dirname, err = os.Getwd(); err != nil { fmt.Println("获取应用目录失败:" + err.Error()) os.Exit(1) } dirname = path.Join(dirname, appname) if err = os.MkdirAll(dirname, 0755); err != nil { fmt.Println("创建应用目录失败:" + err.Error()) os.Exit(1) } if err = downloadPackage(*versionFlag, dirname); err != nil { fmt.Println("下载模板文件失败:" + err.Error()) os.Exit(1) } if err = replaceFiles(dirname, oldPackageName, packageName); err != nil { fmt.Println("生产应用文件失败:" + err.Error()) os.Exit(1) } fmt.Println("创建项目成功") }