├── .gitignore ├── .travis.yml ├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ ├── bitbucket.org │ └── kardianos │ │ └── osext │ │ ├── LICENSE │ │ ├── osext.go │ │ ├── osext_plan9.go │ │ ├── osext_procfs.go │ │ ├── osext_sysctl.go │ │ ├── osext_test.go │ │ └── osext_windows.go │ └── github.com │ ├── daviddengcn │ └── go-colortext │ │ ├── .gitignore │ │ ├── README.md │ │ ├── ct.go │ │ ├── ct_ansi.go │ │ ├── ct_test.go │ │ └── ct_win.go │ ├── ddollar │ ├── dist │ │ ├── Godeps │ │ │ ├── Godeps.json │ │ │ └── Readme │ │ ├── dist.go │ │ └── version.go │ └── go-update │ │ ├── README │ │ └── update.go │ ├── kr │ ├── binarydist │ │ ├── .gitignore │ │ ├── License │ │ ├── Readme.md │ │ ├── bzip2.go │ │ ├── common_test.go │ │ ├── diff.go │ │ ├── diff_test.go │ │ ├── doc.go │ │ ├── encoding.go │ │ ├── patch.go │ │ ├── patch_test.go │ │ ├── seek.go │ │ ├── sort_test.go │ │ └── testdata │ │ │ ├── sample.new │ │ │ ├── sample.old │ │ │ └── sample.patch │ ├── pretty │ │ ├── .gitignore │ │ ├── License │ │ ├── Readme │ │ ├── diff.go │ │ ├── diff_test.go │ │ ├── example_test.go │ │ ├── formatter.go │ │ ├── formatter_test.go │ │ ├── pretty.go │ │ └── zero.go │ └── text │ │ ├── License │ │ ├── Readme │ │ ├── colwriter │ │ ├── Readme │ │ ├── column.go │ │ └── column_test.go │ │ ├── doc.go │ │ ├── indent.go │ │ ├── indent_test.go │ │ ├── mc │ │ ├── Readme │ │ └── mc.go │ │ ├── wrap.go │ │ └── wrap_test.go │ └── subosito │ └── gotenv │ ├── .env │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── examples_test.go │ ├── fixtures │ ├── exported.env │ ├── plain.env │ ├── quoted.env │ └── yaml.env │ ├── gotenv.go │ └── gotenv_test.go ├── Makefile ├── README.md ├── barrier.go ├── command.go ├── eg ├── .env ├── Procfile ├── error ├── spawnee ├── spawner ├── ticker └── utf8 ├── env.go ├── env_test.go ├── error.go ├── fixtures ├── envs │ ├── .env1 │ └── .env2 ├── large_stdout │ ├── Procfile │ └── stdout.rb ├── multiline │ ├── Procfile │ └── stdout.rb └── writey │ ├── Procfile │ └── main.go ├── help.go ├── main.go ├── outlet.go ├── process.go ├── procfile.go ├── run.go ├── start.go ├── start_test.go ├── unix.go ├── update.go ├── util.go ├── version.go ├── version_test.go └── windows.go /.gitignore: -------------------------------------------------------------------------------- 1 | /forego* 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: go 4 | go: 5 | - 1.6 6 | 7 | env: 8 | global: 9 | - PATH=$HOME/gopath/bin:$PATH 10 | 11 | before_install: 12 | - go get github.com/tools/godep 13 | - godep restore 14 | 15 | script: 16 | - make test 17 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/ddollar/forego", 3 | "GoVersion": "go1.4.1", 4 | "Packages": [ 5 | "." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "bitbucket.org/kardianos/osext", 10 | "Comment": "null-13", 11 | "Rev": "5d3ddcf53a508cc2f7404eaebf546ef2cb5cdb6e" 12 | }, 13 | { 14 | "ImportPath": "github.com/daviddengcn/go-colortext", 15 | "Rev": "8386cd264821f95e9bc124a0a339560d1a899cad" 16 | }, 17 | { 18 | "ImportPath": "github.com/ddollar/dist", 19 | "Comment": "v0.2.0-3-gd4be91e", 20 | "Rev": "d4be91e0cd1771c51ae117e18eb6c379757623f4" 21 | }, 22 | { 23 | "ImportPath": "github.com/ddollar/go-update", 24 | "Rev": "4733469d35539f410e0e84552944fa0df7e72c98" 25 | }, 26 | { 27 | "ImportPath": "github.com/kr/binarydist", 28 | "Rev": "9955b0ab8708602d411341e55fffd7e0700f86bd" 29 | }, 30 | { 31 | "ImportPath": "github.com/kr/pretty", 32 | "Comment": "go.weekly.2011-12-22-18-gbc9499c", 33 | "Rev": "bc9499caa0f45ee5edb2f0209fbd61fbf3d9018f" 34 | }, 35 | { 36 | "ImportPath": "github.com/kr/text", 37 | "Rev": "6807e777504f54ad073ecef66747de158294b639" 38 | }, 39 | { 40 | "ImportPath": "github.com/subosito/gotenv", 41 | "Comment": "v0.1.0", 42 | "Rev": "a37a0e8fb3298354bf97daad07b38feb2d0fa263" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Daniel Theophanes 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Extensions to the standard "os" package. 6 | package osext 7 | 8 | import "path/filepath" 9 | 10 | // Executable returns an absolute path that can be used to 11 | // re-invoke the current program. 12 | // It may not be valid after the current program exits. 13 | func Executable() (string, error) { 14 | p, err := executable() 15 | return filepath.Clean(p), err 16 | } 17 | 18 | // Returns same path as Executable, returns just the folder 19 | // path. Excludes the executable name. 20 | func ExecutableFolder() (string, error) { 21 | p, err := Executable() 22 | if err != nil { 23 | return "", err 24 | } 25 | folder, _ := filepath.Split(p) 26 | return folder, nil 27 | } 28 | 29 | // Depricated. Same as Executable(). 30 | func GetExePath() (exePath string, err error) { 31 | return Executable() 32 | } 33 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package osext 6 | 7 | import ( 8 | "syscall" 9 | "os" 10 | "strconv" 11 | ) 12 | 13 | func executable() (string, error) { 14 | f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text") 15 | if err != nil { 16 | return "", err 17 | } 18 | defer f.Close() 19 | return syscall.Fd2path(int(f.Fd())) 20 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux netbsd openbsd 6 | 7 | package osext 8 | 9 | import ( 10 | "errors" 11 | "os" 12 | "runtime" 13 | ) 14 | 15 | func executable() (string, error) { 16 | switch runtime.GOOS { 17 | case "linux": 18 | return os.Readlink("/proc/self/exe") 19 | case "netbsd": 20 | return os.Readlink("/proc/curproc/exe") 21 | case "openbsd": 22 | return os.Readlink("/proc/curproc/file") 23 | } 24 | return "", errors.New("ExecPath not implemented for " + runtime.GOOS) 25 | } 26 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin freebsd 6 | 7 | package osext 8 | 9 | import ( 10 | "os" 11 | "path/filepath" 12 | "runtime" 13 | "syscall" 14 | "unsafe" 15 | ) 16 | 17 | var initCwd, initCwdErr = os.Getwd() 18 | 19 | func executable() (string, error) { 20 | var mib [4]int32 21 | switch runtime.GOOS { 22 | case "freebsd": 23 | mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1} 24 | case "darwin": 25 | mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1} 26 | } 27 | 28 | n := uintptr(0) 29 | // Get length. 30 | _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0) 31 | if errNum != 0 { 32 | return "", errNum 33 | } 34 | if n == 0 { // This shouldn't happen. 35 | return "", nil 36 | } 37 | buf := make([]byte, n) 38 | _, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0) 39 | if errNum != 0 { 40 | return "", errNum 41 | } 42 | if n == 0 { // This shouldn't happen. 43 | return "", nil 44 | } 45 | for i, v := range buf { 46 | if v == 0 { 47 | buf = buf[:i] 48 | break 49 | } 50 | } 51 | var err error 52 | execPath := string(buf) 53 | // execPath will not be empty due to above checks. 54 | // Try to get the absolute path if the execPath is not rooted. 55 | if execPath[0] != '/' { 56 | execPath, err = getAbs(execPath) 57 | if err != nil { 58 | return execPath, err 59 | } 60 | } 61 | // For darwin KERN_PROCARGS may return the path to a symlink rather than the 62 | // actual executable. 63 | if runtime.GOOS == "darwin" { 64 | if execPath, err = filepath.EvalSymlinks(execPath); err != nil { 65 | return execPath, err 66 | } 67 | } 68 | return execPath, nil 69 | } 70 | 71 | func getAbs(execPath string) (string, error) { 72 | if initCwdErr != nil { 73 | return execPath, initCwdErr 74 | } 75 | // The execPath may begin with a "../" or a "./" so clean it first. 76 | // Join the two paths, trailing and starting slashes undetermined, so use 77 | // the generic Join function. 78 | return filepath.Join(initCwd, filepath.Clean(execPath)), nil 79 | } 80 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin linux freebsd netbsd windows 6 | 7 | package osext 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | oexec "os/exec" 13 | "path/filepath" 14 | "runtime" 15 | "testing" 16 | ) 17 | 18 | const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH" 19 | 20 | func TestExecPath(t *testing.T) { 21 | ep, err := Executable() 22 | if err != nil { 23 | t.Fatalf("ExecPath failed: %v", err) 24 | } 25 | // we want fn to be of the form "dir/prog" 26 | dir := filepath.Dir(filepath.Dir(ep)) 27 | fn, err := filepath.Rel(dir, ep) 28 | if err != nil { 29 | t.Fatalf("filepath.Rel: %v", err) 30 | } 31 | cmd := &oexec.Cmd{} 32 | // make child start with a relative program path 33 | cmd.Dir = dir 34 | cmd.Path = fn 35 | // forge argv[0] for child, so that we can verify we could correctly 36 | // get real path of the executable without influenced by argv[0]. 37 | cmd.Args = []string{"-", "-test.run=XXXX"} 38 | cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)} 39 | out, err := cmd.CombinedOutput() 40 | if err != nil { 41 | t.Fatalf("exec(self) failed: %v", err) 42 | } 43 | outs := string(out) 44 | if !filepath.IsAbs(outs) { 45 | t.Fatalf("Child returned %q, want an absolute path", out) 46 | } 47 | if !sameFile(outs, ep) { 48 | t.Fatalf("Child returned %q, not the same file as %q", out, ep) 49 | } 50 | } 51 | 52 | func sameFile(fn1, fn2 string) bool { 53 | fi1, err := os.Stat(fn1) 54 | if err != nil { 55 | return false 56 | } 57 | fi2, err := os.Stat(fn2) 58 | if err != nil { 59 | return false 60 | } 61 | return os.SameFile(fi1, fi2) 62 | } 63 | 64 | func init() { 65 | if e := os.Getenv(execPath_EnvVar); e != "" { 66 | // first chdir to another path 67 | dir := "/" 68 | if runtime.GOOS == "windows" { 69 | dir = filepath.VolumeName(".") 70 | } 71 | os.Chdir(dir) 72 | if ep, err := Executable(); err != nil { 73 | fmt.Fprint(os.Stderr, "ERROR: ", err) 74 | } else { 75 | fmt.Fprint(os.Stderr, ep) 76 | } 77 | os.Exit(0) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package osext 6 | 7 | import ( 8 | "syscall" 9 | "unicode/utf16" 10 | "unsafe" 11 | ) 12 | 13 | var ( 14 | kernel = syscall.MustLoadDLL("kernel32.dll") 15 | getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW") 16 | ) 17 | 18 | // GetModuleFileName() with hModule = NULL 19 | func executable() (exePath string, err error) { 20 | return getModuleFileName() 21 | } 22 | 23 | func getModuleFileName() (string, error) { 24 | var n uint32 25 | b := make([]uint16, syscall.MAX_PATH) 26 | size := uint32(len(b)) 27 | 28 | r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size)) 29 | n = uint32(r0) 30 | if n == 0 { 31 | return "", e1 32 | } 33 | return string(utf16.Decode(b[0:n])), nil 34 | } 35 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/daviddengcn/go-colortext/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/daviddengcn/go-colortext/README.md: -------------------------------------------------------------------------------- 1 | go-colortext package 2 | ==================== 3 | 4 | This is a package to change the color of the text and background in the console, working both in windows and other systems. 5 | 6 | Under windows, the console APIs are used, and otherwise ANSI text is used. 7 | 8 | Docs: http://godoc.org/github.com/daviddengcn/go-colortext ([packages that import ct](http://go-search.org/view?id=github.com%2fdaviddengcn%2fgo-colortext)) 9 | 10 | Usage: 11 | ```go 12 | ChangeColor(Red, true, White, false) 13 | fmt.Println(...) 14 | ChangeColor(Green, false, None, false) 15 | fmt.Println(...) 16 | ResetColor() 17 | ``` 18 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/daviddengcn/go-colortext/ct.go: -------------------------------------------------------------------------------- 1 | /* 2 | ct package provides functions to change the color of console text. 3 | 4 | Under windows platform, the Console api is used. Under other systems, ANSI text mode is used. 5 | */ 6 | package ct 7 | 8 | // Color is the type of color to be set. 9 | type Color int 10 | 11 | const ( 12 | // No change of color 13 | None = Color(iota) 14 | Black 15 | Red 16 | Green 17 | Yellow 18 | Blue 19 | Magenta 20 | Cyan 21 | White 22 | ) 23 | 24 | /* 25 | ResetColor resets the foreground and background to original colors 26 | */ 27 | func ResetColor() { 28 | resetColor() 29 | } 30 | 31 | // ChangeColor sets the foreground and background colors. If the value of the color is None, 32 | // the corresponding color keeps unchanged. 33 | // If fgBright or bgBright is set true, corresponding color use bright color. bgBright may be 34 | // ignored in some OS environment. 35 | func ChangeColor(fg Color, fgBright bool, bg Color, bgBright bool) { 36 | changeColor(fg, fgBright, bg, bgBright) 37 | } 38 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/daviddengcn/go-colortext/ct_ansi.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package ct 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func resetColor() { 10 | fmt.Print("\x1b[0m") 11 | } 12 | 13 | func changeColor(fg Color, fgBright bool, bg Color, bgBright bool) { 14 | if fg == None && bg == None { 15 | return 16 | } // if 17 | 18 | s := "" 19 | if fg != None { 20 | s = fmt.Sprintf("%s%d", s, 30+(int)(fg-Black)) 21 | if fgBright { 22 | s += ";1" 23 | } // if 24 | } // if 25 | 26 | if bg != None { 27 | if s != "" { 28 | s += ";" 29 | } // if 30 | s = fmt.Sprintf("%s%d", s, 40+(int)(bg-Black)) 31 | } // if 32 | 33 | s = "\x1b[0;" + s + "m" 34 | fmt.Print(s) 35 | } 36 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/daviddengcn/go-colortext/ct_test.go: -------------------------------------------------------------------------------- 1 | package ct 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestChangeColor(t *testing.T) { 9 | defer ResetColor() 10 | fmt.Println("Normal text...") 11 | text := "This is an demo of using ChangeColor to output colorful texts" 12 | i := 1 13 | for _, c := range text { 14 | ChangeColor(Color(i/2%8)+Black, i%2 == 1, Color((i+2)/2%8)+Black, false) 15 | fmt.Print(string(c)) 16 | i++ 17 | } // for c 18 | fmt.Println() 19 | ChangeColor(Red, true, White, false) 20 | fmt.Println("Before reset.") 21 | ChangeColor(Red, false, White, true) 22 | fmt.Println("Before reset.") 23 | ResetColor() 24 | fmt.Println("After reset.") 25 | fmt.Println("After reset.") 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/daviddengcn/go-colortext/ct_win.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package ct 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | var fg_colors = []uint16{ 11 | 0, 12 | 0, 13 | foreground_red, 14 | foreground_green, 15 | foreground_red | foreground_green, 16 | foreground_blue, 17 | foreground_red | foreground_blue, 18 | foreground_green | foreground_blue, 19 | foreground_red | foreground_green | foreground_blue} 20 | 21 | var bg_colors = []uint16{ 22 | 0, 23 | 0, 24 | background_red, 25 | background_green, 26 | background_red | background_green, 27 | background_blue, 28 | background_red | background_blue, 29 | background_green | background_blue, 30 | background_red | background_green | background_blue} 31 | 32 | const ( 33 | foreground_blue = uint16(0x0001) 34 | foreground_green = uint16(0x0002) 35 | foreground_red = uint16(0x0004) 36 | foreground_intensity = uint16(0x0008) 37 | background_blue = uint16(0x0010) 38 | background_green = uint16(0x0020) 39 | background_red = uint16(0x0040) 40 | background_intensity = uint16(0x0080) 41 | 42 | foreground_mask = foreground_blue | foreground_green | foreground_red | foreground_intensity 43 | background_mask = background_blue | background_green | background_red | background_intensity 44 | ) 45 | 46 | var ( 47 | kernel32 = syscall.NewLazyDLL("kernel32.dll") 48 | 49 | procGetStdHandle = kernel32.NewProc("GetStdHandle") 50 | procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") 51 | procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 52 | 53 | hStdout uintptr 54 | initScreenInfo *console_screen_buffer_info 55 | ) 56 | 57 | func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { 58 | ret, _, _ := procSetConsoleTextAttribute.Call( 59 | hConsoleOutput, 60 | uintptr(wAttributes)) 61 | return ret != 0 62 | } 63 | 64 | type coord struct { 65 | X, Y int16 66 | } 67 | 68 | type small_rect struct { 69 | Left, Top, Right, Bottom int16 70 | } 71 | 72 | type console_screen_buffer_info struct { 73 | DwSize coord 74 | DwCursorPosition coord 75 | WAttributes uint16 76 | SrWindow small_rect 77 | DwMaximumWindowSize coord 78 | } 79 | 80 | func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *console_screen_buffer_info { 81 | var csbi console_screen_buffer_info 82 | ret, _, _ := procGetConsoleScreenBufferInfo.Call( 83 | hConsoleOutput, 84 | uintptr(unsafe.Pointer(&csbi))) 85 | if ret == 0 { 86 | return nil 87 | } 88 | return &csbi 89 | } 90 | 91 | const ( 92 | std_output_handle = uint32(-11 & 0xFFFFFFFF) 93 | ) 94 | 95 | func init() { 96 | kernel32 := syscall.NewLazyDLL("kernel32.dll") 97 | 98 | procGetStdHandle = kernel32.NewProc("GetStdHandle") 99 | 100 | hStdout, _, _ = procGetStdHandle.Call(uintptr(std_output_handle)) 101 | 102 | initScreenInfo = getConsoleScreenBufferInfo(hStdout) 103 | 104 | syscall.LoadDLL("") 105 | } 106 | 107 | func resetColor() { 108 | if initScreenInfo == nil { // No console info - Ex: stdout redirection 109 | return 110 | } 111 | setConsoleTextAttribute(hStdout, initScreenInfo.WAttributes) 112 | } 113 | 114 | func changeColor(fg Color, fgBright bool, bg Color, bgBright bool) { 115 | attr := uint16(0) 116 | if fg == None || bg == None { 117 | cbufinfo := getConsoleScreenBufferInfo(hStdout) 118 | if cbufinfo == nil { // No console info - Ex: stdout redirection 119 | return 120 | } 121 | attr = getConsoleScreenBufferInfo(hStdout).WAttributes 122 | } // if 123 | 124 | if fg != None { 125 | attr = attr & ^foreground_mask | fg_colors[fg] 126 | if fgBright { 127 | attr |= foreground_intensity 128 | } // if 129 | } // if 130 | 131 | if bg != None { 132 | attr = attr & ^background_mask | bg_colors[bg] 133 | if bgBright { 134 | attr |= background_intensity 135 | } // if 136 | } // if 137 | 138 | setConsoleTextAttribute(hStdout, attr) 139 | } 140 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ddollar/dist/Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/ddollar/dist", 3 | "GoVersion": "go1.4.1", 4 | "Packages": [ 5 | "." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "bitbucket.org/kardianos/osext", 10 | "Comment": "null-13", 11 | "Rev": "5d3ddcf53a508cc2f7404eaebf546ef2cb5cdb6e" 12 | }, 13 | { 14 | "ImportPath": "github.com/ddollar/go-update", 15 | "Rev": "4733469d35539f410e0e84552944fa0df7e72c98" 16 | }, 17 | { 18 | "ImportPath": "github.com/kr/binarydist", 19 | "Rev": "9955b0ab8708602d411341e55fffd7e0700f86bd" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ddollar/dist/Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ddollar/dist/dist.go: -------------------------------------------------------------------------------- 1 | // Handle updating new releases from a godist server 2 | package dist 3 | 4 | import ( 5 | "github.com/ddollar/forego/Godeps/_workspace/src/bitbucket.org/kardianos/osext" 6 | "bytes" 7 | "crypto/tls" 8 | "crypto/x509" 9 | "encoding/json" 10 | "encoding/pem" 11 | "errors" 12 | "fmt" 13 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/ddollar/go-update" 14 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/kr/binarydist" 15 | "io/ioutil" 16 | "net/http" 17 | "os" 18 | "runtime" 19 | "strings" 20 | ) 21 | 22 | var DigicertHighAssuranceCert = `-----BEGIN CERTIFICATE----- 23 | MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs 24 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 25 | d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j 26 | ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL 27 | MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 28 | LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug 29 | RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm 30 | +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW 31 | PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM 32 | xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB 33 | Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 34 | hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg 35 | EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF 36 | MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA 37 | FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec 38 | nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z 39 | eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF 40 | hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 41 | Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe 42 | vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep 43 | +OkuE6N36B9K 44 | -----END CERTIFICATE-----` 45 | 46 | type Dist struct { 47 | Host string 48 | Name string 49 | Project string 50 | Version string 51 | } 52 | 53 | type Release struct { 54 | Version string 55 | Url string 56 | } 57 | 58 | // Initialize a new godist client, speciying a project name 59 | // e.g. "ddollar/forego" 60 | func NewDist(project string, version string) (d *Dist) { 61 | d = new(Dist) 62 | d.Host = "https://godist.herokuapp.com" 63 | d.Name = strings.Split(project, "/")[1] 64 | d.Project = project 65 | d.Version = version 66 | return 67 | } 68 | 69 | // Update the currently running binary to the latest version 70 | func (d *Dist) Update() (to string, err error) { 71 | releases, err := d.fetchReleases() 72 | if len(releases) < 1 { 73 | return "", errors.New("no releases") 74 | } 75 | to = releases[0].Version 76 | return to, d.UpdateTo(to) 77 | } 78 | 79 | // Update the currently running binary to a specific version 80 | func (d *Dist) UpdateTo(to string) (err error) { 81 | if d.Version == to { 82 | return errors.New("nothing to update") 83 | } 84 | binary, _ := osext.Executable() 85 | reader, err := os.Open(binary) 86 | if err != nil { 87 | return err 88 | } 89 | defer reader.Close() 90 | url := fmt.Sprintf("%s/projects/%s/diff/%s/%s/%s-%s", d.Host, d.Project, d.Version, to, runtime.GOOS, runtime.GOARCH) 91 | patch, err := d.httpGet(url) 92 | if err != nil { 93 | return err 94 | } 95 | writer := new(bytes.Buffer) 96 | err = binarydist.Patch(reader, writer, bytes.NewReader(patch)) 97 | if err != nil { 98 | return err 99 | } 100 | reader.Close() 101 | err, _ = update.FromStream(writer) 102 | return 103 | } 104 | 105 | // Update to a specific version regardless of the starting version 106 | func (d *Dist) FullUpdate(to string) (err error) { 107 | url := fmt.Sprintf("%s/projects/%s/releases/%s/%s-%s/%s", d.Host, d.Project, to, runtime.GOOS, runtime.GOARCH, d.Name) 108 | reader, err := d.httpGet(url) 109 | if err != nil { 110 | return err 111 | } 112 | err, _ = update.FromStream(bytes.NewReader(reader)) 113 | return 114 | } 115 | 116 | func (d *Dist) fetchReleases() (releases []Release, err error) { 117 | body, err := d.httpGet(fmt.Sprintf("%s/projects/%s/releases/%s-%s", d.Host, d.Project, runtime.GOOS, runtime.GOARCH)) 118 | if err != nil { 119 | return nil, err 120 | } 121 | err = json.Unmarshal(body, &releases) 122 | return 123 | } 124 | 125 | func (d *Dist) httpClient() (client *http.Client) { 126 | chain := d.rootCertificate() 127 | config := tls.Config{} 128 | config.RootCAs = x509.NewCertPool() 129 | for _, cert := range chain.Certificate { 130 | x509Cert, err := x509.ParseCertificate(cert) 131 | if err != nil { 132 | panic(err) 133 | } 134 | config.RootCAs.AddCert(x509Cert) 135 | } 136 | config.BuildNameToCertificate() 137 | tr := http.Transport{TLSClientConfig: &config} 138 | client = &http.Client{Transport: &tr} 139 | return 140 | } 141 | 142 | func (d *Dist) httpGet(url string) ([]byte, error) { 143 | req, err := http.NewRequest("GET", url, nil) 144 | if err != nil { 145 | return nil, err 146 | } 147 | req.Header.Add("User-Agent", fmt.Sprintf("%s/%s dist/%s (%s-%s)", d.Name, d.Version, Version, runtime.GOOS, runtime.GOARCH)) 148 | res, err := d.httpClient().Do(req) 149 | defer res.Body.Close() 150 | if res.StatusCode != 200 { 151 | body, err := ioutil.ReadAll(res.Body) 152 | if err != nil { 153 | return nil, err 154 | } 155 | return nil, errors.New(string(body)) 156 | } 157 | if err != nil { 158 | return nil, err 159 | } 160 | return ioutil.ReadAll(res.Body) 161 | } 162 | 163 | func (d *Dist) updateFromUrl(url string) (err error) { 164 | client := d.httpClient() 165 | res, err := client.Get(url) 166 | if err != nil { 167 | return err 168 | } 169 | defer res.Body.Close() 170 | err, _ = update.FromStream(res.Body) 171 | return 172 | } 173 | 174 | func (d *Dist) rootCertificate() (cert tls.Certificate) { 175 | certPEMBlock := []byte(DigicertHighAssuranceCert) 176 | var certDERBlock *pem.Block 177 | for { 178 | certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) 179 | if certDERBlock == nil { 180 | break 181 | } 182 | if certDERBlock.Type == "CERTIFICATE" { 183 | cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) 184 | } 185 | } 186 | return 187 | } 188 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ddollar/dist/version.go: -------------------------------------------------------------------------------- 1 | package dist 2 | 3 | const ( 4 | Version = "0.2.0" 5 | ) 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ddollar/go-update/README: -------------------------------------------------------------------------------- 1 | PACKAGE DOCUMENTATION 2 | 3 | package update 4 | import "github.com/inconshreveable/go-update" 5 | 6 | Package update allows a program to "self-update", replacing its 7 | executable file with new bytes. 8 | 9 | Package update provides the facility to create user experiences like 10 | auto-updating or user-approved updates which manifest as user prompts in 11 | commercial applications with copy similar to "Restart to being using the 12 | new version of X". 13 | 14 | Updating your program to a new version is as easy as: 15 | 16 | err := update.FromUrl("http://release.example.com/2.0/myprogram") 17 | if err != nil { 18 | fmt.Printf("Update failed: %v", err) 19 | } 20 | 21 | The most low-level API is FromStream() which updates the current 22 | executable with the bytes read from an io.Reader. 23 | 24 | Additional APIs are provided for common update strategies which include 25 | updating from a file with FromFile() and updating from the internet with 26 | FromUrl(). 27 | 28 | Using the more advaced Download.UpdateFromUrl() API gives you the 29 | ability to resume an interrupted download to enable large updates to 30 | complete even over intermittent or slow connections. This API also 31 | enables more fine-grained control over how the update is downloaded from 32 | the internet as well as access to download progress, 33 | 34 | 35 | VARIABLES 36 | 37 | var ( 38 | // Returned when the remote server indicates that no download is available 39 | UpdateUnavailable error 40 | ) 41 | 42 | 43 | FUNCTIONS 44 | 45 | func FromFile(filepath string) (err error) 46 | FromFile reads the contents of the given file and uses them to update 47 | the current program's executable file by calling FromStream(). 48 | 49 | func FromStream(newBinary io.Reader) (err error) 50 | FromStream reads the contents of the supplied io.Reader newBinary and 51 | uses them to update the current program's executable file. 52 | 53 | FromStream performs the following actions to ensure a cross-platform 54 | safe update: 55 | 56 | - Renames the current program's executable file from 57 | /path/to/program-name to /path/to/.program-name.old 58 | 59 | - Opens the now-empty path /path/to/program-name with mode 0755 and 60 | copies the contents of newBinary into the file. 61 | 62 | - If the copy is successful, it erases /path/to/.program.old. If this 63 | operation fails, no error is reported. 64 | 65 | - If the copy is unsuccessful, it attempts to rename 66 | /path/to/.program-name.old back to /path/to/program-name. If this 67 | operation fails, the error is not reported in order to not mask the 68 | error that caused the rename recovery attempt. 69 | 70 | func FromUrl(url string) error 71 | FromUrl downloads the contents of the given url and uses them to update 72 | the current program's executable file. It is a convenience function 73 | which is equivalent to 74 | 75 | NewDownload().UpdateFromUrl(url) 76 | 77 | See Download.UpdateFromUrl for more details. 78 | 79 | 80 | TYPES 81 | 82 | type Download struct { 83 | // net/http.Client to use when downloading the update. 84 | // If nil, a default http.Client is used 85 | HttpClient *http.Client 86 | 87 | // Path on the file system to dowload the update to 88 | // If empty, a temporary file is used. 89 | // After the download begins, this path will be set 90 | // so that the client can use it to resume aborted 91 | // downloads 92 | Path string 93 | 94 | // Progress returns the percentage of the download 95 | // completed as an integer between 0 and 100 96 | Progress chan (int) 97 | 98 | // HTTP Method to use in the download request. Default is "GET" 99 | Method string 100 | } 101 | Type Download encapsulates the necessary parameters and state needed to 102 | download an update from the internet. Create an instance with the 103 | NewDownload() factory function. 104 | 105 | 106 | func NewDownload() *Download 107 | NewDownload initializes a new Download object 108 | 109 | 110 | func (d *Download) UpdateFromUrl(url string) (err error) 111 | UpdateFromUrl downloads the given url from the internet to a file on 112 | disk and then calls FromStream() to update the current program's 113 | executable file with the contents of that file. 114 | 115 | If the update is successful, the downloaded file will be erased from 116 | disk. Otherwise, it will remain in d.Path to allow the download to 117 | resume later or be skipped entirely. 118 | 119 | Only HTTP/1.1 servers that implement the Range header are supported. 120 | 121 | UpdateFromUrl() uses HTTP status codes to determine what action to take. 122 | 123 | - The HTTP server should return 200 or 206 for the update to be 124 | downloaded. 125 | 126 | - The HTTP server should return 204 if no update is available at this 127 | time. This will cause UpdateFromUrl to return the error 128 | UpdateUnavailable. 129 | 130 | - If the HTTP server returns a 3XX redirect, it will be followed 131 | according to d.HttpClient's redirect policy. 132 | 133 | - Any other HTTP status code will cause UpdateFromUrl to return an 134 | error. 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/ddollar/go-update/update.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package update allows a program to "self-update", replacing its executable file 3 | with new bytes. 4 | 5 | Package update provides the facility to create user experiences like auto-updating 6 | or user-approved updates which manifest as user prompts in commercial applications 7 | with copy similar to "Restart to being using the new version of X". 8 | 9 | Updating your program to a new version is as easy as: 10 | 11 | err := update.FromUrl("http://release.example.com/2.0/myprogram") 12 | if err != nil { 13 | fmt.Printf("Update failed: %v", err) 14 | } 15 | 16 | The most low-level API is FromStream() which updates the current executable 17 | with the bytes read from an io.Reader. 18 | 19 | Additional APIs are provided for common update strategies which include 20 | updating from a file with FromFile() and updating from the internet with 21 | FromUrl(). 22 | 23 | Using the more advaced Download.UpdateFromUrl() API gives you the ability 24 | to resume an interrupted download to enable large updates to complete even 25 | over intermittent or slow connections. This API also enables more fine-grained 26 | control over how the update is downloaded from the internet as well as access to 27 | download progress, 28 | */ 29 | package update 30 | 31 | import ( 32 | "github.com/ddollar/forego/Godeps/_workspace/src/bitbucket.org/kardianos/osext" 33 | "compress/gzip" 34 | "fmt" 35 | "io" 36 | "io/ioutil" 37 | "net/http" 38 | "os" 39 | "path/filepath" 40 | "runtime" 41 | ) 42 | 43 | type MeteredReader struct { 44 | rd io.ReadCloser 45 | totalSize int64 46 | progress chan int 47 | totalRead int64 48 | ticks int64 49 | } 50 | 51 | func (m *MeteredReader) Close() error { 52 | return m.rd.Close() 53 | } 54 | 55 | func (m *MeteredReader) Read(b []byte) (n int, err error) { 56 | chunkSize := (m.totalSize / 100) + 1 57 | lenB := int64(len(b)) 58 | 59 | var nChunk int 60 | for start := int64(0); start < lenB; start += int64(nChunk) { 61 | end := start + chunkSize 62 | if end > lenB { 63 | end = lenB 64 | } 65 | 66 | nChunk, err = m.rd.Read(b[start:end]) 67 | 68 | n += nChunk 69 | m.totalRead += int64(nChunk) 70 | 71 | if m.totalRead > (m.ticks * chunkSize) { 72 | m.ticks += 1 73 | // try to send on channel, but don't block if it's full 74 | select { 75 | case m.progress <- int(m.ticks + 1): 76 | default: 77 | } 78 | 79 | // give the progress channel consumer a chance to run 80 | runtime.Gosched() 81 | } 82 | 83 | if err != nil { 84 | return 85 | } 86 | } 87 | 88 | return 89 | } 90 | 91 | // We wrap the round tripper when making requests 92 | // because we need to add headers to the requests we make 93 | // even when they are requests made after a redirect 94 | type RoundTripper struct { 95 | RoundTripFn func(*http.Request) (*http.Response, error) 96 | } 97 | 98 | func (rt *RoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { 99 | return rt.RoundTripFn(r) 100 | } 101 | 102 | // Type Download encapsulates the necessary parameters and state 103 | // needed to download an update from the internet. Create an instance 104 | // with the NewDownload() factory function. 105 | // 106 | // You may only use a Download once, 107 | type Download struct { 108 | // net/http.Client to use when downloading the update. 109 | // If nil, a default http.Client is used 110 | HttpClient *http.Client 111 | 112 | // Path on the file system to dowload the update to 113 | // If empty, a temporary file is used. 114 | // After the download begins, this path will be set 115 | // so that the client can use it to resume aborted 116 | // downloads 117 | Path string 118 | 119 | // Progress returns the percentage of the download 120 | // completed as an integer between 0 and 100 121 | Progress chan (int) 122 | 123 | // HTTP Method to use in the download request. Default is "GET" 124 | Method string 125 | 126 | // HTTP URL to issue the download request to 127 | Url string 128 | 129 | // Set to true when the server confirms a new version is available 130 | // even if the updating process encounters an error later on 131 | Available bool 132 | } 133 | 134 | // NewDownload initializes a new Download object 135 | func NewDownload(url string) *Download { 136 | return &Download{ 137 | HttpClient: new(http.Client), 138 | Progress: make(chan int), 139 | Method: "GET", 140 | Url: url, 141 | } 142 | } 143 | 144 | func (d *Download) sharedHttp(offset int64) (resp *http.Response, err error) { 145 | // create the download request 146 | req, err := http.NewRequest(d.Method, d.Url, nil) 147 | if err != nil { 148 | return 149 | } 150 | 151 | // we have to add headers like this so they get used across redirects 152 | trans := d.HttpClient.Transport 153 | if trans == nil { 154 | trans = http.DefaultTransport 155 | } 156 | 157 | d.HttpClient.Transport = &RoundTripper{ 158 | RoundTripFn: func(r *http.Request) (*http.Response, error) { 159 | // add header for download continuation 160 | if offset > 0 { 161 | r.Header.Add("Range", fmt.Sprintf("%d-", offset)) 162 | } 163 | 164 | // ask for gzipped content so that net/http won't unzip it for us 165 | // and destroy the content length header we need for progress calculations 166 | r.Header.Add("Accept-Encoding", "gzip") 167 | 168 | return trans.RoundTrip(r) 169 | }, 170 | } 171 | 172 | // issue the download request 173 | return d.HttpClient.Do(req) 174 | } 175 | 176 | func (d *Download) Check() (available bool, err error) { 177 | resp, err := d.sharedHttp(0) 178 | if err != nil { 179 | return 180 | } 181 | resp.Body.Close() 182 | 183 | switch resp.StatusCode { 184 | // ok 185 | case 200, 206: 186 | available = true 187 | 188 | // no update available 189 | case 204: 190 | available = false 191 | 192 | // server error 193 | default: 194 | err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status) 195 | return 196 | } 197 | 198 | return 199 | } 200 | 201 | // Get() downloads the given url from the internet to a file on disk 202 | // and then calls FromStream() to update the current program's executable file 203 | // with the contents of that file. 204 | // 205 | // If the update is successful, the downloaded file will be erased from disk. 206 | // Otherwise, it will remain in d.Path to allow the download to resume later 207 | // or be skipped entirely. 208 | // 209 | // Only HTTP/1.1 servers that implement the Range header support resuming a 210 | // partially completed download. 211 | // 212 | // UpdateFromUrl() uses HTTP status codes to determine what action to take. 213 | // 214 | // - The HTTP server should return 200 or 206 for the update to be downloaded. 215 | // 216 | // - The HTTP server should return 204 if no update is available at this time. 217 | // 218 | // - If the HTTP server returns a 3XX redirect, it will be followed 219 | // according to d.HttpClient's redirect policy. 220 | // 221 | // - Any other HTTP status code will cause UpdateFromUrl to return an error. 222 | func (d *Download) Get() (err error) { 223 | var offset int64 = 0 224 | var fp *os.File 225 | 226 | // Close the progress channel whenever this function completes 227 | defer close(d.Progress) 228 | 229 | // open a file where we will stream the downloaded update to 230 | // we do this first because if the caller specified a non-empty dlpath 231 | // we need to determine how large it is in order to resume the download 232 | if d.Path == "" { 233 | // no dlpath specified, use a random tempfile 234 | fp, err = ioutil.TempFile("", "update") 235 | if err != nil { 236 | return 237 | } 238 | defer fp.Close() 239 | 240 | // remember the path 241 | d.Path = fp.Name() 242 | } else { 243 | fp, err = os.OpenFile(d.Path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0600) 244 | if err != nil { 245 | return 246 | } 247 | defer fp.Close() 248 | 249 | // determine the file size so we can resume the download, if possible 250 | var fi os.FileInfo 251 | fi, err = fp.Stat() 252 | if err != nil { 253 | return 254 | } 255 | 256 | offset = fi.Size() 257 | } 258 | 259 | // start downloading the file 260 | resp, err := d.sharedHttp(offset) 261 | if err != nil { 262 | return 263 | } 264 | defer resp.Body.Close() 265 | 266 | switch resp.StatusCode { 267 | // ok 268 | case 200, 206: 269 | d.Available = true 270 | 271 | // no update available 272 | case 204: 273 | return 274 | 275 | // server error 276 | default: 277 | err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status) 278 | return 279 | } 280 | 281 | // Determine how much we have to download 282 | // net/http sets this to -1 when it is unknown 283 | clength := resp.ContentLength 284 | 285 | // Read the content from the response body 286 | rd := resp.Body 287 | 288 | // meter the rate at which we download content for 289 | // progress reporting if we know how much to expect 290 | if clength > 0 { 291 | rd = &MeteredReader{rd: rd, totalSize: clength, progress: d.Progress} 292 | } 293 | 294 | // Decompress the content if necessary 295 | if resp.Header.Get("Content-Encoding") == "gzip" { 296 | rd, err = gzip.NewReader(rd) 297 | if err != nil { 298 | return 299 | } 300 | } 301 | 302 | // Download the update 303 | _, err = io.Copy(fp, rd) 304 | if err != nil { 305 | return 306 | } 307 | 308 | return 309 | } 310 | 311 | func (d *Download) GetAndUpdate() (err error, errRecover error) { 312 | // check before we download if this will work 313 | if err = SanityCheck(); err != nil { 314 | // keep the contract that d.Progress will close whenever Get() terminates 315 | close(d.Progress) 316 | return 317 | } 318 | 319 | // download the update 320 | if err = d.Get(); err != nil || !d.Available { 321 | return 322 | } 323 | 324 | // apply the update 325 | if err, errRecover = FromFile(d.Path); err != nil || errRecover != nil { 326 | return 327 | } 328 | 329 | // remove the temporary file 330 | os.Remove(d.Path) 331 | return 332 | } 333 | 334 | // FromUrl downloads the contents of the given url and uses them to update 335 | // the current program's executable file. It is a convenience function which is equivalent to 336 | // 337 | // NewDownload(url).GetAndUpdate() 338 | // 339 | // See Download.Get() for more details. 340 | func FromUrl(url string) (err error, errRecover error) { 341 | return NewDownload(url).GetAndUpdate() 342 | } 343 | 344 | // FromFile reads the contents of the given file and uses them 345 | // to update the current program's executable file by calling FromStream(). 346 | func FromFile(filepath string) (err error, errRecover error) { 347 | // open the new binary 348 | fp, err := os.Open(filepath) 349 | if err != nil { 350 | return 351 | } 352 | defer fp.Close() 353 | 354 | // do the update 355 | return FromStream(fp) 356 | } 357 | 358 | // FromStream reads the contents of the supplied io.Reader newBinary 359 | // and uses them to update the current program's executable file. 360 | // 361 | // FromStream performs the following actions to ensure a cross-platform safe 362 | // update: 363 | // 364 | // - Creates a new file, /path/to/.program-name.new with mode 0755 and copies 365 | // the contents of newBinary into the file 366 | // 367 | // - Renames the current program's executable file from /path/to/program-name 368 | // to /path/to/.program-name.old 369 | // 370 | // - Renames /path/to/.program-name.new to /path/to/program-name 371 | // 372 | // - If the rename is successful, it erases /path/to/.program.old. If this operation 373 | // fails, no error is reported. 374 | // 375 | // - If the rename is unsuccessful, it attempts to rename /path/to/.program-name.old 376 | // back to /path/to/program-name. If this operation fails, the error is not reported 377 | // in order to not mask the error that caused the rename recovery attempt. 378 | func FromStream(newBinary io.Reader) (err error, errRecover error) { 379 | // get the path to the executable 380 | thisExecPath, err := osext.Executable() 381 | if err != nil { 382 | return 383 | } 384 | 385 | // get the directory the executable exists in 386 | execDir := filepath.Dir(thisExecPath) 387 | execName := filepath.Base(thisExecPath) 388 | 389 | // Copy the contents of of newbinary to a the new executable file 390 | newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName)) 391 | fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) 392 | if err != nil { 393 | return 394 | } 395 | defer fp.Close() 396 | _, err = io.Copy(fp, newBinary) 397 | 398 | // if we don't call fp.Close(), windows won't let us move the new executable 399 | // because the file will still be "in use" 400 | fp.Close() 401 | 402 | // this is where we'll move the executable to so that we can swap in the updated replacement 403 | oldExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.old", execName)) 404 | 405 | // delete any existing old exec file - this is necessary on Windows for two reasons: 406 | // 1. after a successful update, windows can't remove the .old file because the process is still running 407 | // 2. windows rename operations fail if the destination file already exists 408 | _ = os.Remove(oldExecPath) 409 | 410 | // move the existing executable to a new file in the same directory 411 | err = os.Rename(thisExecPath, oldExecPath) 412 | if err != nil { 413 | return 414 | } 415 | 416 | // move the new exectuable in to become the new program 417 | err = os.Rename(newExecPath, thisExecPath) 418 | 419 | if err != nil { 420 | // copy unsuccessful 421 | errRecover = os.Rename(oldExecPath, thisExecPath) 422 | } else { 423 | // copy successful, remove the old binary 424 | _ = os.Remove(oldExecPath) 425 | } 426 | 427 | return 428 | } 429 | 430 | // SanityCheck() attempts to determine whether an in-place executable update could 431 | // succeed by performing preliminary checks (to establish valid permissions, etc). 432 | // This helps avoid downloading updates when we know the update can't be successfully 433 | // applied later. 434 | func SanityCheck() (err error) { 435 | // get the path to the executable 436 | thisExecPath, err := osext.Executable() 437 | if err != nil { 438 | return 439 | } 440 | 441 | // get the directory the executable exists in 442 | execDir := filepath.Dir(thisExecPath) 443 | execName := filepath.Base(thisExecPath) 444 | 445 | // attempt to open a file in the executable's directory 446 | newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName)) 447 | fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) 448 | if err != nil { 449 | return 450 | } 451 | fp.Close() 452 | 453 | _ = os.Remove(newExecPath) 454 | return 455 | } 456 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/.gitignore: -------------------------------------------------------------------------------- 1 | test.* 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/License: -------------------------------------------------------------------------------- 1 | Copyright 2012 Keith Rarick 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/Readme.md: -------------------------------------------------------------------------------- 1 | # binarydist 2 | 3 | Package binarydist implements binary diff and patch as described on 4 | . It reads and writes files 5 | compatible with the tools there. 6 | 7 | Documentation at . 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/bzip2.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | import ( 4 | "io" 5 | "os/exec" 6 | ) 7 | 8 | type bzip2Writer struct { 9 | c *exec.Cmd 10 | w io.WriteCloser 11 | } 12 | 13 | func (w bzip2Writer) Write(b []byte) (int, error) { 14 | return w.w.Write(b) 15 | } 16 | 17 | func (w bzip2Writer) Close() error { 18 | if err := w.w.Close(); err != nil { 19 | return err 20 | } 21 | return w.c.Wait() 22 | } 23 | 24 | // Package compress/bzip2 implements only decompression, 25 | // so we'll fake it by running bzip2 in another process. 26 | func newBzip2Writer(w io.Writer) (wc io.WriteCloser, err error) { 27 | var bw bzip2Writer 28 | bw.c = exec.Command("bzip2", "-c") 29 | bw.c.Stdout = w 30 | 31 | if bw.w, err = bw.c.StdinPipe(); err != nil { 32 | return nil, err 33 | } 34 | 35 | if err = bw.c.Start(); err != nil { 36 | return nil, err 37 | } 38 | 39 | return bw, nil 40 | } 41 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/common_test.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | ) 9 | 10 | func mustOpen(path string) *os.File { 11 | f, err := os.Open(path) 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | return f 17 | } 18 | 19 | func mustReadAll(r io.Reader) []byte { 20 | b, err := ioutil.ReadAll(r) 21 | if err != nil { 22 | panic(err) 23 | } 24 | return b 25 | } 26 | 27 | func fileCmp(a, b *os.File) int64 { 28 | sa, err := a.Seek(0, 2) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | sb, err := b.Seek(0, 2) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | if sa != sb { 39 | return sa 40 | } 41 | 42 | _, err = a.Seek(0, 0) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | _, err = b.Seek(0, 0) 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | pa, err := ioutil.ReadAll(a) 53 | if err != nil { 54 | panic(err) 55 | } 56 | 57 | pb, err := ioutil.ReadAll(b) 58 | if err != nil { 59 | panic(err) 60 | } 61 | 62 | for i := range pa { 63 | if pa[i] != pb[i] { 64 | return int64(i) 65 | } 66 | } 67 | return -1 68 | } 69 | 70 | func mustWriteRandFile(path string, size int) *os.File { 71 | p := make([]byte, size) 72 | _, err := rand.Read(p) 73 | if err != nil { 74 | panic(err) 75 | } 76 | 77 | f, err := os.Create(path) 78 | if err != nil { 79 | panic(err) 80 | } 81 | 82 | _, err = f.Write(p) 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | _, err = f.Seek(0, 0) 88 | if err != nil { 89 | panic(err) 90 | } 91 | 92 | return f 93 | } 94 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/diff.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "io/ioutil" 8 | ) 9 | 10 | func swap(a []int, i, j int) { a[i], a[j] = a[j], a[i] } 11 | 12 | func split(I, V []int, start, length, h int) { 13 | var i, j, k, x, jj, kk int 14 | 15 | if length < 16 { 16 | for k = start; k < start+length; k += j { 17 | j = 1 18 | x = V[I[k]+h] 19 | for i = 1; k+i < start+length; i++ { 20 | if V[I[k+i]+h] < x { 21 | x = V[I[k+i]+h] 22 | j = 0 23 | } 24 | if V[I[k+i]+h] == x { 25 | swap(I, k+i, k+j) 26 | j++ 27 | } 28 | } 29 | for i = 0; i < j; i++ { 30 | V[I[k+i]] = k + j - 1 31 | } 32 | if j == 1 { 33 | I[k] = -1 34 | } 35 | } 36 | return 37 | } 38 | 39 | x = V[I[start+length/2]+h] 40 | jj = 0 41 | kk = 0 42 | for i = start; i < start+length; i++ { 43 | if V[I[i]+h] < x { 44 | jj++ 45 | } 46 | if V[I[i]+h] == x { 47 | kk++ 48 | } 49 | } 50 | jj += start 51 | kk += jj 52 | 53 | i = start 54 | j = 0 55 | k = 0 56 | for i < jj { 57 | if V[I[i]+h] < x { 58 | i++ 59 | } else if V[I[i]+h] == x { 60 | swap(I, i, jj+j) 61 | j++ 62 | } else { 63 | swap(I, i, kk+k) 64 | k++ 65 | } 66 | } 67 | 68 | for jj+j < kk { 69 | if V[I[jj+j]+h] == x { 70 | j++ 71 | } else { 72 | swap(I, jj+j, kk+k) 73 | k++ 74 | } 75 | } 76 | 77 | if jj > start { 78 | split(I, V, start, jj-start, h) 79 | } 80 | 81 | for i = 0; i < kk-jj; i++ { 82 | V[I[jj+i]] = kk - 1 83 | } 84 | if jj == kk-1 { 85 | I[jj] = -1 86 | } 87 | 88 | if start+length > kk { 89 | split(I, V, kk, start+length-kk, h) 90 | } 91 | } 92 | 93 | func qsufsort(obuf []byte) []int { 94 | var buckets [256]int 95 | var i, h int 96 | I := make([]int, len(obuf)+1) 97 | V := make([]int, len(obuf)+1) 98 | 99 | for _, c := range obuf { 100 | buckets[c]++ 101 | } 102 | for i = 1; i < 256; i++ { 103 | buckets[i] += buckets[i-1] 104 | } 105 | copy(buckets[1:], buckets[:]) 106 | buckets[0] = 0 107 | 108 | for i, c := range obuf { 109 | buckets[c]++ 110 | I[buckets[c]] = i 111 | } 112 | 113 | I[0] = len(obuf) 114 | for i, c := range obuf { 115 | V[i] = buckets[c] 116 | } 117 | 118 | V[len(obuf)] = 0 119 | for i = 1; i < 256; i++ { 120 | if buckets[i] == buckets[i-1]+1 { 121 | I[buckets[i]] = -1 122 | } 123 | } 124 | I[0] = -1 125 | 126 | for h = 1; I[0] != -(len(obuf) + 1); h += h { 127 | var n int 128 | for i = 0; i < len(obuf)+1; { 129 | if I[i] < 0 { 130 | n -= I[i] 131 | i -= I[i] 132 | } else { 133 | if n != 0 { 134 | I[i-n] = -n 135 | } 136 | n = V[I[i]] + 1 - i 137 | split(I, V, i, n, h) 138 | i += n 139 | n = 0 140 | } 141 | } 142 | if n != 0 { 143 | I[i-n] = -n 144 | } 145 | } 146 | 147 | for i = 0; i < len(obuf)+1; i++ { 148 | I[V[i]] = i 149 | } 150 | return I 151 | } 152 | 153 | func matchlen(a, b []byte) (i int) { 154 | for i < len(a) && i < len(b) && a[i] == b[i] { 155 | i++ 156 | } 157 | return i 158 | } 159 | 160 | func search(I []int, obuf, nbuf []byte, st, en int) (pos, n int) { 161 | if en-st < 2 { 162 | x := matchlen(obuf[I[st]:], nbuf) 163 | y := matchlen(obuf[I[en]:], nbuf) 164 | 165 | if x > y { 166 | return I[st], x 167 | } else { 168 | return I[en], y 169 | } 170 | } 171 | 172 | x := st + (en-st)/2 173 | if bytes.Compare(obuf[I[x]:], nbuf) < 0 { 174 | return search(I, obuf, nbuf, x, en) 175 | } else { 176 | return search(I, obuf, nbuf, st, x) 177 | } 178 | panic("unreached") 179 | } 180 | 181 | // Diff computes the difference between old and new, according to the bsdiff 182 | // algorithm, and writes the result to patch. 183 | func Diff(old, new io.Reader, patch io.Writer) error { 184 | obuf, err := ioutil.ReadAll(old) 185 | if err != nil { 186 | return err 187 | } 188 | 189 | nbuf, err := ioutil.ReadAll(new) 190 | if err != nil { 191 | return err 192 | } 193 | 194 | pbuf, err := diffBytes(obuf, nbuf) 195 | if err != nil { 196 | return err 197 | } 198 | 199 | _, err = patch.Write(pbuf) 200 | return err 201 | } 202 | 203 | func diffBytes(obuf, nbuf []byte) ([]byte, error) { 204 | var patch seekBuffer 205 | err := diff(obuf, nbuf, &patch) 206 | if err != nil { 207 | return nil, err 208 | } 209 | return patch.buf, nil 210 | } 211 | 212 | func diff(obuf, nbuf []byte, patch io.WriteSeeker) error { 213 | var lenf int 214 | I := qsufsort(obuf) 215 | db := make([]byte, len(nbuf)) 216 | eb := make([]byte, len(nbuf)) 217 | var dblen, eblen int 218 | 219 | var hdr header 220 | hdr.Magic = magic 221 | hdr.NewSize = int64(len(nbuf)) 222 | err := binary.Write(patch, signMagLittleEndian{}, &hdr) 223 | if err != nil { 224 | return err 225 | } 226 | 227 | // Compute the differences, writing ctrl as we go 228 | pfbz2, err := newBzip2Writer(patch) 229 | if err != nil { 230 | return err 231 | } 232 | var scan, pos, length int 233 | var lastscan, lastpos, lastoffset int 234 | for scan < len(nbuf) { 235 | var oldscore int 236 | scan += length 237 | for scsc := scan; scan < len(nbuf); scan++ { 238 | pos, length = search(I, obuf, nbuf[scan:], 0, len(obuf)) 239 | 240 | for ; scsc < scan+length; scsc++ { 241 | if scsc+lastoffset < len(obuf) && 242 | obuf[scsc+lastoffset] == nbuf[scsc] { 243 | oldscore++ 244 | } 245 | } 246 | 247 | if (length == oldscore && length != 0) || length > oldscore+8 { 248 | break 249 | } 250 | 251 | if scan+lastoffset < len(obuf) && obuf[scan+lastoffset] == nbuf[scan] { 252 | oldscore-- 253 | } 254 | } 255 | 256 | if length != oldscore || scan == len(nbuf) { 257 | var s, Sf int 258 | lenf = 0 259 | for i := 0; lastscan+i < scan && lastpos+i < len(obuf); { 260 | if obuf[lastpos+i] == nbuf[lastscan+i] { 261 | s++ 262 | } 263 | i++ 264 | if s*2-i > Sf*2-lenf { 265 | Sf = s 266 | lenf = i 267 | } 268 | } 269 | 270 | lenb := 0 271 | if scan < len(nbuf) { 272 | var s, Sb int 273 | for i := 1; (scan >= lastscan+i) && (pos >= i); i++ { 274 | if obuf[pos-i] == nbuf[scan-i] { 275 | s++ 276 | } 277 | if s*2-i > Sb*2-lenb { 278 | Sb = s 279 | lenb = i 280 | } 281 | } 282 | } 283 | 284 | if lastscan+lenf > scan-lenb { 285 | overlap := (lastscan + lenf) - (scan - lenb) 286 | s := 0 287 | Ss := 0 288 | lens := 0 289 | for i := 0; i < overlap; i++ { 290 | if nbuf[lastscan+lenf-overlap+i] == obuf[lastpos+lenf-overlap+i] { 291 | s++ 292 | } 293 | if nbuf[scan-lenb+i] == obuf[pos-lenb+i] { 294 | s-- 295 | } 296 | if s > Ss { 297 | Ss = s 298 | lens = i + 1 299 | } 300 | } 301 | 302 | lenf += lens - overlap 303 | lenb -= lens 304 | } 305 | 306 | for i := 0; i < lenf; i++ { 307 | db[dblen+i] = nbuf[lastscan+i] - obuf[lastpos+i] 308 | } 309 | for i := 0; i < (scan-lenb)-(lastscan+lenf); i++ { 310 | eb[eblen+i] = nbuf[lastscan+lenf+i] 311 | } 312 | 313 | dblen += lenf 314 | eblen += (scan - lenb) - (lastscan + lenf) 315 | 316 | err = binary.Write(pfbz2, signMagLittleEndian{}, int64(lenf)) 317 | if err != nil { 318 | pfbz2.Close() 319 | return err 320 | } 321 | 322 | val := (scan - lenb) - (lastscan + lenf) 323 | err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val)) 324 | if err != nil { 325 | pfbz2.Close() 326 | return err 327 | } 328 | 329 | val = (pos - lenb) - (lastpos + lenf) 330 | err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val)) 331 | if err != nil { 332 | pfbz2.Close() 333 | return err 334 | } 335 | 336 | lastscan = scan - lenb 337 | lastpos = pos - lenb 338 | lastoffset = pos - scan 339 | } 340 | } 341 | err = pfbz2.Close() 342 | if err != nil { 343 | return err 344 | } 345 | 346 | // Compute size of compressed ctrl data 347 | l64, err := patch.Seek(0, 1) 348 | if err != nil { 349 | return err 350 | } 351 | hdr.CtrlLen = int64(l64 - 32) 352 | 353 | // Write compressed diff data 354 | pfbz2, err = newBzip2Writer(patch) 355 | if err != nil { 356 | return err 357 | } 358 | n, err := pfbz2.Write(db[:dblen]) 359 | if err != nil { 360 | pfbz2.Close() 361 | return err 362 | } 363 | if n != dblen { 364 | pfbz2.Close() 365 | return io.ErrShortWrite 366 | } 367 | err = pfbz2.Close() 368 | if err != nil { 369 | return err 370 | } 371 | 372 | // Compute size of compressed diff data 373 | n64, err := patch.Seek(0, 1) 374 | if err != nil { 375 | return err 376 | } 377 | hdr.DiffLen = n64 - l64 378 | 379 | // Write compressed extra data 380 | pfbz2, err = newBzip2Writer(patch) 381 | if err != nil { 382 | return err 383 | } 384 | n, err = pfbz2.Write(eb[:eblen]) 385 | if err != nil { 386 | pfbz2.Close() 387 | return err 388 | } 389 | if n != eblen { 390 | pfbz2.Close() 391 | return io.ErrShortWrite 392 | } 393 | err = pfbz2.Close() 394 | if err != nil { 395 | return err 396 | } 397 | 398 | // Seek to the beginning, write the header, and close the file 399 | _, err = patch.Seek(0, 0) 400 | if err != nil { 401 | return err 402 | } 403 | err = binary.Write(patch, signMagLittleEndian{}, &hdr) 404 | if err != nil { 405 | return err 406 | } 407 | return nil 408 | } 409 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/diff_test.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "os" 7 | "os/exec" 8 | "testing" 9 | ) 10 | 11 | var diffT = []struct { 12 | old *os.File 13 | new *os.File 14 | }{ 15 | { 16 | old: mustWriteRandFile("test.old", 1e3), 17 | new: mustWriteRandFile("test.new", 1e3), 18 | }, 19 | { 20 | old: mustOpen("testdata/sample.old"), 21 | new: mustOpen("testdata/sample.new"), 22 | }, 23 | } 24 | 25 | func TestDiff(t *testing.T) { 26 | for _, s := range diffT { 27 | got, err := ioutil.TempFile("/tmp", "bspatch.") 28 | if err != nil { 29 | panic(err) 30 | } 31 | os.Remove(got.Name()) 32 | 33 | exp, err := ioutil.TempFile("/tmp", "bspatch.") 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | cmd := exec.Command("bsdiff", s.old.Name(), s.new.Name(), exp.Name()) 39 | cmd.Stdout = os.Stdout 40 | err = cmd.Run() 41 | os.Remove(exp.Name()) 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | err = Diff(s.old, s.new, got) 47 | if err != nil { 48 | t.Fatal("err", err) 49 | } 50 | 51 | _, err = got.Seek(0, 0) 52 | if err != nil { 53 | panic(err) 54 | } 55 | gotBuf := mustReadAll(got) 56 | expBuf := mustReadAll(exp) 57 | 58 | if !bytes.Equal(gotBuf, expBuf) { 59 | t.Fail() 60 | t.Logf("diff %s %s", s.old.Name(), s.new.Name()) 61 | t.Logf("%s: len(got) = %d", got.Name(), len(gotBuf)) 62 | t.Logf("%s: len(exp) = %d", exp.Name(), len(expBuf)) 63 | i := matchlen(gotBuf, expBuf) 64 | t.Logf("produced different output at pos %d; %d != %d", i, gotBuf[i], expBuf[i]) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/doc.go: -------------------------------------------------------------------------------- 1 | // Package binarydist implements binary diff and patch as described on 2 | // http://www.daemonology.net/bsdiff/. It reads and writes files 3 | // compatible with the tools there. 4 | package binarydist 5 | 6 | var magic = [8]byte{'B', 'S', 'D', 'I', 'F', 'F', '4', '0'} 7 | 8 | // File format: 9 | // 0 8 "BSDIFF40" 10 | // 8 8 X 11 | // 16 8 Y 12 | // 24 8 sizeof(newfile) 13 | // 32 X bzip2(control block) 14 | // 32+X Y bzip2(diff block) 15 | // 32+X+Y ??? bzip2(extra block) 16 | // with control block a set of triples (x,y,z) meaning "add x bytes 17 | // from oldfile to x bytes from the diff block; copy y bytes from the 18 | // extra block; seek forwards in oldfile by z bytes". 19 | type header struct { 20 | Magic [8]byte 21 | CtrlLen int64 22 | DiffLen int64 23 | NewSize int64 24 | } 25 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/encoding.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | // SignMagLittleEndian is the numeric encoding used by the bsdiff tools. 4 | // It implements binary.ByteOrder using a sign-magnitude format 5 | // and little-endian byte order. Only methods Uint64 and String 6 | // have been written; the rest panic. 7 | type signMagLittleEndian struct{} 8 | 9 | func (signMagLittleEndian) Uint16(b []byte) uint16 { panic("unimplemented") } 10 | 11 | func (signMagLittleEndian) PutUint16(b []byte, v uint16) { panic("unimplemented") } 12 | 13 | func (signMagLittleEndian) Uint32(b []byte) uint32 { panic("unimplemented") } 14 | 15 | func (signMagLittleEndian) PutUint32(b []byte, v uint32) { panic("unimplemented") } 16 | 17 | func (signMagLittleEndian) Uint64(b []byte) uint64 { 18 | y := int64(b[0]) | 19 | int64(b[1])<<8 | 20 | int64(b[2])<<16 | 21 | int64(b[3])<<24 | 22 | int64(b[4])<<32 | 23 | int64(b[5])<<40 | 24 | int64(b[6])<<48 | 25 | int64(b[7]&0x7f)<<56 26 | 27 | if b[7]&0x80 != 0 { 28 | y = -y 29 | } 30 | return uint64(y) 31 | } 32 | 33 | func (signMagLittleEndian) PutUint64(b []byte, v uint64) { 34 | x := int64(v) 35 | neg := x < 0 36 | if neg { 37 | x = -x 38 | } 39 | 40 | b[0] = byte(x) 41 | b[1] = byte(x >> 8) 42 | b[2] = byte(x >> 16) 43 | b[3] = byte(x >> 24) 44 | b[4] = byte(x >> 32) 45 | b[5] = byte(x >> 40) 46 | b[6] = byte(x >> 48) 47 | b[7] = byte(x >> 56) 48 | if neg { 49 | b[7] |= 0x80 50 | } 51 | } 52 | 53 | func (signMagLittleEndian) String() string { return "signMagLittleEndian" } 54 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/patch.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | import ( 4 | "bytes" 5 | "compress/bzip2" 6 | "encoding/binary" 7 | "errors" 8 | "io" 9 | "io/ioutil" 10 | ) 11 | 12 | var ErrCorrupt = errors.New("corrupt patch") 13 | 14 | // Patch applies patch to old, according to the bspatch algorithm, 15 | // and writes the result to new. 16 | func Patch(old io.Reader, new io.Writer, patch io.Reader) error { 17 | var hdr header 18 | err := binary.Read(patch, signMagLittleEndian{}, &hdr) 19 | if err != nil { 20 | return err 21 | } 22 | if hdr.Magic != magic { 23 | return ErrCorrupt 24 | } 25 | if hdr.CtrlLen < 0 || hdr.DiffLen < 0 || hdr.NewSize < 0 { 26 | return ErrCorrupt 27 | } 28 | 29 | ctrlbuf := make([]byte, hdr.CtrlLen) 30 | _, err = io.ReadFull(patch, ctrlbuf) 31 | if err != nil { 32 | return err 33 | } 34 | cpfbz2 := bzip2.NewReader(bytes.NewReader(ctrlbuf)) 35 | 36 | diffbuf := make([]byte, hdr.DiffLen) 37 | _, err = io.ReadFull(patch, diffbuf) 38 | if err != nil { 39 | return err 40 | } 41 | dpfbz2 := bzip2.NewReader(bytes.NewReader(diffbuf)) 42 | 43 | // The entire rest of the file is the extra block. 44 | epfbz2 := bzip2.NewReader(patch) 45 | 46 | obuf, err := ioutil.ReadAll(old) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | nbuf := make([]byte, hdr.NewSize) 52 | 53 | var oldpos, newpos int64 54 | for newpos < hdr.NewSize { 55 | var ctrl struct{ Add, Copy, Seek int64 } 56 | err = binary.Read(cpfbz2, signMagLittleEndian{}, &ctrl) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | // Sanity-check 62 | if newpos+ctrl.Add > hdr.NewSize { 63 | return ErrCorrupt 64 | } 65 | 66 | // Read diff string 67 | _, err = io.ReadFull(dpfbz2, nbuf[newpos:newpos+ctrl.Add]) 68 | if err != nil { 69 | return ErrCorrupt 70 | } 71 | 72 | // Add old data to diff string 73 | for i := int64(0); i < ctrl.Add; i++ { 74 | if oldpos+i >= 0 && oldpos+i < int64(len(obuf)) { 75 | nbuf[newpos+i] += obuf[oldpos+i] 76 | } 77 | } 78 | 79 | // Adjust pointers 80 | newpos += ctrl.Add 81 | oldpos += ctrl.Add 82 | 83 | // Sanity-check 84 | if newpos+ctrl.Copy > hdr.NewSize { 85 | return ErrCorrupt 86 | } 87 | 88 | // Read extra string 89 | _, err = io.ReadFull(epfbz2, nbuf[newpos:newpos+ctrl.Copy]) 90 | if err != nil { 91 | return ErrCorrupt 92 | } 93 | 94 | // Adjust pointers 95 | newpos += ctrl.Copy 96 | oldpos += ctrl.Seek 97 | } 98 | 99 | // Write the new file 100 | for len(nbuf) > 0 { 101 | n, err := new.Write(nbuf) 102 | if err != nil { 103 | return err 104 | } 105 | nbuf = nbuf[n:] 106 | } 107 | 108 | return nil 109 | } 110 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/patch_test.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "os/exec" 7 | "testing" 8 | ) 9 | 10 | func TestPatch(t *testing.T) { 11 | mustWriteRandFile("test.old", 1e3) 12 | mustWriteRandFile("test.new", 1e3) 13 | 14 | got, err := ioutil.TempFile("/tmp", "bspatch.") 15 | if err != nil { 16 | panic(err) 17 | } 18 | os.Remove(got.Name()) 19 | 20 | err = exec.Command("bsdiff", "test.old", "test.new", "test.patch").Run() 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | err = Patch(mustOpen("test.old"), got, mustOpen("test.patch")) 26 | if err != nil { 27 | t.Fatal("err", err) 28 | } 29 | 30 | ref, err := got.Seek(0, 2) 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | t.Logf("got %d bytes", ref) 36 | if n := fileCmp(got, mustOpen("test.new")); n > -1 { 37 | t.Fatalf("produced different output at pos %d", n) 38 | } 39 | } 40 | 41 | func TestPatchHk(t *testing.T) { 42 | got, err := ioutil.TempFile("/tmp", "bspatch.") 43 | if err != nil { 44 | panic(err) 45 | } 46 | os.Remove(got.Name()) 47 | 48 | err = Patch(mustOpen("testdata/sample.old"), got, mustOpen("testdata/sample.patch")) 49 | if err != nil { 50 | t.Fatal("err", err) 51 | } 52 | 53 | ref, err := got.Seek(0, 2) 54 | if err != nil { 55 | panic(err) 56 | } 57 | 58 | t.Logf("got %d bytes", ref) 59 | if n := fileCmp(got, mustOpen("testdata/sample.new")); n > -1 { 60 | t.Fatalf("produced different output at pos %d", n) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/seek.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type seekBuffer struct { 8 | buf []byte 9 | pos int 10 | } 11 | 12 | func (b *seekBuffer) Write(p []byte) (n int, err error) { 13 | n = copy(b.buf[b.pos:], p) 14 | if n == len(p) { 15 | b.pos += n 16 | return n, nil 17 | } 18 | b.buf = append(b.buf, p[n:]...) 19 | b.pos += len(p) 20 | return len(p), nil 21 | } 22 | 23 | func (b *seekBuffer) Seek(offset int64, whence int) (ret int64, err error) { 24 | var abs int64 25 | switch whence { 26 | case 0: 27 | abs = offset 28 | case 1: 29 | abs = int64(b.pos) + offset 30 | case 2: 31 | abs = int64(len(b.buf)) + offset 32 | default: 33 | return 0, errors.New("binarydist: invalid whence") 34 | } 35 | if abs < 0 { 36 | return 0, errors.New("binarydist: negative position") 37 | } 38 | if abs >= 1<<31 { 39 | return 0, errors.New("binarydist: position out of range") 40 | } 41 | b.pos = int(abs) 42 | return abs, nil 43 | } 44 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/sort_test.go: -------------------------------------------------------------------------------- 1 | package binarydist 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "testing" 7 | ) 8 | 9 | var sortT = [][]byte{ 10 | mustRandBytes(1000), 11 | mustReadAll(mustOpen("test.old")), 12 | []byte("abcdefabcdef"), 13 | } 14 | 15 | func TestQsufsort(t *testing.T) { 16 | for _, s := range sortT { 17 | I := qsufsort(s) 18 | for i := 1; i < len(I); i++ { 19 | if bytes.Compare(s[I[i-1]:], s[I[i]:]) > 0 { 20 | t.Fatalf("unsorted at %d", i) 21 | } 22 | } 23 | } 24 | } 25 | 26 | func mustRandBytes(n int) []byte { 27 | b := make([]byte, n) 28 | _, err := rand.Read(b) 29 | if err != nil { 30 | panic(err) 31 | } 32 | return b 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.new: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwilder/forego/c69a0e4a4a03bd618db22b2e740605eb361de9d3/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.new -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwilder/forego/c69a0e4a4a03bd618db22b2e740605eb361de9d3/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.old -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.patch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwilder/forego/c69a0e4a4a03bd618db22b2e740605eb361de9d3/Godeps/_workspace/src/github.com/kr/binarydist/testdata/sample.patch -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/.gitignore: -------------------------------------------------------------------------------- 1 | [568].out 2 | _go* 3 | _test* 4 | _obj 5 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/License: -------------------------------------------------------------------------------- 1 | Copyright 2012 Keith Rarick 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/Readme: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import "github.com/kr/pretty" 4 | 5 | Package pretty provides pretty-printing for Go values. 6 | 7 | Documentation 8 | 9 | http://godoc.org/github.com/kr/pretty 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/diff.go: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "reflect" 7 | ) 8 | 9 | type sbuf []string 10 | 11 | func (s *sbuf) Write(b []byte) (int, error) { 12 | *s = append(*s, string(b)) 13 | return len(b), nil 14 | } 15 | 16 | // Diff returns a slice where each element describes 17 | // a difference between a and b. 18 | func Diff(a, b interface{}) (desc []string) { 19 | Fdiff((*sbuf)(&desc), a, b) 20 | return desc 21 | } 22 | 23 | // Fdiff writes to w a description of the differences between a and b. 24 | func Fdiff(w io.Writer, a, b interface{}) { 25 | diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b)) 26 | } 27 | 28 | type diffWriter struct { 29 | w io.Writer 30 | l string // label 31 | } 32 | 33 | func (w diffWriter) printf(f string, a ...interface{}) { 34 | var l string 35 | if w.l != "" { 36 | l = w.l + ": " 37 | } 38 | fmt.Fprintf(w.w, l+f, a...) 39 | } 40 | 41 | func (w diffWriter) diff(av, bv reflect.Value) { 42 | if !av.IsValid() && bv.IsValid() { 43 | w.printf("nil != %#v", bv.Interface()) 44 | return 45 | } 46 | if av.IsValid() && !bv.IsValid() { 47 | w.printf("%#v != nil", av.Interface()) 48 | return 49 | } 50 | if !av.IsValid() && !bv.IsValid() { 51 | return 52 | } 53 | 54 | at := av.Type() 55 | bt := bv.Type() 56 | if at != bt { 57 | w.printf("%v != %v", at, bt) 58 | return 59 | } 60 | 61 | // numeric types, including bool 62 | if at.Kind() < reflect.Array { 63 | a, b := av.Interface(), bv.Interface() 64 | if a != b { 65 | w.printf("%#v != %#v", a, b) 66 | } 67 | return 68 | } 69 | 70 | switch at.Kind() { 71 | case reflect.String: 72 | a, b := av.Interface(), bv.Interface() 73 | if a != b { 74 | w.printf("%q != %q", a, b) 75 | } 76 | case reflect.Ptr: 77 | switch { 78 | case av.IsNil() && !bv.IsNil(): 79 | w.printf("nil != %v", bv.Interface()) 80 | case !av.IsNil() && bv.IsNil(): 81 | w.printf("%v != nil", av.Interface()) 82 | case !av.IsNil() && !bv.IsNil(): 83 | w.diff(av.Elem(), bv.Elem()) 84 | } 85 | case reflect.Struct: 86 | for i := 0; i < av.NumField(); i++ { 87 | w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i)) 88 | } 89 | case reflect.Map: 90 | ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys()) 91 | for _, k := range ak { 92 | w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) 93 | w.printf("%q != (missing)", av.MapIndex(k)) 94 | } 95 | for _, k := range both { 96 | w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) 97 | w.diff(av.MapIndex(k), bv.MapIndex(k)) 98 | } 99 | for _, k := range bk { 100 | w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) 101 | w.printf("(missing) != %q", bv.MapIndex(k)) 102 | } 103 | case reflect.Interface: 104 | w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface())) 105 | default: 106 | if !reflect.DeepEqual(av.Interface(), bv.Interface()) { 107 | w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface())) 108 | } 109 | } 110 | } 111 | 112 | func (d diffWriter) relabel(name string) (d1 diffWriter) { 113 | d1 = d 114 | if d.l != "" && name[0] != '[' { 115 | d1.l += "." 116 | } 117 | d1.l += name 118 | return d1 119 | } 120 | 121 | func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) { 122 | for _, av := range a { 123 | inBoth := false 124 | for _, bv := range b { 125 | if reflect.DeepEqual(av.Interface(), bv.Interface()) { 126 | inBoth = true 127 | both = append(both, av) 128 | break 129 | } 130 | } 131 | if !inBoth { 132 | ak = append(ak, av) 133 | } 134 | } 135 | for _, bv := range b { 136 | inBoth := false 137 | for _, av := range a { 138 | if reflect.DeepEqual(av.Interface(), bv.Interface()) { 139 | inBoth = true 140 | break 141 | } 142 | } 143 | if !inBoth { 144 | bk = append(bk, bv) 145 | } 146 | } 147 | return 148 | } 149 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/diff_test.go: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type difftest struct { 8 | a interface{} 9 | b interface{} 10 | exp []string 11 | } 12 | 13 | type S struct { 14 | A int 15 | S *S 16 | I interface{} 17 | C []int 18 | } 19 | 20 | var diffs = []difftest{ 21 | {a: nil, b: nil}, 22 | {a: S{A: 1}, b: S{A: 1}}, 23 | 24 | {0, "", []string{`int != string`}}, 25 | {0, 1, []string{`0 != 1`}}, 26 | {S{}, new(S), []string{`pretty.S != *pretty.S`}}, 27 | {"a", "b", []string{`"a" != "b"`}}, 28 | {S{}, S{A: 1}, []string{`A: 0 != 1`}}, 29 | {new(S), &S{A: 1}, []string{`A: 0 != 1`}}, 30 | {S{S: new(S)}, S{S: &S{A: 1}}, []string{`S.A: 0 != 1`}}, 31 | {S{}, S{I: 0}, []string{`I: nil != 0`}}, 32 | {S{I: 1}, S{I: "x"}, []string{`I: int != string`}}, 33 | {S{}, S{C: []int{1}}, []string{`C: []int(nil) != []int{1}`}}, 34 | {S{C: []int{}}, S{C: []int{1}}, []string{`C: []int{} != []int{1}`}}, 35 | {S{}, S{A: 1, S: new(S)}, []string{`A: 0 != 1`, `S: nil != &{0 []}`}}, 36 | } 37 | 38 | func TestDiff(t *testing.T) { 39 | for _, tt := range diffs { 40 | got := Diff(tt.a, tt.b) 41 | eq := len(got) == len(tt.exp) 42 | if eq { 43 | for i := range got { 44 | eq = eq && got[i] == tt.exp[i] 45 | } 46 | } 47 | if !eq { 48 | t.Errorf("diffing % #v", tt.a) 49 | t.Errorf("with % #v", tt.b) 50 | diffdiff(t, got, tt.exp) 51 | continue 52 | } 53 | } 54 | } 55 | 56 | func diffdiff(t *testing.T, got, exp []string) { 57 | minus(t, "unexpected:", got, exp) 58 | minus(t, "missing:", exp, got) 59 | } 60 | 61 | func minus(t *testing.T, s string, a, b []string) { 62 | var i, j int 63 | for i = 0; i < len(a); i++ { 64 | for j = 0; j < len(b); j++ { 65 | if a[i] == b[j] { 66 | break 67 | } 68 | } 69 | if j == len(b) { 70 | t.Error(s, a[i]) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/example_test.go: -------------------------------------------------------------------------------- 1 | package pretty_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/kr/pretty" 6 | ) 7 | 8 | func Example() { 9 | type myType struct { 10 | a, b int 11 | } 12 | var x = []myType{{1, 2}, {3, 4}, {5, 6}} 13 | fmt.Printf("%# v", pretty.Formatter(x)) 14 | // output: 15 | // []pretty_test.myType{ 16 | // {a:1, b:2}, 17 | // {a:3, b:4}, 18 | // {a:5, b:6}, 19 | // } 20 | } 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/formatter.go: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/kr/text" 6 | "io" 7 | "reflect" 8 | "strconv" 9 | "text/tabwriter" 10 | ) 11 | 12 | const ( 13 | limit = 50 14 | ) 15 | 16 | type formatter struct { 17 | x interface{} 18 | force bool 19 | quote bool 20 | } 21 | 22 | // Formatter makes a wrapper, f, that will format x as go source with line 23 | // breaks and tabs. Object f responds to the "%v" formatting verb when both the 24 | // "#" and " " (space) flags are set, for example: 25 | // 26 | // fmt.Sprintf("%# v", Formatter(x)) 27 | // 28 | // If one of these two flags is not set, or any other verb is used, f will 29 | // format x according to the usual rules of package fmt. 30 | // In particular, if x satisfies fmt.Formatter, then x.Format will be called. 31 | func Formatter(x interface{}) (f fmt.Formatter) { 32 | return formatter{x: x, quote: true} 33 | } 34 | 35 | func (fo formatter) String() string { 36 | return fmt.Sprint(fo.x) // unwrap it 37 | } 38 | 39 | func (fo formatter) passThrough(f fmt.State, c rune) { 40 | s := "%" 41 | for i := 0; i < 128; i++ { 42 | if f.Flag(i) { 43 | s += string(i) 44 | } 45 | } 46 | if w, ok := f.Width(); ok { 47 | s += fmt.Sprintf("%d", w) 48 | } 49 | if p, ok := f.Precision(); ok { 50 | s += fmt.Sprintf(".%d", p) 51 | } 52 | s += string(c) 53 | fmt.Fprintf(f, s, fo.x) 54 | } 55 | 56 | func (fo formatter) Format(f fmt.State, c rune) { 57 | if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') { 58 | w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0) 59 | p := &printer{tw: w, Writer: w} 60 | p.printValue(reflect.ValueOf(fo.x), true, fo.quote) 61 | w.Flush() 62 | return 63 | } 64 | fo.passThrough(f, c) 65 | } 66 | 67 | type printer struct { 68 | io.Writer 69 | tw *tabwriter.Writer 70 | } 71 | 72 | func (p *printer) indent() *printer { 73 | q := *p 74 | q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0) 75 | q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'}) 76 | return &q 77 | } 78 | 79 | func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) { 80 | if showType { 81 | io.WriteString(p, v.Type().String()) 82 | fmt.Fprintf(p, "(%#v)", x) 83 | } else { 84 | fmt.Fprintf(p, "%#v", x) 85 | } 86 | } 87 | 88 | func (p *printer) printValue(v reflect.Value, showType, quote bool) { 89 | switch v.Kind() { 90 | case reflect.Bool: 91 | p.printInline(v, v.Bool(), showType) 92 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 93 | p.printInline(v, v.Int(), showType) 94 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 95 | p.printInline(v, v.Uint(), showType) 96 | case reflect.Float32, reflect.Float64: 97 | p.printInline(v, v.Float(), showType) 98 | case reflect.Complex64, reflect.Complex128: 99 | fmt.Fprintf(p, "%#v", v.Complex()) 100 | case reflect.String: 101 | p.fmtString(v.String(), quote) 102 | case reflect.Map: 103 | t := v.Type() 104 | if showType { 105 | io.WriteString(p, t.String()) 106 | } 107 | writeByte(p, '{') 108 | if nonzero(v) { 109 | expand := !canInline(v.Type()) 110 | pp := p 111 | if expand { 112 | writeByte(p, '\n') 113 | pp = p.indent() 114 | } 115 | keys := v.MapKeys() 116 | for i := 0; i < v.Len(); i++ { 117 | showTypeInStruct := true 118 | k := keys[i] 119 | mv := v.MapIndex(k) 120 | pp.printValue(k, false, true) 121 | writeByte(pp, ':') 122 | if expand { 123 | writeByte(pp, '\t') 124 | } 125 | showTypeInStruct = t.Elem().Kind() == reflect.Interface 126 | pp.printValue(mv, showTypeInStruct, true) 127 | if expand { 128 | io.WriteString(pp, ",\n") 129 | } else if i < v.Len()-1 { 130 | io.WriteString(pp, ", ") 131 | } 132 | } 133 | if expand { 134 | pp.tw.Flush() 135 | } 136 | } 137 | writeByte(p, '}') 138 | case reflect.Struct: 139 | t := v.Type() 140 | if showType { 141 | io.WriteString(p, t.String()) 142 | } 143 | writeByte(p, '{') 144 | if nonzero(v) { 145 | expand := !canInline(v.Type()) 146 | pp := p 147 | if expand { 148 | writeByte(p, '\n') 149 | pp = p.indent() 150 | } 151 | for i := 0; i < v.NumField(); i++ { 152 | showTypeInStruct := true 153 | if f := t.Field(i); f.Name != "" { 154 | io.WriteString(pp, f.Name) 155 | writeByte(pp, ':') 156 | if expand { 157 | writeByte(pp, '\t') 158 | } 159 | showTypeInStruct = f.Type.Kind() == reflect.Interface 160 | } 161 | pp.printValue(getField(v, i), showTypeInStruct, true) 162 | if expand { 163 | io.WriteString(pp, ",\n") 164 | } else if i < v.NumField()-1 { 165 | io.WriteString(pp, ", ") 166 | } 167 | } 168 | if expand { 169 | pp.tw.Flush() 170 | } 171 | } 172 | writeByte(p, '}') 173 | case reflect.Interface: 174 | switch e := v.Elem(); { 175 | case e.Kind() == reflect.Invalid: 176 | io.WriteString(p, "nil") 177 | case e.IsValid(): 178 | p.printValue(e, showType, true) 179 | default: 180 | io.WriteString(p, v.Type().String()) 181 | io.WriteString(p, "(nil)") 182 | } 183 | case reflect.Array, reflect.Slice: 184 | t := v.Type() 185 | if showType { 186 | io.WriteString(p, t.String()) 187 | } 188 | if v.Kind() == reflect.Slice && v.IsNil() && showType { 189 | io.WriteString(p, "(nil)") 190 | break 191 | } 192 | if v.Kind() == reflect.Slice && v.IsNil() { 193 | io.WriteString(p, "nil") 194 | break 195 | } 196 | writeByte(p, '{') 197 | expand := !canInline(v.Type()) 198 | pp := p 199 | if expand { 200 | writeByte(p, '\n') 201 | pp = p.indent() 202 | } 203 | for i := 0; i < v.Len(); i++ { 204 | showTypeInSlice := t.Elem().Kind() == reflect.Interface 205 | pp.printValue(v.Index(i), showTypeInSlice, true) 206 | if expand { 207 | io.WriteString(pp, ",\n") 208 | } else if i < v.Len()-1 { 209 | io.WriteString(pp, ", ") 210 | } 211 | } 212 | if expand { 213 | pp.tw.Flush() 214 | } 215 | writeByte(p, '}') 216 | case reflect.Ptr: 217 | e := v.Elem() 218 | if !e.IsValid() { 219 | writeByte(p, '(') 220 | io.WriteString(p, v.Type().String()) 221 | io.WriteString(p, ")(nil)") 222 | } else { 223 | writeByte(p, '&') 224 | p.printValue(e, true, true) 225 | } 226 | case reflect.Chan: 227 | x := v.Pointer() 228 | if showType { 229 | writeByte(p, '(') 230 | io.WriteString(p, v.Type().String()) 231 | fmt.Fprintf(p, ")(%#v)", x) 232 | } else { 233 | fmt.Fprintf(p, "%#v", x) 234 | } 235 | case reflect.Func: 236 | io.WriteString(p, v.Type().String()) 237 | io.WriteString(p, " {...}") 238 | case reflect.UnsafePointer: 239 | p.printInline(v, v.Pointer(), showType) 240 | case reflect.Invalid: 241 | io.WriteString(p, "nil") 242 | } 243 | } 244 | 245 | func canInline(t reflect.Type) bool { 246 | switch t.Kind() { 247 | case reflect.Map: 248 | return !canExpand(t.Elem()) 249 | case reflect.Struct: 250 | for i := 0; i < t.NumField(); i++ { 251 | if canExpand(t.Field(i).Type) { 252 | return false 253 | } 254 | } 255 | return true 256 | case reflect.Interface: 257 | return false 258 | case reflect.Array, reflect.Slice: 259 | return !canExpand(t.Elem()) 260 | case reflect.Ptr: 261 | return false 262 | case reflect.Chan, reflect.Func, reflect.UnsafePointer: 263 | return false 264 | } 265 | return true 266 | } 267 | 268 | func canExpand(t reflect.Type) bool { 269 | switch t.Kind() { 270 | case reflect.Map, reflect.Struct, 271 | reflect.Interface, reflect.Array, reflect.Slice, 272 | reflect.Ptr: 273 | return true 274 | } 275 | return false 276 | } 277 | 278 | func (p *printer) fmtString(s string, quote bool) { 279 | if quote { 280 | s = strconv.Quote(s) 281 | } 282 | io.WriteString(p, s) 283 | } 284 | 285 | func tryDeepEqual(a, b interface{}) bool { 286 | defer func() { recover() }() 287 | return reflect.DeepEqual(a, b) 288 | } 289 | 290 | func writeByte(w io.Writer, b byte) { 291 | w.Write([]byte{b}) 292 | } 293 | 294 | func getField(v reflect.Value, i int) reflect.Value { 295 | val := v.Field(i) 296 | if val.Kind() == reflect.Interface && !val.IsNil() { 297 | val = val.Elem() 298 | } 299 | return val 300 | } 301 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/formatter_test.go: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "testing" 7 | "unsafe" 8 | ) 9 | 10 | type test struct { 11 | v interface{} 12 | s string 13 | } 14 | 15 | type LongStructTypeName struct { 16 | longFieldName interface{} 17 | otherLongFieldName interface{} 18 | } 19 | 20 | type SA struct { 21 | t *T 22 | } 23 | 24 | type T struct { 25 | x, y int 26 | } 27 | 28 | type F int 29 | 30 | func (f F) Format(s fmt.State, c rune) { 31 | fmt.Fprintf(s, "F(%d)", int(f)) 32 | } 33 | 34 | var long = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 35 | 36 | var gosyntax = []test{ 37 | {nil, `nil`}, 38 | {"", `""`}, 39 | {"a", `"a"`}, 40 | {1, "int(1)"}, 41 | {1.0, "float64(1)"}, 42 | {[]int(nil), "[]int(nil)"}, 43 | {[0]int{}, "[0]int{}"}, 44 | {complex(1, 0), "(1+0i)"}, 45 | //{make(chan int), "(chan int)(0x1234)"}, 46 | {unsafe.Pointer(uintptr(1)), "unsafe.Pointer(0x1)"}, 47 | {func(int) {}, "func(int) {...}"}, 48 | {map[int]int{1: 1}, "map[int]int{1:1}"}, 49 | {int32(1), "int32(1)"}, 50 | {io.EOF, `&errors.errorString{s:"EOF"}`}, 51 | {[]string{"a"}, `[]string{"a"}`}, 52 | { 53 | []string{long}, 54 | `[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`, 55 | }, 56 | {F(5), "pretty.F(5)"}, 57 | { 58 | SA{&T{1, 2}}, 59 | `pretty.SA{ 60 | t: &pretty.T{x:1, y:2}, 61 | }`, 62 | }, 63 | { 64 | map[int][]byte{1: []byte{}}, 65 | `map[int][]uint8{ 66 | 1: {}, 67 | }`, 68 | }, 69 | { 70 | map[int]T{1: T{}}, 71 | `map[int]pretty.T{ 72 | 1: {}, 73 | }`, 74 | }, 75 | { 76 | long, 77 | `"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"`, 78 | }, 79 | { 80 | LongStructTypeName{ 81 | longFieldName: LongStructTypeName{}, 82 | otherLongFieldName: long, 83 | }, 84 | `pretty.LongStructTypeName{ 85 | longFieldName: pretty.LongStructTypeName{}, 86 | otherLongFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 87 | }`, 88 | }, 89 | { 90 | &LongStructTypeName{ 91 | longFieldName: &LongStructTypeName{}, 92 | otherLongFieldName: (*LongStructTypeName)(nil), 93 | }, 94 | `&pretty.LongStructTypeName{ 95 | longFieldName: &pretty.LongStructTypeName{}, 96 | otherLongFieldName: (*pretty.LongStructTypeName)(nil), 97 | }`, 98 | }, 99 | { 100 | []LongStructTypeName{ 101 | {nil, nil}, 102 | {3, 3}, 103 | {long, nil}, 104 | }, 105 | `[]pretty.LongStructTypeName{ 106 | {}, 107 | { 108 | longFieldName: int(3), 109 | otherLongFieldName: int(3), 110 | }, 111 | { 112 | longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 113 | otherLongFieldName: nil, 114 | }, 115 | }`, 116 | }, 117 | { 118 | []interface{}{ 119 | LongStructTypeName{nil, nil}, 120 | []byte{1, 2, 3}, 121 | T{3, 4}, 122 | LongStructTypeName{long, nil}, 123 | }, 124 | `[]interface {}{ 125 | pretty.LongStructTypeName{}, 126 | []uint8{0x1, 0x2, 0x3}, 127 | pretty.T{x:3, y:4}, 128 | pretty.LongStructTypeName{ 129 | longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 130 | otherLongFieldName: nil, 131 | }, 132 | }`, 133 | }, 134 | } 135 | 136 | func TestGoSyntax(t *testing.T) { 137 | for _, tt := range gosyntax { 138 | s := fmt.Sprintf("%# v", Formatter(tt.v)) 139 | if tt.s != s { 140 | t.Errorf("expected %q", tt.s) 141 | t.Errorf("got %q", s) 142 | t.Errorf("expraw\n%s", tt.s) 143 | t.Errorf("gotraw\n%s", s) 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/pretty.go: -------------------------------------------------------------------------------- 1 | // Package pretty provides pretty-printing for Go values. This is 2 | // useful during debugging, to avoid wrapping long output lines in 3 | // the terminal. 4 | // 5 | // It provides a function, Formatter, that can be used with any 6 | // function that accepts a format string. It also provides 7 | // convenience wrappers for functions in packages fmt and log. 8 | package pretty 9 | 10 | import ( 11 | "fmt" 12 | "io" 13 | "log" 14 | ) 15 | 16 | // Errorf is a convenience wrapper for fmt.Errorf. 17 | // 18 | // Calling Errorf(f, x, y) is equivalent to 19 | // fmt.Errorf(f, Formatter(x), Formatter(y)). 20 | func Errorf(format string, a ...interface{}) error { 21 | return fmt.Errorf(format, wrap(a, false)...) 22 | } 23 | 24 | // Fprintf is a convenience wrapper for fmt.Fprintf. 25 | // 26 | // Calling Fprintf(w, f, x, y) is equivalent to 27 | // fmt.Fprintf(w, f, Formatter(x), Formatter(y)). 28 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) { 29 | return fmt.Fprintf(w, format, wrap(a, false)...) 30 | } 31 | 32 | // Log is a convenience wrapper for log.Printf. 33 | // 34 | // Calling Log(x, y) is equivalent to 35 | // log.Print(Formatter(x), Formatter(y)), but each operand is 36 | // formatted with "%# v". 37 | func Log(a ...interface{}) { 38 | log.Print(wrap(a, true)...) 39 | } 40 | 41 | // Logf is a convenience wrapper for log.Printf. 42 | // 43 | // Calling Logf(f, x, y) is equivalent to 44 | // log.Printf(f, Formatter(x), Formatter(y)). 45 | func Logf(format string, a ...interface{}) { 46 | log.Printf(format, wrap(a, false)...) 47 | } 48 | 49 | // Logln is a convenience wrapper for log.Printf. 50 | // 51 | // Calling Logln(x, y) is equivalent to 52 | // log.Println(Formatter(x), Formatter(y)), but each operand is 53 | // formatted with "%# v". 54 | func Logln(a ...interface{}) { 55 | log.Println(wrap(a, true)...) 56 | } 57 | 58 | // Print pretty-prints its operands and writes to standard output. 59 | // 60 | // Calling Print(x, y) is equivalent to 61 | // fmt.Print(Formatter(x), Formatter(y)), but each operand is 62 | // formatted with "%# v". 63 | func Print(a ...interface{}) (n int, errno error) { 64 | return fmt.Print(wrap(a, true)...) 65 | } 66 | 67 | // Printf is a convenience wrapper for fmt.Printf. 68 | // 69 | // Calling Printf(f, x, y) is equivalent to 70 | // fmt.Printf(f, Formatter(x), Formatter(y)). 71 | func Printf(format string, a ...interface{}) (n int, errno error) { 72 | return fmt.Printf(format, wrap(a, false)...) 73 | } 74 | 75 | // Println pretty-prints its operands and writes to standard output. 76 | // 77 | // Calling Print(x, y) is equivalent to 78 | // fmt.Println(Formatter(x), Formatter(y)), but each operand is 79 | // formatted with "%# v". 80 | func Println(a ...interface{}) (n int, errno error) { 81 | return fmt.Println(wrap(a, true)...) 82 | } 83 | 84 | // Sprintf is a convenience wrapper for fmt.Sprintf. 85 | // 86 | // Calling Sprintf(f, x, y) is equivalent to 87 | // fmt.Sprintf(f, Formatter(x), Formatter(y)). 88 | func Sprintf(format string, a ...interface{}) string { 89 | return fmt.Sprintf(format, wrap(a, false)...) 90 | } 91 | 92 | func wrap(a []interface{}, force bool) []interface{} { 93 | w := make([]interface{}, len(a)) 94 | for i, x := range a { 95 | w[i] = formatter{x: x, force: force} 96 | } 97 | return w 98 | } 99 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/pretty/zero.go: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func nonzero(v reflect.Value) bool { 8 | switch v.Kind() { 9 | case reflect.Bool: 10 | return v.Bool() 11 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 12 | return v.Int() != 0 13 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 14 | return v.Uint() != 0 15 | case reflect.Float32, reflect.Float64: 16 | return v.Float() != 0 17 | case reflect.Complex64, reflect.Complex128: 18 | return v.Complex() != complex(0, 0) 19 | case reflect.String: 20 | return v.String() != "" 21 | case reflect.Struct: 22 | for i := 0; i < v.NumField(); i++ { 23 | if nonzero(getField(v, i)) { 24 | return true 25 | } 26 | } 27 | return false 28 | case reflect.Array: 29 | for i := 0; i < v.Len(); i++ { 30 | if nonzero(v.Index(i)) { 31 | return true 32 | } 33 | } 34 | return false 35 | case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func: 36 | return !v.IsNil() 37 | case reflect.UnsafePointer: 38 | return v.Pointer() != 0 39 | } 40 | return true 41 | } 42 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/License: -------------------------------------------------------------------------------- 1 | Copyright 2012 Keith Rarick 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/Readme: -------------------------------------------------------------------------------- 1 | This is a Go package for manipulating paragraphs of text. 2 | 3 | See http://go.pkgdoc.org/github.com/kr/text for full documentation. 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/colwriter/Readme: -------------------------------------------------------------------------------- 1 | Package colwriter provides a write filter that formats 2 | input lines in multiple columns. 3 | 4 | The package is a straightforward translation from 5 | /src/cmd/draw/mc.c in Plan 9 from User Space. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/colwriter/column.go: -------------------------------------------------------------------------------- 1 | // Package colwriter provides a write filter that formats 2 | // input lines in multiple columns. 3 | // 4 | // The package is a straightforward translation from 5 | // /src/cmd/draw/mc.c in Plan 9 from User Space. 6 | package colwriter 7 | 8 | import ( 9 | "bytes" 10 | "io" 11 | "unicode/utf8" 12 | ) 13 | 14 | const ( 15 | tab = 4 16 | ) 17 | 18 | const ( 19 | // Print each input line ending in a colon ':' separately. 20 | BreakOnColon uint = 1 << iota 21 | ) 22 | 23 | // A Writer is a filter that arranges input lines in as many columns as will 24 | // fit in its width. Tab '\t' chars in the input are translated to sequences 25 | // of spaces ending at multiples of 4 positions. 26 | // 27 | // If BreakOnColon is set, each input line ending in a colon ':' is written 28 | // separately. 29 | // 30 | // The Writer assumes that all Unicode code points have the same width; this 31 | // may not be true in some fonts. 32 | type Writer struct { 33 | w io.Writer 34 | buf []byte 35 | width int 36 | flag uint 37 | } 38 | 39 | // NewWriter allocates and initializes a new Writer writing to w. 40 | // Parameter width controls the total number of characters on each line 41 | // across all columns. 42 | func NewWriter(w io.Writer, width int, flag uint) *Writer { 43 | return &Writer{ 44 | w: w, 45 | width: width, 46 | flag: flag, 47 | } 48 | } 49 | 50 | // Write writes p to the writer w. The only errors returned are ones 51 | // encountered while writing to the underlying output stream. 52 | func (w *Writer) Write(p []byte) (n int, err error) { 53 | var linelen int 54 | var lastWasColon bool 55 | for i, c := range p { 56 | w.buf = append(w.buf, c) 57 | linelen++ 58 | if c == '\t' { 59 | w.buf[len(w.buf)-1] = ' ' 60 | for linelen%tab != 0 { 61 | w.buf = append(w.buf, ' ') 62 | linelen++ 63 | } 64 | } 65 | if w.flag&BreakOnColon != 0 && c == ':' { 66 | lastWasColon = true 67 | } else if lastWasColon { 68 | if c == '\n' { 69 | pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'}) 70 | if pos < 0 { 71 | pos = 0 72 | } 73 | line := w.buf[pos:] 74 | w.buf = w.buf[:pos] 75 | if err = w.columnate(); err != nil { 76 | if len(line) < i { 77 | return i - len(line), err 78 | } 79 | return 0, err 80 | } 81 | if n, err := w.w.Write(line); err != nil { 82 | if r := len(line) - n; r < i { 83 | return i - r, err 84 | } 85 | return 0, err 86 | } 87 | } 88 | lastWasColon = false 89 | } 90 | if c == '\n' { 91 | linelen = 0 92 | } 93 | } 94 | return len(p), nil 95 | } 96 | 97 | // Flush should be called after the last call to Write to ensure that any data 98 | // buffered in the Writer is written to output. 99 | func (w *Writer) Flush() error { 100 | return w.columnate() 101 | } 102 | 103 | func (w *Writer) columnate() error { 104 | words := bytes.Split(w.buf, []byte{'\n'}) 105 | w.buf = nil 106 | if len(words[len(words)-1]) == 0 { 107 | words = words[:len(words)-1] 108 | } 109 | maxwidth := 0 110 | for _, wd := range words { 111 | if n := utf8.RuneCount(wd); n > maxwidth { 112 | maxwidth = n 113 | } 114 | } 115 | maxwidth++ // space char 116 | wordsPerLine := w.width / maxwidth 117 | if wordsPerLine <= 0 { 118 | wordsPerLine = 1 119 | } 120 | nlines := (len(words) + wordsPerLine - 1) / wordsPerLine 121 | for i := 0; i < nlines; i++ { 122 | col := 0 123 | endcol := 0 124 | for j := i; j < len(words); j += nlines { 125 | endcol += maxwidth 126 | _, err := w.w.Write(words[j]) 127 | if err != nil { 128 | return err 129 | } 130 | col += utf8.RuneCount(words[j]) 131 | if j+nlines < len(words) { 132 | for col < endcol { 133 | _, err := w.w.Write([]byte{' '}) 134 | if err != nil { 135 | return err 136 | } 137 | col++ 138 | } 139 | } 140 | } 141 | _, err := w.w.Write([]byte{'\n'}) 142 | if err != nil { 143 | return err 144 | } 145 | } 146 | return nil 147 | } 148 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/colwriter/column_test.go: -------------------------------------------------------------------------------- 1 | package colwriter 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | var src = ` 9 | .git 10 | .gitignore 11 | .godir 12 | Procfile: 13 | README.md 14 | api.go 15 | apps.go 16 | auth.go 17 | darwin.go 18 | data.go 19 | dyno.go: 20 | env.go 21 | git.go 22 | help.go 23 | hkdist 24 | linux.go 25 | ls.go 26 | main.go 27 | plugin.go 28 | run.go 29 | scale.go 30 | ssh.go 31 | tail.go 32 | term 33 | unix.go 34 | update.go 35 | version.go 36 | windows.go 37 | `[1:] 38 | 39 | var tests = []struct{ 40 | wid int 41 | flag uint 42 | src string 43 | want string 44 | }{ 45 | {80, 0, "", ""}, 46 | {80, 0, src, ` 47 | .git README.md darwin.go git.go ls.go scale.go unix.go 48 | .gitignore api.go data.go help.go main.go ssh.go update.go 49 | .godir apps.go dyno.go: hkdist plugin.go tail.go version.go 50 | Procfile: auth.go env.go linux.go run.go term windows.go 51 | `[1:]}, 52 | {80, BreakOnColon, src, ` 53 | .git .gitignore .godir 54 | 55 | Procfile: 56 | README.md api.go apps.go auth.go darwin.go data.go 57 | 58 | dyno.go: 59 | env.go hkdist main.go scale.go term version.go 60 | git.go linux.go plugin.go ssh.go unix.go windows.go 61 | help.go ls.go run.go tail.go update.go 62 | `[1:]}, 63 | {20, 0, ` 64 | Hello 65 | Γειά σου 66 | 안녕 67 | 今日は 68 | `[1:], ` 69 | Hello 안녕 70 | Γειά σου 今日は 71 | `[1:]}, 72 | } 73 | 74 | func TestWriter(t *testing.T) { 75 | for _, test := range tests { 76 | b := new(bytes.Buffer) 77 | w := NewWriter(b, test.wid, test.flag) 78 | if _, err := w.Write([]byte(test.src)); err != nil { 79 | t.Error(err) 80 | } 81 | if err := w.Flush(); err != nil { 82 | t.Error(err) 83 | } 84 | if g := b.String(); test.want != g { 85 | t.Log("\n" + test.want) 86 | t.Log("\n" + g) 87 | t.Errorf("%q != %q", test.want, g) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/doc.go: -------------------------------------------------------------------------------- 1 | // Package text provides rudimentary functions for manipulating text in 2 | // paragraphs. 3 | package text 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/indent.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // Indent inserts prefix at the beginning of each non-empty line of s. The 8 | // end-of-line marker is NL. 9 | func Indent(s, prefix string) string { 10 | return string(IndentBytes([]byte(s), []byte(prefix))) 11 | } 12 | 13 | // IndentBytes inserts prefix at the beginning of each non-empty line of b. 14 | // The end-of-line marker is NL. 15 | func IndentBytes(b, prefix []byte) []byte { 16 | var res []byte 17 | bol := true 18 | for _, c := range b { 19 | if bol && c != '\n' { 20 | res = append(res, prefix...) 21 | } 22 | res = append(res, c) 23 | bol = c == '\n' 24 | } 25 | return res 26 | } 27 | 28 | // Writer indents each line of its input. 29 | type indentWriter struct { 30 | w io.Writer 31 | bol bool 32 | pre [][]byte 33 | sel int 34 | off int 35 | } 36 | 37 | // NewIndentWriter makes a new write filter that indents the input 38 | // lines. Each line is prefixed in order with the corresponding 39 | // element of pre. If there are more lines than elements, the last 40 | // element of pre is repeated for each subsequent line. 41 | func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer { 42 | return &indentWriter{ 43 | w: w, 44 | pre: pre, 45 | bol: true, 46 | } 47 | } 48 | 49 | // The only errors returned are from the underlying indentWriter. 50 | func (w *indentWriter) Write(p []byte) (n int, err error) { 51 | for _, c := range p { 52 | if w.bol { 53 | var i int 54 | i, err = w.w.Write(w.pre[w.sel][w.off:]) 55 | w.off += i 56 | if err != nil { 57 | return n, err 58 | } 59 | } 60 | _, err = w.w.Write([]byte{c}) 61 | if err != nil { 62 | return n, err 63 | } 64 | n++ 65 | w.bol = c == '\n' 66 | if w.bol { 67 | w.off = 0 68 | if w.sel < len(w.pre)-1 { 69 | w.sel++ 70 | } 71 | } 72 | } 73 | return n, nil 74 | } 75 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/indent_test.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | type T struct { 9 | inp, exp, pre string 10 | } 11 | 12 | var tests = []T{ 13 | { 14 | "The quick brown fox\njumps over the lazy\ndog.\nBut not quickly.\n", 15 | "xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\nxxxBut not quickly.\n", 16 | "xxx", 17 | }, 18 | { 19 | "The quick brown fox\njumps over the lazy\ndog.\n\nBut not quickly.", 20 | "xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\n\nxxxBut not quickly.", 21 | "xxx", 22 | }, 23 | } 24 | 25 | func TestIndent(t *testing.T) { 26 | for _, test := range tests { 27 | got := Indent(test.inp, test.pre) 28 | if got != test.exp { 29 | t.Errorf("mismatch %q != %q", got, test.exp) 30 | } 31 | } 32 | } 33 | 34 | type IndentWriterTest struct { 35 | inp, exp string 36 | pre []string 37 | } 38 | 39 | var ts = []IndentWriterTest{ 40 | { 41 | ` 42 | The quick brown fox 43 | jumps over the lazy 44 | dog. 45 | But not quickly. 46 | `[1:], 47 | ` 48 | xxxThe quick brown fox 49 | xxxjumps over the lazy 50 | xxxdog. 51 | xxxBut not quickly. 52 | `[1:], 53 | []string{"xxx"}, 54 | }, 55 | { 56 | ` 57 | The quick brown fox 58 | jumps over the lazy 59 | dog. 60 | But not quickly. 61 | `[1:], 62 | ` 63 | xxaThe quick brown fox 64 | xxxjumps over the lazy 65 | xxxdog. 66 | xxxBut not quickly. 67 | `[1:], 68 | []string{"xxa", "xxx"}, 69 | }, 70 | { 71 | ` 72 | The quick brown fox 73 | jumps over the lazy 74 | dog. 75 | But not quickly. 76 | `[1:], 77 | ` 78 | xxaThe quick brown fox 79 | xxbjumps over the lazy 80 | xxcdog. 81 | xxxBut not quickly. 82 | `[1:], 83 | []string{"xxa", "xxb", "xxc", "xxx"}, 84 | }, 85 | { 86 | ` 87 | The quick brown fox 88 | jumps over the lazy 89 | dog. 90 | 91 | But not quickly.`[1:], 92 | ` 93 | xxaThe quick brown fox 94 | xxxjumps over the lazy 95 | xxxdog. 96 | xxx 97 | xxxBut not quickly.`[1:], 98 | []string{"xxa", "xxx"}, 99 | }, 100 | } 101 | 102 | func TestIndentWriter(t *testing.T) { 103 | for _, test := range ts { 104 | b := new(bytes.Buffer) 105 | pre := make([][]byte, len(test.pre)) 106 | for i := range test.pre { 107 | pre[i] = []byte(test.pre[i]) 108 | } 109 | w := NewIndentWriter(b, pre...) 110 | if _, err := w.Write([]byte(test.inp)); err != nil { 111 | t.Error(err) 112 | } 113 | if got := b.String(); got != test.exp { 114 | t.Errorf("mismatch %q != %q", got, test.exp) 115 | t.Log(got) 116 | t.Log(test.exp) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/mc/Readme: -------------------------------------------------------------------------------- 1 | Command mc prints in multiple columns. 2 | 3 | Usage: mc [-] [-N] [file...] 4 | 5 | Mc splits the input into as many columns as will fit in N 6 | print positions. If the output is a tty, the default N is 7 | the number of characters in a terminal line; otherwise the 8 | default N is 80. Under option - each input line ending in 9 | a colon ':' is printed separately. 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/mc/mc.go: -------------------------------------------------------------------------------- 1 | // Command mc prints in multiple columns. 2 | // 3 | // Usage: mc [-] [-N] [file...] 4 | // 5 | // Mc splits the input into as many columns as will fit in N 6 | // print positions. If the output is a tty, the default N is 7 | // the number of characters in a terminal line; otherwise the 8 | // default N is 80. Under option - each input line ending in 9 | // a colon ':' is printed separately. 10 | package main 11 | 12 | import ( 13 | "github.com/kr/pty" 14 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/kr/text/colwriter" 15 | "io" 16 | "log" 17 | "os" 18 | "strconv" 19 | ) 20 | 21 | func main() { 22 | var width int 23 | var flag uint 24 | args := os.Args[1:] 25 | for len(args) > 0 && len(args[0]) > 0 && args[0][0] == '-' { 26 | if len(args[0]) > 1 { 27 | width, _ = strconv.Atoi(args[0][1:]) 28 | } else { 29 | flag |= colwriter.BreakOnColon 30 | } 31 | args = args[1:] 32 | } 33 | if width < 1 { 34 | _, width, _ = pty.Getsize(os.Stdout) 35 | } 36 | if width < 1 { 37 | width = 80 38 | } 39 | 40 | w := colwriter.NewWriter(os.Stdout, width, flag) 41 | if len(args) > 0 { 42 | for _, s := range args { 43 | if f, err := os.Open(s); err == nil { 44 | copyin(w, f) 45 | f.Close() 46 | } else { 47 | log.Println(err) 48 | } 49 | } 50 | } else { 51 | copyin(w, os.Stdin) 52 | } 53 | } 54 | 55 | func copyin(w *colwriter.Writer, r io.Reader) { 56 | if _, err := io.Copy(w, r); err != nil { 57 | log.Println(err) 58 | } 59 | if err := w.Flush(); err != nil { 60 | log.Println(err) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/wrap.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | import ( 4 | "bytes" 5 | "math" 6 | ) 7 | 8 | var ( 9 | nl = []byte{'\n'} 10 | sp = []byte{' '} 11 | ) 12 | 13 | const defaultPenalty = 1e5 14 | 15 | // Wrap wraps s into a paragraph of lines of length lim, with minimal 16 | // raggedness. 17 | func Wrap(s string, lim int) string { 18 | return string(WrapBytes([]byte(s), lim)) 19 | } 20 | 21 | // WrapBytes wraps b into a paragraph of lines of length lim, with minimal 22 | // raggedness. 23 | func WrapBytes(b []byte, lim int) []byte { 24 | words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp) 25 | var lines [][]byte 26 | for _, line := range WrapWords(words, 1, lim, defaultPenalty) { 27 | lines = append(lines, bytes.Join(line, sp)) 28 | } 29 | return bytes.Join(lines, nl) 30 | } 31 | 32 | // WrapWords is the low-level line-breaking algorithm, useful if you need more 33 | // control over the details of the text wrapping process. For most uses, either 34 | // Wrap or WrapBytes will be sufficient and more convenient. 35 | // 36 | // WrapWords splits a list of words into lines with minimal "raggedness", 37 | // treating each byte as one unit, accounting for spc units between adjacent 38 | // words on each line, and attempting to limit lines to lim units. Raggedness 39 | // is the total error over all lines, where error is the square of the 40 | // difference of the length of the line and lim. Too-long lines (which only 41 | // happen when a single word is longer than lim units) have pen penalty units 42 | // added to the error. 43 | func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte { 44 | n := len(words) 45 | 46 | length := make([][]int, n) 47 | for i := 0; i < n; i++ { 48 | length[i] = make([]int, n) 49 | length[i][i] = len(words[i]) 50 | for j := i + 1; j < n; j++ { 51 | length[i][j] = length[i][j-1] + spc + len(words[j]) 52 | } 53 | } 54 | 55 | nbrk := make([]int, n) 56 | cost := make([]int, n) 57 | for i := range cost { 58 | cost[i] = math.MaxInt32 59 | } 60 | for i := n - 1; i >= 0; i-- { 61 | if length[i][n-1] <= lim { 62 | cost[i] = 0 63 | nbrk[i] = n 64 | } else { 65 | for j := i + 1; j < n; j++ { 66 | d := lim - length[i][j-1] 67 | c := d*d + cost[j] 68 | if length[i][j-1] > lim { 69 | c += pen // too-long lines get a worse penalty 70 | } 71 | if c < cost[i] { 72 | cost[i] = c 73 | nbrk[i] = j 74 | } 75 | } 76 | } 77 | } 78 | 79 | var lines [][][]byte 80 | i := 0 81 | for i < n { 82 | lines = append(lines, words[i:nbrk[i]]) 83 | i = nbrk[i] 84 | } 85 | return lines 86 | } 87 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/kr/text/wrap_test.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | var text = "The quick brown fox jumps over the lazy dog." 9 | 10 | func TestWrap(t *testing.T) { 11 | exp := [][]string{ 12 | {"The", "quick", "brown", "fox"}, 13 | {"jumps", "over", "the", "lazy", "dog."}, 14 | } 15 | words := bytes.Split([]byte(text), sp) 16 | got := WrapWords(words, 1, 24, defaultPenalty) 17 | if len(exp) != len(got) { 18 | t.Fail() 19 | } 20 | for i := range exp { 21 | if len(exp[i]) != len(got[i]) { 22 | t.Fail() 23 | } 24 | for j := range exp[i] { 25 | if exp[i][j] != string(got[i][j]) { 26 | t.Fatal(i, exp[i][j], got[i][j]) 27 | } 28 | } 29 | } 30 | } 31 | 32 | func TestWrapNarrow(t *testing.T) { 33 | exp := "The\nquick\nbrown\nfox\njumps\nover\nthe\nlazy\ndog." 34 | if Wrap(text, 5) != exp { 35 | t.Fail() 36 | } 37 | } 38 | 39 | func TestWrapOneLine(t *testing.T) { 40 | exp := "The quick brown fox jumps over the lazy dog." 41 | if Wrap(text, 500) != exp { 42 | t.Fail() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/.env: -------------------------------------------------------------------------------- 1 | HELLO=world 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/.gitignore: -------------------------------------------------------------------------------- 1 | *.test 2 | annotate.json 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Alif Rachmawadi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/README.md: -------------------------------------------------------------------------------- 1 | # gotenv 2 | 3 | Load environment variables dynamically in Go. 4 | 5 | |- | - | 6 | |---------------|----------------------------------------------------| 7 | | Build Status | [![Build Status][drone-img]][drone-url] | 8 | | Coverage | [![Coverage Status][coveralls-img]][coveralls-url] | 9 | | Documentation | http://godoc.org/github.com/subosito/gotenv | 10 | 11 | ## Installation 12 | 13 | ```bash 14 | $ go get github.com/subosito/gotenv 15 | ``` 16 | 17 | ## Usage 18 | 19 | Store your configuration to `.env` file on your root directory of your project: 20 | 21 | ``` 22 | APP_ID=1234567 23 | APP_SECRET=abcdef 24 | ``` 25 | 26 | Put the gotenv package on your `import` statement: 27 | 28 | ```go 29 | import "github.com/subosito/gotenv" 30 | ``` 31 | 32 | Then somewhere on your application code, put: 33 | 34 | ```go 35 | gotenv.Load() 36 | ``` 37 | 38 | Behind the scene it will then load `.env` file and export the valid variables to the environment variables. Make sure you call the method as soon as possible to ensure all variables are loaded, say, put it on `init()` function. 39 | 40 | Once loaded you can use `os.Getenv()` to get the value of the variable. 41 | 42 | Here's the final example: 43 | 44 | ```go 45 | package main 46 | 47 | import ( 48 | "github.com/subosito/gotenv" 49 | "log" 50 | "os" 51 | ) 52 | 53 | func init() { 54 | gotenv.Load() 55 | } 56 | 57 | func main() { 58 | log.Println(os.Getenv("APP_ID")) // "1234567" 59 | log.Println(os.Getenv("APP_SECRET")) // "abcdef" 60 | } 61 | ``` 62 | 63 | You can also load other than `.env` file if you wish. Just supply filenames when calling `Load()`: 64 | 65 | ```go 66 | gotenv.Load(".env.production", "credentials") 67 | ``` 68 | 69 | That's it :) 70 | 71 | ### Another Scenario 72 | 73 | Just in case you want to parse environment variables from any `io.Reader`, gotenv keeps its `Parse()` function as public API so you can utilize that. 74 | 75 | ```go 76 | // import "strings" 77 | 78 | pairs := gotenv.Parse(strings.NewReader("FOO=test\nBAR=$FOO")) 79 | // gotenv.Env{"FOO": "test", "BAR": "test"} 80 | 81 | pairs = gotenv.Parse(strings.NewReader(`FOO="bar"`)) 82 | // gotenv.Env{"FOO": "bar"} 83 | ``` 84 | 85 | Parse ignores invalid lines and returns `Env` of valid environment variables. 86 | 87 | ### Formats 88 | 89 | The gotenv supports various format for defining environment variables. You can see more about it on: 90 | 91 | - [fixtures](./fixtures) 92 | - [gotenv_test.go](./gotenv_test.go) 93 | 94 | ## Notes 95 | 96 | The gotenv package is a Go port of [`dotenv`](https://github.com/bkeepers/dotenv) project. Most logic and regexp pattern is taken from there and aims will be compatible as close as possible. 97 | 98 | [drone-img]: https://drone.io/github.com/subosito/gotenv/status.png 99 | [drone-url]: https://drone.io/github.com/subosito/gotenv/latest 100 | [coveralls-img]: https://coveralls.io/repos/subosito/gotenv/badge.png?branch=master 101 | [coveralls-url]: https://coveralls.io/r/subosito/gotenv?branch=master 102 | 103 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/examples_test.go: -------------------------------------------------------------------------------- 1 | package gotenv_test 2 | 3 | import ( 4 | "strings" 5 | "fmt" 6 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/subosito/gotenv" 7 | ) 8 | 9 | func ExampleParse() { 10 | pairs := gotenv.Parse(strings.NewReader("FOO=test\nBAR=$FOO")) 11 | fmt.Printf("%+v\n", pairs) // gotenv.Env{"FOO": "test", "BAR": "test"} 12 | 13 | pairs = gotenv.Parse(strings.NewReader(`FOO="bar"`)) 14 | fmt.Printf("%+v\n", pairs) // gotenv.Env{"FOO": "bar"} 15 | } 16 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/fixtures/exported.env: -------------------------------------------------------------------------------- 1 | export OPTION_A=2 2 | export OPTION_B='\n' 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/fixtures/plain.env: -------------------------------------------------------------------------------- 1 | OPTION_A=1 2 | OPTION_B=2 3 | OPTION_C= 3 4 | OPTION_D =4 5 | OPTION_E = 5 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/fixtures/quoted.env: -------------------------------------------------------------------------------- 1 | OPTION_A='1' 2 | OPTION_B='2' 3 | OPTION_C='' 4 | OPTION_D='\n' 5 | OPTION_E="1" 6 | OPTION_F="2" 7 | OPTION_G="" 8 | OPTION_H="\n" 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/fixtures/yaml.env: -------------------------------------------------------------------------------- 1 | OPTION_A: 1 2 | OPTION_B: '2' 3 | OPTION_C: '' 4 | OPTION_D: '\n' 5 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/gotenv.go: -------------------------------------------------------------------------------- 1 | // Package gotenv provides functionality to dynamically load the environment variables 2 | package gotenv 3 | 4 | import ( 5 | "bufio" 6 | "io" 7 | "os" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | const ( 13 | // Pattern for detecting valid line format 14 | linePattern = `\A(?:export\s+)?([\w\.]+)(?:\s*=\s*|:\s+?)('(?:\'|[^'])*'|"(?:\"|[^"])*"|[^#\n]+)?(?:\s*\#.*)?\z` 15 | 16 | // Pattern for detecting valid variable within a value 17 | variablePattern = `(\\)?(\$)(\{?([A-Z0-9_]+)\}?)` 18 | ) 19 | 20 | // Holds key/value pair of valid environment variable 21 | type Env map[string]string 22 | 23 | /* 24 | Load is function to load a file or multiple files and then export the valid variables which found into environment variables. 25 | When it's called with no argument, it will load `.env` file on the current path and set the environment variables. 26 | Otherwise, it will loop over the filenames parameter and set the proper environment variables. 27 | 28 | // processing `.env` 29 | gotenv.Load() 30 | 31 | // processing multiple files 32 | gotenv.Load("production.env", "credentials") 33 | 34 | */ 35 | func Load(filenames ...string) error { 36 | if len(filenames) == 0 { 37 | filenames = []string{".env"} 38 | } 39 | 40 | for _, filename := range filenames { 41 | f, err := os.Open(filename) 42 | if err != nil { 43 | return err 44 | } 45 | defer f.Close() 46 | 47 | // set environment 48 | env := Parse(f) 49 | for key, val := range env { 50 | os.Setenv(key, val) 51 | } 52 | } 53 | 54 | return nil 55 | } 56 | 57 | // Parse if a function to parse line by line any io.Reader supplied and returns the valid Env key/value pair of valid variables. 58 | // It expands the value of a variable from environment variable, but does not set the value to the environment itself. 59 | // This function is skipping any invalid lines and only processing the valid one. 60 | func Parse(r io.Reader) Env { 61 | env := make(Env) 62 | scanner := bufio.NewScanner(r) 63 | 64 | for scanner.Scan() { 65 | parseLine(scanner.Text(), env) 66 | } 67 | 68 | return env 69 | } 70 | 71 | func parseLine(s string, env Env) { 72 | r := regexp.MustCompile(linePattern) 73 | matches := r.FindStringSubmatch(s) 74 | if len(matches) == 0 { 75 | return 76 | } 77 | 78 | key := matches[1] 79 | val := matches[2] 80 | 81 | // determine if string has quote prefix 82 | hq := strings.HasPrefix(val, `"`) 83 | 84 | // trim whitespace 85 | val = strings.Trim(val, " ") 86 | 87 | // remove quotes '' or "" 88 | rq := regexp.MustCompile(`\A(['"])(.*)(['"])\z`) 89 | val = rq.ReplaceAllString(val, "$2") 90 | 91 | if hq { 92 | val = strings.Replace(val, `\n`, "\n", -1) 93 | // Unescape all characters except $ so variables can be escaped properly 94 | re := regexp.MustCompile(`\\([^$])`) 95 | val = re.ReplaceAllString(val, "$1") 96 | } 97 | 98 | rv := regexp.MustCompile(variablePattern) 99 | xv := rv.FindStringSubmatch(val) 100 | 101 | if len(xv) > 0 { 102 | var replace string 103 | var ok bool 104 | 105 | if xv[1] == "\\" { 106 | replace = strings.Join(xv[2:4], "") 107 | } else { 108 | replace, ok = env[xv[4]] 109 | if !ok { 110 | replace = os.Getenv(xv[4]) 111 | } 112 | } 113 | 114 | val = strings.Replace(val, strings.Join(xv[0:1], ""), replace, -1) 115 | } 116 | 117 | env[key] = val 118 | return 119 | } 120 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/subosito/gotenv/gotenv_test.go: -------------------------------------------------------------------------------- 1 | package gotenv 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | var formats = []struct { 11 | in string 12 | out Env 13 | preset bool 14 | }{ 15 | // parses unquoted values 16 | {`FOO=bar`, Env{"FOO": "bar"}, false}, 17 | 18 | // parses values with spaces around equal sign 19 | {`FOO =bar`, Env{"FOO": "bar"}, false}, 20 | {`FOO= bar`, Env{"FOO": "bar"}, false}, 21 | 22 | // parses double quoted values 23 | {`FOO="bar"`, Env{"FOO": "bar"}, false}, 24 | 25 | // parses single quoted values 26 | {`FOO='bar'`, Env{"FOO": "bar"}, false}, 27 | 28 | // parses escaped double quotes 29 | {`FOO="escaped\"bar"`, Env{"FOO": `escaped"bar`}, false}, 30 | 31 | // parses empty values 32 | {`FOO=`, Env{"FOO": ""}, false}, 33 | 34 | // expands variables found in values 35 | {"FOO=test\nBAR=$FOO", Env{"FOO": "test", "BAR": "test"}, false}, 36 | 37 | // parses variables wrapped in brackets 38 | {"FOO=test\nBAR=${FOO}bar", Env{"FOO": "test", "BAR": "testbar"}, false}, 39 | 40 | // reads variables from ENV when expanding if not found in local env 41 | {`BAR=$FOO`, Env{"BAR": "test"}, true}, 42 | 43 | // expands undefined variables to an empty string 44 | {`BAR=$FOO`, Env{"BAR": ""}, false}, 45 | 46 | // expands variables in quoted strings 47 | {"FOO=test\nBAR='quote $FOO'", Env{"FOO": "test", "BAR": "quote test"}, false}, 48 | 49 | // does not expand escaped variables 50 | {`FOO="foo\$BAR"`, Env{"FOO": "foo$BAR"}, false}, 51 | {`FOO="foo\${BAR}"`, Env{"FOO": "foo${BAR}"}, false}, 52 | 53 | // parses yaml style options 54 | {"OPTION_A: 1", Env{"OPTION_A": "1"}, false}, 55 | 56 | // parses export keyword 57 | {"export OPTION_A=2", Env{"OPTION_A": "2"}, false}, 58 | 59 | // expands newlines in quoted strings 60 | {`FOO="bar\nbaz"`, Env{"FOO": "bar\nbaz"}, false}, 61 | 62 | // parses varibales with "." in the name 63 | {`FOO.BAR=foobar`, Env{"FOO.BAR": "foobar"}, false}, 64 | 65 | // strips unquoted values 66 | {`foo=bar `, Env{"foo": "bar"}, false}, // not 'bar ' 67 | 68 | // ignores empty lines 69 | {"\n \t \nfoo=bar\n \nfizz=buzz", Env{"foo": "bar", "fizz": "buzz"}, false}, 70 | 71 | // ignores inline comments 72 | {"foo=bar # this is foo", Env{"foo": "bar"}, false}, 73 | 74 | // allows # in quoted value 75 | {`foo="bar#baz" # comment`, Env{"foo": "bar#baz"}, false}, 76 | 77 | // ignores comment lines 78 | {"\n\n\n # HERE GOES FOO \nfoo=bar", Env{"foo": "bar"}, false}, 79 | 80 | // parses # in quoted values 81 | {`foo="ba#r"`, Env{"foo": "ba#r"}, false}, 82 | {"foo='ba#r'", Env{"foo": "ba#r"}, false}, 83 | 84 | // incorrect line format 85 | {"lol$wut", Env{}, false}, 86 | } 87 | 88 | var fixtures = []struct { 89 | filename string 90 | results Env 91 | }{ 92 | { 93 | "fixtures/exported.env", 94 | Env{ 95 | "OPTION_A": "2", 96 | "OPTION_B": `\n`, 97 | }, 98 | }, 99 | { 100 | "fixtures/plain.env", 101 | Env{ 102 | "OPTION_A": "1", 103 | "OPTION_B": "2", 104 | "OPTION_C": "3", 105 | "OPTION_D": "4", 106 | "OPTION_E": "5", 107 | }, 108 | }, 109 | { 110 | "fixtures/quoted.env", 111 | Env{ 112 | "OPTION_A": "1", 113 | "OPTION_B": "2", 114 | "OPTION_C": "", 115 | "OPTION_D": `\n`, 116 | "OPTION_E": "1", 117 | "OPTION_F": "2", 118 | "OPTION_G": "", 119 | "OPTION_H": "\n", 120 | }, 121 | }, 122 | { 123 | "fixtures/yaml.env", 124 | Env{ 125 | "OPTION_A": "1", 126 | "OPTION_B": "2", 127 | "OPTION_C": "", 128 | "OPTION_D": `\n`, 129 | }, 130 | }, 131 | } 132 | 133 | func TestParse(t *testing.T) { 134 | for i, tt := range formats { 135 | if tt.preset { 136 | os.Setenv("FOO", "test") 137 | } 138 | 139 | exp := Parse(strings.NewReader(tt.in)) 140 | 141 | x := fmt.Sprintf("%+v\n", exp) 142 | o := fmt.Sprintf("%+v\n", tt.out) 143 | 144 | if x != o { 145 | t.Logf("%q\n", tt.in) 146 | t.Errorf("(%d) %s != %s\n", i, x, o) 147 | } 148 | 149 | os.Clearenv() 150 | } 151 | } 152 | 153 | func TestLoad(t *testing.T) { 154 | for i, tt := range fixtures { 155 | Load(tt.filename) 156 | 157 | for key, val := range tt.results { 158 | if eval := os.Getenv(key); eval != val { 159 | t.Errorf("(%d) %s => %s != %s", i, key, eval, val) 160 | } 161 | } 162 | 163 | os.Clearenv() 164 | } 165 | } 166 | 167 | func TestLoadEnv(t *testing.T) { 168 | Load() 169 | 170 | tkey := "HELLO" 171 | val := "world" 172 | 173 | if tval := os.Getenv(tkey); tval != val { 174 | t.Errorf("%s => %s != %s", tkey, tval, val) 175 | } 176 | 177 | os.Clearenv() 178 | } 179 | 180 | func TestLoadNonExist(t *testing.T) { 181 | file := ".nonexist.env" 182 | 183 | err := Load(file) 184 | if err == nil { 185 | t.Errorf("Load(`%s`) => error: `no such file or directory` != nil", file) 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN = forego 2 | SRC = $(shell ls *.go) 3 | 4 | .PHONY: all build clean install test lint 5 | 6 | all: build 7 | 8 | build: $(BIN) 9 | 10 | clean: 11 | rm -f $(BIN) 12 | 13 | install: forego 14 | cp $< ${GOPATH}/bin/ 15 | 16 | lint: $(SRC) 17 | go fmt 18 | 19 | test: lint build 20 | go test ./... -cover 21 | cd eg && ../forego start 22 | 23 | $(BIN): $(SRC) 24 | godep go build -o $@ 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## forego 2 | 3 | 4 | 5 | 6 | 7 | Foreman in Go. 8 | 9 | ### Installation 10 | 11 | ##### OS X (Homebrew) 12 | 13 | brew install forego 14 | 15 | ##### Precompiled Binaries 16 | 17 | * [Linux](https://godist.herokuapp.com/projects/ddollar/forego/releases/current/linux-amd64/forego) 18 | * [OSX](https://godist.herokuapp.com/projects/ddollar/forego/releases/current/darwin-amd64/forego) 19 | * [Windows](https://godist.herokuapp.com/projects/ddollar/forego/releases/current/windows-amd64/forego.exe) 20 | 21 | ##### Compile from Source 22 | 23 | $ go get -u github.com/ddollar/forego 24 | 25 | ### Usage 26 | 27 | $ cat Procfile 28 | web: bin/web start -p $PORT 29 | worker: bin/worker queue=FOO 30 | 31 | $ forego start 32 | web | listening on port 5000 33 | worker | listening to queue FOO 34 | 35 | ### License 36 | 37 | Apache 2.0 © 2015 David Dollar 38 | -------------------------------------------------------------------------------- /barrier.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Direct import of https://github.com/pwaller/barrier/blob/master/barrier.go 8 | 9 | // The zero of Barrier is a ready-to-use value 10 | type Barrier struct { 11 | channel chan struct{} 12 | fall, initialize sync.Once 13 | FallHook func() 14 | } 15 | 16 | func (b *Barrier) init() { 17 | b.initialize.Do(func() { b.channel = make(chan struct{}) }) 18 | } 19 | 20 | // `b.Fall()` can be called any number of times and causes the channel returned 21 | // by `b.Barrier()` to become closed (permanently available for immediate reading) 22 | func (b *Barrier) Fall() { 23 | b.init() 24 | b.fall.Do(func() { 25 | if b.FallHook != nil { 26 | b.FallHook() 27 | } 28 | close(b.channel) 29 | }) 30 | } 31 | 32 | // When `b.Fall()` is called, the channel returned by Barrier() is closed 33 | // (and becomes always readable) 34 | func (b *Barrier) Barrier() <-chan struct{} { 35 | b.init() 36 | return b.channel 37 | } 38 | -------------------------------------------------------------------------------- /command.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | var flagEnv string 10 | var flagProcfile string 11 | 12 | type Command struct { 13 | // args does not include the command name 14 | Run func(cmd *Command, args []string) 15 | Flag flag.FlagSet 16 | 17 | Disabled bool 18 | Usage string // first word is the command name 19 | Short string // `forego help` output 20 | Long string // `forego help cmd` output 21 | } 22 | 23 | func (c *Command) printUsage() { 24 | if c.Runnable() { 25 | fmt.Printf("Usage: forego %s\n\n", c.Usage) 26 | } 27 | fmt.Println(strings.Trim(c.Long, "\n")) 28 | } 29 | 30 | func (c *Command) Name() string { 31 | name := c.Usage 32 | i := strings.Index(name, " ") 33 | if i >= 0 { 34 | name = name[:i] 35 | } 36 | return name 37 | } 38 | 39 | func (c *Command) Runnable() bool { 40 | return c.Run != nil && c.Disabled != true 41 | } 42 | 43 | func (c *Command) List() bool { 44 | return c.Short != "" 45 | } 46 | -------------------------------------------------------------------------------- /eg/.env: -------------------------------------------------------------------------------- 1 | FOO=bar 2 | 3 | #BAZ=buz 4 | -------------------------------------------------------------------------------- /eg/Procfile: -------------------------------------------------------------------------------- 1 | ticker: ruby ./ticker $PORT 2 | error: ruby ./error 3 | utf8: ruby ./utf8 4 | spawner: ./spawner 5 | -------------------------------------------------------------------------------- /eg/error: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | $stdout.sync = true 4 | 5 | puts "will error in 10s" 6 | sleep 5 7 | raise "Dying" 8 | -------------------------------------------------------------------------------- /eg/spawnee: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | NAME="$1" 4 | 5 | sigterm() { 6 | echo "$NAME: got sigterm" 7 | } 8 | 9 | #trap sigterm SIGTERM 10 | 11 | while true; do 12 | echo "$NAME: ping $$" 13 | sleep 1 14 | done 15 | -------------------------------------------------------------------------------- /eg/spawner: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./spawnee A & 4 | ./spawnee B & 5 | ./spawnee C & 6 | 7 | wait 8 | -------------------------------------------------------------------------------- /eg/ticker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | $stdout.sync = true 4 | 5 | %w( SIGINT SIGTERM ).each do |signal| 6 | trap(signal) do 7 | puts "received #{signal} but i'm ignoring it!" 8 | end 9 | end 10 | 11 | while true 12 | puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}" 13 | sleep 1 14 | end 15 | -------------------------------------------------------------------------------- /eg/utf8: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: BINARY 3 | 4 | $stdout.sync = true 5 | 6 | while true 7 | puts "\u65e5\u672c\u8a9e\u6587\u5b57\u5217" 8 | puts "\u0915\u0932\u094d\u0907\u0928\u0643\u0637\u0628\u041a\u0430\u043b\u0438\u043d\u0430" 9 | puts "\xff\x03" 10 | sleep 1 11 | end 12 | -------------------------------------------------------------------------------- /env.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "regexp" 7 | 8 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/subosito/gotenv" 9 | ) 10 | 11 | var envEntryRegexp = regexp.MustCompile("^([A-Za-z_0-9]+)=(.*)$") 12 | 13 | type Env map[string]string 14 | 15 | type envFiles []string 16 | 17 | func (e *envFiles) String() string { 18 | return fmt.Sprintf("%s", *e) 19 | } 20 | 21 | func (e *envFiles) Set(value string) error { 22 | *e = append(*e, value) 23 | return nil 24 | } 25 | 26 | func loadEnvs(files []string) (Env, error) { 27 | if len(files) == 0 { 28 | files = []string{".env"} 29 | } 30 | 31 | env := make(Env) 32 | 33 | for _, file := range files { 34 | tmpEnv, err := ReadEnv(file) 35 | 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | // Merge the file I just read into the env. 41 | for k, v := range tmpEnv { 42 | env[k] = v 43 | } 44 | } 45 | return env, nil 46 | } 47 | 48 | func ReadEnv(filename string) (Env, error) { 49 | if _, err := os.Stat(filename); os.IsNotExist(err) { 50 | return make(Env), nil 51 | } 52 | fd, err := os.Open(filename) 53 | if err != nil { 54 | return nil, err 55 | } 56 | defer fd.Close() 57 | env := make(Env) 58 | for key, val := range gotenv.Parse(fd) { 59 | env[key] = val 60 | } 61 | return env, nil 62 | } 63 | 64 | func (e *Env) asArray() (env []string) { 65 | for _, pair := range os.Environ() { 66 | env = append(env, pair) 67 | } 68 | for name, val := range *e { 69 | env = append(env, fmt.Sprintf("%s=%s", name, val)) 70 | } 71 | return 72 | } 73 | -------------------------------------------------------------------------------- /env_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestMultipleEnvironmentFiles(t *testing.T) { 6 | envs := []string{"fixtures/envs/.env1", "fixtures/envs/.env2"} 7 | env, err := loadEnvs(envs) 8 | 9 | if err != nil { 10 | t.Fatalf("Could not read environments: %s", err) 11 | } 12 | 13 | if env["env1"] == "" { 14 | t.Fatalf("$env1 should be present and is not") 15 | } 16 | 17 | if env["env2"] == "" { 18 | t.Fatalf("$env2 should be present and is not") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func handleError(err error) { 9 | if err != nil { 10 | fmt.Println("ERROR:", err) 11 | os.Exit(1) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /fixtures/envs/.env1: -------------------------------------------------------------------------------- 1 | env1=present 2 | -------------------------------------------------------------------------------- /fixtures/envs/.env2: -------------------------------------------------------------------------------- 1 | env2=present 2 | -------------------------------------------------------------------------------- /fixtures/large_stdout/Procfile: -------------------------------------------------------------------------------- 1 | stdout1: ruby ./stdout.rb 2 | stdout2: ruby ./stdout.rb 3 | -------------------------------------------------------------------------------- /fixtures/large_stdout/stdout.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | $stdout.sync = true 4 | 5 | 10.times do |i| 6 | puts "sample log message... " * rand(i*1000) + "ok - #{i}" 7 | sleep 1 8 | end 9 | 10 | puts "finish!" 11 | -------------------------------------------------------------------------------- /fixtures/multiline/Procfile: -------------------------------------------------------------------------------- 1 | stdout1: ruby ./stdout.rb 2 | stdout2: ruby ./stdout.rb 3 | -------------------------------------------------------------------------------- /fixtures/multiline/stdout.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | $stdout.sync = true 4 | 5 | puts "a" 6 | sleep 1 7 | puts "a\nb" 8 | sleep 1 9 | puts "a\nb\nc" 10 | sleep 1 11 | puts "a\nb\nc\nd" 12 | sleep 1 13 | puts "finish!" 14 | -------------------------------------------------------------------------------- /fixtures/writey/Procfile: -------------------------------------------------------------------------------- 1 | writey1: writey 2 | writey2: writey 3 | -------------------------------------------------------------------------------- /fixtures/writey/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | rand.Seed(time.Now().UnixNano()) 14 | print("Foo") 15 | time.Sleep(10 * time.Millisecond) 16 | println("Bar") 17 | 18 | print("Baz") 19 | time.Sleep(10 * time.Millisecond) 20 | println("Qux") 21 | 22 | fmt.Fprintln(os.Stdout, "This is on \x1b[32mstdout") 23 | 24 | os.Stdout.Close() 25 | 26 | s := rand.Intn(3) + 1 27 | 28 | c := make(chan os.Signal) 29 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 30 | 31 | select { 32 | case <-c: 33 | if rand.Intn(4) == 1 { 34 | println("IGNORING EXIT") 35 | time.Sleep(100 * time.Second) 36 | } 37 | println("Got SIGTERM") 38 | case <-time.After(time.Duration(s) * time.Second): 39 | println("Timed out") 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /help.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "text/template" 8 | ) 9 | 10 | var cmdHelp = &Command{ 11 | Usage: "help [topic]", 12 | Short: "Show this help", 13 | Long: `Help shows usage for a command.`, 14 | } 15 | 16 | func init() { 17 | cmdHelp.Run = runHelp // break init loop 18 | } 19 | 20 | func runHelp(cmd *Command, args []string) { 21 | if len(args) == 0 { 22 | printUsage() 23 | return 24 | } 25 | if len(args) != 1 { 26 | log.Fatal("too many arguments") 27 | } 28 | 29 | for _, cmd := range commands { 30 | if cmd.Name() == args[0] { 31 | cmd.printUsage() 32 | return 33 | } 34 | } 35 | 36 | fmt.Fprintf(os.Stderr, "Unknown help topic: %q. Run 'forego help'.\n", args[0]) 37 | os.Exit(2) 38 | } 39 | 40 | var usageTemplate = template.Must(template.New("usage").Parse(` 41 | Usage: forego [] 42 | 43 | Available commands:{{range .Commands}}{{if .Runnable}}{{if .List}} 44 | {{.Name | printf "%-8s"}} {{.Short}}{{end}}{{end}}{{end}} 45 | 46 | Run 'forego help [command]' for details. 47 | `[1:])) 48 | 49 | func printUsage() { 50 | usageTemplate.Execute(os.Stdout, struct { 51 | Commands []*Command 52 | }{ 53 | commands, 54 | }) 55 | } 56 | 57 | func usage() { 58 | printUsage() 59 | os.Exit(2) 60 | } 61 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "os" 4 | 5 | var commands = []*Command{ 6 | cmdStart, 7 | cmdRun, 8 | cmdUpdate, 9 | cmdVersion, 10 | cmdHelp, 11 | } 12 | 13 | var allowUpdate string = "true" 14 | 15 | func main() { 16 | args := os.Args[1:] 17 | if len(args) < 1 { 18 | usage() 19 | } 20 | 21 | if allowUpdate == "false" { 22 | cmdUpdate.Disabled = true 23 | } 24 | 25 | for _, cmd := range commands { 26 | if cmd.Name() == args[0] && cmd.Runnable() { 27 | cmd.Flag.Usage = func() { 28 | cmd.printUsage() 29 | } 30 | if err := cmd.Flag.Parse(args[1:]); err != nil { 31 | os.Exit(2) 32 | } 33 | cmd.Run(cmd, cmd.Flag.Args()) 34 | return 35 | } 36 | } 37 | usage() 38 | } 39 | -------------------------------------------------------------------------------- /outlet.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/daviddengcn/go-colortext" 8 | "io" 9 | "os" 10 | "sync" 11 | ) 12 | 13 | type OutletFactory struct { 14 | Padding int 15 | 16 | sync.Mutex 17 | } 18 | 19 | var colors = []ct.Color{ 20 | ct.Cyan, 21 | ct.Yellow, 22 | ct.Green, 23 | ct.Magenta, 24 | ct.Red, 25 | ct.Blue, 26 | } 27 | 28 | func NewOutletFactory() (of *OutletFactory) { 29 | return new(OutletFactory) 30 | } 31 | 32 | func (of *OutletFactory) LineReader(wg *sync.WaitGroup, name string, index int, r io.Reader, isError bool) { 33 | defer wg.Done() 34 | 35 | color := colors[index%len(colors)] 36 | 37 | reader := bufio.NewReader(r) 38 | 39 | var buffer bytes.Buffer 40 | 41 | for { 42 | buf := make([]byte, 1024) 43 | 44 | if n, err := reader.Read(buf); err != nil { 45 | return 46 | } else { 47 | buf = buf[:n] 48 | } 49 | 50 | for { 51 | i := bytes.IndexByte(buf, '\n') 52 | if i < 0 { 53 | break 54 | } 55 | buffer.Write(buf[0:i]) 56 | of.WriteLine(name, buffer.String(), color, ct.None, isError) 57 | buffer.Reset() 58 | buf = buf[i+1:] 59 | } 60 | 61 | buffer.Write(buf) 62 | } 63 | } 64 | 65 | func (of *OutletFactory) SystemOutput(str string) { 66 | of.WriteLine("forego", str, ct.White, ct.None, false) 67 | } 68 | 69 | func (of *OutletFactory) ErrorOutput(str string) { 70 | fmt.Printf("ERROR: %s\n", str) 71 | os.Exit(1) 72 | } 73 | 74 | // Write out a single coloured line 75 | func (of *OutletFactory) WriteLine(left, right string, leftC, rightC ct.Color, isError bool) { 76 | of.Lock() 77 | defer of.Unlock() 78 | 79 | ct.ChangeColor(leftC, true, ct.None, false) 80 | formatter := fmt.Sprintf("%%-%ds | ", of.Padding) 81 | fmt.Printf(formatter, left) 82 | 83 | if isError { 84 | ct.ChangeColor(ct.Red, true, ct.None, true) 85 | } else { 86 | ct.ResetColor() 87 | } 88 | fmt.Println(right) 89 | if isError { 90 | ct.ResetColor() 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /process.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "syscall" 7 | ) 8 | 9 | type Process struct { 10 | Command string 11 | Env Env 12 | Interactive bool 13 | 14 | *exec.Cmd 15 | } 16 | 17 | func NewProcess(workdir, command string, env Env, interactive bool) (p *Process) { 18 | argv := ShellInvocationCommand(interactive, workdir, command) 19 | return &Process{ 20 | command, env, interactive, exec.Command(argv[0], argv[1:]...), 21 | } 22 | } 23 | 24 | func (p *Process) Start() error { 25 | p.Cmd.Env = p.Env.asArray() 26 | p.PlatformSpecificInit() 27 | return p.Cmd.Start() 28 | } 29 | 30 | func (p *Process) Signal(signal syscall.Signal) error { 31 | group, err := os.FindProcess(-1 * p.Process.Pid) 32 | if err == nil { 33 | err = group.Signal(signal) 34 | } 35 | return err 36 | } 37 | -------------------------------------------------------------------------------- /procfile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | _ "github.com/ddollar/forego/Godeps/_workspace/src/github.com/kr/pretty" 7 | "io" 8 | "math" 9 | "os" 10 | "regexp" 11 | ) 12 | 13 | var procfileEntryRegexp = regexp.MustCompile("^([A-Za-z0-9_]+):\\s*(.+)$") 14 | 15 | type ProcfileEntry struct { 16 | Name string 17 | Command string 18 | } 19 | 20 | type Procfile struct { 21 | Entries []ProcfileEntry 22 | } 23 | 24 | func ReadProcfile(filename string) (*Procfile, error) { 25 | fd, err := os.Open(filename) 26 | if err != nil { 27 | return nil, err 28 | } 29 | defer fd.Close() 30 | return parseProcfile(fd) 31 | } 32 | 33 | func (pf *Procfile) HasProcess(name string) (exists bool) { 34 | for _, entry := range pf.Entries { 35 | if name == entry.Name { 36 | return true 37 | } 38 | } 39 | return false 40 | } 41 | 42 | func (pf *Procfile) LongestProcessName(concurrency map[string]int) (longest int) { 43 | longest = 6 // length of forego 44 | for _, entry := range pf.Entries { 45 | thisLen := len(entry.Name) 46 | // The "." 47 | thisLen += 1 48 | if c, ok := concurrency[entry.Name]; ok { 49 | // Add the number of digits 50 | thisLen += int(math.Log10(float64(c))) + 1 51 | } 52 | if thisLen > longest { 53 | longest = thisLen 54 | } 55 | } 56 | return 57 | } 58 | 59 | func parseProcfile(r io.Reader) (*Procfile, error) { 60 | pf := new(Procfile) 61 | scanner := bufio.NewScanner(r) 62 | for scanner.Scan() { 63 | parts := procfileEntryRegexp.FindStringSubmatch(scanner.Text()) 64 | if len(parts) > 0 { 65 | pf.Entries = append(pf.Entries, ProcfileEntry{parts[1], parts[2]}) 66 | } 67 | } 68 | if err := scanner.Err(); err != nil { 69 | return nil, fmt.Errorf("Reading Procfile: %s", err) 70 | } 71 | return pf, nil 72 | } 73 | -------------------------------------------------------------------------------- /run.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | var cmdRun = &Command{ 9 | Run: runRun, 10 | Usage: "run [-e env] [-p port]", 11 | Short: "Run a one-off command", 12 | Long: ` 13 | Run a one-off command 14 | 15 | Examples: 16 | 17 | forego run bin/migrate 18 | `, 19 | } 20 | 21 | var runEnvs envFiles 22 | 23 | func init() { 24 | cmdRun.Flag.Var(&runEnvs, "e", "env") 25 | } 26 | 27 | func runRun(cmd *Command, args []string) { 28 | if len(args) < 1 { 29 | cmd.printUsage() 30 | os.Exit(1) 31 | } 32 | workDir, err := os.Getwd() 33 | if err != nil { 34 | handleError(err) 35 | } 36 | 37 | env, err := loadEnvs(runEnvs) 38 | handleError(err) 39 | 40 | const interactive = true 41 | ps := NewProcess(workDir, strings.Join(args, " "), env, interactive) 42 | ps.Stdin = os.Stdin 43 | ps.Stdout = os.Stdout 44 | ps.Stderr = os.Stderr 45 | 46 | err = ps.Start() 47 | handleError(err) 48 | 49 | err = ps.Wait() 50 | handleError(err) 51 | } 52 | -------------------------------------------------------------------------------- /start.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "path/filepath" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | const shutdownGraceTime = 3 * time.Second 16 | const defaultPort = 5000 17 | 18 | var flagPort int 19 | var flagConcurrency string 20 | var flagRestart bool 21 | var envs envFiles 22 | 23 | var cmdStart = &Command{ 24 | Run: runStart, 25 | Usage: "start [process name] [-f procfile] [-e env] [-c concurrency] [-p port] [-r]", 26 | Short: "Start the application", 27 | Long: ` 28 | Start the application specified by a Procfile (defaults to ./Procfile) 29 | 30 | Examples: 31 | 32 | forego start 33 | forego start web 34 | forego start -f Procfile.test -e .env.test 35 | `, 36 | } 37 | 38 | func init() { 39 | cmdStart.Flag.StringVar(&flagProcfile, "f", "Procfile", "procfile") 40 | cmdStart.Flag.Var(&envs, "e", "env") 41 | cmdStart.Flag.IntVar(&flagPort, "p", defaultPort, "port") 42 | cmdStart.Flag.StringVar(&flagConcurrency, "c", "", "concurrency") 43 | cmdStart.Flag.BoolVar(&flagRestart, "r", false, "restart") 44 | } 45 | 46 | func parseConcurrency(value string) (map[string]int, error) { 47 | concurrency := map[string]int{} 48 | if strings.TrimSpace(value) == "" { 49 | return concurrency, nil 50 | } 51 | 52 | parts := strings.Split(value, ",") 53 | for _, part := range parts { 54 | if !strings.Contains(part, "=") { 55 | return concurrency, errors.New("Concurrency should be in the format: foo=1,bar=2") 56 | } 57 | 58 | nameValue := strings.Split(part, "=") 59 | n, v := strings.TrimSpace(nameValue[0]), strings.TrimSpace(nameValue[1]) 60 | if n == "" || v == "" { 61 | return concurrency, errors.New("Concurrency should be in the format: foo=1,bar=2") 62 | } 63 | 64 | numProcs, err := strconv.ParseInt(v, 10, 16) 65 | if err != nil { 66 | return concurrency, err 67 | } 68 | 69 | concurrency[n] = int(numProcs) 70 | } 71 | return concurrency, nil 72 | } 73 | 74 | type Forego struct { 75 | outletFactory *OutletFactory 76 | 77 | teardown, teardownNow Barrier // signal shutting down 78 | 79 | wg sync.WaitGroup 80 | } 81 | 82 | func (f *Forego) monitorInterrupt() { 83 | handler := make(chan os.Signal, 1) 84 | signal.Notify(handler, os.Interrupt) 85 | 86 | first := true 87 | 88 | for sig := range handler { 89 | switch sig { 90 | case os.Interrupt: 91 | fmt.Println(" | ctrl-c detected") 92 | 93 | f.teardown.Fall() 94 | if !first { 95 | f.teardownNow.Fall() 96 | } 97 | first = false 98 | } 99 | } 100 | } 101 | 102 | func basePort(env Env) (int, error) { 103 | if flagPort != defaultPort { 104 | return flagPort, nil 105 | } else if env["PORT"] != "" { 106 | return strconv.Atoi(env["PORT"]) 107 | } else if os.Getenv("PORT") != "" { 108 | return strconv.Atoi(os.Getenv("PORT")) 109 | } 110 | return defaultPort, nil 111 | } 112 | 113 | func (f *Forego) startProcess(idx, procNum int, proc ProcfileEntry, env Env, of *OutletFactory) { 114 | port, err := basePort(env) 115 | if err != nil { 116 | panic(err) 117 | } 118 | 119 | port = port + (idx * 100) 120 | 121 | const interactive = false 122 | workDir := filepath.Dir(flagProcfile) 123 | ps := NewProcess(workDir, proc.Command, env, interactive) 124 | procName := fmt.Sprint(proc.Name, ".", procNum+1) 125 | ps.Env["PORT"] = strconv.Itoa(port) 126 | 127 | ps.Stdin = nil 128 | 129 | stdout, err := ps.StdoutPipe() 130 | if err != nil { 131 | panic(err) 132 | } 133 | stderr, err := ps.StderrPipe() 134 | if err != nil { 135 | panic(err) 136 | } 137 | 138 | pipeWait := new(sync.WaitGroup) 139 | pipeWait.Add(2) 140 | go of.LineReader(pipeWait, procName, idx, stdout, false) 141 | go of.LineReader(pipeWait, procName, idx, stderr, true) 142 | 143 | of.SystemOutput(fmt.Sprintf("starting %s on port %d", procName, port)) 144 | 145 | finished := make(chan struct{}) // closed on process exit 146 | 147 | err = ps.Start() 148 | if err != nil { 149 | f.teardown.Fall() 150 | of.SystemOutput(fmt.Sprint("Failed to start ", procName, ": ", err)) 151 | return 152 | } 153 | 154 | f.wg.Add(1) 155 | go func() { 156 | defer f.wg.Done() 157 | defer close(finished) 158 | pipeWait.Wait() 159 | ps.Wait() 160 | }() 161 | 162 | f.wg.Add(1) 163 | go func() { 164 | defer f.wg.Done() 165 | 166 | // Prevent goroutine from exiting before process has finished. 167 | defer func() { <-finished }() 168 | if !flagRestart { 169 | defer f.teardown.Fall() 170 | } 171 | 172 | select { 173 | case <-finished: 174 | if flagRestart { 175 | f.startProcess(idx, procNum, proc, env, of) 176 | return 177 | } 178 | 179 | case <-f.teardown.Barrier(): 180 | // Forego tearing down 181 | 182 | if !osHaveSigTerm { 183 | of.SystemOutput(fmt.Sprintf("Killing %s", procName)) 184 | ps.Process.Kill() 185 | return 186 | } 187 | 188 | of.SystemOutput(fmt.Sprintf("sending SIGTERM to %s", procName)) 189 | ps.SendSigTerm() 190 | 191 | // Give the process a chance to exit, otherwise kill it. 192 | select { 193 | case <-f.teardownNow.Barrier(): 194 | of.SystemOutput(fmt.Sprintf("Killing %s", procName)) 195 | ps.SendSigKill() 196 | case <-finished: 197 | } 198 | } 199 | }() 200 | } 201 | 202 | func runStart(cmd *Command, args []string) { 203 | pf, err := ReadProcfile(flagProcfile) 204 | handleError(err) 205 | 206 | concurrency, err := parseConcurrency(flagConcurrency) 207 | handleError(err) 208 | 209 | env, err := loadEnvs(envs) 210 | handleError(err) 211 | 212 | of := NewOutletFactory() 213 | of.Padding = pf.LongestProcessName(concurrency) 214 | 215 | f := &Forego{ 216 | outletFactory: of, 217 | } 218 | 219 | go f.monitorInterrupt() 220 | 221 | // When teardown fires, start the grace timer 222 | f.teardown.FallHook = func() { 223 | go func() { 224 | time.Sleep(shutdownGraceTime) 225 | of.SystemOutput("Grace time expired") 226 | f.teardownNow.Fall() 227 | }() 228 | } 229 | 230 | var singleton string = "" 231 | if len(args) > 0 { 232 | singleton = args[0] 233 | if !pf.HasProcess(singleton) { 234 | of.ErrorOutput(fmt.Sprintf("no such process: %s", singleton)) 235 | } 236 | } 237 | 238 | defaultConcurrency := 1 239 | 240 | for name, num := range concurrency { 241 | if name == "all" { 242 | defaultConcurrency = num 243 | } 244 | } 245 | 246 | for idx, proc := range pf.Entries { 247 | numProcs := defaultConcurrency 248 | if value, ok := concurrency[proc.Name]; ok { 249 | numProcs = value 250 | } 251 | for i := 0; i < numProcs; i++ { 252 | if (singleton == "") || (singleton == proc.Name) { 253 | f.startProcess(idx, i, proc, env, of) 254 | } 255 | } 256 | } 257 | 258 | <-f.teardown.Barrier() 259 | 260 | f.wg.Wait() 261 | } 262 | -------------------------------------------------------------------------------- /start_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestParseConcurrencyFlagEmpty(t *testing.T) { 9 | c, err := parseConcurrency("") 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | if len(c) > 0 { 14 | t.Fatal("expected no concurrency settings with ''") 15 | } 16 | } 17 | 18 | func TestParseConcurrencyFlagSimle(t *testing.T) { 19 | c, err := parseConcurrency("foo=2") 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | if len(c) != 1 { 25 | t.Fatal("expected 1 concurrency settings with 'foo=2'") 26 | } 27 | 28 | if c["foo"] != 2 { 29 | t.Fatal("expected concurrency settings of 2 with 'foo=2'") 30 | } 31 | } 32 | 33 | func TestParseConcurrencyFlagMultiple(t *testing.T) { 34 | c, err := parseConcurrency("foo=2,bar=3") 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | if len(c) != 2 { 40 | t.Fatal("expected 1 concurrency settings with 'foo=2'") 41 | } 42 | 43 | if c["foo"] != 2 { 44 | t.Fatal("expected concurrency settings of 2 with 'foo=2'") 45 | } 46 | 47 | if c["bar"] != 3 { 48 | t.Fatal("expected concurrency settings of 3 with 'bar=3'") 49 | } 50 | } 51 | 52 | func TestParseConcurrencyFlagNonInt(t *testing.T) { 53 | _, err := parseConcurrency("foo=x") 54 | if err == nil { 55 | t.Fatal("foo=x should fail") 56 | } 57 | } 58 | 59 | func TestParseConcurrencyFlagWhitespace(t *testing.T) { 60 | c, err := parseConcurrency("foo = 2, bar = 3") 61 | if err != nil { 62 | t.Fatalf("foo = 2, bar = 4 should not fail:%s", err) 63 | } 64 | 65 | if len(c) != 2 { 66 | t.Fatal("expected 1 concurrency settings with 'foo=2'") 67 | } 68 | 69 | if c["foo"] != 2 { 70 | t.Fatal("expected concurrency settings of 2 with 'foo=2'") 71 | } 72 | 73 | if c["bar"] != 3 { 74 | t.Fatal("expected concurrency settings of 3 with 'bar=3'") 75 | } 76 | } 77 | 78 | func TestParseConcurrencyFlagMultipleEquals(t *testing.T) { 79 | _, err := parseConcurrency("foo===2") 80 | if err == nil { 81 | t.Fatalf("foo===2 should fail: %s", err) 82 | } 83 | } 84 | 85 | func TestParseConcurrencyFlagNoValue(t *testing.T) { 86 | _, err := parseConcurrency("foo=") 87 | if err == nil { 88 | t.Fatalf("foo= should fail: %s", err) 89 | } 90 | 91 | _, err = parseConcurrency("=") 92 | if err == nil { 93 | t.Fatalf("= should fail: %s", err) 94 | } 95 | 96 | _, err = parseConcurrency("=1") 97 | if err == nil { 98 | t.Fatalf("= should fail: %s", err) 99 | } 100 | 101 | _, err = parseConcurrency(",") 102 | if err == nil { 103 | t.Fatalf(", should fail: %s", err) 104 | } 105 | 106 | _, err = parseConcurrency(",,,") 107 | if err == nil { 108 | t.Fatalf(",,, should fail: %s", err) 109 | } 110 | 111 | } 112 | 113 | func TestPortFromEnv(t *testing.T) { 114 | env := make(Env) 115 | port, err := basePort(env) 116 | if err != nil { 117 | t.Fatalf("Can not get base port: %s", err) 118 | } 119 | if port != 5000 { 120 | t.Fatal("Base port should be 5000") 121 | } 122 | 123 | os.Setenv("PORT", "4000") 124 | port, err = basePort(env) 125 | if err != nil { 126 | t.Fatal("Can not get port: %s", err) 127 | } 128 | if port != 4000 { 129 | t.Fatal("Base port should be 4000") 130 | } 131 | 132 | env["PORT"] = "6000" 133 | port, err = basePort(env) 134 | if err != nil { 135 | t.Fatalf("Can not get base port: %s", err) 136 | } 137 | if port != 6000 { 138 | t.Fatal("Base port should be 6000") 139 | } 140 | 141 | env["PORT"] = "forego" 142 | port, err = basePort(env) 143 | if err == nil { 144 | t.Fatalf("Port 'forego' should fail: %s", err) 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /unix.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd linux netbsd openbsd 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "path/filepath" 8 | "syscall" 9 | ) 10 | 11 | const osHaveSigTerm = true 12 | 13 | func ShellInvocationCommand(interactive bool, root, command string) []string { 14 | shellArgument := "-c" 15 | if interactive { 16 | shellArgument = "-ic" 17 | } 18 | profile := filepath.Join(root, ".profile") 19 | shellCommand := fmt.Sprintf("source \"%s\" 2>/dev/null; %s", profile, command) 20 | return []string{"bash", shellArgument, shellCommand} 21 | 22 | } 23 | 24 | func (p *Process) PlatformSpecificInit() { 25 | if !p.Interactive { 26 | p.SysProcAttr = &syscall.SysProcAttr{} 27 | p.SysProcAttr.Setsid = true 28 | } 29 | return 30 | } 31 | 32 | func (p *Process) SendSigTerm() { 33 | p.Signal(syscall.SIGTERM) 34 | } 35 | 36 | func (p *Process) SendSigKill() { 37 | p.Signal(syscall.SIGKILL) 38 | } 39 | -------------------------------------------------------------------------------- /update.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ddollar/forego/Godeps/_workspace/src/github.com/ddollar/dist" 6 | ) 7 | 8 | var cmdUpdate = &Command{ 9 | Run: runUpdate, 10 | Usage: "update", 11 | Short: "Update forego", 12 | Long: ` 13 | Update forego 14 | 15 | Examples: 16 | 17 | forego update 18 | `, 19 | } 20 | 21 | func init() { 22 | } 23 | 24 | func runUpdate(cmd *Command, args []string) { 25 | if Version == "dev" { 26 | fmt.Println("ERROR: can't update dev version") 27 | return 28 | } 29 | d := dist.NewDist("ddollar/forego", Version) 30 | to, err := d.Update() 31 | if err != nil { 32 | fmt.Printf("ERROR: %s\n", err) 33 | } else { 34 | fmt.Printf("updated to %s\n", to) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | var stdout io.Writer = os.Stdout 10 | 11 | func Println(a ...interface{}) (n int, err error) { 12 | return fmt.Fprintln(stdout, a...) 13 | } 14 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var Version = "dev" 4 | 5 | var cmdVersion = &Command{ 6 | Run: runVersion, 7 | Usage: "version", 8 | Short: "Display current version", 9 | Long: ` 10 | Display current version 11 | 12 | Examples: 13 | 14 | forego version 15 | `, 16 | } 17 | 18 | func runVersion(cmd *Command, args []string) { 19 | Println(Version) 20 | } 21 | -------------------------------------------------------------------------------- /version_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestVersion(t *testing.T) { 9 | var b bytes.Buffer 10 | stdout = &b 11 | cmdVersion.Run(cmdVersion, []string{}) 12 | output := b.String() 13 | assertEqual(t, output, "dev\n") 14 | } 15 | 16 | func assertEqual(t *testing.T, a, b interface{}) { 17 | if a != b { 18 | t.Fatalf(`Expected %#v to equal %#v`, a, b) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package main 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | const osHaveSigTerm = false 10 | 11 | func ShellInvocationCommand(interactive bool, root, command string) []string { 12 | return []string{"cmd", "/C", command} 13 | } 14 | 15 | func (p *Process) PlatformSpecificInit() { 16 | // NOP on windows for now. 17 | return 18 | } 19 | 20 | func (p *Process) SendSigTerm() { 21 | panic("SendSigTerm() not implemented on this platform") 22 | } 23 | 24 | func (p *Process) SendSigKill() { 25 | p.Signal(syscall.SIGKILL) 26 | } 27 | --------------------------------------------------------------------------------