mirror of
https://github.com/beego/bee.git
synced 2024-11-24 03:20:55 +00:00
add pack feature, not can pack project into compressed file
This commit is contained in:
parent
a73c185d16
commit
bbf565e9d8
8
main.go
8
main.go
@ -58,6 +58,7 @@ func (c *Command) Runnable() bool {
|
|||||||
var commands = []*Command{
|
var commands = []*Command{
|
||||||
cmdCreate,
|
cmdCreate,
|
||||||
cmdStart,
|
cmdStart,
|
||||||
|
cmdPack,
|
||||||
//cmdReStart,
|
//cmdReStart,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,17 +82,22 @@ func main() {
|
|||||||
cmd.Flag.Usage = func() { cmd.Usage() }
|
cmd.Flag.Usage = func() { cmd.Usage() }
|
||||||
if cmd.CustomFlags {
|
if cmd.CustomFlags {
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
|
} else {
|
||||||
|
if len(args) > 2 {
|
||||||
|
cmd.Flag.Parse(args[2:])
|
||||||
|
args = append([]string{args[1]}, cmd.Flag.Args()...)
|
||||||
} else {
|
} else {
|
||||||
cmd.Flag.Parse(args[1:])
|
cmd.Flag.Parse(args[1:])
|
||||||
args = cmd.Flag.Args()
|
args = cmd.Flag.Args()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cmd.Run(cmd, args)
|
cmd.Run(cmd, args)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user