From 0d6a6222e5059bac453aaf8ac9d31e4393c5c09f Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 27 Aug 2013 03:46:44 +0800 Subject: [PATCH] Huge imporve download repos on google code --- doc/bitbucket.go | 8 +- doc/error.go | 22 ------ doc/github.go | 4 +- doc/google.go | 187 ++++------------------------------------------- doc/http.go | 102 +------------------------- doc/launchpad.go | 10 ++- doc/oschina.go | 4 +- doc/vcs.go | 135 +++++++++++++++++++--------------- gopm.go | 2 +- 9 files changed, 113 insertions(+), 361 deletions(-) diff --git a/doc/bitbucket.go b/doc/bitbucket.go index 752613c93..5b0e537a0 100644 --- a/doc/bitbucket.go +++ b/doc/bitbucket.go @@ -25,6 +25,8 @@ import ( "path" "regexp" "strings" + + "github.com/Unknwon/com" ) var ( @@ -41,7 +43,7 @@ func getBitbucketDoc(client *http.Client, match map[string]string, installRepoPa var repo struct { Scm string } - if err := httpGetJSON(client, expand("https://api.bitbucket.org/1.0/repositories/{owner}/{repo}", match), &repo); err != nil { + if err := com.HttpGetJSON(client, expand("https://api.bitbucket.org/1.0/repositories/{owner}/{repo}", match), &repo); err != nil { return nil, err } match["vcs"] = repo.Scm @@ -62,7 +64,7 @@ func getBitbucketDoc(client *http.Client, match map[string]string, installRepoPa var nodes map[string]struct { Node string } - if err := httpGetJSON(client, expand("https://api.bitbucket.org/1.0/repositories/{owner}/{repo}/{0}", match, nodeType), &nodes); err != nil { + if err := com.HttpGetJSON(client, expand("https://api.bitbucket.org/1.0/repositories/{owner}/{repo}/{0}", match, nodeType), &nodes); err != nil { return nil, err } for t, n := range nodes { @@ -94,7 +96,7 @@ func getBitbucketDoc(client *http.Client, match map[string]string, installRepoPa // tarball : https://bitbucket.org/{owner}/{repo}/get/{commit}.tar.gz // Downlaod archive. - p, err := HttpGetBytes(client, expand("https://bitbucket.org/{owner}/{repo}/get/{commit}.tar.gz", match), nil) + p, err := com.HttpGetBytes(client, expand("https://bitbucket.org/{owner}/{repo}/get/{commit}.tar.gz", match), nil) if err != nil { return nil, err } diff --git a/doc/error.go b/doc/error.go index 3c6ea8406..56890c961 100644 --- a/doc/error.go +++ b/doc/error.go @@ -23,25 +23,3 @@ var ( errNoMatch = errors.New("no match") errUpdateTimeout = errors.New("update timeout") ) - -type NotFoundError struct { - Message string -} - -func (e NotFoundError) Error() string { - return e.Message -} - -func isNotFound(err error) bool { - _, ok := err.(NotFoundError) - return ok -} - -type RemoteError struct { - Host string - err error -} - -func (e *RemoteError) Error() string { - return e.err.Error() -} diff --git a/doc/github.go b/doc/github.go index fe968335f..7ab20ea8a 100644 --- a/doc/github.go +++ b/doc/github.go @@ -24,6 +24,8 @@ import ( "path" "regexp" "strings" + + "github.com/Unknwon/com" ) var ( @@ -65,7 +67,7 @@ func getGithubDoc(client *http.Client, match map[string]string, installRepoPath // tarball: https://github.com/{owner}/{repo}/tarball/{sha} // Downlaod archive. - p, err := HttpGetBytes(client, expand("https://github.com/{owner}/{repo}/archive/{sha}.zip", match), nil) + p, err := com.HttpGetBytes(client, expand("https://github.com/{owner}/{repo}/archive/{sha}.zip", match), nil) if err != nil { return nil, errors.New("Fail to donwload Github repo -> " + err.Error()) } diff --git a/doc/google.go b/doc/google.go index 97877aa41..e9e3000f7 100644 --- a/doc/google.go +++ b/doc/google.go @@ -19,58 +19,20 @@ import ( "os" "path" "regexp" - "strings" + + "github.com/Unknwon/com" + "github.com/Unknwon/ctw/packer" ) var ( - googleRepoRe = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`) - googleRevisionRe = regexp.MustCompile(`

(?:[^ ]+ - )?Revision *([^:]+):`) - googleEtagRe = regexp.MustCompile(`^(hg|git|svn)-`) - googleFileRe = regexp.MustCompile(`
  • [a-z0-9\-]+)(:?\.(?P[a-z0-9\-]+))?(?P/[a-z0-9A-Z_.\-/]+)?$`) + googlePattern = regexp.MustCompile(`^code\.google\.com/p/(?P[a-z0-9\-]+)(:?\.(?P[a-z0-9\-]+))?(?P/[a-z0-9A-Z_.\-/]+)?$`) ) -func setupGoogleMatch(match map[string]string) { - if s := match["subrepo"]; s != "" { - match["dot"] = "." - match["query"] = "?repo=" + s - } else { - match["dot"] = "" - match["query"] = "" - } -} - -func getGoogleVCS(client *http.Client, match map[string]string) error { - // Scrape the HTML project page to find the VCS. - p, err := HttpGetBytes(client, expand("http://code.google.com/p/{repo}/source/checkout", match), nil) - if err != nil { - return err - } - m := googleRepoRe.FindSubmatch(p) - if m == nil { - return NotFoundError{"Could not VCS on Google Code project page."} - } - - match["vcs"] = string(m[1]) - return nil -} - // getGoogleDoc downloads raw files from code.google.com. func getGoogleDoc(client *http.Client, match map[string]string, installRepoPath string, nod *Node, cmdFlags map[string]bool) ([]string, error) { - setupGoogleMatch(match) + packer.SetupGoogleMatch(match) // Check version control. - if m := googleEtagRe.FindStringSubmatch(nod.Value); m != nil { - match["vcs"] = m[1] - } else if err := getGoogleVCS(client, match); err != nil { - return nil, err - } - - rootPath := expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/", match) - - // Scrape the repo browser to find the project revision and individual Go files. - p, err := HttpGetBytes(client, rootPath+"?r="+nod.Value, nil) - if err != nil { + if err := packer.GetGoogleVCS(client, match); err != nil { return nil, err } @@ -89,143 +51,24 @@ func getGoogleDoc(client *http.Client, match map[string]string, installRepoPath // Remove old files. os.RemoveAll(installPath + "/") - os.MkdirAll(installPath+"/", os.ModePerm) - - // Get source files in root path. - files := make([]*source, 0, 5) - for _, m := range googleFileRe.FindAllSubmatch(p, -1) { - fname := strings.Split(string(m[1]), "?")[0] - if strings.HasPrefix(fname, ".") { - continue - } - - files = append(files, &source{ - name: fname, - rawURL: expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/{0}", match, fname) + "?r=" + nod.Value, - }) - } - - // Fetch files from VCS. - if err := fetchFiles(client, files, nil); err != nil { + match["tag"] = nod.Value + err := packer.PackToFile(match["importPath"], installPath+".zip", match) + if err != nil { return nil, err } - // Save files. - for _, f := range files { - absPath := installPath + "/" - - // Create diretory before create file. - os.MkdirAll(path.Dir(absPath), os.ModePerm) - - // Write data to file - fw, err := os.Create(absPath + f.name) - if err != nil { - return nil, err - } - - _, err = fw.Write(f.data) - fw.Close() - if err != nil { - return nil, err - } - } - - dirs := make([]string, 0, 3) - // Get subdirectories. - for _, m := range googleDirRe.FindAllSubmatch(p, -1) { - dirName := strings.Split(string(m[1]), "?")[0] - if strings.HasSuffix(dirName, "/") { - dirs = append(dirs, dirName) - } - } - - err = downloadFiles(client, match, rootPath, installPath+"/", nod.Value, dirs) + dirs, err := com.Unzip(installPath+".zip", path.Dir(installPath)) if err != nil { return nil, err } - - var imports []string + os.Remove(installPath + ".zip") + os.Rename(path.Dir(installPath)+"/"+dirs[0], installPath) // Check if need to check imports. if nod.IsGetDeps { - dirs, err := GetDirsInfo(installPath + "/") - if err != nil { - return nil, err - } - - for _, d := range dirs { - if d.IsDir() && !(!cmdFlags["-e"] && strings.Contains(d.Name(), "example")) { - absPath := installPath + "/" + d.Name() + "/" - importPkgs, err := CheckImports(absPath, match["importPath"]) - if err != nil { - return nil, err - } - imports = append(imports, importPkgs...) - } - } + imports := getImports(installPath+"/", match, cmdFlags) + return imports, err } - return imports, err -} - -func downloadFiles(client *http.Client, match map[string]string, rootPath, installPath, commit string, dirs []string) error { - for _, d := range dirs { - p, err := HttpGetBytes(client, rootPath+d+"?r="+commit, nil) - if err != nil { - return err - } - - // Create destination directory. - os.MkdirAll(installPath+d, os.ModePerm) - - // Get source files in current path. - files := make([]*source, 0, 5) - for _, m := range googleFileRe.FindAllSubmatch(p, -1) { - fname := strings.Split(string(m[1]), "?")[0] - files = append(files, &source{ - name: fname, - rawURL: expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/", match) + d + fname + "?r=" + commit, - }) - } - - // Fetch files from VCS. - if err := fetchFiles(client, files, nil); err != nil { - return err - } - - // Save files. - for _, f := range files { - absPath := installPath + d - - // Create diretory before create file. - os.MkdirAll(path.Dir(absPath), os.ModePerm) - - // Write data to file - fw, err := os.Create(absPath + f.name) - if err != nil { - return err - } - - _, err = fw.Write(f.data) - fw.Close() - if err != nil { - return err - } - } - - subdirs := make([]string, 0, 3) - // Get subdirectories. - for _, m := range googleDirRe.FindAllSubmatch(p, -1) { - dirName := strings.Split(string(m[1]), "?")[0] - if strings.HasSuffix(dirName, "/") { - subdirs = append(subdirs, d+dirName) - } - } - - err = downloadFiles(client, match, rootPath, installPath, commit, subdirs) - if err != nil { - return err - } - } - return nil + return nil, err } diff --git a/doc/http.go b/doc/http.go index d63e31da9..e5f34e05b 100644 --- a/doc/http.go +++ b/doc/http.go @@ -15,20 +15,14 @@ package doc import ( - "encoding/json" "flag" - "fmt" - "io" - "io/ioutil" "net" "net/http" "time" - "github.com/astaxie/beego" + "github.com/Unknwon/com" ) -var userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1541.0 Safari/537.36" - var ( dialTimeout = flag.Duration("dial_timeout", 10*time.Second, "Timeout for dialing an HTTP connection.") requestTimeout = flag.Duration("request_timeout", 20*time.Second, "Time out for roundtripping an HTTP request.") @@ -45,7 +39,7 @@ type transport struct { func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { timer := time.AfterFunc(*requestTimeout, func() { t.t.CancelRequest(req) - beego.Warn("Canceled request for %s", req.URL) + com.ColorLog("[WARN] Canceled request for %s, please interrupt the program.\n", req.URL) }) defer timer.Stop() resp, err := t.t.RoundTrip(req) @@ -56,95 +50,3 @@ var ( httpTransport = &transport{t: http.Transport{Dial: timeoutDial, ResponseHeaderTimeout: *requestTimeout / 2}} HttpClient = &http.Client{Transport: httpTransport} ) - -// httpGet gets the specified resource. ErrNotFound is returned if the server -// responds with status 404. -func HttpGetBytes(client *http.Client, url string, header http.Header) ([]byte, error) { - rc, err := httpGet(client, url, header) - if err != nil { - return nil, err - } - p, err := ioutil.ReadAll(rc) - rc.Close() - return p, err -} - -// httpGet gets the specified resource. ErrNotFound is returned if the -// server responds with status 404. -func httpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - req.Header.Set("User-Agent", userAgent) - for k, vs := range header { - req.Header[k] = vs - } - resp, err := client.Do(req) - if err != nil { - return nil, &RemoteError{req.URL.Host, err} - } - if resp.StatusCode == 200 { - return resp.Body, nil - } - resp.Body.Close() - if resp.StatusCode == 404 { // 403 can be rate limit error. || resp.StatusCode == 403 { - err = NotFoundError{"Resource not found: " + url} - } else { - err = &RemoteError{req.URL.Host, fmt.Errorf("get %s -> %d", url, resp.StatusCode)} - } - return nil, err -} - -// fetchFiles fetches the source files specified by the rawURL field in parallel. -func fetchFiles(client *http.Client, files []*source, header http.Header) error { - ch := make(chan error, len(files)) - for i := range files { - go func(i int) { - req, err := http.NewRequest("GET", files[i].rawURL, nil) - if err != nil { - ch <- err - return - } - req.Header.Set("User-Agent", userAgent) - for k, vs := range header { - req.Header[k] = vs - } - resp, err := client.Do(req) - if err != nil { - ch <- &RemoteError{req.URL.Host, err} - return - } - defer resp.Body.Close() - if resp.StatusCode != 200 { - ch <- &RemoteError{req.URL.Host, fmt.Errorf("get %s -> %d", req.URL, resp.StatusCode)} - return - } - files[i].data, err = ioutil.ReadAll(resp.Body) - if err != nil { - ch <- &RemoteError{req.URL.Host, err} - return - } - ch <- nil - }(i) - } - for _ = range files { - if err := <-ch; err != nil { - return err - } - } - return nil -} - -func httpGetJSON(client *http.Client, url string, v interface{}) error { - rc, err := httpGet(client, url, nil) - if err != nil { - return err - } - defer rc.Close() - err = json.NewDecoder(rc).Decode(v) - if _, ok := err.(*json.SyntaxError); ok { - err = NotFoundError{"JSON syntax error at " + url} - } - return err -} diff --git a/doc/launchpad.go b/doc/launchpad.go index 17fdcc7bb..b7ac63c8d 100644 --- a/doc/launchpad.go +++ b/doc/launchpad.go @@ -21,9 +21,10 @@ import ( "io" "net/http" "os" - //"path" "regexp" "strings" + + "github.com/Unknwon/com" ) var launchpadPattern = regexp.MustCompile(`^launchpad\.net/(?P(?P[a-z0-9A-Z_.\-]+)(?P/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+)(?P/[a-z0-9A-Z_.\-/]+)*$`) @@ -32,12 +33,13 @@ var launchpadPattern = regexp.MustCompile(`^launchpad\.net/(?P(?P func getLaunchpadDoc(client *http.Client, match map[string]string, installRepoPath string, nod *Node, cmdFlags map[string]bool) ([]string, error) { if match["project"] != "" && match["series"] != "" { - rc, err := httpGet(client, expand("https://code.launchpad.net/{project}{series}/.bzr/branch-format", match), nil) + rc, err := com.HttpGet(client, expand("https://code.launchpad.net/{project}{series}/.bzr/branch-format", match), nil) + _, isNotFound := err.(com.NotFoundError) switch { case err == nil: rc.Close() // The structure of the import path is launchpad.net/{root}/{dir}. - case isNotFound(err): + case isNotFound: // The structure of the import path is is launchpad.net/{project}/{dir}. match["repo"] = match["project"] match["dir"] = expand("{series}{dir}", match) @@ -55,7 +57,7 @@ func getLaunchpadDoc(client *http.Client, match map[string]string, installRepoPa } // Scrape the repo browser to find the project revision and individual Go files. - p, err := HttpGetBytes(client, downloadPath, nil) + p, err := com.HttpGetBytes(client, downloadPath, nil) if err != nil { return nil, err } diff --git a/doc/oschina.go b/doc/oschina.go index 47c075ef3..d6ca10bfe 100644 --- a/doc/oschina.go +++ b/doc/oschina.go @@ -23,6 +23,8 @@ import ( "os" "regexp" "strings" + + "github.com/Unknwon/com" ) var ( @@ -49,7 +51,7 @@ func getOSCDoc(client *http.Client, match map[string]string, installRepoPath str // zip: http://{projectRoot}/repository/archive?ref={sha} // Downlaod archive. - p, err := HttpGetBytes(client, expand("http://git.oschina.net/{owner}/{repo}/repository/archive?ref={sha}", match), nil) + p, err := com.HttpGetBytes(client, expand("http://git.oschina.net/{owner}/{repo}/repository/archive?ref={sha}", match), nil) if err != nil { return nil, errors.New("Fail to donwload OSChina repo -> " + err.Error()) } diff --git a/doc/vcs.go b/doc/vcs.go index c54ef4435..1a7f94926 100644 --- a/doc/vcs.go +++ b/doc/vcs.go @@ -28,6 +28,8 @@ import ( "regexp" "strconv" "strings" + + "github.com/Unknwon/com" ) var ( @@ -112,7 +114,7 @@ func downloadGit(schemes []string, repo, savedEtag string) (string, string, erro } if scheme == "" { - return "", "", NotFoundError{"VCS not found"} + return "", "", com.NotFoundError{"VCS not found"} } tags := make(map[string]string) @@ -172,7 +174,7 @@ func bestTag(tags map[string]string, defaultTag string) (string, string, error) if commit, ok := tags[defaultTag]; ok { return defaultTag, commit, nil } - return "", "", NotFoundError{"Tag or branch not found."} + return "", "", com.NotFoundError{"Tag or branch not found."} } // expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match. @@ -199,56 +201,6 @@ func expand(template string, match map[string]string, subs ...string) string { return string(p) } -// checkImports checks package denpendencies. -func CheckImports(absPath, importPath string) (importPkgs []string, err error) { - dir, err := os.Open(absPath) - if err != nil { - return nil, err - } - defer dir.Close() - - // Get file info slice. - fis, err := dir.Readdir(0) - if err != nil { - return nil, err - } - - files := make([]*source, 0, 10) - for _, fi := range fis { - // Only handle files. - if strings.HasSuffix(fi.Name(), ".go") { - f, err := os.Open(absPath + fi.Name()) - if err != nil { - return nil, err - } - - fbytes := make([]byte, fi.Size()) - _, err = f.Read(fbytes) - f.Close() - - if err != nil { - return nil, err - } - - files = append(files, &source{ - name: fi.Name(), - data: fbytes, - }) - } - } - - // Check if has Go source files. - if len(files) > 0 { - w := &walker{ImportPath: importPath} - importPkgs, err = w.build(files) - if err != nil { - return nil, err - } - } - - return importPkgs, err -} - // PureDownload downloads package without version control. func PureDownload(nod *Node, installRepoPath string, flags map[string]bool) ([]string, error) { for _, s := range services { @@ -287,7 +239,7 @@ func getDynamic(client *http.Client, nod *Node, installRepoPath string, flags ma return nil, err } if rootMatch["projectRoot"] != match["projectRoot"] { - return nil, NotFoundError{"Project root mismatch."} + return nil, com.NotFoundError{"Project root mismatch."} } } @@ -312,7 +264,7 @@ func fetchMeta(client *http.Client, importPath string) (map[string]string, error scheme = "http" resp, err = client.Get(scheme + "://" + uri) if err != nil { - return nil, &RemoteError{strings.SplitN(importPath, "/", 2)[0], err} + return nil, &com.RemoteError{strings.SplitN(importPath, "/", 2)[0], err} } } defer resp.Body.Close() @@ -359,7 +311,7 @@ metaScan: continue metaScan } if match != nil { - return nil, NotFoundError{"More than one found at " + scheme + "://" + importPath} + return nil, com.NotFoundError{"More than one found at " + scheme + "://" + importPath} } projectRoot, vcs, repo := f[0], f[1], f[2] @@ -367,7 +319,7 @@ metaScan: repo = strings.TrimSuffix(repo, "."+vcs) i := strings.Index(repo, "://") if i < 0 { - return nil, NotFoundError{"Bad repo URL in ."} + return nil, com.NotFoundError{"Bad repo URL in ."} } proto := repo[:i] repo = repo[i+len("://"):] @@ -390,7 +342,76 @@ metaScan: } } if match == nil { - return nil, NotFoundError{" not found."} + return nil, com.NotFoundError{" not found."} } return match, nil } + +func getImports(rootPath string, match map[string]string, cmdFlags map[string]bool) (imports []string) { + dirs, err := GetDirsInfo(rootPath) + if err != nil { + return nil + } + + for _, d := range dirs { + if d.IsDir() && !(!cmdFlags["-e"] && strings.Contains(d.Name(), "example")) { + absPath := rootPath + d.Name() + "/" + importPkgs, err := CheckImports(absPath, match["importPath"]) + if err != nil { + return nil + } + imports = append(imports, importPkgs...) + } + } + return imports +} + +// checkImports checks package denpendencies. +func CheckImports(absPath, importPath string) (importPkgs []string, err error) { + dir, err := os.Open(absPath) + if err != nil { + return nil, err + } + defer dir.Close() + + // Get file info slice. + fis, err := dir.Readdir(0) + if err != nil { + return nil, err + } + + files := make([]*source, 0, 10) + for _, fi := range fis { + // Only handle files. + if strings.HasSuffix(fi.Name(), ".go") { + f, err := os.Open(absPath + fi.Name()) + if err != nil { + return nil, err + } + + fbytes := make([]byte, fi.Size()) + _, err = f.Read(fbytes) + f.Close() + + if err != nil { + return nil, err + } + + files = append(files, &source{ + name: fi.Name(), + data: fbytes, + }) + } + } + + // Check if has Go source files. + if len(files) > 0 { + w := &walker{ImportPath: importPath} + importPkgs, err = w.build(files) + if err != nil { + return nil, err + } + } + + return importPkgs, err +} diff --git a/gopm.go b/gopm.go index b564be06b..a558f3b58 100644 --- a/gopm.go +++ b/gopm.go @@ -37,7 +37,7 @@ import ( // Test that go1.1 tag above is included in builds. main.go refers to this definition. const go11tag = true -const APP_VER = "0.2.2.0819" +const APP_VER = "0.2.5.0827" var ( config map[string]interface{}