@ -16,99 +16,181 @@
package doc
import (
"archive/tar"
"bytes"
"compress/gzip"
"io"
"net/http"
"os"
"path"
"regexp"
"github.com/Unknwon/gowalker/utils"
"strings"
)
var (
b itbucketPattern = regexp . MustCompile ( ` ^bitbucket\.org/(?P<owner>[a-z0-9A-Z_.\-]+)/(?P<repo>[a-z0-9A-Z_.\-]+)(?P<dir>/[a-z0-9A-Z_.\-/]*)?$ ` )
B itbucketPattern = regexp . MustCompile ( ` ^bitbucket\.org/(?P<owner>[a-z0-9A-Z_.\-]+)/(?P<repo>[a-z0-9A-Z_.\-]+)(?P<dir>/[a-z0-9A-Z_.\-/]*)?$ ` )
bitbucketEtagRe = regexp . MustCompile ( ` ^(hg|git)- ` )
)
func getBitbucketDoc ( client * http . Client , match map [ string ] string , savedEtag string ) ( * Package , error ) {
if m := bitbucketEtagRe . FindStringSubmatch ( savedEtag ) ; m != nil {
// 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 , err
return nil , nil , err
}
match [ "vcs" ] = repo . Scm
}
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 , err
// 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
}
}
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
}
var err error
match [ "tag" ] , match [ "commit" ] , err = bestTag ( tags , defaultTags [ match [ "vcs" ] ] )
// 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 , err
return nil , nil , err
}
// Check revision tag.
etag := expand ( "{vcs}-{commit}" , match )
if etag == savedEtag {
return nil , errNotModified
}
importPath := "bitbucket.org/" + expand ( "{owner}/{repo}" , match )
installPath := installGOPATH + "/src/" + importPath
var node struct {
Files [ ] struct {
Path string
}
Directories [ ] string
}
// Remove old files.
os . RemoveAll ( installPath + "/" )
// Create destination directory.
os . MkdirAll ( installPath + "/" , os . ModePerm )
if err := httpGetJSON ( client , expand ( "https://api.bitbucket.org/1.0/repositories/{owner}/{repo}/src/{tag}{dir}/" , match ) , & node ) ; err != nil {
return nil , err
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.
files := make ( [ ] * source , 0 , 5 )
for _ , f := range node . Files {
_ , name := path . Split ( f . Path )
if utils . IsDocFile ( name ) {
files = append ( files , & source {
name : name ,
browseURL : expand ( "https://bitbucket.org/{owner}/{repo}/src/{tag}/{0}" , match , f . Path ) ,
rawURL : expand ( "https://api.bitbucket.org/1.0/repositories/{owner}/{repo}/raw/{tag}/{0}" , match , f . Path ) ,
} )
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
}
}
}
if len ( files ) == 0 && len ( node . Directories ) == 0 {
return nil , NotFoundError { "Directory tree does not contain Go files and subdirs." }
pkg := & Package {
ImportPath : importPath ,
AbsPath : installPath ,
Commit : commit ,
}
// Fetch file from VCS.
if err := fetchFiles ( client , files , nil ) ; err != nil {
return nil , err
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
}
}
}
}
// Start generating data.
w := & walker {
lineFmt : "#cl-%d" ,
pdoc : & Package {
ImportPath : match [ "importPath" ] ,
ProjectName : match [ "repo" ] ,
Etag : etag ,
Dirs : node . Directories ,
} ,
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 w . build ( files )
return false
}