├── .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 |
--------------------------------------------------------------------------------