// Copyright 2011 Gary Burd // Copyright 2013 Unknown // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package doc import ( "archive/tar" "bytes" "compress/gzip" "io" "net/http" "os" "path" "regexp" "strings" ) var ( BitbucketPattern = regexp.MustCompile(`^bitbucket\.org/(?P[a-z0-9A-Z_.\-]+)/(?P[a-z0-9A-Z_.\-]+)(?P/[a-z0-9A-Z_.\-/]*)?$`) bitbucketEtagRe = regexp.MustCompile(`^(hg|git)-`) ) // GetBitbucketDoc downloads tarball from bitbucket.org. func GetBitbucketDoc(client *http.Client, match map[string]string, installGOPATH, commit string, cmdFlags map[string]bool) (*Package, []string, error) { // Check version control. if m := bitbucketEtagRe.FindStringSubmatch(commit); m != nil { match["vcs"] = m[1] } else { var repo struct { Scm string } if err := httpGetJSON(client, expand("https://api.bitbucket.org/1.0/repositories/{owner}/{repo}", match), &repo); err != nil { return nil, nil, err } match["vcs"] = repo.Scm } // bundle and snapshot will have commit 'B' and 'S', // but does not need to download dependencies. isCheckImport := len(commit) == 0 // Check if download with specific revision. if isCheckImport || len(commit) == 1 { tags := make(map[string]string) for _, nodeType := range []string{"branches", "tags"} { 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 { return nil, nil, err } for t, n := range nodes { tags[t] = n.Node } } // Check revision tag. var err error match["tag"], match["commit"], err = bestTag(tags, defaultTags[match["vcs"]]) if err != nil { return nil, nil, err } } else { match["commit"] = commit } // We use .tar.gz here. // zip : https://bitbucket.org/{owner}/{repo}/get/{commit}.zip // 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) if err != nil { return nil, nil, err } importPath := "bitbucket.org/" + expand("{owner}/{repo}", match) installPath := installGOPATH + "/src/" + importPath // Remove old files. os.RemoveAll(installPath + "/") // Create destination directory. os.MkdirAll(installPath+"/", os.ModePerm) gzr, err := gzip.NewReader(bytes.NewReader(p)) if err != nil { return nil, nil, err } defer gzr.Close() tr := tar.NewReader(gzr) var autoPath string // Auto path is the root path that generated by bitbucket.org. // Get source file data. dirs := make([]string, 0, 5) for { h, err := tr.Next() if err == io.EOF { break } else if err != nil { return nil, nil, err } fn := h.FileInfo().Name() // In case that we find directory, usually we should not. if !strings.HasSuffix(fn, "/") { // Check root path. if len(autoPath) == 0 { autoPath = fn[:strings.Index(fn, "/")] } absPath := strings.Replace(fn, autoPath, installPath, 1) // Create diretory before create file. dir := path.Dir(absPath) if !checkDir(dir, dirs) { dirs = append(dirs, dir) os.MkdirAll(dir+"/", os.ModePerm) } // Get data from archive. fbytes := make([]byte, h.Size) if _, err := io.ReadFull(tr, fbytes); err != nil { return nil, nil, err } // Write data to file fw, err := os.Create(absPath) if err != nil { return nil, nil, err } _, err = fw.Write(fbytes) fw.Close() if err != nil { return nil, nil, err } } } pkg := &Package{ ImportPath: importPath, AbsPath: installPath, Commit: commit, } var imports []string // Check if need to check imports. if isCheckImport { rootdir, err := os.Open(installPath + "/") if err != nil { return nil, nil, err } defer rootdir.Close() dirs, err := rootdir.Readdir(0) if err != nil { return nil, nil, err } for _, d := range dirs { if d.IsDir() { absPath := installPath + "/" + d.Name() + "/" imports, err = checkImports(absPath, importPath) if err != nil { return nil, nil, err } } } } return pkg, imports, err } // checkDir checks if current directory has been saved. func checkDir(dir string, dirs []string) bool { for _, d := range dirs { if dir == d { return true } } return false }