Created
December 19, 2013 21:30
-
-
Save askn/8046560 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"fmt" | |
"os" | |
"os/exec" | |
"path/filepath" | |
"regexp" | |
"strings" | |
) | |
var ( | |
SRCDIR string | |
) | |
func init() { | |
// default SRCDIR | |
if os.Getenv("SRCDIR") == "" { | |
SRCDIR = filepath.Join(os.Getenv("HOME"), "src") | |
} else { | |
SRCDIR = os.Getenv("SRCDIR") | |
} | |
} | |
type vcsPath struct { | |
prefix string // prefix this description applies to | |
re string // pattern for import path | |
repo string // repository to use (expand with match of re) | |
vcs string // version control system to use (expand with match of re) | |
check func(match map[string]string) error // additional checks | |
ping bool // ping for scheme to use to download repo | |
} | |
var github = &vcsPath{ | |
prefix: "github.com/", | |
re: `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`, | |
vcs: "git", | |
repo: "https://{root}", | |
check: noVCSSuffix, | |
} | |
type repoRoot struct { | |
vcs *vcsCmd | |
// repo is the repository URL, including scheme | |
repo string | |
// root is the import path corresponding to the root of the | |
// repository | |
root string | |
} | |
func git(url string, scheme string) (*repoRoot, error) { | |
// içeriyor mu | |
if strings.Contains(url, "://") { | |
// invalid import path | |
return nil, fmt.Errorf("hatalı url") | |
} | |
if !strings.HasPrefix(url, github.prefix) { | |
return nil, fmt.Errorf("Bilinmeyen site") | |
} | |
re := regexp.MustCompile(github.re) | |
m := re.FindStringSubmatch(url) | |
if m == nil { | |
return nil, fmt.Errorf("hatalı url") | |
} | |
match := map[string]string{ | |
"prefix": github.prefix, | |
"import": url, | |
} | |
for i, name := range re.SubexpNames() { | |
if name != "" && match[name] == "" { | |
match[name] = m[i] | |
} | |
} | |
if github.vcs != "" { | |
match["vcs"] = expand(match, github.vcs) | |
} | |
if github.repo != "" { | |
match["repo"] = expand(match, github.repo) | |
} | |
if github.check != nil { | |
if err := github.check(match); err != nil { | |
return nil, err | |
} | |
} | |
if match["vcs"] != vcsGit.cmd { | |
return nil, fmt.Errorf("vcs hatası") | |
} | |
// fmt.Printf("%s\n", github.ping) | |
if github.ping { | |
fmt.Printf("%s\n", "askn") | |
if scheme != "" { | |
match["repo"] = scheme + "://" + match["repo"] | |
} else { | |
for _, scheme := range vcsGit.scheme { | |
if vcsGit.ping(scheme, match["repo"]) == nil { | |
match["repo"] = scheme + "://" + match["repo"] | |
break | |
} | |
} | |
} | |
} | |
rr := &repoRoot{ | |
vcs: vcsGit, | |
repo: match["repo"], | |
root: match["root"], | |
} | |
return rr, nil | |
} | |
// run1 is the generalized implementation of run and runOutput. | |
func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { | |
m := make(map[string]string) | |
for i := 0; i < len(keyval); i += 2 { | |
m[keyval[i]] = keyval[i+1] | |
} | |
args := strings.Fields(cmdline) | |
for i, arg := range args { | |
args[i] = expand(m, arg) | |
} | |
cmd := exec.Command(v.cmd, args...) | |
cmd.Dir = dir | |
var buf bytes.Buffer | |
cmd.Stdout = &buf | |
cmd.Stderr = &buf | |
err := cmd.Run() | |
out := buf.Bytes() | |
if err != nil { | |
return nil, err | |
} | |
return out, nil | |
} | |
// ping pings to determine scheme to use. | |
func (v *vcsCmd) ping(scheme, repo string) error { | |
return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo) | |
} | |
func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error { | |
_, err := v.run1(dir, cmd, keyval, false) | |
return err | |
} | |
func expand(match map[string]string, s string) string { | |
for k, v := range match { | |
s = strings.Replace(s, "{"+k+"}", v, -1) | |
} | |
return s | |
} | |
type vcsCmd struct { | |
name string | |
cmd string // name of binary to invoke command | |
createCmd string // command to download a fresh copy of a repository | |
downloadCmd string // command to download updates into an existing repository | |
scheme []string | |
pingCmd string | |
} | |
var vcsGit = &vcsCmd{ | |
name: "Git", | |
cmd: "git", | |
createCmd: "clone {repo} {dir}", | |
downloadCmd: "fetch", | |
scheme: []string{"git", "https", "http", "git+ssh"}, | |
pingCmd: "ls-remote {scheme}://{repo}", | |
} | |
func noVCSSuffix(match map[string]string) error { | |
repo := match["repo"] | |
if strings.HasSuffix(repo, "."+vcsGit.cmd) { | |
return fmt.Errorf("invalid version control suffix in %s path", match["prefix"]) | |
} | |
return nil | |
} | |
func x(url string) error { | |
rr, e := git(url, "") | |
if e != nil { | |
return e | |
} | |
// fmt.Println(rr) | |
vcs, repo, rootPath := rr.vcs, rr.repo, rr.root | |
root := filepath.Join(SRCDIR, rootPath) | |
meta := filepath.Join(root, "."+vcs.cmd) | |
st, e := os.Stat(meta) | |
if e != nil { | |
if _, err := os.Stat(root); err == nil { | |
return fmt.Errorf("checkout %s", st) | |
} | |
parent, _ := filepath.Split(root) | |
if err := os.MkdirAll(parent, 0777); err != nil { | |
return err | |
} | |
if err := vcs.create(root, repo); err != nil { | |
return err | |
} | |
} else { | |
// Metadata directory does exist; download incremental updates. | |
if err := vcs.download(root); err != nil { | |
return err | |
} | |
} | |
return nil | |
} | |
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error { | |
_, err := v.run1(dir, cmd, keyval, true) | |
return err | |
} | |
// create creates a new copy of repo in dir. | |
// The parent of dir must exist; dir must not. | |
func (v *vcsCmd) create(dir, repo string) error { | |
return v.run(".", v.createCmd, "dir", dir, "repo", repo) | |
} | |
// download downloads any new changes for the repo in dir. | |
func (v *vcsCmd) download(dir string) error { | |
return v.run(dir, v.downloadCmd) | |
} | |
func main() { | |
// fmt.Println(len(os.Args)) | |
if len(os.Args) == 2 { | |
// e := x("github.com/askn/go_examples") | |
e := x(os.Args[1]) | |
if e != nil { | |
fmt.Println(e) | |
} | |
} else { | |
fmt.Println("usage: program_name github.com/foo/bar") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment