mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
5.5 KiB
246 lines
5.5 KiB
// 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 |
|
}
|
|
|