mirror of
				https://github.com/beego/bee.git
				synced 2025-11-04 09:23:24 +00:00 
			
		
		
		
	add pack feature, not can pack project into compressed file
This commit is contained in:
		
							
								
								
									
										12
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								main.go
									
									
									
									
									
								
							@@ -58,6 +58,7 @@ func (c *Command) Runnable() bool {
 | 
			
		||||
var commands = []*Command{
 | 
			
		||||
	cmdCreate,
 | 
			
		||||
	cmdStart,
 | 
			
		||||
	cmdPack,
 | 
			
		||||
	//cmdReStart,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -82,8 +83,13 @@ func main() {
 | 
			
		||||
			if cmd.CustomFlags {
 | 
			
		||||
				args = args[1:]
 | 
			
		||||
			} else {
 | 
			
		||||
				cmd.Flag.Parse(args[1:])
 | 
			
		||||
				args = cmd.Flag.Args()
 | 
			
		||||
				if len(args) > 2 {
 | 
			
		||||
					cmd.Flag.Parse(args[2:])
 | 
			
		||||
					args = append([]string{args[1]}, cmd.Flag.Args()...)
 | 
			
		||||
				} else {
 | 
			
		||||
					cmd.Flag.Parse(args[1:])
 | 
			
		||||
					args = cmd.Flag.Args()
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			cmd.Run(cmd, args)
 | 
			
		||||
			os.Exit(2)
 | 
			
		||||
@@ -91,7 +97,7 @@ func main() {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0])
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "bee: unknown subcommand %q\nRun 'bee help' for usage.\n", args[0])
 | 
			
		||||
	os.Exit(2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										456
									
								
								pack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										456
									
								
								pack.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,456 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"archive/tar"
 | 
			
		||||
	"archive/zip"
 | 
			
		||||
	"compress/gzip"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	path "path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdPack = &Command{
 | 
			
		||||
	UsageLine: "pack [appPath]",
 | 
			
		||||
	Short:     "pack an beego project into one execute file",
 | 
			
		||||
	Long: `
 | 
			
		||||
compress an project
 | 
			
		||||
 | 
			
		||||
-b        build specify platform app. default true
 | 
			
		||||
-o        compressed file output path. default use appname
 | 
			
		||||
-f        format. [ tar.gz / zip ]. default tar.gz. note: zip doesn't support embed symlink, skip it
 | 
			
		||||
-exp      path exclude prefix
 | 
			
		||||
-exs      path exclude suffix. default: .go:.DS_Store:.tmp
 | 
			
		||||
          all path use : as separator
 | 
			
		||||
-fs       follow symlink. default false
 | 
			
		||||
-ss       skip symlink. default false
 | 
			
		||||
          default embed symlink into compressed file
 | 
			
		||||
-v        verbose
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	excludeP string
 | 
			
		||||
	excludeS string
 | 
			
		||||
	outputP  string
 | 
			
		||||
	fsym     bool
 | 
			
		||||
	ssym     bool
 | 
			
		||||
	build    bool
 | 
			
		||||
	verbose  bool
 | 
			
		||||
	format   string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	fs := flag.NewFlagSet("pack", flag.ExitOnError)
 | 
			
		||||
	fs.StringVar(&excludeP, "exp", "", "")
 | 
			
		||||
	fs.StringVar(&excludeS, "exs", ".go:.DS_Store", "")
 | 
			
		||||
	fs.StringVar(&outputP, "o", "", "")
 | 
			
		||||
	fs.BoolVar(&build, "b", true, "")
 | 
			
		||||
	fs.BoolVar(&fsym, "fs", false, "")
 | 
			
		||||
	fs.BoolVar(&ssym, "ss", false, "")
 | 
			
		||||
	fs.BoolVar(&verbose, "v", false, "")
 | 
			
		||||
	fs.StringVar(&format, "f", "tar.gz", "")
 | 
			
		||||
	cmdPack.Flag = *fs
 | 
			
		||||
	cmdPack.Run = packApp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func exitPrint(con string) {
 | 
			
		||||
	fmt.Fprintln(os.Stderr, con)
 | 
			
		||||
	os.Exit(2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type walker interface {
 | 
			
		||||
	isExclude(string) bool
 | 
			
		||||
	isEmpty(string) bool
 | 
			
		||||
	relName(string) string
 | 
			
		||||
	virPath(string) string
 | 
			
		||||
	compress(string, string, os.FileInfo) (bool, error)
 | 
			
		||||
	walkRoot(string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type byName []os.FileInfo
 | 
			
		||||
 | 
			
		||||
func (f byName) Len() int           { return len(f) }
 | 
			
		||||
func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
 | 
			
		||||
func (f byName) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
 | 
			
		||||
 | 
			
		||||
type walkFileTree struct {
 | 
			
		||||
	wak           walker
 | 
			
		||||
	prefix        string
 | 
			
		||||
	excludePrefix []string
 | 
			
		||||
	excludeSuffix []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) setPrefix(prefix string) {
 | 
			
		||||
	wft.prefix = prefix
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) isExclude(name string) bool {
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, prefix := range wft.excludePrefix {
 | 
			
		||||
		if strings.HasPrefix(name, prefix) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, suffix := range wft.excludeSuffix {
 | 
			
		||||
		if strings.HasSuffix(name, suffix) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) isEmpty(fpath string) bool {
 | 
			
		||||
	fh, _ := os.Open(fpath)
 | 
			
		||||
	defer fh.Close()
 | 
			
		||||
	infos, _ := fh.Readdir(-1)
 | 
			
		||||
	for _, fi := range infos {
 | 
			
		||||
		fp := path.Join(fpath, fi.Name())
 | 
			
		||||
		if wft.isExclude(wft.virPath(fp)) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if fi.Mode()&os.ModeSymlink > 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if fi.IsDir() && wft.isEmpty(fp) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) relName(fpath string) string {
 | 
			
		||||
	name, _ := path.Rel(wft.prefix, fpath)
 | 
			
		||||
	return name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) virPath(fpath string) string {
 | 
			
		||||
	name := fpath[len(wft.prefix):]
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	name = name[1:]
 | 
			
		||||
	return name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) readDir(dirname string) ([]os.FileInfo, error) {
 | 
			
		||||
	f, err := os.Open(dirname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	list, err := f.Readdir(-1)
 | 
			
		||||
	f.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(byName(list))
 | 
			
		||||
	return list, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) walkLeaf(fpath string, fi os.FileInfo, err error) error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if fi.IsDir() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := wft.virPath(fpath)
 | 
			
		||||
	if wft.isExclude(name) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ssym && fi.Mode()&os.ModeSymlink > 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if added, err := wft.wak.compress(name, fpath, fi); added {
 | 
			
		||||
		if verbose {
 | 
			
		||||
			fmt.Printf("Compressed: %s\n", name)
 | 
			
		||||
		}
 | 
			
		||||
		return err
 | 
			
		||||
	} else {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) iterDirectory(fpath string, fi os.FileInfo) error {
 | 
			
		||||
	doFSym := fsym && fi.Mode()&os.ModeSymlink > 0
 | 
			
		||||
	if doFSym {
 | 
			
		||||
		nfi, err := os.Stat(fpath)
 | 
			
		||||
		if os.IsNotExist(err) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		fi = nfi
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := wft.walkLeaf(fpath, fi, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if fi.IsDir() && err == path.SkipDir {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !fi.IsDir() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	list, err := wft.readDir(fpath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return wft.walkLeaf(fpath, fi, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, fileInfo := range list {
 | 
			
		||||
		err = wft.iterDirectory(path.Join(fpath, fileInfo.Name()), fileInfo)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if !fileInfo.IsDir() || err != path.SkipDir {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *walkFileTree) walkRoot(root string) error {
 | 
			
		||||
	wft.prefix = root
 | 
			
		||||
	fi, err := os.Stat(root)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return wft.iterDirectory(root, fi)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tarWalk struct {
 | 
			
		||||
	walkFileTree
 | 
			
		||||
	tw *tar.Writer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *tarWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
 | 
			
		||||
	isSym := fi.Mode()&os.ModeSymlink > 0
 | 
			
		||||
	link := ""
 | 
			
		||||
	if isSym {
 | 
			
		||||
		link, _ = os.Readlink(fpath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hdr, err := tar.FileInfoHeader(fi, link)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	hdr.Name = name
 | 
			
		||||
 | 
			
		||||
	tw := wft.tw
 | 
			
		||||
	err = tw.WriteHeader(hdr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isSym == false {
 | 
			
		||||
		fr, err := os.Open(fpath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		defer fr.Close()
 | 
			
		||||
		_, err = io.Copy(tw, fr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		tw.Flush()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type zipWalk struct {
 | 
			
		||||
	walkFileTree
 | 
			
		||||
	zw *zip.Writer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (wft *zipWalk) compress(name, fpath string, fi os.FileInfo) (bool, error) {
 | 
			
		||||
	isSym := fi.Mode()&os.ModeSymlink > 0
 | 
			
		||||
	if isSym {
 | 
			
		||||
		// golang1.1 doesn't support embed symlink
 | 
			
		||||
		// what i miss something?
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hdr, err := zip.FileInfoHeader(fi)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	hdr.Name = name
 | 
			
		||||
 | 
			
		||||
	zw := wft.zw
 | 
			
		||||
	w, err := zw.CreateHeader(hdr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isSym == false {
 | 
			
		||||
		fr, err := os.Open(fpath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		defer fr.Close()
 | 
			
		||||
		_, err = io.Copy(w, fr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func packDirectory(excludePrefix []string, excludeSuffix []string, includePath ...string) (err error) {
 | 
			
		||||
	fmt.Printf("exclude prefix: %s\n", strings.Join(excludePrefix, ":"))
 | 
			
		||||
	fmt.Printf("exclude suffix: %s\n", strings.Join(excludeSuffix, ":"))
 | 
			
		||||
 | 
			
		||||
	w, err := os.OpenFile(outputP, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wft walker
 | 
			
		||||
 | 
			
		||||
	if format == "zip" {
 | 
			
		||||
		walk := new(zipWalk)
 | 
			
		||||
		zw := zip.NewWriter(w)
 | 
			
		||||
		defer func() {
 | 
			
		||||
			zw.Close()
 | 
			
		||||
		}()
 | 
			
		||||
		walk.zw = zw
 | 
			
		||||
		walk.wak = walk
 | 
			
		||||
		walk.excludePrefix = excludePrefix
 | 
			
		||||
		walk.excludeSuffix = excludeSuffix
 | 
			
		||||
		wft = walk
 | 
			
		||||
	} else {
 | 
			
		||||
		walk := new(tarWalk)
 | 
			
		||||
		cw := gzip.NewWriter(w)
 | 
			
		||||
		tw := tar.NewWriter(cw)
 | 
			
		||||
 | 
			
		||||
		defer func() {
 | 
			
		||||
			tw.Flush()
 | 
			
		||||
			cw.Flush()
 | 
			
		||||
			tw.Close()
 | 
			
		||||
			cw.Close()
 | 
			
		||||
		}()
 | 
			
		||||
		walk.tw = tw
 | 
			
		||||
		walk.wak = walk
 | 
			
		||||
		walk.excludePrefix = excludePrefix
 | 
			
		||||
		walk.excludeSuffix = excludeSuffix
 | 
			
		||||
		wft = walk
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, p := range includePath {
 | 
			
		||||
		err = wft.walkRoot(p)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func packApp(cmd *Command, args []string) {
 | 
			
		||||
	if len(args) == 0 {
 | 
			
		||||
		fmt.Fprintln(os.Stderr, "need appPath")
 | 
			
		||||
		cmdPack.Usage()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	curPath, _ := os.Getwd()
 | 
			
		||||
	thePath := ""
 | 
			
		||||
	appPath := args[0]
 | 
			
		||||
 | 
			
		||||
	if path.IsAbs(appPath) == false {
 | 
			
		||||
		thePath = path.Join(curPath, appPath)
 | 
			
		||||
	}
 | 
			
		||||
	thePath, err := path.Abs(thePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		exitPrint(fmt.Sprintf("wrong app path: %s", thePath))
 | 
			
		||||
	}
 | 
			
		||||
	if stat, err := os.Stat(thePath); os.IsNotExist(err) || stat.IsDir() == false {
 | 
			
		||||
		exitPrint(fmt.Sprintf("not exist app path: %s", thePath))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	appName := path.Base(thePath)
 | 
			
		||||
 | 
			
		||||
	goos := runtime.GOOS
 | 
			
		||||
	if v, found := syscall.Getenv("GOOS"); found {
 | 
			
		||||
		goos = v
 | 
			
		||||
	}
 | 
			
		||||
	goarch := runtime.GOARCH
 | 
			
		||||
	if v, found := syscall.Getenv("GOARCH"); found {
 | 
			
		||||
		goarch = v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	str := strconv.FormatInt(time.Now().UnixNano(), 10)[9:]
 | 
			
		||||
 | 
			
		||||
	gobin := path.Join(runtime.GOROOT(), "bin", "go")
 | 
			
		||||
	tmpdir := path.Join(os.TempDir(), "beePack-"+str)
 | 
			
		||||
 | 
			
		||||
	os.Mkdir(tmpdir, 0700)
 | 
			
		||||
 | 
			
		||||
	if build {
 | 
			
		||||
		fmt.Println("GOOS", goos, "GOARCH", goarch)
 | 
			
		||||
		fmt.Println("build", appName)
 | 
			
		||||
 | 
			
		||||
		os.Setenv("GOOS", goos)
 | 
			
		||||
		os.Setenv("GOARCH", goarch)
 | 
			
		||||
 | 
			
		||||
		binPath := path.Join(tmpdir, appName)
 | 
			
		||||
		execmd := exec.Command(gobin, "build", "-o", binPath)
 | 
			
		||||
		execmd.Stdout = os.Stdout
 | 
			
		||||
		execmd.Stderr = os.Stderr
 | 
			
		||||
		err = execmd.Run()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			exitPrint(err.Error())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fmt.Println("build success")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch format {
 | 
			
		||||
	case "zip":
 | 
			
		||||
	default:
 | 
			
		||||
		format = "tar.gz"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if outputP == "" {
 | 
			
		||||
		outputP = path.Join(curPath, appName+"."+format)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if stat, err := os.Stat(outputP); err == nil && stat.IsDir() {
 | 
			
		||||
		outputP = path.Join(outputP, appName+"."+format)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var exp, exs []string
 | 
			
		||||
	for _, p := range strings.Split(excludeP, ":") {
 | 
			
		||||
		if len(p) > 0 {
 | 
			
		||||
			exp = append(exp, p)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, p := range strings.Split(excludeS, ":") {
 | 
			
		||||
		if len(p) > 0 {
 | 
			
		||||
			exs = append(exs, p)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = packDirectory(exp, exs, thePath, tmpdir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		exitPrint(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("file write to `%s`\n", outputP)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user