This commit is contained in:
Jingwen Owen Ou 2013-12-18 14:37:10 -08:00
Родитель 6c7d48ef95 89b15b9c52
Коммит 958169bfac
22 изменённых файлов: 1195 добавлений и 54 удалений

13
Godeps/Godeps.json сгенерированный
Просмотреть файл

@ -5,6 +5,11 @@
"./..." "./..."
], ],
"Deps": [ "Deps": [
{
"ImportPath": "bitbucket.org/kardianos/osext",
"Comment": "null-9",
"Rev": "364fb577de68fb646c4cb39cc0e09c887ee16376"
},
{ {
"ImportPath": "code.google.com/p/go-netrc/netrc", "ImportPath": "code.google.com/p/go-netrc/netrc",
"Comment": "null-12", "Comment": "null-12",
@ -24,10 +29,14 @@
"ImportPath": "github.com/howeyc/gopass", "ImportPath": "github.com/howeyc/gopass",
"Rev": "4cf66881dcc3d6f0071eb6473d95c39e460de696" "Rev": "4cf66881dcc3d6f0071eb6473d95c39e460de696"
}, },
{
"ImportPath": "github.com/inconshreveable/go-update",
"Rev": "3f0466666779bd2143f368a207b0641f0ed536e8"
},
{ {
"ImportPath": "github.com/jingweno/go-octokit/octokit", "ImportPath": "github.com/jingweno/go-octokit/octokit",
"Comment": "v0.4.0-27-g1dbe02b", "Comment": "v0.4.0-41-g85bc6b5",
"Rev": "1dbe02be6d8bd53eaf3869d97b1746ff82e2a807" "Rev": "85bc6b536f8e8cb24d4db262e4dfea3d7d2d8138"
}, },
{ {
"ImportPath": "github.com/jtacoma/uritemplates", "ImportPath": "github.com/jtacoma/uritemplates",

20
Godeps/_workspace/src/bitbucket.org/kardianos/osext/LICENSE сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
Copyright (c) 2012 Daniel Theophanes
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

32
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,32 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Extensions to the standard "os" package.
package osext
import "path/filepath"
// Executable returns an absolute path that can be used to
// re-invoke the current program.
// It may not be valid after the current program exits.
func Executable() (string, error) {
p, err := executable()
return filepath.Clean(p), err
}
// Returns same path as Executable, returns just the folder
// path. Excludes the executable name.
func ExecutableFolder() (string, error) {
p, err := Executable()
if err != nil {
return "", err
}
folder, _ := filepath.Split(p)
return folder, nil
}
// Depricated. Same as Executable().
func GetExePath() (exePath string, err error) {
return Executable()
}

16
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_plan9.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,16 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package osext
import "syscall"
func executable() (string, error) {
f, err := Open("/proc/" + itoa(Getpid()) + "/text")
if err != nil {
return "", err
}
defer f.Close()
return syscall.Fd2path(int(f.Fd()))
}

25
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_procfs.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,25 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux netbsd openbsd
package osext
import (
"errors"
"os"
"runtime"
)
func executable() (string, error) {
switch runtime.GOOS {
case "linux":
return os.Readlink("/proc/self/exe")
case "netbsd":
return os.Readlink("/proc/curproc/exe")
case "openbsd":
return os.Readlink("/proc/curproc/file")
}
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
}

82
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_sysctl.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,82 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin freebsd
package osext
import (
"os"
"path/filepath"
"runtime"
"syscall"
"unsafe"
)
var startUpcwd, getwdError = os.Getwd()
func executable() (string, error) {
var mib [4]int32
switch runtime.GOOS {
case "freebsd":
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
case "darwin":
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
}
n := uintptr(0)
// get length
_, _, err := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
if err != 0 {
return "", err
}
if n == 0 { // shouldn't happen
return "", nil
}
buf := make([]byte, n)
_, _, err = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
if err != 0 {
return "", err
}
if n == 0 { // shouldn't happen
return "", nil
}
for i, v := range buf {
if v == 0 {
buf = buf[:i]
break
}
}
var strpath string
if buf[0] != '/' {
var e error
if strpath, e = getAbs(buf); e != nil {
return strpath, e
}
} else {
strpath = string(buf)
}
// darwin KERN_PROCARGS may return the path to a symlink rather than the
// actual executable
if runtime.GOOS == "darwin" {
if strpath, err := filepath.EvalSymlinks(strpath); err != nil {
return strpath, err
}
}
return strpath, nil
}
func getAbs(buf []byte) (string, error) {
if getwdError != nil {
return string(buf), getwdError
} else {
if buf[0] == '.' {
buf = buf[1:]
}
if startUpcwd[len(startUpcwd)-1] != '/' && buf[0] != '/' {
return startUpcwd + "/" + string(buf), nil
}
return startUpcwd + string(buf), nil
}
}

79
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_test.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,79 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin linux freebsd netbsd windows
package osext
import (
"fmt"
"os"
oexec "os/exec"
"path/filepath"
"runtime"
"testing"
)
const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH"
func TestExecPath(t *testing.T) {
ep, err := Executable()
if err != nil {
t.Fatalf("ExecPath failed: %v", err)
}
// we want fn to be of the form "dir/prog"
dir := filepath.Dir(filepath.Dir(ep))
fn, err := filepath.Rel(dir, ep)
if err != nil {
t.Fatalf("filepath.Rel: %v", err)
}
cmd := &oexec.Cmd{}
// make child start with a relative program path
cmd.Dir = dir
cmd.Path = fn
// forge argv[0] for child, so that we can verify we could correctly
// get real path of the executable without influenced by argv[0].
cmd.Args = []string{"-", "-test.run=XXXX"}
cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)}
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("exec(self) failed: %v", err)
}
outs := string(out)
if !filepath.IsAbs(outs) {
t.Fatalf("Child returned %q, want an absolute path", out)
}
if !sameFile(outs, ep) {
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
}
}
func sameFile(fn1, fn2 string) bool {
fi1, err := os.Stat(fn1)
if err != nil {
return false
}
fi2, err := os.Stat(fn2)
if err != nil {
return false
}
return os.SameFile(fi1, fi2)
}
func init() {
if e := os.Getenv(execPath_EnvVar); e != "" {
// first chdir to another path
dir := "/"
if runtime.GOOS == "windows" {
dir = filepath.VolumeName(".")
}
os.Chdir(dir)
if ep, err := Executable(); err != nil {
fmt.Fprint(os.Stderr, "ERROR: ", err)
} else {
fmt.Fprint(os.Stderr, ep)
}
os.Exit(0)
}
}

34
Godeps/_workspace/src/bitbucket.org/kardianos/osext/osext_windows.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,34 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package osext
import (
"syscall"
"unicode/utf16"
"unsafe"
)
var (
kernel = syscall.MustLoadDLL("kernel32.dll")
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
)
// GetModuleFileName() with hModule = NULL
func executable() (exePath string, err error) {
return getModuleFileName()
}
func getModuleFileName() (string, error) {
var n uint32
b := make([]uint16, syscall.MAX_PATH)
size := uint32(len(b))
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
n = uint32(r0)
if n == 0 {
return "", e1
}
return string(utf16.Decode(b[0:n])), nil
}

137
Godeps/_workspace/src/github.com/inconshreveable/go-update/README сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,137 @@
PACKAGE DOCUMENTATION
package update
import "github.com/inconshreveable/go-update"
Package update allows a program to "self-update", replacing its
executable file with new bytes.
Package update provides the facility to create user experiences like
auto-updating or user-approved updates which manifest as user prompts in
commercial applications with copy similar to "Restart to being using the
new version of X".
Updating your program to a new version is as easy as:
err := update.FromUrl("http://release.example.com/2.0/myprogram")
if err != nil {
fmt.Printf("Update failed: %v", err)
}
The most low-level API is FromStream() which updates the current
executable with the bytes read from an io.Reader.
Additional APIs are provided for common update strategies which include
updating from a file with FromFile() and updating from the internet with
FromUrl().
Using the more advaced Download.UpdateFromUrl() API gives you the
ability to resume an interrupted download to enable large updates to
complete even over intermittent or slow connections. This API also
enables more fine-grained control over how the update is downloaded from
the internet as well as access to download progress,
VARIABLES
var (
// Returned when the remote server indicates that no download is available
UpdateUnavailable error
)
FUNCTIONS
func FromFile(filepath string) (err error)
FromFile reads the contents of the given file and uses them to update
the current program's executable file by calling FromStream().
func FromStream(newBinary io.Reader) (err error)
FromStream reads the contents of the supplied io.Reader newBinary and
uses them to update the current program's executable file.
FromStream performs the following actions to ensure a cross-platform
safe update:
- Renames the current program's executable file from
/path/to/program-name to /path/to/.program-name.old
- Opens the now-empty path /path/to/program-name with mode 0755 and
copies the contents of newBinary into the file.
- If the copy is successful, it erases /path/to/.program.old. If this
operation fails, no error is reported.
- If the copy is unsuccessful, it attempts to rename
/path/to/.program-name.old back to /path/to/program-name. If this
operation fails, the error is not reported in order to not mask the
error that caused the rename recovery attempt.
func FromUrl(url string) error
FromUrl downloads the contents of the given url and uses them to update
the current program's executable file. It is a convenience function
which is equivalent to
NewDownload().UpdateFromUrl(url)
See Download.UpdateFromUrl for more details.
TYPES
type Download struct {
// net/http.Client to use when downloading the update.
// If nil, a default http.Client is used
HttpClient *http.Client
// Path on the file system to dowload the update to
// If empty, a temporary file is used.
// After the download begins, this path will be set
// so that the client can use it to resume aborted
// downloads
Path string
// Progress returns the percentage of the download
// completed as an integer between 0 and 100
Progress chan (int)
// HTTP Method to use in the download request. Default is "GET"
Method string
}
Type Download encapsulates the necessary parameters and state needed to
download an update from the internet. Create an instance with the
NewDownload() factory function.
func NewDownload() *Download
NewDownload initializes a new Download object
func (d *Download) UpdateFromUrl(url string) (err error)
UpdateFromUrl downloads the given url from the internet to a file on
disk and then calls FromStream() to update the current program's
executable file with the contents of that file.
If the update is successful, the downloaded file will be erased from
disk. Otherwise, it will remain in d.Path to allow the download to
resume later or be skipped entirely.
Only HTTP/1.1 servers that implement the Range header are supported.
UpdateFromUrl() uses HTTP status codes to determine what action to take.
- The HTTP server should return 200 or 206 for the update to be
downloaded.
- The HTTP server should return 204 if no update is available at this
time. This will cause UpdateFromUrl to return the error
UpdateUnavailable.
- If the HTTP server returns a 3XX redirect, it will be followed
according to d.HttpClient's redirect policy.
- Any other HTTP status code will cause UpdateFromUrl to return an
error.

455
Godeps/_workspace/src/github.com/inconshreveable/go-update/update.go сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,455 @@
/*
Package update allows a program to "self-update", replacing its executable file
with new bytes.
Package update provides the facility to create user experiences like auto-updating
or user-approved updates which manifest as user prompts in commercial applications
with copy similar to "Restart to being using the new version of X".
Updating your program to a new version is as easy as:
err := update.FromUrl("http://release.example.com/2.0/myprogram")
if err != nil {
fmt.Printf("Update failed: %v", err)
}
The most low-level API is FromStream() which updates the current executable
with the bytes read from an io.Reader.
Additional APIs are provided for common update strategies which include
updating from a file with FromFile() and updating from the internet with
FromUrl().
Using the more advaced Download.UpdateFromUrl() API gives you the ability
to resume an interrupted download to enable large updates to complete even
over intermittent or slow connections. This API also enables more fine-grained
control over how the update is downloaded from the internet as well as access to
download progress,
*/
package update
import (
"compress/gzip"
"fmt"
"bitbucket.org/kardianos/osext"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"runtime"
)
type MeteredReader struct {
rd io.ReadCloser
totalSize int64
progress chan int
totalRead int64
ticks int64
}
func (m *MeteredReader) Close() error {
return m.rd.Close()
}
func (m *MeteredReader) Read(b []byte) (n int, err error) {
chunkSize := (m.totalSize / 100) + 1
lenB := int64(len(b))
var nChunk int
for start := int64(0); start < lenB; start += int64(nChunk) {
end := start + chunkSize
if end > lenB {
end = lenB
}
nChunk, err = m.rd.Read(b[start:end])
n += nChunk
m.totalRead += int64(nChunk)
if m.totalRead > (m.ticks * chunkSize) {
m.ticks += 1
// try to send on channel, but don't block if it's full
select {
case m.progress <- int(m.ticks + 1):
default:
}
// give the progress channel consumer a chance to run
runtime.Gosched()
}
if err != nil {
return
}
}
return
}
// We wrap the round tripper when making requests
// because we need to add headers to the requests we make
// even when they are requests made after a redirect
type RoundTripper struct {
RoundTripFn func(*http.Request) (*http.Response, error)
}
func (rt *RoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
return rt.RoundTripFn(r)
}
// Type Download encapsulates the necessary parameters and state
// needed to download an update from the internet. Create an instance
// with the NewDownload() factory function.
//
// You may only use a Download once,
type Download struct {
// net/http.Client to use when downloading the update.
// If nil, a default http.Client is used
HttpClient *http.Client
// Path on the file system to dowload the update to
// If empty, a temporary file is used.
// After the download begins, this path will be set
// so that the client can use it to resume aborted
// downloads
Path string
// Progress returns the percentage of the download
// completed as an integer between 0 and 100
Progress chan (int)
// HTTP Method to use in the download request. Default is "GET"
Method string
// HTTP URL to issue the download request to
Url string
// Set to true when the server confirms a new version is available
// even if the updating process encounters an error later on
Available bool
}
// NewDownload initializes a new Download object
func NewDownload(url string) *Download {
return &Download{
HttpClient: new(http.Client),
Progress: make(chan int),
Method: "GET",
Url: url,
}
}
func (d *Download) sharedHttp(offset int64) (resp *http.Response, err error) {
// create the download request
req, err := http.NewRequest(d.Method, d.Url, nil)
if err != nil {
return
}
// we have to add headers like this so they get used across redirects
trans := d.HttpClient.Transport
if trans == nil {
trans = http.DefaultTransport
}
d.HttpClient.Transport = &RoundTripper{
RoundTripFn: func(r *http.Request) (*http.Response, error) {
// add header for download continuation
if offset > 0 {
r.Header.Add("Range", fmt.Sprintf("%d-", offset))
}
// ask for gzipped content so that net/http won't unzip it for us
// and destroy the content length header we need for progress calculations
r.Header.Add("Accept-Encoding", "gzip")
return trans.RoundTrip(r)
},
}
// issue the download request
return d.HttpClient.Do(req)
}
func (d *Download) Check() (available bool, err error) {
resp, err := d.sharedHttp(0)
if err != nil {
return
}
resp.Body.Close()
switch resp.StatusCode {
// ok
case 200, 206:
available = true
// no update available
case 204:
available = false
// server error
default:
err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status)
return
}
return
}
// Get() downloads the given url from the internet to a file on disk
// and then calls FromStream() to update the current program's executable file
// with the contents of that file.
//
// If the update is successful, the downloaded file will be erased from disk.
// Otherwise, it will remain in d.Path to allow the download to resume later
// or be skipped entirely.
//
// Only HTTP/1.1 servers that implement the Range header support resuming a
// partially completed download.
//
// UpdateFromUrl() uses HTTP status codes to determine what action to take.
//
// - The HTTP server should return 200 or 206 for the update to be downloaded.
//
// - The HTTP server should return 204 if no update is available at this time.
//
// - If the HTTP server returns a 3XX redirect, it will be followed
// according to d.HttpClient's redirect policy.
//
// - Any other HTTP status code will cause UpdateFromUrl to return an error.
func (d *Download) Get() (err error) {
var offset int64 = 0
var fp *os.File
// Close the progress channel whenever this function completes
defer close(d.Progress)
// open a file where we will stream the downloaded update to
// we do this first because if the caller specified a non-empty dlpath
// we need to determine how large it is in order to resume the download
if d.Path == "" {
// no dlpath specified, use a random tempfile
fp, err = ioutil.TempFile("", "update")
if err != nil {
return
}
defer fp.Close()
// remember the path
d.Path = fp.Name()
} else {
fp, err = os.OpenFile(d.Path, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0600)
if err != nil {
return
}
defer fp.Close()
// determine the file size so we can resume the download, if possible
var fi os.FileInfo
fi, err = fp.Stat()
if err != nil {
return
}
offset = fi.Size()
}
// start downloading the file
resp, err := d.sharedHttp(offset)
if err != nil {
return
}
defer resp.Body.Close()
switch resp.StatusCode {
// ok
case 200, 206:
d.Available = true
// no update available
case 204:
return
// server error
default:
err = fmt.Errorf("Non 2XX response when downloading update: %s", resp.Status)
return
}
// Determine how much we have to download
// net/http sets this to -1 when it is unknown
clength := resp.ContentLength
// Read the content from the response body
rd := resp.Body
// meter the rate at which we download content for
// progress reporting if we know how much to expect
if clength > 0 {
rd = &MeteredReader{rd: rd, totalSize: clength, progress: d.Progress}
}
// Decompress the content if necessary
if resp.Header.Get("Content-Encoding") == "gzip" {
rd, err = gzip.NewReader(rd)
if err != nil {
return
}
}
// Download the update
_, err = io.Copy(fp, rd)
if err != nil {
return
}
return
}
func (d *Download) GetAndUpdate() (err error, errRecover error) {
// check before we download if this will work
if err = SanityCheck(); err != nil {
// keep the contract that d.Progress will close whenever Get() terminates
close(d.Progress)
return
}
// download the update
if err = d.Get(); err != nil || !d.Available {
return
}
// apply the update
if err, errRecover = FromFile(d.Path); err != nil || errRecover != nil {
return
}
// remove the temporary file
os.Remove(d.Path)
return
}
// FromUrl downloads the contents of the given url and uses them to update
// the current program's executable file. It is a convenience function which is equivalent to
//
// NewDownload(url).GetAndUpdate()
//
// See Download.Get() for more details.
func FromUrl(url string) (err error, errRecover error) {
return NewDownload(url).GetAndUpdate()
}
// FromFile reads the contents of the given file and uses them
// to update the current program's executable file by calling FromStream().
func FromFile(filepath string) (err error, errRecover error) {
// open the new binary
fp, err := os.Open(filepath)
if err != nil {
return
}
defer fp.Close()
// do the update
return FromStream(fp)
}
// FromStream reads the contents of the supplied io.Reader newBinary
// and uses them to update the current program's executable file.
//
// FromStream performs the following actions to ensure a cross-platform safe
// update:
//
// - Creates a new file, /path/to/.program-name.new with mode 0755 and copies
// the contents of newBinary into the file
//
// - Renames the current program's executable file from /path/to/program-name
// to /path/to/.program-name.old
//
// - Renames /path/to/.program-name.new to /path/to/program-name
//
// - If the rename is successful, it erases /path/to/.program.old. If this operation
// fails, no error is reported.
//
// - If the rename is unsuccessful, it attempts to rename /path/to/.program-name.old
// back to /path/to/program-name. If this operation fails, the error is not reported
// in order to not mask the error that caused the rename recovery attempt.
func FromStream(newBinary io.Reader) (err error, errRecover error) {
// get the path to the executable
thisExecPath, err := osext.Executable()
if err != nil {
return
}
// get the directory the executable exists in
execDir := filepath.Dir(thisExecPath)
execName := filepath.Base(thisExecPath)
// Copy the contents of of newbinary to a the new executable file
newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName))
fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
if err != nil {
return
}
defer fp.Close()
_, err = io.Copy(fp, newBinary)
// if we don't call fp.Close(), windows won't let us move the new executable
// because the file will still be "in use"
fp.Close()
// this is where we'll move the executable to so that we can swap in the updated replacement
oldExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.old", execName))
// delete any existing old exec file - this is necessary on Windows for two reasons:
// 1. after a successful update, windows can't remove the .old file because the process is still running
// 2. windows rename operations fail if the destination file already exists
_ = os.Remove(oldExecPath)
// move the existing executable to a new file in the same directory
err = os.Rename(thisExecPath, oldExecPath)
if err != nil {
return
}
// move the new exectuable in to become the new program
err = os.Rename(newExecPath, thisExecPath)
if err != nil {
// copy unsuccessful
errRecover = os.Rename(oldExecPath, thisExecPath)
} else {
// copy successful, remove the old binary
_ = os.Remove(oldExecPath)
}
return
}
// SanityCheck() attempts to determine whether an in-place executable update could
// succeed by performing preliminary checks (to establish valid permissions, etc).
// This helps avoid downloading updates when we know the update can't be successfully
// applied later.
func SanityCheck() (err error) {
// get the path to the executable
thisExecPath, err := osext.Executable()
if err != nil {
return
}
// get the directory the executable exists in
execDir := filepath.Dir(thisExecPath)
execName := filepath.Base(thisExecPath)
// attempt to open a file in the executable's directory
newExecPath := filepath.Join(execDir, fmt.Sprintf(".%s.new", execName))
fp, err := os.OpenFile(newExecPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
if err != nil {
return
}
fp.Close()
_ = os.Remove(newExecPath)
return
}

68
Godeps/_workspace/src/github.com/jingweno/go-octokit/octokit/client.go сгенерированный поставляемый
Просмотреть файл

@ -44,65 +44,49 @@ func (c *Client) NewRequest(urlStr string) (req *Request, err error) {
} }
func (c *Client) head(url *url.URL, output interface{}) (result *Result) { func (c *Client) head(url *url.URL, output interface{}) (result *Result) {
req, err := c.NewRequest(url.String()) return sendRequest(c, url, func(req *Request) (*Response, error) {
if err != nil { return req.Head(output)
result = newResult(nil, err) })
return
}
resp, err := req.Head(output)
result = newResult(resp, err)
return
} }
func (c *Client) get(url *url.URL, output interface{}) (result *Result) { func (c *Client) get(url *url.URL, output interface{}) (result *Result) {
req, err := c.NewRequest(url.String()) return sendRequest(c, url, func(req *Request) (*Response, error) {
if err != nil { return req.Get(output)
result = newResult(nil, err) })
return
}
resp, err := req.Get(output)
result = newResult(resp, err)
return
} }
func (c *Client) post(url *url.URL, input interface{}, output interface{}) (result *Result) { func (c *Client) post(url *url.URL, input interface{}, output interface{}) (result *Result) {
req, err := c.NewRequest(url.String()) return sendRequest(c, url, func(req *Request) (*Response, error) {
if err != nil { return req.Post(input, output)
result = newResult(nil, err) })
return
}
resp, err := req.Post(input, output)
result = newResult(resp, err)
return
} }
func (c *Client) put(url *url.URL, input interface{}, output interface{}) (result *Result) { func (c *Client) put(url *url.URL, input interface{}, output interface{}) *Result {
req, err := c.NewRequest(url.String()) return sendRequest(c, url, func(req *Request) (*Response, error) {
if err != nil { return req.Put(input, output)
result = newResult(nil, err) })
return
}
resp, err := req.Put(input, output)
result = newResult(resp, err)
return
} }
func (c *Client) delete(url *url.URL, output interface{}) (result *Result) { func (c *Client) delete(url *url.URL, output interface{}) (result *Result) {
return sendRequest(c, url, func(req *Request) (*Response, error) {
return req.Delete(output)
})
}
func (c *Client) patch(url *url.URL, input interface{}, output interface{}) (result *Result) {
return sendRequest(c, url, func(req *Request) (*Response, error) {
return req.Patch(input, output)
})
}
func sendRequest(c *Client, url *url.URL, fn func(r *Request) (*Response, error)) (result *Result) {
req, err := c.NewRequest(url.String()) req, err := c.NewRequest(url.String())
if err != nil { if err != nil {
result = newResult(nil, err) result = newResult(nil, err)
return return
} }
resp, err := req.Delete(output) resp, err := fn(req)
result = newResult(resp, err) result = newResult(resp, err)
return return

19
Godeps/_workspace/src/github.com/jingweno/go-octokit/octokit/issues.go сгенерированный поставляемый
Просмотреть файл

@ -31,6 +31,16 @@ func (i *IssuesService) All() (issues []Issue, result *Result) {
return return
} }
func (i *IssuesService) Create(params interface{}) (issue *Issue, result *Result) {
result = i.client.post(i.URL, params, &issue)
return
}
func (i *IssuesService) Update(params interface{}) (issue *Issue, result *Result) {
result = i.client.patch(i.URL, params, &issue)
return
}
type Issue struct { type Issue struct {
*hypermedia.HALResource *hypermedia.HALResource
@ -69,3 +79,12 @@ type Issue struct {
ClosedAt *time.Time `json:"closed_at,omitempty"` ClosedAt *time.Time `json:"closed_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"`
} }
type IssueParams struct {
Title string `json:"title,omitempty"`
Body string `json:"body,omitempty"`
Assignee string `json:"assignee,omitempty"`
State string `json:"state,omitempty"`
Milestone uint64 `json:"milestone,omitempty"`
Labels []string `json:"labels,omitempty"`
}

46
Godeps/_workspace/src/github.com/jingweno/go-octokit/octokit/issues_test.go сгенерированный поставляемый
Просмотреть файл

@ -45,6 +45,52 @@ func TestIssuesService_One(t *testing.T) {
validateIssue(t, *issue) validateIssue(t, *issue)
} }
func TestIssuesService_Create(t *testing.T) {
setup()
defer tearDown()
mux.HandleFunc("/repos/octocat/Hello-World/issues", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testBody(t, r, "{\"title\":\"title\",\"body\":\"body\"}\n")
respondWithJSON(w, loadFixture("issue.json"))
})
url, err := RepoIssuesURL.Expand(M{"owner": "octocat", "repo": "Hello-World"})
assert.Equal(t, nil, err)
params := IssueParams{
Title: "title",
Body: "body",
}
issue, result := client.Issues(url).Create(params)
assert.T(t, !result.HasError())
validateIssue(t, *issue)
}
func TestIssuesService_Update(t *testing.T) {
setup()
defer tearDown()
mux.HandleFunc("/repos/octocat/Hello-World/issues/1347", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PATCH")
testBody(t, r, "{\"title\":\"title\",\"body\":\"body\"}\n")
respondWithJSON(w, loadFixture("issue.json"))
})
url, err := RepoIssuesURL.Expand(M{"owner": "octocat", "repo": "Hello-World", "number": 1347})
assert.Equal(t, nil, err)
params := IssueParams{
Title: "title",
Body: "body",
}
issue, result := client.Issues(url).Update(params)
assert.T(t, !result.HasError())
validateIssue(t, *issue)
}
func validateIssue(t *testing.T, issue Issue) { func validateIssue(t *testing.T, issue Issue) {
assert.Equal(t, "https://api.github.com/repos/octocat/Hello-World/issues/1347", issue.URL) assert.Equal(t, "https://api.github.com/repos/octocat/Hello-World/issues/1347", issue.URL)

5
Godeps/_workspace/src/github.com/jingweno/go-octokit/octokit/releases.go сгенерированный поставляемый
Просмотреть файл

@ -63,6 +63,11 @@ func (r *ReleasesService) Create(params interface{}) (release *Release, result *
return return
} }
func (r *ReleasesService) Update(params interface{}) (release *Release, result *Result) {
result = r.client.patch(r.URL, params, &release)
return
}
type ReleaseParams struct { type ReleaseParams struct {
TagName string `json:"tag_name,omitempty"` TagName string `json:"tag_name,omitempty"`
TargetCommitish string `json:"target_commitish,omitempty"` TargetCommitish string `json:"target_commitish,omitempty"`

23
Godeps/_workspace/src/github.com/jingweno/go-octokit/octokit/releases_test.go сгенерированный поставляемый
Просмотреть файл

@ -75,3 +75,26 @@ func TestCreateRelease(t *testing.T) {
assert.T(t, !result.HasError()) assert.T(t, !result.HasError())
assert.Equal(t, "v1.0.0", release.TagName) assert.Equal(t, "v1.0.0", release.TagName)
} }
func TestUpdateRelease(t *testing.T) {
setup()
defer tearDown()
mux.HandleFunc("/repos/octokit/Hello-World/releases/123", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PATCH")
testBody(t, r, "{\"tag_name\":\"v1.0.0\",\"target_commitish\":\"master\"}\n")
respondWithJSON(w, loadFixture("create_release.json"))
})
url, err := ReleasesURL.Expand(M{"owner": "octokit", "repo": "Hello-World", "id": "123"})
assert.Equal(t, nil, err)
params := Release{
TagName: "v1.0.0",
TargetCommitish: "master",
}
release, result := client.Releases(url).Update(params)
assert.T(t, !result.HasError())
assert.Equal(t, "v1.0.0", release.TagName)
}

17
Godeps/_workspace/src/github.com/jingweno/go-octokit/octokit/request.go сгенерированный поставляемый
Просмотреть файл

@ -10,27 +10,32 @@ type Request struct {
} }
func (r *Request) Head(output interface{}) (resp *Response, err error) { func (r *Request) Head(output interface{}) (resp *Response, err error) {
resp, err = r.do("HEAD", nil, output) resp, err = r.do(sawyer.HeadMethod, nil, output)
return return
} }
func (r *Request) Get(output interface{}) (resp *Response, err error) { func (r *Request) Get(output interface{}) (resp *Response, err error) {
resp, err = r.do("GET", nil, output) resp, err = r.do(sawyer.GetMethod, nil, output)
return return
} }
func (r *Request) Post(input interface{}, output interface{}) (resp *Response, err error) { func (r *Request) Post(input interface{}, output interface{}) (resp *Response, err error) {
resp, err = r.do("POST", input, output) resp, err = r.do(sawyer.PostMethod, input, output)
return return
} }
func (r *Request) Put(input interface{}, output interface{}) (resp *Response, err error) { func (r *Request) Put(input interface{}, output interface{}) (resp *Response, err error) {
resp, err = r.do("PUT", input, output) resp, err = r.do(sawyer.PutMethod, input, output)
return return
} }
func (r *Request) Delete(output interface{}) (resp *Response, err error) { func (r *Request) Delete(output interface{}) (resp *Response, err error) {
resp, err = r.do("DELETE", nil, output) resp, err = r.do(sawyer.DeleteMethod, nil, output)
return
}
func (r *Request) Patch(input interface{}, output interface{}) (resp *Response, err error) {
resp, err = r.do(sawyer.PatchMethod, input, output)
return return
} }
@ -50,6 +55,8 @@ func (r *Request) do(method string, input interface{}, output interface{}) (resp
r.sawyerReq.SetBody(mtype, input) r.sawyerReq.SetBody(mtype, input)
sawyerResp = r.sawyerReq.Put() sawyerResp = r.sawyerReq.Put()
case sawyer.PatchMethod: case sawyer.PatchMethod:
mtype, _ := mediatype.Parse(defaultMediaType)
r.sawyerReq.SetBody(mtype, input)
sawyerResp = r.sawyerReq.Patch() sawyerResp = r.sawyerReq.Patch()
case sawyer.DeleteMethod: case sawyer.DeleteMethod:
sawyerResp = r.sawyerReq.Delete() sawyerResp = r.sawyerReq.Delete()

2
Godeps/_workspace/src/github.com/jingweno/go-octokit/octokit/users.go сгенерированный поставляемый
Просмотреть файл

@ -44,6 +44,7 @@ func (u *UsersService) All() (users []User, result *Result) {
type User struct { type User struct {
*hypermedia.HALResource *hypermedia.HALResource
SiteAdmin bool `json:"site_admin,omitempty"`
Login string `json:"login,omitempty"` Login string `json:"login,omitempty"`
ID int `json:"id,omitempty"` ID int `json:"id,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"` AvatarURL string `json:"avatar_url,omitempty"`
@ -57,7 +58,6 @@ type User struct {
Hireable bool `json:"hireable,omitempty"` Hireable bool `json:"hireable,omitempty"`
Bio string `json:"bio,omitempty"` Bio string `json:"bio,omitempty"`
PublicRepos int `json:"public_repos,omitempty"` PublicRepos int `json:"public_repos,omitempty"`
PublicGists int `json:"public_gists,omitempty"`
Followers int `json:"followers,omitempty"` Followers int `json:"followers,omitempty"`
Following int `json:"following,omitempty"` Following int `json:"following,omitempty"`
HTMLURL string `json:"html_url,omitempty"` HTMLURL string `json:"html_url,omitempty"`

8
Godeps/_workspace/src/github.com/jingweno/go-octokit/octokit/users_test.go сгенерированный поставляемый
Просмотреть файл

@ -25,10 +25,10 @@ func TestUsersService_GetCurrentUser(t *testing.T) {
assert.Equal(t, "jingweno", user.Login) assert.Equal(t, "jingweno", user.Login)
assert.Equal(t, "jingweno@gmail.com", user.Email) assert.Equal(t, "jingweno@gmail.com", user.Email)
assert.Equal(t, "User", user.Type) assert.Equal(t, "User", user.Type)
assert.Equal(t, 3, user.Following) assert.Equal(t, 17, user.Following)
assert.Equal(t, 19, user.Followers) assert.Equal(t, 28, user.Followers)
assert.Equal(t, 80, user.PublicRepos) assert.Equal(t, 90, user.PublicRepos)
assert.Equal(t, 25, user.PublicGists) assert.Equal(t, false, user.SiteAdmin)
assert.Equal(t, "https://api.github.com/users/jingweno/repos", string(user.ReposURL)) assert.Equal(t, "https://api.github.com/users/jingweno/repos", string(user.ReposURL))
} }

Просмотреть файл

@ -90,6 +90,7 @@ func All() []*Command {
all = append(all, cmdAlias) all = append(all, cmdAlias)
all = append(all, cmdVersion) all = append(all, cmdVersion)
all = append(all, cmdHelp) all = append(all, cmdHelp)
all = append(all, cmdUpdate)
return all return all
} }

Просмотреть файл

@ -86,6 +86,7 @@ GitHub Commands:
issue Manipulate issues (beta) issue Manipulate issues (beta)
See 'git help <command>' for more information on a specific command. See 'git help <command>' for more information on a specific command.
Run 'git update' to update to the latest version of gh.
` `
func printUsage() { func printUsage() {

154
commands/update.go Normal file
Просмотреть файл

@ -0,0 +1,154 @@
package commands
import (
"archive/zip"
"fmt"
updater "github.com/inconshreveable/go-update"
"github.com/jingweno/gh/github"
"github.com/jingweno/gh/utils"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
)
var cmdUpdate = &Command{
Run: update,
Usage: "update",
Short: "Update gh",
Long: `Update gh to the latest version.
Examples:
git update
`,
}
func update(cmd *Command, args *Args) {
err := doUpdate()
utils.Check(err)
os.Exit(0)
}
func doUpdate() (err error) {
client := github.NewClient(github.GitHubHost)
releases, err := client.Releases(github.NewProject("jingweno", "gh", github.GitHubHost))
if err != nil {
err = fmt.Errorf("Error fetching releases: %s", err)
return
}
latestRelease := releases[0]
tagName := latestRelease.TagName
version := strings.TrimPrefix(tagName, "v")
if version == Version {
err = fmt.Errorf("You're already on the latest version: %s", Version)
return
}
fmt.Printf("Updating gh to release %s...\n", version)
downloadURL := fmt.Sprintf("https://github.com/jingweno/gh/releases/download/%s/gh_%s-snapshot_%s_%s.zip", tagName, version, runtime.GOOS, runtime.GOARCH)
path, err := downloadFile(downloadURL)
if err != nil {
err = fmt.Errorf("Can't download update from %s to %s", downloadURL, path)
return
}
exec, err := unzipExecutable(path)
if err != nil {
err = fmt.Errorf("Can't unzip gh executable: %s", err)
return
}
err, _ = updater.FromFile(exec)
if err == nil {
fmt.Println("Done!")
}
return
}
func unzipExecutable(path string) (exec string, err error) {
rc, err := zip.OpenReader(path)
if err != nil {
err = fmt.Errorf("Can't open zip file %s: %s", path, err)
return
}
defer rc.Close()
for _, file := range rc.File {
if !strings.HasPrefix(file.Name, "gh") {
continue
}
dir := filepath.Dir(path)
exec, err = unzipFile(file, dir)
break
}
if exec == "" && err == nil {
err = fmt.Errorf("No gh executable is found in %s", path)
}
return
}
func unzipFile(file *zip.File, to string) (exec string, err error) {
frc, err := file.Open()
if err != nil {
err = fmt.Errorf("Can't open zip entry %s when reading: %s", file.Name, err)
return
}
defer frc.Close()
dest := filepath.Join(to, filepath.Base(file.Name))
f, err := os.Create(dest)
if err != nil {
return
}
defer f.Close()
copied, err := io.Copy(f, frc)
if err != nil {
return
}
if uint32(copied) != file.UncompressedSize {
err = fmt.Errorf("Zip entry %s is corrupted", file.Name)
return
}
exec = f.Name()
return
}
func downloadFile(url string) (path string, err error) {
dir, err := ioutil.TempDir("", "gh-update")
if err != nil {
return
}
file, err := os.Create(filepath.Join(dir, filepath.Base(url)))
if err != nil {
return
}
defer file.Close()
resp, err := http.Get(url)
if err != nil {
return
}
defer resp.Body.Close()
_, err = io.Copy(file, resp.Body)
if err != nil {
return
}
path = file.Name()
return
}

12
linux.goxc.json Normal file
Просмотреть файл

@ -0,0 +1,12 @@
{
"ConfigVersion": "0.9",
"BuildConstraints": "linux",
"TaskSettings": {
"archive-tar-gz": {
"platforms": "!linux"
},
"archive-zip": {
"platforms": "linux"
}
}
}