mirror of https://github.com/gogits/gogs.git
Unknown
12 years ago
13 changed files with 1775 additions and 811 deletions
@ -1,394 +0,0 @@ |
|||||||
package cmd |
|
||||||
|
|
||||||
import ( |
|
||||||
"../doc" |
|
||||||
"fmt" |
|
||||||
"github.com/syndtr/goleveldb/leveldb" |
|
||||||
"github.com/syndtr/goleveldb/leveldb/errors" |
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt" |
|
||||||
"io/ioutil" |
|
||||||
"net/http" |
|
||||||
"os" |
|
||||||
"os/user" |
|
||||||
"strconv" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
dbDir = "~/.gopm/db" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
STOP = iota |
|
||||||
LOCALRUN |
|
||||||
RUNNING |
|
||||||
) |
|
||||||
|
|
||||||
var CmdServe = &Command{ |
|
||||||
UsageLine: "serve [:port]", |
|
||||||
Short: "serve for package search", |
|
||||||
Long: ` |
|
||||||
serve provide a web service to search packages, download packages
|
|
||||||
|
|
||||||
The serve flags are: |
|
||||||
|
|
||||||
-l |
|
||||||
only service for localhost ip |
|
||||||
`, |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
CmdServe.Run = runServe |
|
||||||
CmdServe.Flags = map[string]bool{ |
|
||||||
"-l": false, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func printServePrompt(flag string) { |
|
||||||
switch flag { |
|
||||||
case "-l": |
|
||||||
doc.ColorLog("[INFO] You enabled start a service only localhost.\n") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Not implemented
|
|
||||||
func autoPort() string { |
|
||||||
return "8991" |
|
||||||
} |
|
||||||
|
|
||||||
// search packages
|
|
||||||
func runServe(cmd *Command, args []string) { |
|
||||||
// Check flags.
|
|
||||||
num := checkFlags(cmd.Flags, args, printServePrompt) |
|
||||||
if num == -1 { |
|
||||||
return |
|
||||||
} |
|
||||||
args = args[num:] |
|
||||||
|
|
||||||
var listen string |
|
||||||
var port string |
|
||||||
if cmd.Flags["-l"] { |
|
||||||
listen += "127.0.0.1:" |
|
||||||
port = autoPort() |
|
||||||
} else { |
|
||||||
listen += "0.0.0.0:" |
|
||||||
port = "8991" |
|
||||||
} |
|
||||||
|
|
||||||
// Check length of arguments.
|
|
||||||
if len(args) >= 1 { |
|
||||||
port = args[0] |
|
||||||
} |
|
||||||
|
|
||||||
startService(listen + port) |
|
||||||
} |
|
||||||
|
|
||||||
func splitWord(word string, res *map[string]bool) { |
|
||||||
for i, _ := range word { |
|
||||||
for j, _ := range word[i:] { |
|
||||||
w := word[i : i+j+1] |
|
||||||
(*res)[w] = true |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func splitPkgName(pkgName string) (res map[string]bool) { |
|
||||||
//var src string
|
|
||||||
ps := strings.Split(pkgName, "/") |
|
||||||
if len(ps) > 1 { |
|
||||||
ps = ps[1:] |
|
||||||
} |
|
||||||
|
|
||||||
res = make(map[string]bool, 0) |
|
||||||
res[strings.Join(ps, "/")] = true |
|
||||||
for _, w := range ps { |
|
||||||
splitWord(w, &res) |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
var ( |
|
||||||
ro *opt.ReadOptions = &opt.ReadOptions{} |
|
||||||
wo *opt.WriteOptions = &opt.WriteOptions{} |
|
||||||
) |
|
||||||
|
|
||||||
func dbGet(key string) (string, error) { |
|
||||||
v, err := db.Get([]byte(key), ro) |
|
||||||
return string(v), err |
|
||||||
} |
|
||||||
|
|
||||||
func dbPut(key string, value string) error { |
|
||||||
fmt.Println("put ", key, ": ", value) |
|
||||||
return db.Put([]byte(key), []byte(value), wo) |
|
||||||
} |
|
||||||
|
|
||||||
func batchPut(batch *leveldb.Batch, key string, value string) error { |
|
||||||
fmt.Println("put ", key, ": ", value) |
|
||||||
batch.Put([]byte(key), []byte(value)) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func addPkg(pkg *Pkg) error { |
|
||||||
batch := new(leveldb.Batch) |
|
||||||
strLastId, err := dbGet("lastId") |
|
||||||
if err != nil { |
|
||||||
if err == errors.ErrNotFound { |
|
||||||
strLastId = "0" |
|
||||||
err = batchPut(batch, "lastId", strLastId) |
|
||||||
} else { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
fmt.Println("last id is ", strLastId) |
|
||||||
|
|
||||||
lastId, err := strconv.ParseInt(strLastId, 0, 64) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
pkgKey := fmt.Sprintf("index:%v", pkg.Name) |
|
||||||
|
|
||||||
id, err := dbGet(pkgKey) |
|
||||||
if err != nil { |
|
||||||
if err == errors.ErrNotFound { |
|
||||||
id = fmt.Sprintf("%v", lastId+1) |
|
||||||
fmt.Println(id) |
|
||||||
err = batchPut(batch, "lastId", id) |
|
||||||
if err == nil { |
|
||||||
err = batchPut(batch, pkgKey, id) |
|
||||||
} |
|
||||||
if err == nil { |
|
||||||
err = batchPut(batch, "pkg:"+id, pkg.Name) |
|
||||||
} |
|
||||||
total, err := dbGet("total") |
|
||||||
if err != nil { |
|
||||||
if err == errors.ErrNotFound { |
|
||||||
total = "1" |
|
||||||
} else { |
|
||||||
return err |
|
||||||
} |
|
||||||
} else { |
|
||||||
totalInt, err := strconv.ParseInt(total, 0, 64) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
totalInt = totalInt + 1 |
|
||||||
total = fmt.Sprintf("%v", totalInt) |
|
||||||
} |
|
||||||
|
|
||||||
err = batchPut(batch, "total", total) |
|
||||||
} else { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
vers, err := dbGet("ver:" + id) |
|
||||||
needSplit := (err == errors.ErrNotFound) |
|
||||||
if err != nil { |
|
||||||
if err != errors.ErrNotFound { |
|
||||||
return err |
|
||||||
} |
|
||||||
} else { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
if vers == "" { |
|
||||||
fmt.Println(pkg) |
|
||||||
vers = pkg.VerString() |
|
||||||
} else { |
|
||||||
if !strings.Contains(vers, pkg.VerString()) { |
|
||||||
vers = vers + "," + pkg.VerString() |
|
||||||
} else { |
|
||||||
return nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
err = batchPut(batch, "ver:"+id, vers) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
if !needSplit { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
keys := splitPkgName(pkg.Name) |
|
||||||
|
|
||||||
for key, _ := range keys { |
|
||||||
err = batchPut(batch, fmt.Sprintf("key:%v:%v", key, id), "") |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return db.Write(batch, wo) |
|
||||||
} |
|
||||||
|
|
||||||
func rmPkg(pkg *Pkg) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
var db *leveldb.DB |
|
||||||
|
|
||||||
// service should be run
|
|
||||||
func autoRun() { |
|
||||||
s, _, _ := runningStatus() |
|
||||||
if s == STOP { |
|
||||||
os.StartProcess("gopm", []string{"serve", "-l"}, nil) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func runningStatus() (int, int, int) { |
|
||||||
contentByte, err := ioutil.ReadFile("~/.gopm/var/pid") |
|
||||||
if err != nil { |
|
||||||
return STOP, 0, 0 |
|
||||||
} |
|
||||||
content := string(contentByte) |
|
||||||
if len(content) < 0 || !strings.Contains(content, ",") { |
|
||||||
return STOP, 0, 0 |
|
||||||
} |
|
||||||
cs := strings.Split(string(content), ",") |
|
||||||
if len(cs) != 3 { |
|
||||||
return STOP, 0, 0 |
|
||||||
} |
|
||||||
status, err := strconv.Atoi(cs[0]) |
|
||||||
if err != nil { |
|
||||||
return STOP, 0, 0 |
|
||||||
} |
|
||||||
if status < STOP || status > RUNNING { |
|
||||||
return STOP, 0, 0 |
|
||||||
} |
|
||||||
pid, err := strconv.Atoi(cs[1]) |
|
||||||
if err != nil { |
|
||||||
return STOP, 0, 0 |
|
||||||
} |
|
||||||
|
|
||||||
_, err = os.FindProcess(pid) |
|
||||||
if err != nil { |
|
||||||
return STOP, 0, 0 |
|
||||||
} |
|
||||||
|
|
||||||
port, err := strconv.Atoi(cs[2]) |
|
||||||
if err != nil { |
|
||||||
return STOP, 0, 0 |
|
||||||
} |
|
||||||
|
|
||||||
return status, pid, port |
|
||||||
} |
|
||||||
|
|
||||||
func startService(listen string) { |
|
||||||
// check the pre serve's type
|
|
||||||
curUser, err := user.Current() |
|
||||||
if err != nil { |
|
||||||
fmt.Println(err) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
dbDir = strings.Replace(dbDir, "~", curUser.HomeDir, -1) |
|
||||||
|
|
||||||
db, err = leveldb.OpenFile(dbDir, &opt.Options{Flag: opt.OFCreateIfMissing}) |
|
||||||
if err != nil { |
|
||||||
fmt.Println(err) |
|
||||||
return |
|
||||||
} |
|
||||||
defer db.Close() |
|
||||||
|
|
||||||
// these handlers should only access by localhost
|
|
||||||
http.HandleFunc("/add", addHandler) |
|
||||||
http.HandleFunc("/rm", rmHandler) |
|
||||||
|
|
||||||
// these handlers can be accessed according listen's ip
|
|
||||||
http.HandleFunc("/search", searchHandler) |
|
||||||
http.HandleFunc("/searche", searcheHandler) |
|
||||||
http.ListenAndServe(listen, nil) |
|
||||||
} |
|
||||||
|
|
||||||
func searchHandler(w http.ResponseWriter, r *http.Request) { |
|
||||||
r.ParseForm() |
|
||||||
ids := make(map[string]bool) |
|
||||||
for key, _ := range r.Form { |
|
||||||
iter := db.NewIterator(ro) |
|
||||||
rkey := fmt.Sprintf("key:%v:", key) |
|
||||||
if iter.Seek([]byte(rkey)) { |
|
||||||
k := iter.Key() |
|
||||||
if !strings.HasPrefix(string(k), rkey) { |
|
||||||
break |
|
||||||
} else { |
|
||||||
ids[string(k)] = true |
|
||||||
} |
|
||||||
} |
|
||||||
for iter.Next() { |
|
||||||
k := iter.Key() |
|
||||||
if !strings.HasPrefix(string(k), rkey) { |
|
||||||
break |
|
||||||
} |
|
||||||
ids[string(k)] = true |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pkgs := make([]string, 0) |
|
||||||
|
|
||||||
for id, _ := range ids { |
|
||||||
idkeys := strings.SplitN(id, ":", -1) |
|
||||||
rId := idkeys[len(idkeys)-1] |
|
||||||
fmt.Println(rId) |
|
||||||
pkg, err := dbGet(fmt.Sprintf("pkg:%v", rId)) |
|
||||||
if err != nil { |
|
||||||
doc.ColorLog(err.Error()) |
|
||||||
continue |
|
||||||
} |
|
||||||
pkgs = append(pkgs, pkg) |
|
||||||
} |
|
||||||
|
|
||||||
w.Write([]byte("[\"" + strings.Join(pkgs, "\", \"") + "\"]")) |
|
||||||
} |
|
||||||
|
|
||||||
func searcheHandler(w http.ResponseWriter, r *http.Request) { |
|
||||||
//if r.Method == "POST" {
|
|
||||||
r.ParseForm() |
|
||||||
pkgs := make([]string, 0) |
|
||||||
for key, _ := range r.Form { |
|
||||||
_, err := dbGet("index:" + key) |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
doc.ColorLog(err.Error()) |
|
||||||
continue |
|
||||||
} |
|
||||||
|
|
||||||
pkgs = append(pkgs, key) |
|
||||||
} |
|
||||||
|
|
||||||
w.Write([]byte("[\"" + strings.Join(pkgs, "\", \"") + "\"]")) |
|
||||||
//}
|
|
||||||
} |
|
||||||
|
|
||||||
func addHandler(w http.ResponseWriter, r *http.Request) { |
|
||||||
//if r.Method == "POST" {
|
|
||||||
r.ParseForm() |
|
||||||
for key, _ := range r.Form { |
|
||||||
fmt.Println(key) |
|
||||||
pkg := NewPkg(key, "") |
|
||||||
if pkg != nil { |
|
||||||
err := addPkg(pkg) |
|
||||||
if err != nil { |
|
||||||
fmt.Println(err) |
|
||||||
} |
|
||||||
} else { |
|
||||||
fmt.Println(key) |
|
||||||
} |
|
||||||
} |
|
||||||
//}
|
|
||||||
} |
|
||||||
|
|
||||||
func rmHandler(w http.ResponseWriter, r *http.Request) { |
|
||||||
|
|
||||||
} |
|
@ -1,245 +0,0 @@ |
|||||||
// Copyright 2013 gopm authors.
|
|
||||||
//
|
|
||||||
// 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 cmd |
|
||||||
|
|
||||||
import ( |
|
||||||
//"errors"
|
|
||||||
"fmt" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
TRUNK = "trunk" |
|
||||||
TAG = "tag" |
|
||||||
BRANCH = "branch" |
|
||||||
COMMIT = "commit" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
downloadCache map[string]bool // Saves packages that have been downloaded.
|
|
||||||
services []Service = []Service{ |
|
||||||
&GithubService{}, |
|
||||||
&GitOscService{}, |
|
||||||
&BitBucketService{}, |
|
||||||
//&GitCafeService{},
|
|
||||||
//&CodeCSDNSource{},
|
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
func getService(pkgName string) Service { |
|
||||||
for _, service := range services { |
|
||||||
if service.HasPkg(pkgName) { |
|
||||||
return service |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type Service interface { |
|
||||||
PkgUrl(pkg *Pkg) string |
|
||||||
HasPkg(pkgName string) bool |
|
||||||
PkgExt() string |
|
||||||
} |
|
||||||
|
|
||||||
type Pkg struct { |
|
||||||
Service Service |
|
||||||
Name string |
|
||||||
Ver string |
|
||||||
VerId string |
|
||||||
} |
|
||||||
|
|
||||||
func (p *Pkg) VerSimpleString() string { |
|
||||||
if p.VerId != "" { |
|
||||||
return p.VerId |
|
||||||
} |
|
||||||
return p.Ver |
|
||||||
} |
|
||||||
|
|
||||||
func (p *Pkg) VerString() string { |
|
||||||
if p.VerId == "" { |
|
||||||
return p.Ver |
|
||||||
} |
|
||||||
return fmt.Sprintf("%v:%v", p.Ver, p.VerId) |
|
||||||
} |
|
||||||
|
|
||||||
func (p *Pkg) Url() string { |
|
||||||
return p.Service.PkgUrl(p) |
|
||||||
} |
|
||||||
|
|
||||||
func (p *Pkg) FileName() string { |
|
||||||
return fmt.Sprintf("%v.%v", p.VerSimpleString(), p.Service.PkgExt()) |
|
||||||
} |
|
||||||
|
|
||||||
func NewPkg(pkgName string, ver string) *Pkg { |
|
||||||
vers := strings.Split(ver, ":") |
|
||||||
if len(vers) > 2 { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
var verId string |
|
||||||
if len(vers) == 2 { |
|
||||||
verId = vers[1] |
|
||||||
} |
|
||||||
|
|
||||||
if len(vers) == 1 { |
|
||||||
vers[0] = TRUNK |
|
||||||
} |
|
||||||
|
|
||||||
service := getService(pkgName) |
|
||||||
if service == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
return &Pkg{service, pkgName, vers[0], verId} |
|
||||||
} |
|
||||||
|
|
||||||
// github repository
|
|
||||||
type GithubService struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GithubService) PkgUrl(pkg *Pkg) string { |
|
||||||
var verPath string |
|
||||||
if pkg.Ver == TRUNK { |
|
||||||
verPath = "master" |
|
||||||
} else { |
|
||||||
verPath = pkg.VerId |
|
||||||
} |
|
||||||
return fmt.Sprintf("https://%v/archive/%v.zip", pkg.Name, verPath) |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GithubService) HasPkg(pkgName string) bool { |
|
||||||
return strings.HasPrefix(pkgName, "github.com") |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GithubService) PkgExt() string { |
|
||||||
return "zip" |
|
||||||
} |
|
||||||
|
|
||||||
// git osc repos
|
|
||||||
type GitOscService struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitOscService) PkgUrl(pkg *Pkg) string { |
|
||||||
var verPath string |
|
||||||
if pkg.Ver == TRUNK { |
|
||||||
verPath = "master" |
|
||||||
} else { |
|
||||||
verPath = pkg.VerId |
|
||||||
} |
|
||||||
return fmt.Sprintf("https://%v/repository/archive?ref=%v", pkg.Name, verPath) |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitOscService) HasPkg(pkgName string) bool { |
|
||||||
return strings.HasPrefix(pkgName, "git.oschina.net") |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitOscService) PkgExt() string { |
|
||||||
return "zip" |
|
||||||
} |
|
||||||
|
|
||||||
// bitbucket.org
|
|
||||||
type BitBucketService struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (s *BitBucketService) PkgUrl(pkg *Pkg) string { |
|
||||||
var verPath string |
|
||||||
if pkg.Ver == TRUNK { |
|
||||||
verPath = "default" |
|
||||||
} else { |
|
||||||
verPath = pkg.VerId |
|
||||||
} |
|
||||||
|
|
||||||
return fmt.Sprintf("https://%v/get/%v.zip", pkg.Name, verPath) |
|
||||||
} |
|
||||||
|
|
||||||
func (s *BitBucketService) HasPkg(pkgName string) bool { |
|
||||||
return strings.HasPrefix(pkgName, "bitbucket.org") |
|
||||||
} |
|
||||||
|
|
||||||
func (s *BitBucketService) PkgExt() string { |
|
||||||
return "zip" |
|
||||||
} |
|
||||||
|
|
||||||
type GitCafeService struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitCafeService) PkgUrl(pkg *Pkg) string { |
|
||||||
var verPath string |
|
||||||
if pkg.Ver == TRUNK { |
|
||||||
verPath = "master" |
|
||||||
} else { |
|
||||||
verPath = pkg.VerId |
|
||||||
} |
|
||||||
|
|
||||||
return fmt.Sprintf("https://%v/tarball/%v", pkg.Name, verPath) |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitCafeService) HasPkg(pkgName string) bool { |
|
||||||
return strings.HasPrefix(pkgName, "gitcafe.com") |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitCafeService) PkgExt() string { |
|
||||||
return "tar.gz" |
|
||||||
} |
|
||||||
|
|
||||||
// git lab repos, not completed
|
|
||||||
type GitLabService struct { |
|
||||||
DomainOrIp string |
|
||||||
Username string |
|
||||||
Passwd string |
|
||||||
PrivateKey string |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitLabService) PkgUrl(pkg *Pkg) string { |
|
||||||
var verPath string |
|
||||||
if pkg.Ver == TRUNK { |
|
||||||
verPath = "master" |
|
||||||
} else { |
|
||||||
verPath = pkg.VerId |
|
||||||
} |
|
||||||
|
|
||||||
return fmt.Sprintf("https://%v/repository/archive/%v", pkg.Name, verPath) |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitLabService) HasPkg(pkgName string) bool { |
|
||||||
return strings.HasPrefix(pkgName, s.DomainOrIp) |
|
||||||
} |
|
||||||
|
|
||||||
func (s *GitLabService) PkgExt() string { |
|
||||||
return "tar.gz" |
|
||||||
} |
|
||||||
|
|
||||||
// code.csdn.net
|
|
||||||
type CodeCSDNService struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (s *CodeCSDNService) PkgUrl(pkg *Pkg) string { |
|
||||||
var verPath string |
|
||||||
if pkg.Ver == TRUNK { |
|
||||||
verPath = "master" |
|
||||||
} else { |
|
||||||
verPath = pkg.VerId |
|
||||||
} |
|
||||||
|
|
||||||
return fmt.Sprintf("https://%v/repository/archive?ref=%v", pkg.Name, verPath) |
|
||||||
} |
|
||||||
|
|
||||||
func (s *CodeCSDNService) HasPkg(pkgName string) bool { |
|
||||||
return strings.HasPrefix(pkgName, "code.csdn.net") |
|
||||||
} |
|
||||||
|
|
||||||
func (s *CodeCSDNService) PkgExt() string { |
|
||||||
return "zip" |
|
||||||
} |
|
@ -0,0 +1,47 @@ |
|||||||
|
// Copyright 2013 gopm authors.
|
||||||
|
//
|
||||||
|
// 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 ( |
||||||
|
"errors" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
errNotModified = errors.New("Package not modified") |
||||||
|
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() |
||||||
|
} |
@ -0,0 +1,196 @@ |
|||||||
|
// Copyright 2013 gopm authors.
|
||||||
|
//
|
||||||
|
// 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/zip" |
||||||
|
"bytes" |
||||||
|
"errors" |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
"path" |
||||||
|
"regexp" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
githubRawHeader = http.Header{"Accept": {"application/vnd.github-blob.raw"}} |
||||||
|
GithubPattern = regexp.MustCompile(`^github\.com/(?P<owner>[a-z0-9A-Z_.\-]+)/(?P<repo>[a-z0-9A-Z_.\-]+)(?P<dir>/[a-z0-9A-Z_.\-/]*)?$`) |
||||||
|
githubCred string |
||||||
|
) |
||||||
|
|
||||||
|
/*func SetGithubCredentials(id, secret string) { |
||||||
|
//githubCred = "client_id=" + id + "&client_secret=" + secret
|
||||||
|
}*/ |
||||||
|
|
||||||
|
func SetGithubCredentials(token string) { |
||||||
|
if len(token) > 0 { |
||||||
|
githubCred = "access_token=" + token |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// GetGithubDoc downloads tarball from github.com.
|
||||||
|
func GetGithubDoc(client *http.Client, match map[string]string, installRepoPath string, nod *Node, cmdFlags map[string]bool) ([]string, error) { |
||||||
|
match["cred"] = githubCred |
||||||
|
|
||||||
|
if nod.Type == BRANCH { |
||||||
|
nod.Value = MASTER |
||||||
|
match["sha"] = nod.Value |
||||||
|
} |
||||||
|
|
||||||
|
ColorLog("[TRAC] Downloading package( %s => %s:%s )\n", |
||||||
|
nod.ImportPath, nod.Type, nod.Value) |
||||||
|
|
||||||
|
// JSON struct for github.com.
|
||||||
|
var refs []*struct { |
||||||
|
Ref string |
||||||
|
Url string |
||||||
|
Object struct { |
||||||
|
Sha string |
||||||
|
Type string |
||||||
|
Url string |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if nod.IsGetDeps { |
||||||
|
if nod.Type == COMMIT { |
||||||
|
// Get up-to-date version.
|
||||||
|
err := httpGetJSON(client, expand("https://api.github.com/repos/{owner}/{repo}/git/refs?{cred}", match), &refs) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
for _, ref := range refs { |
||||||
|
if strings.HasPrefix(ref.Ref, "refs/heads/master") { |
||||||
|
match["sha"] = ref.Object.Sha |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
nod.Value = match["sha"] |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Check downlaod type.
|
||||||
|
switch nod.Type { |
||||||
|
case TAG, COMMIT, BRANCH: |
||||||
|
match["sha"] = nod.Value |
||||||
|
default: |
||||||
|
return nil, errors.New("Unknown node type: " + nod.Type) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// We use .zip here.
|
||||||
|
// zip : https://github.com/{owner}/{repo}/archive/{sha}.zip
|
||||||
|
// 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) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
shaName := expand("{repo}-{sha}", match) |
||||||
|
if nod.Type == "tag" { |
||||||
|
shaName = strings.Replace(shaName, "-v", "-", 1) |
||||||
|
} |
||||||
|
|
||||||
|
projectPath := expand("github.com/{owner}/{repo}", match) |
||||||
|
installPath := installRepoPath + "/" + projectPath + "." + nod.Value |
||||||
|
nod.ImportPath = projectPath |
||||||
|
|
||||||
|
// Remove old files.
|
||||||
|
os.RemoveAll(installPath + "/") |
||||||
|
// Create destination directory.
|
||||||
|
os.MkdirAll(installPath+"/", os.ModePerm) |
||||||
|
|
||||||
|
r, err := zip.NewReader(bytes.NewReader(p), int64(len(p))) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
dirs := make([]string, 0, 5) |
||||||
|
// Need to add root path because we cannot get from tarball.
|
||||||
|
dirs = append(dirs, installPath+"/") |
||||||
|
for _, f := range r.File { |
||||||
|
absPath := strings.Replace(f.FileInfo().Name(), shaName, installPath, 1) |
||||||
|
// Create diretory before create file.
|
||||||
|
os.MkdirAll(path.Dir(absPath)+"/", os.ModePerm) |
||||||
|
|
||||||
|
compareDir: |
||||||
|
switch { |
||||||
|
case strings.HasSuffix(absPath, "/"): // Directory.
|
||||||
|
// Check if current directory is example.
|
||||||
|
if !(!cmdFlags["-e"] && strings.Contains(absPath, "example")) { |
||||||
|
for _, d := range dirs { |
||||||
|
if d == absPath { |
||||||
|
break compareDir |
||||||
|
} |
||||||
|
} |
||||||
|
dirs = append(dirs, absPath) |
||||||
|
} |
||||||
|
case !strings.HasPrefix(f.FileInfo().Name(), "."): |
||||||
|
// Get file from archive.
|
||||||
|
rc, err := f.Open() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// Write data to file
|
||||||
|
fw, _ := os.Create(absPath) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
_, err = io.Copy(fw, rc) |
||||||
|
// Close files.
|
||||||
|
rc.Close() |
||||||
|
fw.Close() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// Set modify time.
|
||||||
|
os.Chtimes(absPath, f.ModTime(), f.ModTime()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var imports []string |
||||||
|
|
||||||
|
// Check if need to check imports.
|
||||||
|
if nod.IsGetDeps { |
||||||
|
for _, d := range dirs { |
||||||
|
importPkgs, err := CheckImports(d, match["importPath"]) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
imports = append(imports, importPkgs...) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*fpath := appPath + "repo/tarballs/" + node.ImportPath + "-" + node.Value + ".zip" |
||||||
|
// Save tarball.
|
||||||
|
if autoBackup && !utils.IsExist(fpath) { |
||||||
|
os.MkdirAll(path.Dir(fpath)+"/", os.ModePerm) |
||||||
|
f, err := os.Create(fpath) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
defer f.Close() |
||||||
|
_, err = f.Write(p) |
||||||
|
}*/ |
||||||
|
|
||||||
|
return imports, err |
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
// Copyright 2013 gopm authors.
|
||||||
|
//
|
||||||
|
// 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 ( |
||||||
|
"encoding/json" |
||||||
|
"flag" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"io/ioutil" |
||||||
|
"net" |
||||||
|
"net/http" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/astaxie/beego" |
||||||
|
) |
||||||
|
|
||||||
|
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.") |
||||||
|
) |
||||||
|
|
||||||
|
func timeoutDial(network, addr string) (net.Conn, error) { |
||||||
|
return net.DialTimeout(network, addr, *dialTimeout) |
||||||
|
} |
||||||
|
|
||||||
|
type transport struct { |
||||||
|
t http.Transport |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
}) |
||||||
|
defer timer.Stop() |
||||||
|
resp, err := t.t.RoundTrip(req) |
||||||
|
return resp, err |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
// Copyright 2013 gopm authors.
|
||||||
|
//
|
||||||
|
// 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 ( |
||||||
|
"go/token" |
||||||
|
"os" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
TRUNK = "trunk" |
||||||
|
MASTER = "master" |
||||||
|
TAG = "tag" |
||||||
|
BRANCH = "branch" |
||||||
|
COMMIT = "commit" |
||||||
|
) |
||||||
|
|
||||||
|
type Node struct { |
||||||
|
ImportPath string |
||||||
|
Type string |
||||||
|
Value string // Branch, tag or commit.
|
||||||
|
IsGetDeps bool |
||||||
|
} |
||||||
|
|
||||||
|
// source is source code file.
|
||||||
|
type source struct { |
||||||
|
rawURL string |
||||||
|
name string |
||||||
|
data []byte |
||||||
|
} |
||||||
|
|
||||||
|
func (s *source) Name() string { return s.name } |
||||||
|
func (s *source) Size() int64 { return int64(len(s.data)) } |
||||||
|
func (s *source) Mode() os.FileMode { return 0 } |
||||||
|
func (s *source) ModTime() time.Time { return time.Time{} } |
||||||
|
func (s *source) IsDir() bool { return false } |
||||||
|
func (s *source) Sys() interface{} { return nil } |
||||||
|
|
||||||
|
// walker holds the state used when building the documentation.
|
||||||
|
type walker struct { |
||||||
|
ImportPath string |
||||||
|
srcs map[string]*source // Source files.
|
||||||
|
fset *token.FileSet |
||||||
|
} |
@ -0,0 +1,246 @@ |
|||||||
|
// Copyright 2013 gopm authors.
|
||||||
|
//
|
||||||
|
// 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 ( |
||||||
|
"bytes" |
||||||
|
"io/ioutil" |
||||||
|
"log" |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"path" |
||||||
|
"regexp" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
appPath string |
||||||
|
autoBackup bool |
||||||
|
) |
||||||
|
|
||||||
|
func SetAppConfig(path string, backup bool) { |
||||||
|
appPath = path |
||||||
|
autoBackup = backup |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: specify with command line flag
|
||||||
|
const repoRoot = "/tmp/gddo" |
||||||
|
|
||||||
|
var urlTemplates = []struct { |
||||||
|
re *regexp.Regexp |
||||||
|
template string |
||||||
|
lineFmt string |
||||||
|
}{ |
||||||
|
{ |
||||||
|
regexp.MustCompile(`^git\.gitorious\.org/(?P<repo>[^/]+/[^/]+)$`), |
||||||
|
"https://gitorious.org/{repo}/blobs/{tag}/{dir}{0}", |
||||||
|
"#line%d", |
||||||
|
}, |
||||||
|
{ |
||||||
|
regexp.MustCompile(`^camlistore\.org/r/p/(?P<repo>[^/]+)$`), |
||||||
|
"http://camlistore.org/code/?p={repo}.git;hb={tag};f={dir}{0}", |
||||||
|
"#l%d", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
// lookupURLTemplate finds an expand() template, match map and line number
|
||||||
|
// format for well known repositories.
|
||||||
|
func lookupURLTemplate(repo, dir, tag string) (string, map[string]string, string) { |
||||||
|
if strings.HasPrefix(dir, "/") { |
||||||
|
dir = dir[1:] + "/" |
||||||
|
} |
||||||
|
for _, t := range urlTemplates { |
||||||
|
if m := t.re.FindStringSubmatch(repo); m != nil { |
||||||
|
match := map[string]string{ |
||||||
|
"dir": dir, |
||||||
|
"tag": tag, |
||||||
|
} |
||||||
|
for i, name := range t.re.SubexpNames() { |
||||||
|
if name != "" { |
||||||
|
match[name] = m[i] |
||||||
|
} |
||||||
|
} |
||||||
|
return t.template, match, t.lineFmt |
||||||
|
} |
||||||
|
} |
||||||
|
return "", nil, "" |
||||||
|
} |
||||||
|
|
||||||
|
type vcsCmd struct { |
||||||
|
schemes []string |
||||||
|
download func([]string, string, string) (string, string, error) |
||||||
|
} |
||||||
|
|
||||||
|
var vcsCmds = map[string]*vcsCmd{ |
||||||
|
"git": &vcsCmd{ |
||||||
|
schemes: []string{"http", "https", "git"}, |
||||||
|
download: downloadGit, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
var lsremoteRe = regexp.MustCompile(`(?m)^([0-9a-f]{40})\s+refs/(?:tags|heads)/(.+)$`) |
||||||
|
|
||||||
|
func downloadGit(schemes []string, repo, savedEtag string) (string, string, error) { |
||||||
|
var p []byte |
||||||
|
var scheme string |
||||||
|
for i := range schemes { |
||||||
|
cmd := exec.Command("git", "ls-remote", "--heads", "--tags", schemes[i]+"://"+repo+".git") |
||||||
|
log.Println(strings.Join(cmd.Args, " ")) |
||||||
|
var err error |
||||||
|
p, err = cmd.Output() |
||||||
|
if err == nil { |
||||||
|
scheme = schemes[i] |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if scheme == "" { |
||||||
|
return "", "", NotFoundError{"VCS not found"} |
||||||
|
} |
||||||
|
|
||||||
|
tags := make(map[string]string) |
||||||
|
for _, m := range lsremoteRe.FindAllSubmatch(p, -1) { |
||||||
|
tags[string(m[2])] = string(m[1]) |
||||||
|
} |
||||||
|
|
||||||
|
tag, commit, err := bestTag(tags, "master") |
||||||
|
if err != nil { |
||||||
|
return "", "", err |
||||||
|
} |
||||||
|
|
||||||
|
etag := scheme + "-" + commit |
||||||
|
|
||||||
|
if etag == savedEtag { |
||||||
|
return "", "", errNotModified |
||||||
|
} |
||||||
|
|
||||||
|
dir := path.Join(repoRoot, repo+".git") |
||||||
|
p, err = ioutil.ReadFile(path.Join(dir, ".git/HEAD")) |
||||||
|
switch { |
||||||
|
case err != nil: |
||||||
|
if err := os.MkdirAll(dir, 0777); err != nil { |
||||||
|
return "", "", err |
||||||
|
} |
||||||
|
cmd := exec.Command("git", "clone", scheme+"://"+repo, dir) |
||||||
|
log.Println(strings.Join(cmd.Args, " ")) |
||||||
|
if err := cmd.Run(); err != nil { |
||||||
|
return "", "", err |
||||||
|
} |
||||||
|
case string(bytes.TrimRight(p, "\n")) == commit: |
||||||
|
return tag, etag, nil |
||||||
|
default: |
||||||
|
cmd := exec.Command("git", "fetch") |
||||||
|
log.Println(strings.Join(cmd.Args, " ")) |
||||||
|
cmd.Dir = dir |
||||||
|
if err := cmd.Run(); err != nil { |
||||||
|
return "", "", err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
cmd := exec.Command("git", "checkout", "--detach", "--force", commit) |
||||||
|
cmd.Dir = dir |
||||||
|
if err := cmd.Run(); err != nil { |
||||||
|
return "", "", err |
||||||
|
} |
||||||
|
|
||||||
|
return tag, etag, nil |
||||||
|
} |
||||||
|
|
||||||
|
var defaultTags = map[string]string{"git": "master", "hg": "default"} |
||||||
|
|
||||||
|
func bestTag(tags map[string]string, defaultTag string) (string, string, error) { |
||||||
|
if commit, ok := tags["go1"]; ok { |
||||||
|
return "go1", commit, nil |
||||||
|
} |
||||||
|
if commit, ok := tags[defaultTag]; ok { |
||||||
|
return defaultTag, commit, nil |
||||||
|
} |
||||||
|
return "", "", NotFoundError{"Tag or branch not found."} |
||||||
|
} |
||||||
|
|
||||||
|
// expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match.
|
||||||
|
func expand(template string, match map[string]string, subs ...string) string { |
||||||
|
var p []byte |
||||||
|
var i int |
||||||
|
for { |
||||||
|
i = strings.Index(template, "{") |
||||||
|
if i < 0 { |
||||||
|
break |
||||||
|
} |
||||||
|
p = append(p, template[:i]...) |
||||||
|
template = template[i+1:] |
||||||
|
i = strings.Index(template, "}") |
||||||
|
if s, ok := match[template[:i]]; ok { |
||||||
|
p = append(p, s...) |
||||||
|
} else { |
||||||
|
j, _ := strconv.Atoi(template[:i]) |
||||||
|
p = append(p, subs[j]...) |
||||||
|
} |
||||||
|
template = template[i+1:] |
||||||
|
} |
||||||
|
p = append(p, template...) |
||||||
|
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 |
||||||
|
} |
@ -0,0 +1,144 @@ |
|||||||
|
// Copyright 2013 gopm authors.
|
||||||
|
//
|
||||||
|
// 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 ( |
||||||
|
"bytes" |
||||||
|
"errors" |
||||||
|
"go/ast" |
||||||
|
"go/build" |
||||||
|
"go/parser" |
||||||
|
"go/token" |
||||||
|
"io" |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"path" |
||||||
|
"runtime" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
type sliceWriter struct{ p *[]byte } |
||||||
|
|
||||||
|
func (w sliceWriter) Write(p []byte) (int, error) { |
||||||
|
*w.p = append(*w.p, p...) |
||||||
|
return len(p), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (w *walker) readDir(dir string) ([]os.FileInfo, error) { |
||||||
|
if dir != w.ImportPath { |
||||||
|
panic("unexpected") |
||||||
|
} |
||||||
|
fis := make([]os.FileInfo, 0, len(w.srcs)) |
||||||
|
for _, src := range w.srcs { |
||||||
|
fis = append(fis, src) |
||||||
|
} |
||||||
|
return fis, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (w *walker) openFile(path string) (io.ReadCloser, error) { |
||||||
|
if strings.HasPrefix(path, w.ImportPath+"/") { |
||||||
|
if src, ok := w.srcs[path[len(w.ImportPath)+1:]]; ok { |
||||||
|
return ioutil.NopCloser(bytes.NewReader(src.data)), nil |
||||||
|
} |
||||||
|
} |
||||||
|
panic("unexpected") |
||||||
|
} |
||||||
|
|
||||||
|
func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, error) { |
||||||
|
pkg := imports[path] |
||||||
|
if pkg == nil { |
||||||
|
// Guess the package name without importing it. Start with the last
|
||||||
|
// element of the path.
|
||||||
|
name := path[strings.LastIndex(path, "/")+1:] |
||||||
|
|
||||||
|
// Trim commonly used prefixes and suffixes containing illegal name
|
||||||
|
// runes.
|
||||||
|
name = strings.TrimSuffix(name, ".go") |
||||||
|
name = strings.TrimSuffix(name, "-go") |
||||||
|
name = strings.TrimPrefix(name, "go.") |
||||||
|
name = strings.TrimPrefix(name, "go-") |
||||||
|
name = strings.TrimPrefix(name, "biogo.") |
||||||
|
|
||||||
|
// It's also common for the last element of the path to contain an
|
||||||
|
// extra "go" prefix, but not always. TODO: examine unresolved ids to
|
||||||
|
// detect when trimming the "go" prefix is appropriate.
|
||||||
|
|
||||||
|
pkg = ast.NewObj(ast.Pkg, name) |
||||||
|
pkg.Data = ast.NewScope(nil) |
||||||
|
imports[path] = pkg |
||||||
|
} |
||||||
|
return pkg, nil |
||||||
|
} |
||||||
|
|
||||||
|
// build gets imports from source files.
|
||||||
|
func (w *walker) build(srcs []*source) ([]string, error) { |
||||||
|
// Add source files to walker, I skipped references here.
|
||||||
|
w.srcs = make(map[string]*source) |
||||||
|
for _, src := range srcs { |
||||||
|
w.srcs[src.name] = src |
||||||
|
} |
||||||
|
|
||||||
|
w.fset = token.NewFileSet() |
||||||
|
|
||||||
|
// Find the package and associated files.
|
||||||
|
ctxt := build.Context{ |
||||||
|
GOOS: runtime.GOOS, |
||||||
|
GOARCH: runtime.GOARCH, |
||||||
|
CgoEnabled: true, |
||||||
|
JoinPath: path.Join, |
||||||
|
IsAbsPath: path.IsAbs, |
||||||
|
SplitPathList: func(list string) []string { return strings.Split(list, ":") }, |
||||||
|
IsDir: func(path string) bool { panic("unexpected") }, |
||||||
|
HasSubdir: func(root, dir string) (rel string, ok bool) { panic("unexpected") }, |
||||||
|
ReadDir: func(dir string) (fi []os.FileInfo, err error) { return w.readDir(dir) }, |
||||||
|
OpenFile: func(path string) (r io.ReadCloser, err error) { return w.openFile(path) }, |
||||||
|
Compiler: "gc", |
||||||
|
} |
||||||
|
|
||||||
|
bpkg, err := ctxt.ImportDir(w.ImportPath, 0) |
||||||
|
// Continue if there are no Go source files; we still want the directory info.
|
||||||
|
_, nogo := err.(*build.NoGoError) |
||||||
|
if err != nil { |
||||||
|
if nogo { |
||||||
|
err = nil |
||||||
|
} else { |
||||||
|
return nil, errors.New("doc.walker.build(): " + err.Error()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Parse the Go files
|
||||||
|
|
||||||
|
files := make(map[string]*ast.File) |
||||||
|
for _, name := range append(bpkg.GoFiles, bpkg.CgoFiles...) { |
||||||
|
file, err := parser.ParseFile(w.fset, name, w.srcs[name].data, parser.ParseComments) |
||||||
|
if err != nil { |
||||||
|
//beego.Error("doc.walker.build():", err)
|
||||||
|
continue |
||||||
|
} |
||||||
|
files[name] = file |
||||||
|
} |
||||||
|
|
||||||
|
w.ImportPath = strings.Replace(w.ImportPath, "\\", "/", -1) |
||||||
|
var imports []string |
||||||
|
for _, v := range bpkg.Imports { |
||||||
|
// Skip strandard library.
|
||||||
|
if !IsGoRepoPath(v) && |
||||||
|
(GetProjectPath(v) != GetProjectPath(w.ImportPath)) { |
||||||
|
imports = append(imports, v) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return imports, err |
||||||
|
} |
Loading…
Reference in new issue