Browse Source

Before merge

pull/103/head
Meaglith Ma 11 years ago
parent
commit
3b801b615a
  1. 1
      .gitignore
  2. 43
      .gopmfile
  3. 10
      README.md
  4. 191
      cmd/bin.go
  5. 86
      cmd/build.go
  6. 88
      cmd/cmd.go
  7. 31
      cmd/cmd_test.go
  8. 60
      cmd/config.go
  9. 91
      cmd/gen.go
  10. 402
      cmd/get.go
  11. 292
      cmd/gopath.go
  12. 13
      cmd/helper.go
  13. 81
      cmd/helper_windows.go
  14. 112
      cmd/install.go
  15. 58
      cmd/run.go
  16. 112
      cmd/search.go
  17. 552
      cmd/serve.go
  18. 213
      cmd/service.go
  19. 47
      cmd/test.go
  20. 215
      cmd/update.go
  21. 202
      doc/bitbucket.go
  22. 147
      doc/conf.go
  23. 25
      doc/error.go
  24. 191
      doc/github.go
  25. 286
      doc/google.go
  26. 52
      doc/http.go
  27. 134
      doc/launchpad.go
  28. 128
      doc/oschina.go
  29. 125
      doc/struct.go
  30. 759
      doc/utils.go
  31. 71
      doc/utils_test.go
  32. 307
      doc/vcs.go
  33. 239
      doc/walker.go
  34. 67
      gopm.go
  35. 121
      log/log.go
  36. 80
      log/logP.go
  37. 48
      log/log_windows.go

1
.gitignore vendored

@ -33,3 +33,4 @@ _testmain.go
*.exe~
gogs
__pycache__
*.pem

43
.gopmfile

@ -1,28 +1,25 @@
[target]
path=github.com/gogits/gogs
path = github.com/gogits/gogs
[deps]
github.com/codegangsta/cli=
github.com/go-martini/martini=
github.com/Unknwon/com=
github.com/Unknwon/cae=
github.com/Unknwon/goconfig=
github.com/dchest/scrypt=
github.com/nfnt/resize=
github.com/lunny/xorm=
github.com/go-sql-driver/mysql=
github.com/lib/pq=
github.com/gogits/logs=
github.com/gogits/binding=
github.com/gogits/git=
github.com/gogits/gfm=
github.com/gogits/cache=
github.com/gogits/session=
github.com/gogits/webdav=
github.com/martini-contrib/oauth2=
github.com/martini-contrib/sessions=
code.google.com/p/goauth2=
github.com/codegangsta/cli =
github.com/go-martini/martini =
github.com/Unknwon/com =
github.com/Unknwon/cae =
github.com/Unknwon/goconfig =
github.com/nfnt/resize =
github.com/lunny/xorm =
github.com/go-sql-driver/mysql =
github.com/lib/pq =
github.com/qiniu/log =
code.google.com/p/goauth2 =
github.com/gogits/logs =
github.com/gogits/binding =
github.com/gogits/git =
github.com/gogits/gfm =
github.com/gogits/cache =
github.com/gogits/session =
github.com/gogits/webdav =
[res]
include=templates|public|conf
include = templates|public|conf

10
README.md

@ -5,9 +5,9 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language
![Demo](http://gowalker.org/public/gogs_demo.gif)
##### Current version: 0.2.0 Alpha
##### Current version: 0.2.3 Alpha
#### Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in March 29, 2014 and will reset multiple times after. Please do NOT put your important data on the site.
#### Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in April 6, 2014 and will reset multiple times after. Please do NOT put your important data on the site.
#### Other language version
@ -29,9 +29,9 @@ More importantly, Gogs only needs one binary to setup your own project hosting o
## Features
- Activity timeline
- SSH/HTTPS(Clone only) protocol support.
- SSH/HTTP(S) protocol support.
- Register/delete/rename account.
- Create/delete/watch/rename public repository.
- Create/delete/watch/rename/transfer public repository.
- Repository viewer.
- Issue tracker.
- Gravatar and cache support.
@ -63,4 +63,4 @@ This project was launched by [Unknown](https://github.com/Unknwon) and [lunny](h
## License
Gogs is under the MIT License. See the [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) file for the full license text.
Gogs is under the MIT License. See the [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) file for the full license text.

191
cmd/bin.go

@ -0,0 +1,191 @@
// Copyright 2013-2014 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 (
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var CmdBin = cli.Command{
Name: "bin",
Usage: "download and link dependencies and build executable binary",
Description: `Command bin downloads and links dependencies according to gopmfile,
and build executable binary to work directory
gopm bin <import path>@[<tag|commit|branch>:<value>]
gopm bin <package name>@[<tag|commit|branch>:<value>]
Can only specify one each time, and only works for projects that
contain main package`,
Action: runBin,
Flags: []cli.Flag{
cli.BoolFlag{"dir, d", "build binary to given directory(second argument)"},
cli.BoolFlag{"update, u", "update pakcage(s) and dependencies if any"},
cli.BoolFlag{"verbose, v", "show process details"},
},
}
func runBin(ctx *cli.Context) {
setup(ctx)
if len(ctx.Args()) == 0 {
log.Error("bin", "Cannot start command:")
log.Fatal("", "\tNo package specified")
}
installRepoPath = doc.HomeDir + "/repos"
log.Log("Local repository path: %s", installRepoPath)
// Check arguments.
num := 1
if ctx.Bool("dir") {
num = 2
}
if len(ctx.Args()) != num {
log.Error("bin", "Cannot start command:")
log.Fatal("", "\tMissing indicated path to build binary")
}
// Check if given directory exists.
if ctx.Bool("dir") && !com.IsDir(ctx.Args()[1]) {
log.Error("bin", "Cannot start command:")
log.Fatal("", "\tIndicated path does not exist or is not a directory")
}
// Parse package version.
info := ctx.Args()[0]
pkgPath := info
node := doc.NewNode(pkgPath, pkgPath, doc.BRANCH, "", true)
var err error
if i := strings.Index(info, "@"); i > -1 {
pkgPath = info[:i]
node.ImportPath = pkgPath
node.DownloadURL = pkgPath
node.Type, node.Value = validPath(info[i+1:])
}
// Check package name.
if !strings.Contains(pkgPath, "/") {
pkgPath = doc.GetPkgFullPath(pkgPath)
}
// Get code.
downloadPackages(ctx, []*doc.Node{node})
// Check if previous steps were successful.
repoPath := installRepoPath + "/" + pkgPath + versionSuffix(node.Value)
if !com.IsDir(repoPath) {
log.Error("bin", "Cannot continue command:")
log.Fatal("", "\tPrevious steps weren't successful")
}
wd, err := os.Getwd()
if err != nil {
log.Error("bin", "Cannot get work directory:")
log.Fatal("", "\t"+err.Error())
}
// Change to repository path.
log.Log("Changing work directory to %s", repoPath)
if err = os.Chdir(repoPath); err != nil {
log.Error("bin", "Fail to change work directory:")
log.Fatal("", "\t"+err.Error())
}
// Build application.
buildBinary(ctx)
defer func() {
// Clean files.
os.RemoveAll(path.Join(repoPath, doc.VENDOR))
}()
includes := make([]string, 0, 3)
// Check if previous steps were successful.
if com.IsFile(doc.GOPM_FILE_NAME) {
log.Trace("Loading gopmfile...")
gf := doc.NewGopmfile(".")
var err error
pkgName, err = gf.GetValue("target", "path")
if err == nil {
log.Log("Target name: %s", pkgName)
}
includes = strings.Split(gf.MustValue("res", "include"), "|")
}
if len(pkgName) == 0 {
_, pkgName = filepath.Split(pkgPath)
}
// Because build command moved binary to root path.
binName := path.Base(pkgName)
if runtime.GOOS == "windows" {
binName += ".exe"
}
if !com.IsFile(binName) {
log.Error("bin", "Binary does not exist:")
log.Error("", "\t"+binName)
log.Fatal("", "\tPrevious steps weren't successful or the project does not contain main package")
}
// Move binary to given directory.
movePath := wd
if ctx.Bool("dir") {
movePath = ctx.Args()[1]
}
if com.IsExist(movePath + "/" + binName) {
if err = os.Remove(movePath + "/" + binName); err != nil {
log.Warn("Cannot remove binary in work directory:")
log.Warn("\t %s", err)
}
}
if err = os.Rename(binName, movePath+"/"+binName); err != nil {
log.Error("bin", "Fail to move binary:")
log.Fatal("", "\t"+err.Error())
}
os.Chmod(movePath+"/"+binName, os.ModePerm)
if len(includes) > 0 {
log.Log("Copying resources to %s", movePath)
for _, include := range includes {
if com.IsDir(include) {
if err = com.CopyDir(include, filepath.Join(movePath, include)); err != nil {
log.Error("bin", "Fail to copy following resource:")
log.Error("", "\t"+include)
}
}
}
}
log.Log("Changing work directory back to %s", wd)
os.Chdir(wd)
log.Success("SUCC", "bin", "Command executed successfully!")
fmt.Println("Binary has been built into: " + movePath)
}

86
cmd/build.go

@ -0,0 +1,86 @@
// 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 (
"os"
"path"
"path/filepath"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var CmdBuild = cli.Command{
Name: "build",
Usage: "link dependencies and go build",
Description: `Command build links dependencies according to gopmfile,
and execute 'go build'
gopm build <go build commands>`,
Action: runBuild,
Flags: []cli.Flag{
cli.BoolFlag{"update, u", "update pakcage(s) and dependencies if any"},
cli.BoolFlag{"verbose, v", "show process details"},
},
}
func runBuild(ctx *cli.Context) {
setup(ctx)
// Get GOPATH.
installGopath = com.GetGOPATHs()[0]
if com.IsDir(installGopath) {
isHasGopath = true
log.Log("Indicated GOPATH: %s", installGopath)
installGopath += "/src"
}
buildBinary(ctx, ctx.Args()...)
log.Success("SUCC", "build", "Command executed successfully!")
}
func buildBinary(ctx *cli.Context, args ...string) {
genNewGoPath(ctx, false)
log.Trace("Building...")
cmdArgs := []string{"go", "build"}
cmdArgs = append(cmdArgs, args...)
err := execCmd(newGoPath, newCurPath, cmdArgs...)
if err != nil {
log.Error("build", "fail to build program:")
log.Fatal("", "\t"+err.Error())
}
if isWindowsXP {
fName := path.Base(pkgName)
binName := fName + ".exe"
os.Remove(binName)
exePath := filepath.Join(curPath, doc.VENDOR, "src", pkgName, binName)
if com.IsFile(exePath) {
err = os.Rename(exePath, filepath.Join(curPath, binName))
if err != nil {
log.Error("build", "fail to move binary:")
log.Fatal("", "\t"+err.Error())
}
} else {
log.Warn("No binary generated")
}
}
}

88
cmd/cmd.go

@ -0,0 +1,88 @@
// Copyright 2013-2014 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 (
"os"
"strings"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var (
workDir string // The path of gopm was executed.
)
// setup initialize common environment for commands.
func setup(ctx *cli.Context) {
var err error
workDir, err = os.Getwd()
if err != nil {
log.Error("setup", "Fail to get work directory:")
log.Fatal("", "\t"+err.Error())
}
log.PureMode = ctx.GlobalBool("noterm")
log.Verbose = ctx.Bool("verbose")
}
// parseTarget returns "." when target is empty string.
func parseTarget(target string) string {
if len(target) == 0 {
target = "."
}
return target
}
// validPath checks if the information of the package is valid.
func validPath(info string) (string, string) {
infos := strings.Split(info, ":")
l := len(infos)
switch {
case l == 1:
// For local imports.
if com.IsFile(infos[0]) {
return doc.LOCAL, infos[0]
}
case l == 2:
switch infos[1] {
case doc.TRUNK, doc.MASTER, doc.DEFAULT:
infos[1] = ""
}
return infos[0], infos[1]
}
log.Error("", "Cannot parse dependency version:")
log.Error("", "\t"+info)
log.Help("Try 'gopm help get' to get more information")
return "", ""
}
func versionSuffix(value string) string {
if len(value) > 0 {
return "." + value
}
return ""
}
func isSubpackage(rootPath, targetPath string) bool {
return strings.HasSuffix(strings.Replace(workDir, "\\", "/", -1), rootPath) ||
strings.HasPrefix(rootPath, targetPath)
}

31
cmd/cmd_test.go

@ -0,0 +1,31 @@
// Copyright 2013-2014 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 (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_parseTarget(t *testing.T) {
Convey("Target is empty", t, func() {
So(parseTarget(""), ShouldEqual, ".")
})
Convey("Target is not empty", t, func() {
So(parseTarget("github.com/gpmgo/gopm"), ShouldEqual, "github.com/gpmgo/gopm")
})
}

60
cmd/config.go

@ -0,0 +1,60 @@
// Copyright 2013-2014 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 (
"path"
"github.com/Unknwon/goconfig"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var CmdConfig = cli.Command{
Name: "config",
Usage: "configurate gopm global settings",
Description: `Command config configurates gopm global settings
gopm config github [client_id] [client_secret]
`,
Action: runConfig,
Flags: []cli.Flag{
cli.BoolFlag{"verbose, v", "show process details"},
},
}
func runConfig(ctx *cli.Context) {
setup(ctx)
if len(ctx.Args()) == 0 {
log.Error("config", "Cannot start command:")
log.Fatal("", "\tNo section specified")
}
switch ctx.Args()[0] {
case "github":
if len(ctx.Args()) < 3 {
log.Error("config", "Cannot config section 'github'")
log.Fatal("", "\tNot enough arguments for client_id and client_secret")
}
doc.Cfg.SetValue("github", "client_id", ctx.Args()[1])
doc.Cfg.SetValue("github", "client_secret", ctx.Args()[2])
goconfig.SaveConfigFile(doc.Cfg, path.Join(doc.HomeDir, doc.GOPM_CONFIG_FILE))
}
log.Success("SUCC", "config", "Command executed successfully!")
}

91
cmd/gen.go

@ -0,0 +1,91 @@
// Copyright 2013-2014 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 (
"os"
"strings"
"github.com/Unknwon/com"
"github.com/Unknwon/goconfig"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var CmdGen = cli.Command{
Name: "gen",
Usage: "generate a gopmfile according current Go project",
Description: `Command gen gets dependencies and generates a gopmfile
gopm gen
Make sure you run this command in the root path of a go project.`,
Action: runGen,
Flags: []cli.Flag{
cli.BoolFlag{"example, e", "check dependencies for example(s)"},
cli.BoolFlag{"verbose, v", "show process details"},
},
}
var commonRes = []string{"views", "templates", "static", "public", "conf"}
func runGen(ctx *cli.Context) {
setup(ctx)
if !com.IsExist(".gopmfile") {
os.Create(".gopmfile")
}
gf, err := goconfig.LoadConfigFile(".gopmfile")
if err != nil {
log.Error("gen", "Cannot load gopmfile:")
log.Fatal("", "\t"+err.Error())
}
targetPath := parseTarget(gf.MustValue("target", "path"))
// Get and set dependencies.
imports := doc.GetAllImports([]string{workDir}, targetPath, ctx.Bool("example"), false)
for _, p := range imports {
p = doc.GetProjectPath(p)
// Skip subpackage(s) of current project.
if isSubpackage(p, targetPath) {
continue
}
// Check if user specified the version.
if value := gf.MustValue("deps", p); len(value) == 0 {
gf.SetValue("deps", p, "")
}
}
// Get and set resources.
res := make([]string, 0, len(commonRes))
for _, cr := range commonRes {
if com.IsExist(cr) {
res = append(res, cr)
}
}
gf.SetValue("res", "include", strings.Join(res, "|"))
err = goconfig.SaveConfigFile(gf, ".gopmfile")
if err != nil {
log.Error("gen", "Fail to save gopmfile:")
log.Fatal("", "\t"+err.Error())
}
log.Success("SUCC", "gen", "Generate gopmfile successfully!")
}

402
cmd/get.go

@ -0,0 +1,402 @@
// Copyright 2013-2014 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 (
"fmt"
"os"
"path"
"strings"
"github.com/Unknwon/com"
"github.com/Unknwon/goconfig"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var (
installRepoPath string // The path of gopm local repository.
installGopath string // The first path in the GOPATH.
isHasGopath bool // Indicates whether system has GOPATH.
downloadCache map[string]bool // Saves packages that have been downloaded.
downloadCount int
failConut int
)
var CmdGet = cli.Command{
Name: "get",
Usage: "fetch remote package(s) and dependencies to local repository",
Description: `Command get fetches a package, and any pakcage that it depents on.
If the package has a gopmfile, the fetch process will be driven by that.
gopm get
gopm get <import path>@[<tag|commit|branch>:<value>]
gopm get <package name>@[<tag|commit|branch>:<value>]
Can specify one or more: gopm get beego@tag:v0.9.0 github.com/beego/bee
If no version specified and package exists in GOPATH,
it will be skipped unless user enabled '--remote, -r' option
then all the packages go into gopm local repository.`,
Action: runGet,
Flags: []cli.Flag{
cli.BoolFlag{"gopath, g", "download all pakcages to GOPATH"},
cli.BoolFlag{"update, u", "update pakcage(s) and dependencies if any"},
cli.BoolFlag{"example, e", "download dependencies for example folder"},
cli.BoolFlag{"remote, r", "download all pakcages to gopm local repository"},
cli.BoolFlag{"verbose, v", "show process details"},
},
}
func init() {
downloadCache = make(map[string]bool)
}
func runGet(ctx *cli.Context) {
setup(ctx)
// Check conflicts.
if ctx.Bool("gopath") && ctx.Bool("remote") {
log.Error("get", "Command options have conflicts")
log.Error("", "Following options are not supposed to use at same time:")
log.Error("", "\t'--gopath, -g' '--remote, -r'")
log.Help("Try 'gopm help get' to get more information")
}
if !ctx.Bool("remote") {
// Get GOPATH.
installGopath = com.GetGOPATHs()[0]
if com.IsDir(installGopath) {
isHasGopath = true
log.Log("Indicated GOPATH: %s", installGopath)
installGopath += "/src"
} else {
if ctx.Bool("gopath") {
log.Error("get", "Invalid GOPATH path")
log.Error("", "GOPATH does not exist or is not a directory:")
log.Error("", "\t"+installGopath)
log.Help("Try 'go help gopath' to get more information")
} else {
// It's OK that no GOPATH setting
// when user does not specify to use.
log.Warn("No GOPATH setting available")
}
}
}
// The gopm local repository.
installRepoPath = path.Join(doc.HomeDir, "repos")
log.Log("Local repository path: %s", installRepoPath)
// Check number of arguments to decide which function to call.
switch len(ctx.Args()) {
case 0:
getByGopmfile(ctx)
case 1:
getByPath(ctx)
default:
log.Error("get", "too many arguments")
log.Help("Try 'gopm help get' to get more information")
}
}
func getByGopmfile(ctx *cli.Context) {
// Check if gopmfile exists and generate one if not.
if !com.IsFile(".gopmfile") {
runGen(ctx)
}
gf := doc.NewGopmfile(".")
targetPath := parseTarget(gf.MustValue("target", "path"))
// Get dependencies.
imports := doc.GetAllImports([]string{workDir}, targetPath, ctx.Bool("example"), false)
nodes := make([]*doc.Node, 0, len(imports))
for _, p := range imports {
// TODO: DOING TEST CASES!!!
p = doc.GetProjectPath(p)
// Skip subpackage(s) of current project.
if isSubpackage(p, targetPath) {
continue
}
node := doc.NewNode(p, p, doc.BRANCH, "", true)
// Check if user specified the version.
if v, err := gf.GetValue("deps", p); err == nil && len(v) > 0 {
node.Type, node.Value = validPath(v)
}
nodes = append(nodes, node)
}
downloadPackages(ctx, nodes)
doc.SaveLocalNodes()
log.Log("%d package(s) downloaded, %d failed", downloadCount, failConut)
}
func getByPath(ctx *cli.Context) {
nodes := make([]*doc.Node, 0, len(ctx.Args()))
for _, info := range ctx.Args() {
pkgPath := info
node := doc.NewNode(pkgPath, pkgPath, doc.BRANCH, "", true)
if i := strings.Index(info, "@"); i > -1 {
pkgPath = info[:i]
tp, ver := validPath(info[i+1:])
node = doc.NewNode(pkgPath, pkgPath, tp, ver, true)
}
// Check package name.
if !strings.Contains(pkgPath, "/") {
pkgPath = doc.GetPkgFullPath(pkgPath)
}
nodes = append(nodes, node)
}
downloadPackages(ctx, nodes)
doc.SaveLocalNodes()
log.Log("%d package(s) downloaded, %d failed", downloadCount, failConut)
}
func copyToGopath(srcPath, destPath string) {
importPath := strings.TrimPrefix(destPath, installGopath+"/")
if len(getVcsName(destPath)) > 0 {
log.Warn("Package in GOPATH has version control: %s", importPath)
return
}
os.RemoveAll(destPath)
err := com.CopyDir(srcPath, destPath)
if err != nil {
log.Error("download", "Fail to copy to GOPATH:")
log.Fatal("", "\t"+err.Error())
}
log.Log("Package copied to GOPATH: %s", importPath)
}
// downloadPackages downloads packages with certain commit,
// if the commit is empty string, then it downloads all dependencies,
// otherwise, it only downloada package with specific commit only.
func downloadPackages(ctx *cli.Context, nodes []*doc.Node) {
// Check all packages, they may be raw packages path.
for _, n := range nodes {
// Check if local reference
if n.Type == doc.LOCAL {
continue
}
// Check if it is a valid remote path or C.
if n.ImportPath == "C" {
continue
} else if !doc.IsValidRemotePath(n.ImportPath) {
// Invalid import path.
log.Error("download", "Skipped invalid package: "+fmt.Sprintf("%s@%s:%s",
n.ImportPath, n.Type, doc.CheckNodeValue(n.Value)))
failConut++
continue
}
// Valid import path.
gopathDir := path.Join(installGopath, n.ImportPath)
n.RootPath = doc.GetProjectPath(n.ImportPath)
installPath := path.Join(installRepoPath, n.RootPath) + versionSuffix(n.Value)
if isSubpackage(n.RootPath, ".") {
continue
}
// Indicates whether need to download package again.
if n.IsFixed() && com.IsExist(installPath) {
n.IsGetDepsOnly = true
}
if !ctx.Bool("update") {
// Check if package has been downloaded.
if (len(n.Value) == 0 && !ctx.Bool("remote") && com.IsExist(gopathDir)) ||
com.IsExist(installPath) {
log.Trace("Skipped installed package: %s@%s:%s",
n.ImportPath, n.Type, doc.CheckNodeValue(n.Value))
// Only copy when no version control.
if ctx.Bool("gopath") && (com.IsExist(installPath) ||
len(getVcsName(gopathDir)) == 0) {
copyToGopath(installPath, gopathDir)
}
continue
} else {
doc.LocalNodes.SetValue(n.RootPath, "value", "")
}
}
if downloadCache[n.RootPath] {
log.Trace("Skipped downloaded package: %s@%s:%s",
n.ImportPath, n.Type, doc.CheckNodeValue(n.Value))
continue
}
// Download package.
nod, imports := downloadPackage(ctx, n)
if len(imports) > 0 {
var gf *goconfig.ConfigFile
// Check if has gopmfile.
if com.IsFile(installPath + "/" + doc.GOPM_FILE_NAME) {
log.Log("Found gopmfile: %s@%s:%s",
n.ImportPath, n.Type, doc.CheckNodeValue(n.Value))
gf = doc.NewGopmfile(installPath)
}
// Need to download dependencies.
// Generate temporary nodes.
nodes := make([]*doc.Node, len(imports))
for i := range nodes {
nodes[i] = doc.NewNode(imports[i], imports[i], doc.BRANCH, "", true)
if gf == nil {
continue
}
// Check if user specified the version.
if v, err := gf.GetValue("deps", imports[i]); err == nil && len(v) > 0 {
nodes[i].Type, nodes[i].Value = validPath(v)
}
}
downloadPackages(ctx, nodes)
}
// Only save package information with specific commit.
if nod == nil {
continue
}
// Save record in local nodes.
log.Success("SUCC", "GET", fmt.Sprintf("%s@%s:%s",
n.ImportPath, n.Type, doc.CheckNodeValue(n.Value)))
downloadCount++
// Only save non-commit node.
if len(nod.Value) == 0 && len(nod.Revision) > 0 {
doc.LocalNodes.SetValue(nod.RootPath, "value", nod.Revision)
}
if ctx.Bool("gopath") && com.IsExist(installPath) && !ctx.Bool("update") &&
len(getVcsName(path.Join(installGopath, nod.RootPath))) == 0 {
copyToGopath(installPath, gopathDir)
}
}
}
// downloadPackage downloads package either use version control tools or not.
func downloadPackage(ctx *cli.Context, nod *doc.Node) (*doc.Node, []string) {
log.Message("Downloading", fmt.Sprintf("package: %s@%s:%s",
nod.ImportPath, nod.Type, doc.CheckNodeValue(nod.Value)))
// Mark as donwloaded.
downloadCache[nod.RootPath] = true
// Check if only need to use VCS tools.
var imports []string
var err error
gopathDir := path.Join(installGopath, nod.RootPath)
vcs := getVcsName(gopathDir)
if ctx.Bool("update") && ctx.Bool("gopath") && len(vcs) > 0 {
err = updateByVcs(vcs, gopathDir)
imports = doc.GetAllImports([]string{gopathDir}, nod.RootPath, false, false)
} else {
// If package has revision and exist, then just check dependencies.
if nod.IsGetDepsOnly {
return nod, doc.GetAllImports([]string{path.Join(installRepoPath, nod.RootPath) + versionSuffix(nod.Value)},
nod.RootPath, ctx.Bool("example"), false)
}
nod.Revision = doc.LocalNodes.MustValue(nod.RootPath, "value")
imports, err = doc.PureDownload(nod, installRepoPath, ctx) //CmdGet.Flags)
}
if err != nil {
log.Error("get", "Fail to download pakage: "+nod.ImportPath)
log.Error("", "\t"+err.Error())
failConut++
os.RemoveAll(installRepoPath + "/" + nod.RootPath)
return nil, nil
}
return nod, imports
}
func getVcsName(dirPath string) string {
switch {
case com.IsExist(path.Join(dirPath, ".git")):
return "git"
case com.IsExist(path.Join(dirPath, ".hg")):
return "hg"
case com.IsExist(path.Join(dirPath, ".svn")):
return "svn"
}
return ""
}
func updateByVcs(vcs, dirPath string) error {
err := os.Chdir(dirPath)
if err != nil {
log.Error("Update by VCS", "Fail to change work directory:")
log.Fatal("", "\t"+err.Error())
}
defer os.Chdir(workDir)
switch vcs {
case "git":
branch, _, err := com.ExecCmd("git", "rev-parse", "--abbrev-ref", "HEAD")
if err != nil {
log.Error("", "Error occurs when 'git rev-parse --abbrev-ref HEAD'")
log.Error("", "\t"+err.Error())
}
_, _, err = com.ExecCmd("git", "pull", "origin", branch)
if err != nil {
log.Error("", "Error occurs when 'git pull origin "+branch+"'")
log.Error("", "\t"+err.Error())
}
case "hg":
_, stderr, err := com.ExecCmd("hg", "pull")
if err != nil {
log.Error("", "Error occurs when 'hg pull'")
log.Error("", "\t"+err.Error())
}
if len(stderr) > 0 {
log.Error("", "Error: "+stderr)
}
_, stderr, err = com.ExecCmd("hg", "up")
if err != nil {
log.Error("", "Error occurs when 'hg up'")
log.Error("", "\t"+err.Error())
}
if len(stderr) > 0 {
log.Error("", "Error: "+stderr)
}
case "svn":
_, stderr, err := com.ExecCmd("svn", "update")
if err != nil {
log.Error("", "Error occurs when 'svn update'")
log.Error("", "\t"+err.Error())
}
if len(stderr) > 0 {
log.Error("", "Error: "+stderr)
}
}
return nil
}

292
cmd/gopath.go

@ -0,0 +1,292 @@
// Copyright 2013-2014 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"
"go/build"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var isWindowsXP = false
func getGopmPkgs(dirPath string, isTest bool) (pkgs map[string]*doc.Pkg, err error) {
absPath, err := filepath.Abs(dirPath)
if err != nil {
log.Error("", "Fail to get absolute path of work directory:")
log.Fatal("", "\t"+err.Error())
}
var builds map[string]string
if com.IsFile(absPath + "/" + doc.GOPM_FILE_NAME) {
gf := doc.NewGopmfile(absPath)
if builds, err = gf.GetSection("deps"); err != nil {
builds = nil
}
}
imports := doc.GetAllImports([]string{dirPath}, ".", false, false)
pkgs = make(map[string]*doc.Pkg)
for _, name := range imports {
if name == "C" {
continue
}
if !doc.IsGoRepoPath(name) {
if builds != nil {
if info, ok := builds[name]; ok {
// Check version. there should chek
// local first because d:\ contains :
if com.IsDir(info) {
pkgs[name] = &doc.Pkg{
ImportPath: name,
Type: doc.LOCAL,
Value: info,
}
continue
} else if i := strings.Index(info, ":"); i > -1 {
pkgs[name] = &doc.Pkg{
ImportPath: name,
Type: info[:i],
Value: info[i+1:],
}
continue
}
}
}
pkgs[name] = doc.NewDefaultPkg(name)
}
}
return pkgs, nil
}
func pkgInCache(name string, cachePkgs map[string]*doc.Pkg) bool {
_, ok := cachePkgs[name]
return ok
}
func autoLink(oldPath, newPath string) error {
newPPath, _ := filepath.Split(newPath)
os.MkdirAll(newPPath, os.ModePerm)
return makeLink(oldPath, newPath)
}
func getChildPkgs(ctx *cli.Context, cpath string, ppkg *doc.Pkg, cachePkgs map[string]*doc.Pkg, isTest bool) error {
pkgs, err := getGopmPkgs(cpath, isTest)
if err != nil {
return errors.New("Fail to get gopmfile deps: " + err.Error())
}
for name, pkg := range pkgs {
pkg.RootPath = doc.GetProjectPath(pkg.ImportPath)
if !pkgInCache(pkg.RootPath, cachePkgs) {
var newPath string
if !build.IsLocalImport(name) && pkg.Type != doc.LOCAL {
suf := versionSuffix(pkg.Value)
pkgPath := strings.Replace(
pkg.ImportPath, pkg.RootPath, pkg.RootPath+suf, 1)
newPath = filepath.Join(installRepoPath, pkgPath)
if len(suf) == 0 && !ctx.Bool("remote") &&
com.IsDir(filepath.Join(installGopath, pkgPath)) {
newPath = filepath.Join(installGopath, pkgPath)
}
if pkgName != "" && strings.HasPrefix(pkg.ImportPath, pkgName) {
newPath = filepath.Join(curPath, strings.TrimPrefix(pkg.ImportPath, pkgName))
} else {
if !com.IsExist(newPath) || ctx.Bool("update") {
node := doc.NewNode(pkg.ImportPath, pkg.ImportPath,
pkg.Type, pkg.Value, true)
nodes := []*doc.Node{node}
downloadPackages(ctx, nodes)
// TODO: Should handler download failed
}
}
} else {
if pkg.Type == doc.LOCAL {
newPath, err = filepath.Abs(pkg.Value)
} else {
newPath, err = filepath.Abs(name)
}
if err != nil {
return err
}
}
cachePkgs[pkg.RootPath] = pkg
err = getChildPkgs(ctx, newPath, pkg, cachePkgs, false)
if err != nil {
return err
}
}
}
return nil
}
var pkgName string
var curPath string
var newCurPath string
var newGoPath string
var oldGoPath string
func execCmd(gopath, curPath string, args ...string) error {
cwd, err := os.Getwd()
if err != nil {
log.Error("", "Fail to get work directory:")
log.Fatal("", "\t"+err.Error())
}
log.Log("Changing work directory to %s", curPath)
err = os.Chdir(curPath)
if err != nil {
log.Error("", "Fail to change work directory:")
log.Fatal("", "\t"+err.Error())
}
defer func() {
log.Log("Changing work directory back to %s", cwd)
os.Chdir(cwd)
}()
err = os.Chdir(curPath)
if err != nil {
log.Error("", "Fail to change work directory:")
log.Fatal("", "\t"+err.Error())
}
oldGoPath = os.Getenv("GOPATH")
log.Log("Setting GOPATH to %s", gopath)
sep := ":"
if runtime.GOOS == "windows" {
sep = ";"
}
err = os.Setenv("GOPATH", gopath+sep+oldGoPath)
if err != nil {
log.Error("", "Fail to setting GOPATH:")
log.Fatal("", "\t"+err.Error())
}
defer func() {
log.Log("Setting GOPATH back to %s", oldGoPath)
os.Setenv("GOPATH", oldGoPath)
}()
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Log("===== application outputs start =====\n")
err = cmd.Run()
log.Log("====== application outputs end ======")
return err
}
func genNewGoPath(ctx *cli.Context, isTest bool) {
var err error
curPath, err = os.Getwd()
if err != nil {
log.Error("", "Fail to get work directory:")
log.Fatal("", "\t"+err.Error())
}
installRepoPath = doc.HomeDir + "/repos"
if com.IsFile(curPath + "/" + doc.GOPM_FILE_NAME) {
log.Trace("Loading gopmfile...")
gf := doc.NewGopmfile(curPath)
var err error
pkgName, err = gf.GetValue("target", "path")
if err == nil {
log.Log("Target name: %s", pkgName)
}
}
if len(pkgName) == 0 {
_, pkgName = filepath.Split(curPath)
}
cachePkgs := make(map[string]*doc.Pkg)
err = getChildPkgs(ctx, curPath, nil, cachePkgs, isTest)
if err != nil {
log.Error("", "Fail to get child pakcages:")
log.Fatal("", "\t"+err.Error())
}
newGoPath = filepath.Join(curPath, doc.VENDOR)
newGoPathSrc := filepath.Join(newGoPath, "src")
os.RemoveAll(newGoPathSrc)
os.MkdirAll(newGoPathSrc, os.ModePerm)
for name, pkg := range cachePkgs {
suf := versionSuffix(pkg.Value)
var oldPath string
if pkg.Type == doc.LOCAL {
oldPath, _ = filepath.Abs(pkg.Value)
} else {
oldPath = filepath.Join(installRepoPath, name) + suf
}
newPath := filepath.Join(newGoPathSrc, name)
paths := strings.Split(name, "/")
var isExistP, isCurChild bool
if name == pkgName {
continue
}
for i := 0; i < len(paths)-1; i++ {
pName := strings.Join(paths[:len(paths)-1-i], "/")
if _, ok := cachePkgs[pName]; ok {
isExistP = true
break
}
if pkgName == pName {
isCurChild = true
break
}
}
if isCurChild {
continue
}
if !isExistP && (len(pkg.Value) > 0 || ctx.Bool("remote") ||
!com.IsDir(filepath.Join(installGopath, pkg.ImportPath))) {
log.Log("Linking %s", name+suf)
err = autoLink(oldPath, newPath)
if err != nil {
log.Error("", "Fail to make link:")
log.Fatal("", "\t"+err.Error())
}
}
}
newCurPath = filepath.Join(newGoPathSrc, pkgName)
log.Log("Linking %s", pkgName)
err = autoLink(curPath, newCurPath)
if err != nil {
log.Error("", "Fail to make link:")
log.Fatal("", "\t"+err.Error())
}
}

13
cmd/helper.go

@ -0,0 +1,13 @@
//
// +build !windows
package cmd
import (
"os/exec"
)
func makeLink(srcPath, destPath string) error {
cmd := exec.Command("ln", "-s", srcPath, destPath)
return cmd.Run()
}

81
cmd/helper_windows.go

@ -0,0 +1,81 @@
package cmd
import (
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"unsafe"
"github.com/Unknwon/com"
"github.com/gpmgo/gopm/doc"
)
func makeLink(srcPath, destPath string) error {
// Check if Windows version is XP.
if getWindowsVersion() >= 6 {
cmd := exec.Command("cmd", "/c", "mklink", "/j", destPath, srcPath)
return cmd.Run()
}
// XP.
isWindowsXP = true
// if both are ntfs file system
if volumnType(srcPath) == "NTFS" && volumnType(destPath) == "NTFS" {
// if has junction command installed
file, err := exec.LookPath("junction")
if err == nil {
path, _ := filepath.Abs(file)
if com.IsFile(path) {
cmd := exec.Command("cmd", "/c", "junction", destPath, srcPath)
return cmd.Run()
}
}
}
os.RemoveAll(destPath)
return com.CopyDir(srcPath, destPath, func(filePath string) bool {
return strings.Contains(filePath, doc.VENDOR)
})
}
func volumnType(dir string) string {
pd := dir[:3]
dll := syscall.MustLoadDLL("kernel32.dll")
GetVolumeInformation := dll.MustFindProc("GetVolumeInformationW")
var volumeNameSize uint32 = 260
var nFileSystemNameSize, lpVolumeSerialNumber uint32
var lpFileSystemFlags, lpMaximumComponentLength uint32
var lpFileSystemNameBuffer, volumeName [260]byte
var ps *uint16 = syscall.StringToUTF16Ptr(pd)
_, _, _ = GetVolumeInformation.Call(uintptr(unsafe.Pointer(ps)),
uintptr(unsafe.Pointer(&volumeName)),
uintptr(volumeNameSize),
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
uintptr(unsafe.Pointer(&lpFileSystemFlags)),
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer)),
uintptr(unsafe.Pointer(&nFileSystemNameSize)), 0)
var bytes []byte
if lpFileSystemNameBuffer[6] == 0 {
bytes = []byte{lpFileSystemNameBuffer[0], lpFileSystemNameBuffer[2],
lpFileSystemNameBuffer[4]}
} else {
bytes = []byte{lpFileSystemNameBuffer[0], lpFileSystemNameBuffer[2],
lpFileSystemNameBuffer[4], lpFileSystemNameBuffer[6]}
}
return string(bytes)
}
func getWindowsVersion() int {
dll := syscall.MustLoadDLL("kernel32.dll")
p := dll.MustFindProc("GetVersion")
v, _, _ := p.Call()
return int(byte(v))
}

112
cmd/install.go

@ -0,0 +1,112 @@
// Copyright 2013-2014 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 (
"path/filepath"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var CmdInstall = cli.Command{
Name: "install",
Usage: "link dependencies and go install",
Description: `Command install links dependencies according to gopmfile,
and execute 'go install'
gopm install
gopm install <import path>
If no argument is supplied, then gopmfile must be present`,
Action: runInstall,
Flags: []cli.Flag{
cli.BoolFlag{"pkg, p", "only install non-main packages"},
cli.BoolFlag{"verbose, v", "show process details"},
},
}
func runInstall(ctx *cli.Context) {
setup(ctx)
var target string
switch len(ctx.Args()) {
case 0:
if !com.IsFile(".gopmfile") {
break
}
gf := doc.NewGopmfile(".")
target = gf.MustValue("target", "path")
case 1:
target = ctx.Args()[0]
default:
log.Fatal("install", "Too many arguments")
}
// Get GOPATH.
installGopath = com.GetGOPATHs()[0]
if com.IsDir(installGopath) {
isHasGopath = true
log.Log("Indicated GOPATH: %s", installGopath)
installGopath += "/src"
} else {
if ctx.Bool("gopath") {
log.Error("get", "Invalid GOPATH path")
log.Error("", "GOPATH does not exist or is not a directory:")
log.Error("", "\t"+installGopath)
log.Help("Try 'go help gopath' to get more information")
} else {
// It's OK that no GOPATH setting
// when user does not specify to use.
log.Warn("No GOPATH setting available")
}
}
genNewGoPath(ctx, false)
var installRepos []string
if ctx.Bool("pkg") {
curPath, _ := filepath.Abs(".")
installRepos = doc.GetAllImports([]string{curPath}, ".", ctx.Bool("example"), false)
} else {
if len(target) == 0 {
target = pkgName
}
installRepos = []string{target}
}
log.Trace("Installing...")
for _, repo := range installRepos {
cmdArgs := []string{"go", "install"}
if ctx.Bool("verbose") {
cmdArgs = append(cmdArgs, "-v")
}
cmdArgs = append(cmdArgs, repo)
err := execCmd(newGoPath, newCurPath, cmdArgs...)
if err != nil {
log.Error("install", "Fail to install program:")
log.Fatal("", "\t"+err.Error())
}
}
log.Success("SUCC", "install", "Command executed successfully!")
}

58
cmd/run.go

@ -0,0 +1,58 @@
// 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 (
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/log"
)
var CmdRun = cli.Command{
Name: "run",
Usage: "link dependencies and go run",
Description: `Command run links dependencies according to gopmfile,
and execute 'go run'
gopm run <go run commands>`,
Action: runRun,
}
func runRun(ctx *cli.Context) {
setup(ctx)
// Get GOPATH.
installGopath = com.GetGOPATHs()[0]
if com.IsDir(installGopath) {
isHasGopath = true
log.Log("Indicated GOPATH: %s", installGopath)
installGopath += "/src"
}
genNewGoPath(ctx, false)
log.Trace("Running...")
cmdArgs := []string{"go", "run"}
cmdArgs = append(cmdArgs, ctx.Args()...)
err := execCmd(newGoPath, newCurPath, cmdArgs...)
if err != nil {
log.Error("run", "Fail to run program:")
log.Fatal("", "\t"+err.Error())
}
log.Success("SUCC", "run", "Command executed successfully!")
}

112
cmd/search.go

@ -0,0 +1,112 @@
// 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 (
// "encoding/json"
// "fmt"
// "io/ioutil"
// "net/http"
// "github.com/Unknwon/com"
// )
// var CmdSearch = &Command{
// UsageLine: "search [keyword]",
// Short: "search for package",
// Long: `
// search packages
// The search flags are:
// -e
// search extactly, you should input an exactly package name as keyword
// `,
// }
// func init() {
// CmdSearch.Run = runSearch
// CmdSearch.Flags = map[string]bool{
// "-e": false,
// }
// }
// func printSearchPrompt(flag string) {
// switch flag {
// case "-e":
// com.ColorLog("[INFO] You enabled exactly search.\n")
// }
// }
// // search packages
// func runSearch(cmd *Command, args []string) {
// // Check length of arguments.
// if len(args) < 1 {
// com.ColorLog("[ERROR] Please input package's keyword.\n")
// return
// }
// var host, port string
// host = "localhost"
// port = "8991"
// if cmd.Flags["-e"] {
// search(host, port, args[0], true)
// } else {
// search(host, port, args[0], false)
// }
// }
// type searchRes struct {
// Pkg string
// Desc string
// }
// /*
// request local or remote search service to find packages according to keyword inputed
// */
// func search(host, port, keyword string, isExactly bool) {
// url := fmt.Sprintf("http://%v:%v/search?%v", host, port, keyword)
// if isExactly {
// url = fmt.Sprintf("http://%v:%v/searche?%v", host, port, keyword)
// }
// resp, err := http.Get(url)
// if err != nil {
// com.ColorLog(err.Error())
// return
// }
// defer resp.Body.Close()
// if resp.StatusCode == 200 {
// contents, err := ioutil.ReadAll(resp.Body)
// if err != nil {
// com.ColorLog(err.Error())
// return
// }
// pkgs := make([]searchRes, 0)
// err = json.Unmarshal(contents, &pkgs)
// if err != nil {
// com.ColorLog(err.Error())
// return
// }
// for i, pkg := range pkgs {
// fmt.Println(i+1, pkg.Pkg, "\t", pkg.Desc)
// }
// } else {
// com.ColorLog(resp.Status)
// }
// }

552
cmd/serve.go

@ -0,0 +1,552 @@
// 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"
// "github.com/Unknwon/com"
// "github.com/gpmgo/gopm/doc"
// "github.com/syndtr/goleveldb/leveldb"
// "github.com/syndtr/goleveldb/leveldb/opt"
// "io/ioutil"
// "net/http"
// "net/url"
// "os"
// "os/exec"
// "path/filepath"
// "strconv"
// "strings"
// "time"
// )
// 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":
// com.ColorLog("[INFO] You enabled start a service only localhost.\n")
// }
// }
// // Not implemented
// func autoPort() string {
// return "8991"
// }
// func exePath() (string, error) {
// file, err := exec.LookPath(os.Args[0])
// if err != nil {
// return "", err
// }
// return filepath.Abs(file)
// }
// // 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]
// }
// err := startService(listen, port)
// if err != nil {
// com.ColorLog("[ERRO] %v\n", err)
// }
// }
// 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)
// res[strings.Join(ps, "/")] = true
// for _, w := range ps {
// splitWord(w, &res)
// }
// return
// }
// func splitSynopsis(synopsis string) map[string]bool {
// res := make(map[string]bool)
// ss := strings.Fields(synopsis)
// for _, s := range ss {
// res[s] = true
// }
// return res
// }
// 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 getServeHost() string {
// return "localhost"
// }
// func getServePort() string {
// return "8991"
// }
// // for exernal of serve to add node to db
// func saveNode(nod *doc.Node) error {
// urlPath := fmt.Sprintf("http://%v:%v/add", getServeHost(), getServePort())
// resp, err := http.PostForm(urlPath,
// url.Values{"importPath": {nod.ImportPath},
// "synopsis": {nod.Synopsis},
// "downloadURL": {nod.DownloadURL},
// "isGetDeps": {strconv.FormatBool(nod.IsGetDeps)},
// "type": {nod.Type},
// "value": {nod.Value}})
// if err != nil {
// com.ColorLog("[ERRO] Fail to save node[ %s ]\n", err)
// return err
// }
// defer resp.Body.Close()
// if resp.StatusCode == 200 {
// return nil
// }
// return errors.New("save node failed with " + resp.Status)
// }
// // for inetrnal of serve to add node to db
// func addNode(nod *doc.Node) error {
// batch := new(leveldb.Batch)
// strLastId, err := dbGet("lastId")
// if err != nil {
// if err == leveldb.ErrNotFound {
// strLastId = "0"
// err = batchPut(batch, "lastId", strLastId)
// } else {
// return err
// }
// }
// if err != nil {
// return err
// }
// lastId, err := strconv.ParseInt(strLastId, 0, 64)
// if err != nil {
// return err
// }
// nodKey := fmt.Sprintf("index:%v", nod.ImportPath)
// id, err := dbGet(nodKey)
// if err != nil {
// if err == leveldb.ErrNotFound {
// id = fmt.Sprintf("%v", lastId+1)
// err = batchPut(batch, "lastId", id)
// if err == nil {
// err = batchPut(batch, nodKey, id)
// }
// if err == nil {
// err = batchPut(batch, "pkg:"+id, nod.ImportPath)
// }
// if err == nil {
// err = batchPut(batch, "desc:"+id, nod.Synopsis)
// }
// if err == nil {
// err = batchPut(batch, "down:"+id, nod.DownloadURL)
// }
// if err == nil {
// err = batchPut(batch, "deps:"+id, strconv.FormatBool(nod.IsGetDeps))
// }
// // save totals
// total, err := dbGet("total")
// if err != nil {
// if err == leveldb.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
// }
// // save vers
// vers, err := dbGet("ver:" + id)
// needSplit := (err == leveldb.ErrNotFound)
// if err != nil {
// if err != leveldb.ErrNotFound {
// return err
// }
// } else {
// return nil
// }
// if vers == "" {
// //fmt.Println(nod)
// vers = nod.VerString()
// } else {
// if !strings.Contains(vers, nod.VerString()) {
// vers = vers + "," + nod.VerString()
// } else {
// return nil
// }
// }
// err = batchPut(batch, "ver:"+id, vers)
// if err != nil {
// return err
// }
// if !needSplit {
// return nil
// }
// // indexing package name
// keys := splitPkgName(nod.ImportPath)
// for key, _ := range keys {
// err = batchPut(batch, fmt.Sprintf("key:%v:%v", strings.ToLower(key), id), "")
// if err != nil {
// return err
// }
// }
// if nod.Synopsis != "" {
// fields := splitSynopsis(nod.Synopsis)
// for field, _ := range fields {
// err = batchPut(batch, fmt.Sprintf("key:%v:%v", strings.ToLower(field), id), "")
// if err != nil {
// return err
// }
// }
// }
// return db.Write(batch, wo)
// }
// func rmPkg(nod *doc.Node) {
// }
// var db *leveldb.DB
// // service should be run
// func AutoRun() error {
// s, _, _ := runningStatus()
// if s == STOP {
// // current path
// curPath, err := os.Getwd()
// if err != nil {
// return err
// }
// attr := &os.ProcAttr{
// Dir: curPath,
// Env: os.Environ(),
// //Files: []*os.File{nil, nil, nil},
// Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
// }
// p, err := exePath()
// if err != nil {
// return err
// }
// //com.ColorLog("[INFO] now is starting search daemon ...\n")
// _, err = os.StartProcess(p, []string{"gopm", "serve", "-l"}, attr)
// if err != nil {
// return err
// }
// time.Sleep(time.Second)
// }
// return nil
// }
// func runningStatus() (int, int, int) {
// pFile, err := getPidPath()
// if err != nil {
// return STOP, 0, 0
// }
// contentByte, err := ioutil.ReadFile(pFile)
// 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 getPidPath() (string, error) {
// homeDir, err := com.HomeDir()
// if err != nil {
// return "", err
// }
// pFile := strings.Replace("~/.gopm/var/", "~", homeDir, -1)
// os.MkdirAll(pFile, os.ModePerm)
// return pFile + "pid", nil
// }
// func startService(listen, port string) error {
// homeDir, err := com.HomeDir()
// if err != nil {
// return err
// }
// pFile, err := getPidPath()
// if err != nil {
// return err
// }
// f, err := os.OpenFile(pFile, os.O_RDWR|os.O_CREATE, 0700)
// if err != nil {
// return err
// }
// defer f.Close()
// _, err = f.WriteString(fmt.Sprintf("%v,%v,%v", RUNNING, os.Getpid(), port))
// if err != nil {
// return err
// }
// dbDir = strings.Replace(dbDir, "~", homeDir, -1)
// db, err = leveldb.OpenFile(dbDir, nil)
// if err != nil {
// return err
// }
// 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+":"+port, nil)
// return 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:", strings.ToLower(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 {
// com.ColorLog(err.Error())
// continue
// }
// desc, err := dbGet(fmt.Sprintf("desc:%v", rId))
// if err != nil {
// com.ColorLog(err.Error())
// continue
// }
// pkgs = append(pkgs, fmt.Sprintf(`{"pkg":"%v", "desc":"%v"}`, pkg, desc))
// }
// 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 {
// rId, err := dbGet("index:" + key)
// if err != nil {
// com.ColorLog(err.Error())
// continue
// }
// desc, err := dbGet(fmt.Sprintf("desc:%v", rId))
// if err != nil {
// com.ColorLog(err.Error())
// continue
// }
// pkgs = append(pkgs, fmt.Sprintf(`{"pkg":"%v", "desc":"%v"}`, key, desc))
// }
// w.Write([]byte("[" + strings.Join(pkgs, ", ") + "]"))
// //}
// }
// func addHandler(w http.ResponseWriter, r *http.Request) {
// //if r.Method == "POST" {
// r.ParseForm()
// nod := new(doc.Node)
// nod.ImportPath = r.FormValue("importPath")
// nod.Synopsis = r.FormValue("synopsis")
// nod.DownloadURL = r.FormValue("downloadURL")
// isGetDeps, err := strconv.ParseBool(r.FormValue("isGetDeps"))
// if err != nil {
// com.ColorLog("[ERRO] SEVER: Cannot get deps")
// }
// nod.IsGetDeps = isGetDeps
// nod.Type = r.FormValue("type")
// nod.Value = r.FormValue("value")
// err = addNode(nod)
// if err != nil {
// com.ColorLog("[ERRO] SEVER: Cannot add node[ %s ]\n", err)
// }
// //}
// }
// func rmHandler(w http.ResponseWriter, r *http.Request) {
// }

213
cmd/service.go

@ -0,0 +1,213 @@
// 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
// 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 {
// Name string
// Ver string
// VerId string
//}
// 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}
// }
// func (p *Pkg) VerSimpleString() string {
// if p.VerId != "" {
// return p.VerId
// }
// return p.Ver
// }
// 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())
// }
// // 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"
// }

47
cmd/test.go

@ -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 cmd
import (
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/log"
)
var CmdTest = cli.Command{
Name: "test",
Usage: "link dependencies and go test",
Description: `Command test links dependencies according to gopmfile,
and execute 'go test'
gopm test <go test commands>`,
Action: runTest,
}
func runTest(ctx *cli.Context) {
genNewGoPath(ctx, true)
log.Trace("Testing...")
cmdArgs := []string{"go", "test"}
cmdArgs = append(cmdArgs, ctx.Args()...)
err := execCmd(newGoPath, newCurPath, cmdArgs...)
if err != nil {
log.Error("Test", "Fail to test program")
log.Fatal("", err.Error())
}
log.Success("SUCC", "Test", "Command execute successfully!")
}

215
cmd/update.go

@ -0,0 +1,215 @@
// 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 (
"encoding/json"
"fmt"
"net/http"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/doc"
"github.com/gpmgo/gopm/log"
)
var CmdUpdate = cli.Command{
Name: "update",
Usage: "check and update gopm resources including itself",
Description: `Command update checks updates of resources and gopm itself.
gopm update
Resources will be updated automatically after executed this command,
but you have to confirm before updaing gopm itself.`,
Action: runUpdate,
Flags: []cli.Flag{
cli.BoolFlag{"verbose, v", "show process details"},
},
}
func exePath() string {
file, err := exec.LookPath(os.Args[0])
if err != nil {
log.Error("Update", "Fail to execute exec.LookPath")
log.Fatal("", err.Error())
}
path, err := filepath.Abs(file)
if err != nil {
log.Error("Update", "Fail to get absolutely path")
log.Fatal("", err.Error())
}
return path
}
type version struct {
Gopm int
PackageNameList int `json:"package_name_list"`
}
func runUpdate(ctx *cli.Context) {
setup(ctx)
isAnythingUpdated := false
// Load local version info.
localVerInfo := loadLocalVerInfo()
// Get remote version info.
var remoteVerInfo version
if err := com.HttpGetJSON(http.DefaultClient, "http://gopm.io/VERSION.json", &remoteVerInfo); err != nil {
log.Error("Update", "Fail to fetch VERSION.json")
log.Fatal("", err.Error())
}
// Package name list.
if remoteVerInfo.PackageNameList > localVerInfo.PackageNameList {
log.Log("Updating pkgname.list...%v > %v",
localVerInfo.PackageNameList, remoteVerInfo.PackageNameList)
data, err := com.HttpGetBytes(http.DefaultClient, "https://raw2.github.com/gpmgo/docs/master/pkgname.list", nil)
if err != nil {
log.Error("Update", "Fail to update pkgname.list")
log.Fatal("", err.Error())
}
_, err = com.SaveFile(path.Join(doc.HomeDir, doc.PKG_NAME_LIST_PATH), data)
if err != nil {
log.Error("Update", "Fail to save pkgname.list")
log.Fatal("", err.Error())
}
log.Log("Update pkgname.list to %v succeed!", remoteVerInfo.PackageNameList)
isAnythingUpdated = true
}
// Gopm.
if remoteVerInfo.Gopm > localVerInfo.Gopm {
log.Log("Updating gopm...%v > %v",
localVerInfo.Gopm, remoteVerInfo.Gopm)
installRepoPath = doc.HomeDir + "/repos"
tmpDirPath := filepath.Join(doc.HomeDir, "temp")
tmpBinPath := filepath.Join(tmpDirPath, "gopm")
if runtime.GOOS == "windows" {
tmpBinPath += ".exe"
}
os.MkdirAll(tmpDirPath, os.ModePerm)
os.Remove(tmpBinPath)
// Fetch code.
args := []string{"bin", "-u", "-d"}
if ctx.Bool("verbose") {
args = append(args, "-v")
}
args = append(args, []string{"github.com/gpmgo/gopm", tmpDirPath}...)
stdout, stderr, err := com.ExecCmd("gopm", args...)
if err != nil {
log.Error("Update", "Fail to execute 'gopm bin -u -d github.com/gpmgo/gopm "+tmpDirPath+"'")
log.Fatal("", err.Error())
}
if len(stderr) > 0 {
fmt.Print(stderr)
}
if len(stdout) > 0 {
fmt.Print(stdout)
}
// Check if previous steps were successful.
if !com.IsExist(tmpBinPath) {
log.Error("Update", "Fail to continue command")
log.Fatal("", "Previous steps weren't successful, no binary produced")
}
movePath := exePath()
log.Log("New binary will be replaced for %s", movePath)
// Move binary to given directory.
if runtime.GOOS != "windows" {
err := os.Rename(tmpBinPath, movePath)
if err != nil {
log.Error("Update", "Fail to move binary")
log.Fatal("", err.Error())
}
os.Chmod(movePath+"/"+path.Base(tmpBinPath), os.ModePerm)
} else {
batPath := filepath.Join(tmpDirPath, "update.bat")
f, err := os.Create(batPath)
if err != nil {
log.Error("Update", "Fail to generate bat file")
log.Fatal("", err.Error())
}
f.WriteString("@echo off\r\n")
f.WriteString(fmt.Sprintf("ping -n 1 127.0.0.1>nul\r\ncopy \"%v\" \"%v\" >nul\r\ndel \"%v\" >nul\r\n\r\n",
tmpBinPath, movePath, tmpBinPath))
//f.WriteString(fmt.Sprintf("del \"%v\"\r\n", batPath))
f.Close()
attr := &os.ProcAttr{
Dir: workDir,
Env: os.Environ(),
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
}
_, err = os.StartProcess(batPath, []string{batPath}, attr)
if err != nil {
log.Error("Update", "Fail to start bat process")
log.Fatal("", err.Error())
}
}
log.Success("SUCC", "Update", "Command execute successfully!")
isAnythingUpdated = true
}
// Save JSON.
f, err := os.Create(path.Join(doc.HomeDir, doc.VER_PATH))
if err != nil {
log.Error("Update", "Fail to create VERSION.json")
log.Fatal("", err.Error())
}
if err := json.NewEncoder(f).Encode(&remoteVerInfo); err != nil {
log.Error("Update", "Fail to encode VERSION.json")
log.Fatal("", err.Error())
}
if !isAnythingUpdated {
log.Log("Nothing need to be updated")
}
log.Log("Exit old gopm")
}
func loadLocalVerInfo() (ver version) {
verPath := path.Join(doc.HomeDir, doc.VER_PATH)
// First time run should not exist.
if !com.IsExist(verPath) {
return ver
}
f, err := os.Open(verPath)
if err != nil {
log.Error("Update", "Fail to open VERSION.json")
log.Fatal("", err.Error())
}
if err := json.NewDecoder(f).Decode(&ver); err != nil {
log.Error("Update", "Fail to decode VERSION.json")
log.Fatal("", err.Error())
}
return ver
}

202
doc/bitbucket.go

@ -0,0 +1,202 @@
// 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/tar"
"bytes"
"compress/gzip"
"errors"
"io"
"net/http"
"os"
"path"
"regexp"
"strings"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
//"github.com/gpmgo/gopm/log"
)
var (
bitbucketPattern = 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)-`)
)
// getBitbucketDoc downloads tarball from bitbucket.org.
func getBitbucketDoc(client *http.Client, match map[string]string, installRepoPath string, nod *Node, ctx *cli.Context) ([]string, error) {
// Check version control.
if m := bitbucketEtagRe.FindStringSubmatch(nod.Value); m != nil {
match["vcs"] = m[1]
} else {
var repo struct {
Scm string
}
if err := com.HttpGetJSON(client, com.Expand("https://api.bitbucket.org/1.0/repositories/{owner}/{repo}", match), &repo); err != nil {
return nil, err
}
match["vcs"] = repo.Scm
}
if nod.Type == BRANCH {
if len(nod.Value) == 0 {
match["commit"] = defaultTags[match["vcs"]]
} else {
match["commit"] = nod.Value
}
}
if nod.IsGetDeps {
if nod.Type == COMMIT {
tags := make(map[string]string)
for _, nodeType := range []string{"branches", "tags"} {
var nodes map[string]struct {
Node string
}
if err := com.HttpGetJSON(client, com.Expand("https://api.bitbucket.org/1.0/repositories/{owner}/{repo}/{0}", match, nodeType), &nodes); err != nil {
return 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, err
}
nod.Value = match["commit"]
}
} else {
// Check downlaod type.
switch nod.Type {
case TAG, COMMIT, BRANCH:
match["commit"] = nod.Value
default:
return nil, errors.New("Unknown node type: " + nod.Type)
}
}
// 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 := com.HttpGetBytes(client, com.Expand("https://bitbucket.org/{owner}/{repo}/get/{commit}.tar.gz", match), nil)
if err != nil {
return nil, err
}
var installPath string
if nod.ImportPath == nod.DownloadURL {
suf := "." + nod.Value
if len(suf) == 1 {
suf = ""
}
projectPath := com.Expand("bitbucket.org/{owner}/{repo}", match)
installPath = installRepoPath + "/" + projectPath + suf
nod.ImportPath = projectPath
} else {
installPath = installRepoPath + "/" + nod.ImportPath
}
// Remove old files.
os.RemoveAll(installPath + "/")
os.MkdirAll(installPath+"/", os.ModePerm)
gzr, err := gzip.NewReader(bytes.NewReader(p))
if err != nil {
return 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, err
}
fn := h.Name
// In case that we find directory, usually we should not.
if strings.HasSuffix(fn, "/") {
continue
}
// 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) && !(!ctx.Bool("example") && strings.Contains(absPath, "example")) {
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, err
}
_, err = com.SaveFile(absPath, fbytes)
if err != nil {
return nil, err
}
// Set modify time.
os.Chtimes(absPath, h.AccessTime, h.ModTime)
}
var imports []string
// Check if need to check imports.
if nod.IsGetDeps {
for _, d := range dirs {
importPkgs, err := CheckImports(d+"/", match["importPath"], nod)
if err != nil {
return nil, err
}
imports = append(imports, importPkgs...)
}
}
return 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
}

147
doc/conf.go

@ -0,0 +1,147 @@
// 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 (
"os"
"path"
"path/filepath"
"strings"
"github.com/Unknwon/com"
"github.com/Unknwon/goconfig"
"github.com/gpmgo/gopm/log"
)
const (
GOPM_FILE_NAME = ".gopmfile"
GOPM_CONFIG_FILE = "data/gopm.ini"
PKG_NAME_LIST_PATH = "data/pkgname.list"
VER_PATH = "data/VERSION.json"
RawHomeDir = "~/.gopm"
)
var (
HomeDir = "~/.gopm"
LocalNodesFile = "/data/localnodes.list"
LocalNodes *goconfig.ConfigFile
Cfg *goconfig.ConfigFile
)
func init() {
hd, err := com.HomeDir()
if err != nil {
log.Error("", "Fail to get current user")
log.Fatal("", err.Error())
}
HomeDir = strings.Replace(RawHomeDir, "~", hd, -1)
cfgPath := path.Join(HomeDir, GOPM_CONFIG_FILE)
if !com.IsExist(cfgPath) {
os.MkdirAll(path.Dir(cfgPath), os.ModePerm)
if _, err = os.Create(cfgPath); err != nil {
log.Error("", "Fail to create gopm config file")
log.Fatal("", err.Error())
}
}
Cfg, err = goconfig.LoadConfigFile(cfgPath)
if err != nil {
log.Error("", "Fail to load gopm config file")
log.Fatal("", err.Error())
}
LoadLocalNodes()
LoadPkgNameList(path.Join(HomeDir, PKG_NAME_LIST_PATH))
}
// NewGopmfile loads gopmgile in given directory.
func NewGopmfile(dirPath string) *goconfig.ConfigFile {
dirPath, _ = filepath.Abs(dirPath)
gf, err := goconfig.LoadConfigFile(path.Join(dirPath, GOPM_FILE_NAME))
if err != nil {
log.Error("", "Fail to load gopmfile:")
log.Fatal("", "\t"+err.Error())
}
return gf
}
var PackageNameList map[string]string
func LoadPkgNameList(filePath string) {
PackageNameList = make(map[string]string)
// If file does not exist, simply ignore.
if !com.IsFile(filePath) {
return
}
data, err := com.ReadFile(filePath)
if err != nil {
log.Error("Package name list", "Fail to read file")
log.Fatal("", err.Error())
}
pkgs := strings.Split(string(data), "\n")
for i, line := range pkgs {
infos := strings.Split(line, "=")
if len(infos) != 2 {
// Last item might be empty line.
if i == len(pkgs)-1 {
continue
}
log.Error("", "Fail to parse package name: "+line)
log.Fatal("", "Invalid package name information")
}
PackageNameList[strings.TrimSpace(infos[0])] =
strings.TrimSpace(infos[1])
}
}
func GetPkgFullPath(short string) string {
name, ok := PackageNameList[short]
if !ok {
log.Error("", "Invalid package name")
log.Error("", "It's not a invalid import path and no match in the package name list:")
log.Fatal("", "\t"+short)
}
return name
}
func LoadLocalNodes() {
if !com.IsDir(HomeDir + "/data") {
os.MkdirAll(HomeDir+"/data", os.ModePerm)
}
if !com.IsFile(HomeDir + LocalNodesFile) {
os.Create(HomeDir + LocalNodesFile)
}
var err error
LocalNodes, err = goconfig.LoadConfigFile(path.Join(HomeDir + LocalNodesFile))
if err != nil {
log.Error("load node", "Fail to load localnodes.list")
log.Fatal("", err.Error())
}
}
func SaveLocalNodes() {
if err := goconfig.SaveConfigFile(LocalNodes,
path.Join(HomeDir+LocalNodesFile)); err != nil {
log.Error("", "Fail to save localnodes.list:")
log.Error("", "\t"+err.Error())
}
}

25
doc/error.go

@ -0,0 +1,25 @@
// 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")
)

191
doc/github.go

@ -0,0 +1,191 @@
// 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"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/log"
)
var (
githubPattern = regexp.MustCompile(`^github\.com/(?P<owner>[a-z0-9A-Z_.\-]+)/(?P<repo>[a-z0-9A-Z_.\-]+)(?P<dir>/[a-z0-9A-Z_.\-/]*)?$`)
)
func GetGithubCredentials() string {
return "client_id=" + Cfg.MustValue("github", "client_id") +
"&client_secret=" + Cfg.MustValue("github", "client_secret")
}
// getGithubDoc downloads tarball from github.com.
func getGithubDoc(client *http.Client, match map[string]string, installRepoPath string, nod *Node, ctx *cli.Context) ([]string, error) {
match["cred"] = GetGithubCredentials()
// Check downlaod type.
switch nod.Type {
case BRANCH:
if len(nod.Value) == 0 {
match["sha"] = MASTER
// Only get and check revision with the latest version.
var refs []*struct {
Ref string
Url string
Object struct {
Sha string
Type string
Url string
}
}
err := com.HttpGetJSON(client, com.Expand("https://api.github.com/repos/{owner}/{repo}/git/refs?{cred}", match), &refs)
if err != nil {
if strings.Contains(err.Error(), "403") {
break
}
log.Warn("GET", "Fail to get revision")
log.Warn("", err.Error())
break
}
var etag string
COMMIT_LOOP:
for _, ref := range refs {
switch {
case strings.HasPrefix(ref.Ref, "refs/heads/master"):
etag = ref.Object.Sha
break COMMIT_LOOP
}
}
if etag == nod.Revision {
log.Log("GET Package hasn't changed: %s", nod.ImportPath)
return nil, nil
}
nod.Revision = etag
} else {
match["sha"] = nod.Value
}
case TAG, COMMIT:
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 := com.HttpGetBytes(client, com.Expand("https://github.com/{owner}/{repo}/archive/{sha}.zip", match), nil)
if err != nil {
return nil, errors.New("Fail to donwload Github repo -> " + err.Error())
}
shaName := com.Expand("{repo}-{sha}", match)
if nod.Type == "tag" {
shaName = strings.Replace(shaName, "-v", "-", 1)
}
var installPath string
if nod.ImportPath == nod.DownloadURL {
suf := "." + nod.Value
if len(suf) == 1 {
suf = ""
}
projectPath := com.Expand("github.com/{owner}/{repo}", match)
installPath = installRepoPath + "/" + projectPath + suf
nod.ImportPath = projectPath
} else {
installPath = installRepoPath + "/" + nod.ImportPath
}
// Remove old files.
os.RemoveAll(installPath + "/")
os.MkdirAll(installPath+"/", os.ModePerm)
r, err := zip.NewReader(bytes.NewReader(p), int64(len(p)))
if err != nil {
return nil, errors.New(nod.ImportPath + " -> new zip: " + err.Error())
}
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.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 !(!ctx.Bool("example") && strings.Contains(absPath, "example")) {
for _, d := range dirs {
if d == absPath {
break compareDir
}
}
dirs = append(dirs, absPath)
}
default:
// Get file from archive.
r, err := f.Open()
if err != nil {
return nil, err
}
fbytes := make([]byte, f.FileInfo().Size())
_, err = io.ReadFull(r, fbytes)
if err != nil {
return nil, err
}
_, err = com.SaveFile(absPath, fbytes)
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"], nod)
if err != nil {
return nil, err
}
imports = append(imports, importPkgs...)
}
}
return imports, err
}

286
doc/google.go

@ -0,0 +1,286 @@
// 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"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/log"
)
var (
googleRepoRe = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)
googleRevisionRe = regexp.MustCompile(`<h2>(?:[^ ]+ - )?Revision *([^:]+):`)
googleFileRe = regexp.MustCompile(`<li><a href="([^"/]+)"`)
googleDirRe = regexp.MustCompile(`<li><a href="([^".]+)"`)
googlePattern = regexp.MustCompile(`^code\.google\.com/p/(?P<repo>[a-z0-9\-]+)(:?\.(?P<subrepo>[a-z0-9\-]+))?(?P<dir>/[a-z0-9A-Z_.\-/]+)?$`)
)
// getGoogleDoc downloads raw files from code.google.com.
func getGoogleDoc(client *http.Client, match map[string]string, installRepoPath string, nod *Node, ctx *cli.Context) ([]string, error) {
setupGoogleMatch(match)
// Check version control.
if err := getGoogleVCS(client, match); err != nil {
return nil, errors.New("fail to get vcs " + nod.ImportPath + " : " + err.Error())
}
switch nod.Type {
case BRANCH:
if len(nod.Value) == 0 {
match["tag"] = defaultTags[match["vcs"]]
// Only get and check revision with the latest version.
p, err := com.HttpGetBytes(client,
com.Expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/?r={tag}", match), nil)
if err != nil {
log.Error("GET", "Fail to get revision")
log.Error("", err.Error())
break
}
if m := googleRevisionRe.FindSubmatch(p); m == nil {
log.Error("GET", "Fail to get revision")
log.Error("", err.Error())
} else {
etag := string(m[1])
if etag == nod.Revision {
log.Log("GET Package hasn't changed: %s", nod.ImportPath)
return nil, nil
}
nod.Revision = etag
}
} else {
match["tag"] = nod.Value
}
case TAG, COMMIT:
match["tag"] = nod.Value
default:
return nil, errors.New("Unknown node type: " + nod.Type)
}
var installPath string
projectPath := GetProjectPath(nod.ImportPath)
if nod.ImportPath == nod.DownloadURL {
suf := "." + nod.Value
if len(suf) == 1 {
suf = ""
}
installPath = installRepoPath + "/" + projectPath + suf
} else {
installPath = installRepoPath + "/" + projectPath
}
// Remove old files.
os.RemoveAll(installPath + "/")
os.MkdirAll(installPath+"/", os.ModePerm)
if match["vcs"] == "svn" {
com.ColorLog("[WARN] SVN detected, may take very long time.\n")
rootPath := com.Expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}", match)
d, f := path.Split(rootPath)
err := downloadFiles(client, match, d, installPath+"/", match["tag"],
[]string{f + "/"})
if err != nil {
return nil, errors.New("Fail to download " + nod.ImportPath + " : " + err.Error())
}
}
p, err := com.HttpGetBytes(client, com.Expand("http://{subrepo}{dot}{repo}.googlecode.com/archive/{tag}.zip", match), nil)
if err != nil {
return nil, errors.New("Fail to download " + nod.ImportPath + " : " + err.Error())
}
r, err := zip.NewReader(bytes.NewReader(p), int64(len(p)))
if err != nil {
return nil, errors.New(nod.ImportPath + " -> new zip: " + err.Error())
}
nameLen := strings.Index(r.File[0].Name, "/")
dirPrefix := match["dir"]
if len(dirPrefix) != 0 {
dirPrefix = dirPrefix[1:] + "/"
}
dirs := make([]string, 0, 5)
for _, f := range r.File {
absPath := strings.Replace(f.Name, f.Name[:nameLen], installPath, 1)
// Create diretory before create file.
dir := path.Dir(absPath)
if !checkDir(dir, dirs) && !(!ctx.Bool("example") && strings.Contains(absPath, "example")) {
dirs = append(dirs, dir+"/")
os.MkdirAll(dir+"/", os.ModePerm)
}
// Get file from archive.
r, err := f.Open()
if err != nil {
return nil, err
}
fbytes := make([]byte, f.FileInfo().Size())
_, err = io.ReadFull(r, fbytes)
if err != nil {
return nil, err
}
_, err = com.SaveFile(absPath, fbytes)
if err != nil {
return nil, err
}
}
var imports []string
// Check if need to check imports.
if nod.IsGetDeps {
for _, d := range dirs {
importPkgs, err := CheckImports(d, match["importPath"], nod)
if err != nil {
return nil, err
}
imports = append(imports, importPkgs...)
}
}
return imports, err
}
type rawFile struct {
name string
rawURL string
data []byte
}
func (rf *rawFile) Name() string {
return rf.name
}
func (rf *rawFile) RawUrl() string {
return rf.rawURL
}
func (rf *rawFile) Data() []byte {
return rf.data
}
func (rf *rawFile) SetData(p []byte) {
rf.data = p
}
func downloadFiles(client *http.Client, match map[string]string, rootPath, installPath, commit string, dirs []string) error {
suf := "?r=" + commit
if len(commit) == 0 {
suf = ""
}
for _, d := range dirs {
p, err := com.HttpGetBytes(client, rootPath+d+suf, nil)
if err != nil {
return err
}
// Create destination directory.
os.MkdirAll(installPath+d, os.ModePerm)
// Get source files in current path.
files := make([]com.RawFile, 0, 5)
for _, m := range googleFileRe.FindAllSubmatch(p, -1) {
fname := strings.Split(string(m[1]), "?")[0]
files = append(files, &rawFile{
name: fname,
rawURL: rootPath + d + fname + suf,
})
}
// Fetch files from VCS.
if err := com.FetchFilesCurl(files); err != nil {
return err
}
// Save files.
for _, f := range files {
absPath := installPath + d
// Create diretory before create file.
os.MkdirAll(path.Dir(absPath), os.ModePerm)
// Write data to file
fw, err := os.Create(absPath + f.Name())
if err != nil {
return err
}
_, err = fw.Write(f.Data())
fw.Close()
if err != nil {
return err
}
}
files = nil
subdirs := make([]string, 0, 3)
// Get subdirectories.
for _, m := range googleDirRe.FindAllSubmatch(p, -1) {
dirName := strings.Split(string(m[1]), "?")[0]
if strings.HasSuffix(dirName, "/") {
subdirs = append(subdirs, d+dirName)
}
}
err = downloadFiles(client, match, rootPath, installPath, commit, subdirs)
if err != nil {
return err
}
}
return nil
}
func setupGoogleMatch(match map[string]string) {
if s := match["subrepo"]; s != "" {
match["dot"] = "."
match["query"] = "?repo=" + s
} else {
match["dot"] = ""
match["query"] = ""
}
}
func getGoogleVCS(client *http.Client, match map[string]string) error {
// Scrape the HTML project page to find the VCS.
p, err := com.HttpGetBytes(client, com.Expand("http://code.google.com/p/{repo}/source/checkout", match), nil)
if err != nil {
return errors.New("doc.getGoogleVCS(" + match["importPath"] + ") -> " + err.Error())
}
m := googleRepoRe.FindSubmatch(p)
if m == nil {
return com.NotFoundError{"Could not VCS on Google Code project page."}
}
match["vcs"] = string(m[1])
return nil
}

52
doc/http.go

@ -0,0 +1,52 @@
// 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 (
"flag"
"net"
"net/http"
"time"
"github.com/Unknwon/com"
)
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)
com.ColorLog("[WARN] Canceled request for %s, please interrupt the program.\n", 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}
)

134
doc/launchpad.go

@ -0,0 +1,134 @@
// 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/tar"
"bytes"
"compress/gzip"
"io"
"net/http"
"os"
"regexp"
"strings"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
)
var launchpadPattern = regexp.MustCompile(`^launchpad\.net/(?P<repo>(?P<project>[a-z0-9A-Z_.\-]+)(?P<series>/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+)(?P<dir>/[a-z0-9A-Z_.\-/]+)*$`)
// getLaunchpadDoc downloads tarball from launchpad.net.
func getLaunchpadDoc(client *http.Client, match map[string]string, installRepoPath string, nod *Node, ctx *cli.Context) ([]string, error) {
if match["project"] != "" && match["series"] != "" {
rc, err := com.HttpGet(client, com.Expand("https://code.launchpad.net/{project}{series}/.bzr/branch-format", match), nil)
_, isNotFound := err.(com.NotFoundError)
switch {
case err == nil:
rc.Close()
// The structure of the import path is launchpad.net/{root}/{dir}.
case isNotFound:
// The structure of the import path is is launchpad.net/{project}/{dir}.
match["repo"] = match["project"]
match["dir"] = com.Expand("{series}{dir}", match)
default:
return nil, err
}
}
var downloadPath string
// Check if download with specific revision.
if len(nod.Value) == 0 {
downloadPath = com.Expand("https://bazaar.launchpad.net/+branch/{repo}/tarball", match)
} else {
downloadPath = com.Expand("https://bazaar.launchpad.net/+branch/{repo}/tarball/"+nod.Value, match)
}
// Scrape the repo browser to find the project revision and individual Go files.
p, err := com.HttpGetBytes(client, downloadPath, nil)
if err != nil {
return nil, err
}
installPath := installRepoPath + "/" + nod.ImportPath
// Remove old files.
os.RemoveAll(installPath + "/")
os.MkdirAll(installPath+"/", os.ModePerm)
gzr, err := gzip.NewReader(bytes.NewReader(p))
if err != nil {
return 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, err
}
fn := h.Name
// Check root path.
if len(autoPath) == 0 {
autoPath = fn[:strings.Index(fn, match["repo"])+len(match["repo"])]
}
absPath := strings.Replace(fn, autoPath, installPath, 1)
switch {
case h.FileInfo().IsDir(): // Directory.
// Create diretory before create file.
os.MkdirAll(absPath+"/", os.ModePerm)
// Check if current directory is example.
if !(!ctx.Bool("example") && strings.Contains(absPath, "example")) {
dirs = append(dirs, absPath)
}
case !strings.HasPrefix(fn, "."):
// Get data from archive.
fbytes := make([]byte, h.Size)
if _, err := io.ReadFull(tr, fbytes); err != nil {
return nil, err
}
_, err = com.SaveFile(absPath, fbytes)
if err != nil {
return nil, err
}
}
}
var imports []string
// Check if need to check imports.
if nod.IsGetDeps {
for _, d := range dirs {
importPkgs, err := CheckImports(d+"/", match["importPath"], nod)
if err != nil {
return nil, err
}
imports = append(imports, importPkgs...)
}
}
return imports, err
}

128
doc/oschina.go

@ -0,0 +1,128 @@
// 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"
"regexp"
"strings"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
)
var (
oscTagRe = regexp.MustCompile(`/repository/archive\?ref=(.*)">`)
oscPattern = regexp.MustCompile(`^git\.oschina\.net/(?P<owner>[a-z0-9A-Z_.\-]+)/(?P<repo>[a-z0-9A-Z_.\-]+)(?P<dir>/[a-z0-9A-Z_.\-/]*)?$`)
)
// getGithubDoc downloads tarball from git.oschina.com.
func getOSCDoc(client *http.Client, match map[string]string, installRepoPath string, nod *Node, ctx *cli.Context) ([]string, error) {
// Check downlaod type.
switch nod.Type {
case BRANCH:
if len(nod.Value) == 0 {
match["sha"] = MASTER
} else {
match["sha"] = nod.Value
}
case TAG, COMMIT:
match["sha"] = nod.Value
default:
return nil, errors.New("Unknown node type: " + nod.Type)
}
// zip: http://{projectRoot}/repository/archive?ref={sha}
// Downlaod archive.
p, err := com.HttpGetBytes(client, com.Expand("http://git.oschina.net/{owner}/{repo}/repository/archive?ref={sha}", match), nil)
if err != nil {
return nil, errors.New("Fail to donwload OSChina repo -> " + err.Error())
}
var installPath string
if nod.ImportPath == nod.DownloadURL {
suf := "." + nod.Value
if len(suf) == 1 {
suf = ""
}
projectPath := com.Expand("git.oschina.net/{owner}/{repo}", match)
installPath = installRepoPath + "/" + projectPath + suf
nod.ImportPath = projectPath
} else {
installPath = installRepoPath + "/" + nod.ImportPath
}
// Remove old files.
os.RemoveAll(installPath + "/")
os.MkdirAll(installPath+"/", os.ModePerm)
r, err := zip.NewReader(bytes.NewReader(p), int64(len(p)))
if err != nil {
return nil, errors.New("Fail to unzip OSChina repo -> " + err.Error())
}
nameLen := len(match["repo"])
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 {
fileName := f.Name[nameLen+1:]
absPath := installPath + "/" + fileName
if strings.HasSuffix(absPath, "/") {
dirs = append(dirs, absPath)
os.MkdirAll(absPath, os.ModePerm)
continue
}
// Get file from archive.
r, err := f.Open()
if err != nil {
return nil, errors.New("Fail to open OSChina repo -> " + err.Error())
}
fbytes := make([]byte, f.FileInfo().Size())
_, err = io.ReadFull(r, fbytes)
if err != nil {
return nil, err
}
_, err = com.SaveFile(absPath, fbytes)
if err != nil {
return nil, err
}
}
var imports []string
// Check if need to check imports.
if nod.IsGetDeps {
for _, d := range dirs {
importPkgs, err := CheckImports(d, match["importPath"], nod)
if err != nil {
return nil, err
}
imports = append(imports, importPkgs...)
}
}
return imports, err
}

125
doc/struct.go

@ -0,0 +1,125 @@
// 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 (
"fmt"
"go/token"
"net/http"
"os"
"regexp"
"time"
"github.com/codegangsta/cli"
)
const (
TRUNK = "trunk"
MASTER = "master"
DEFAULT = "default"
TAG = "tag"
BRANCH = "branch"
COMMIT = "commit"
LOCAL = "local"
)
type Pkg struct {
ImportPath string
RootPath string
Type string
Value string // Branch, tag, commit or local.
}
// If the package is fixed and no need to updated.
// For commit, tag and local, it's fixed. For branch
func (pkg *Pkg) IsFixed() bool {
if pkg.Type == BRANCH || len(pkg.Value) == 0 {
return false
}
return true
}
func (pkg *Pkg) VerString() string {
if pkg.Value == "" {
return pkg.Type
}
return fmt.Sprintf("%v:%v", pkg.Type, pkg.Value)
}
func NewPkg(importPath, tp, value string) *Pkg {
return &Pkg{importPath, "", tp, value}
}
func NewDefaultPkg(importPath string) *Pkg {
return NewPkg(importPath, BRANCH, "")
}
type Node struct {
Pkg
DownloadURL string
Synopsis string
IsGetDeps bool
IsGetDepsOnly bool
Revision string
}
func NewNode(importPath, downloadUrl, tp, value string, isGetDeps bool) *Node {
return &Node{
Pkg: Pkg{
ImportPath: importPath,
Type: tp,
Value: value,
},
DownloadURL: downloadUrl,
IsGetDeps: isGetDeps,
}
}
// 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
}
// service represents a source code control service.
type service struct {
pattern *regexp.Regexp
prefix string
get func(*http.Client, map[string]string, string, *Node, *cli.Context) ([]string, error)
}
// services is the list of source code control services handled by gopkgdoc.
var services = []*service{
{githubPattern, "github.com/", getGithubDoc},
{googlePattern, "code.google.com/", getGoogleDoc},
{bitbucketPattern, "bitbucket.org/", getBitbucketDoc},
{launchpadPattern, "launchpad.net/", getLaunchpadDoc},
{oscPattern, "git.oschina.net/", getOSCDoc},
}

759
doc/utils.go

@ -0,0 +1,759 @@
// Copyright 2013-2014 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"
"go/build"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/gpmgo/gopm/log"
)
const VENDOR = ".vendor"
// GetDirsInfo returns os.FileInfo of all sub-directories in root path.
func GetDirsInfo(rootPath string) ([]os.FileInfo, error) {
if !com.IsDir(rootPath) {
log.Warn("Directory %s does not exist", rootPath)
return []os.FileInfo{}, nil
}
rootDir, err := os.Open(rootPath)
if err != nil {
return nil, err
}
defer rootDir.Close()
dirs, err := rootDir.Readdir(0)
if err != nil {
return nil, err
}
return dirs, nil
}
// A Source describles a Source code file.
type Source struct {
SrcName string
SrcData []byte
}
func (s *Source) Name() string { return s.SrcName }
func (s *Source) Size() int64 { return int64(len(s.SrcData)) }
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 }
func (s *Source) Data() []byte { return s.SrcData }
type Context struct {
build.Context
importPath string
srcFiles map[string]*Source
}
func (ctx *Context) readDir(dir string) ([]os.FileInfo, error) {
fis := make([]os.FileInfo, 0, len(ctx.srcFiles))
for _, src := range ctx.srcFiles {
fis = append(fis, src)
}
return fis, nil
}
func (ctx *Context) openFile(path string) (r io.ReadCloser, err error) {
if src, ok := ctx.srcFiles[filepath.Base(path)]; ok {
return ioutil.NopCloser(bytes.NewReader(src.Data())), nil
}
return nil, os.ErrNotExist
}
// GetImports returns package denpendencies.
func GetImports(absPath, importPath string, example, test bool) []string {
fis, err := GetDirsInfo(absPath)
if err != nil {
log.Error("", "Fail to get directory's information")
log.Fatal("", err.Error())
}
absPath += "/"
ctx := new(Context)
ctx.importPath = importPath
ctx.srcFiles = make(map[string]*Source)
ctx.Context = build.Default
ctx.JoinPath = path.Join
ctx.IsAbsPath = path.IsAbs
ctx.ReadDir = ctx.readDir
ctx.OpenFile = ctx.openFile
// TODO: Load too much, need to make sure which is imported which are not.
dirs := make([]string, 0, 10)
for _, fi := range fis {
if strings.Contains(fi.Name(), VENDOR) {
continue
}
if fi.IsDir() {
dirs = append(dirs, absPath+fi.Name())
continue
} else if !test && strings.HasSuffix(fi.Name(), "_test.go") {
continue
} else if !strings.HasSuffix(fi.Name(), ".go") || strings.HasPrefix(fi.Name(), ".") ||
strings.HasPrefix(fi.Name(), "_") {
continue
}
src := &Source{SrcName: fi.Name()}
src.SrcData, err = ioutil.ReadFile(absPath + fi.Name())
if err != nil {
log.Error("", "Fail to read file")
log.Fatal("", err.Error())
}
ctx.srcFiles[fi.Name()] = src
}
pkg, err := ctx.ImportDir(absPath, build.AllowBinary)
if err != nil {
if _, ok := err.(*build.NoGoError); !ok {
log.Error("", "Fail to get imports")
log.Fatal("", err.Error())
}
}
imports := make([]string, 0, len(pkg.Imports))
for _, p := range pkg.Imports {
if !IsGoRepoPath(p) && !strings.HasPrefix(p, importPath) {
imports = append(imports, p)
}
}
if len(dirs) > 0 {
imports = append(imports, GetAllImports(dirs, importPath, example, test)...)
}
return imports
}
// isVcsPath returns true if the directory was created by VCS.
func isVcsPath(dirPath string) bool {
return strings.Contains(dirPath, "/.git") ||
strings.Contains(dirPath, "/.hg") ||
strings.Contains(dirPath, "/.svn")
}
// GetAllImports returns all imports in given directory and all sub-directories.
func GetAllImports(dirs []string, importPath string, example, test bool) (imports []string) {
for _, d := range dirs {
if !isVcsPath(d) &&
!(!example && strings.Contains(d, "example")) {
imports = append(imports, GetImports(d, importPath, example, test)...)
}
}
return imports
}
// GetGOPATH returns best matched GOPATH.
func GetBestMatchGOPATH(appPath string) string {
paths := com.GetGOPATHs()
for _, p := range paths {
if strings.HasPrefix(p, appPath) {
return strings.Replace(p, "\\", "/", -1)
}
}
return paths[0]
}
// CheckIsExistWithVCS returns false if directory only has VCS folder,
// or doesn't exist.
func CheckIsExistWithVCS(path string) bool {
// Check if directory exist.
if !com.IsExist(path) {
return false
}
// Check if only has VCS folder.
dirs, err := GetDirsInfo(path)
if err != nil {
log.Error("", "Fail to get directory's information")
log.Fatal("", err.Error())
}
if len(dirs) > 1 {
return true
} else if len(dirs) == 0 {
return false
}
switch dirs[0].Name() {
case ".git", ".hg", ".svn":
return false
}
return true
}
// CheckIsExistInGOPATH checks if given package import path exists in any path in GOPATH/src,
// and returns corresponding GOPATH.
func CheckIsExistInGOPATH(importPath string) (string, bool) {
paths := com.GetGOPATHs()
for _, p := range paths {
if CheckIsExistWithVCS(p + "/src/" + importPath + "/") {
return p, true
}
}
return "", false
}
// GetProjectPath returns project path of import path.
func GetProjectPath(importPath string) (projectPath string) {
projectPath = importPath
// Check project hosting.
switch {
case strings.HasPrefix(importPath, "github.com") ||
strings.HasPrefix(importPath, "git.oschina.net"):
projectPath = joinPath(importPath, 3)
case strings.HasPrefix(importPath, "code.google.com"):
projectPath = joinPath(importPath, 3)
case strings.HasPrefix(importPath, "bitbucket.org"):
projectPath = joinPath(importPath, 3)
case strings.HasPrefix(importPath, "launchpad.net"):
projectPath = joinPath(importPath, 2)
}
return projectPath
}
func joinPath(importPath string, num int) string {
subdirs := strings.Split(importPath, "/")
if len(subdirs) > num {
return strings.Join(subdirs[:num], "/")
}
return importPath
}
var validTLD = map[string]bool{
// curl http://data.iana.org/TLD/tlds-alpha-by-domain.txt | sed -e '/#/ d' -e 's/.*/"&": true,/' | tr [:upper:] [:lower:]
".ac": true,
".ad": true,
".ae": true,
".aero": true,
".af": true,
".ag": true,
".ai": true,
".al": true,
".am": true,
".an": true,
".ao": true,
".aq": true,
".ar": true,
".arpa": true,
".as": true,
".asia": true,
".at": true,
".au": true,
".aw": true,
".ax": true,
".az": true,
".ba": true,
".bb": true,
".bd": true,
".be": true,
".bf": true,
".bg": true,
".bh": true,
".bi": true,
".biz": true,
".bj": true,
".bm": true,
".bn": true,
".bo": true,
".br": true,
".bs": true,
".bt": true,
".bv": true,
".bw": true,
".by": true,
".bz": true,
".ca": true,
".cat": true,
".cc": true,
".cd": true,
".cf": true,
".cg": true,
".ch": true,
".ci": true,
".ck": true,
".cl": true,
".cm": true,
".cn": true,
".co": true,
".com": true,
".coop": true,
".cr": true,
".cu": true,
".cv": true,
".cw": true,
".cx": true,
".cy": true,
".cz": true,
".de": true,
".dj": true,
".dk": true,
".dm": true,
".do": true,
".dz": true,
".ec": true,
".edu": true,
".ee": true,
".eg": true,
".er": true,
".es": true,
".et": true,
".eu": true,
".fi": true,
".fj": true,
".fk": true,
".fm": true,
".fo": true,
".fr": true,
".ga": true,
".gb": true,
".gd": true,
".ge": true,
".gf": true,
".gg": true,
".gh": true,
".gi": true,
".gl": true,
".gm": true,
".gn": true,
".gov": true,
".gp": true,
".gq": true,
".gr": true,
".gs": true,
".gt": true,
".gu": true,
".gw": true,
".gy": true,
".hk": true,
".hm": true,
".hn": true,
".hr": true,
".ht": true,
".hu": true,
".id": true,
".ie": true,
".il": true,
".im": true,
".in": true,
".info": true,
".int": true,
".io": true,
".iq": true,
".ir": true,
".is": true,
".it": true,
".je": true,
".jm": true,
".jo": true,
".jobs": true,
".jp": true,
".ke": true,
".kg": true,
".kh": true,
".ki": true,
".km": true,
".kn": true,
".kp": true,
".kr": true,
".kw": true,
".ky": true,
".kz": true,
".la": true,
".lb": true,
".lc": true,
".li": true,
".lk": true,
".lr": true,
".ls": true,
".lt": true,
".lu": true,
".lv": true,
".ly": true,
".ma": true,
".mc": true,
".md": true,
".me": true,
".mg": true,
".mh": true,
".mil": true,
".mk": true,
".ml": true,
".mm": true,
".mn": true,
".mo": true,
".mobi": true,
".mp": true,
".mq": true,
".mr": true,
".ms": true,
".mt": true,
".mu": true,
".museum": true,
".mv": true,
".mw": true,
".mx": true,
".my": true,
".mz": true,
".na": true,
".name": true,
".nc": true,
".ne": true,
".net": true,
".nf": true,
".ng": true,
".ni": true,
".nl": true,
".no": true,
".np": true,
".nr": true,
".nu": true,
".nz": true,
".om": true,
".org": true,
".pa": true,
".pe": true,
".pf": true,
".pg": true,
".ph": true,
".pk": true,
".pl": true,
".pm": true,
".pn": true,
".post": true,
".pr": true,
".pro": true,
".ps": true,
".pt": true,
".pw": true,
".py": true,
".qa": true,
".re": true,
".ro": true,
".rs": true,
".ru": true,
".rw": true,
".sa": true,
".sb": true,
".sc": true,
".sd": true,
".se": true,
".sg": true,
".sh": true,
".si": true,
".sj": true,
".sk": true,
".sl": true,
".sm": true,
".sn": true,
".so": true,
".sr": true,
".st": true,
".su": true,
".sv": true,
".sx": true,
".sy": true,
".sz": true,
".tc": true,
".td": true,
".tel": true,
".tf": true,
".tg": true,
".th": true,
".tj": true,
".tk": true,
".tl": true,
".tm": true,
".tn": true,
".to": true,
".tp": true,
".tr": true,
".travel": true,
".tt": true,
".tv": true,
".tw": true,
".tz": true,
".ua": true,
".ug": true,
".uk": true,
".us": true,
".uy": true,
".uz": true,
".va": true,
".vc": true,
".ve": true,
".vg": true,
".vi": true,
".vn": true,
".vu": true,
".wf": true,
".ws": true,
".xn--0zwm56d": true,
".xn--11b5bs3a9aj6g": true,
".xn--3e0b707e": true,
".xn--45brj9c": true,
".xn--80akhbyknj4f": true,
".xn--80ao21a": true,
".xn--90a3ac": true,
".xn--9t4b11yi5a": true,
".xn--clchc0ea0b2g2a9gcd": true,
".xn--deba0ad": true,
".xn--fiqs8s": true,
".xn--fiqz9s": true,
".xn--fpcrj9c3d": true,
".xn--fzc2c9e2c": true,
".xn--g6w251d": true,
".xn--gecrj9c": true,
".xn--h2brj9c": true,
".xn--hgbk6aj7f53bba": true,
".xn--hlcj6aya9esc7a": true,
".xn--j6w193g": true,
".xn--jxalpdlp": true,
".xn--kgbechtv": true,
".xn--kprw13d": true,
".xn--kpry57d": true,
".xn--lgbbat1ad8j": true,
".xn--mgb9awbf": true,
".xn--mgbaam7a8h": true,
".xn--mgbayh7gpa": true,
".xn--mgbbh1a71e": true,
".xn--mgbc0a9azcg": true,
".xn--mgberp4a5d4ar": true,
".xn--mgbx4cd0ab": true,
".xn--o3cw4h": true,
".xn--ogbpf8fl": true,
".xn--p1ai": true,
".xn--pgbs0dh": true,
".xn--s9brj9c": true,
".xn--wgbh1c": true,
".xn--wgbl6a": true,
".xn--xkc2al3hye2a": true,
".xn--xkc2dl3a5ee0h": true,
".xn--yfro4i67o": true,
".xn--ygbi2ammx": true,
".xn--zckzah": true,
".xxx": true,
".ye": true,
".yt": true,
".za": true,
".zm": true,
".zw": true,
}
var (
validHost = regexp.MustCompile(`^[-a-z0-9]+(?:\.[-a-z0-9]+)+$`)
validPathElement = regexp.MustCompile(`^[-A-Za-z0-9~+][-A-Za-z0-9_.]*$`)
)
// IsValidRemotePath returns true if importPath is structurally valid for "go get".
func IsValidRemotePath(importPath string) bool {
parts := strings.Split(importPath, "/")
if len(parts) <= 1 {
// Import path must contain at least one "/".
return false
}
if !validTLD[path.Ext(parts[0])] {
return false
}
if !validHost.MatchString(parts[0]) {
return false
}
for _, part := range parts[1:] {
if !validPathElement.MatchString(part) || part == "testdata" {
return false
}
}
return true
}
var standardPath = map[string]bool{
"builtin": true,
// go list -f '"{{.ImportPath}}": true,' std | grep -v 'cmd/|exp/'
"cmd/api": true,
"cmd/cgo": true,
"cmd/fix": true,
"cmd/go": true,
"cmd/gofmt": true,
"cmd/vet": true,
"cmd/yacc": true,
"archive/tar": true,
"archive/zip": true,
"bufio": true,
"bytes": true,
"compress/bzip2": true,
"compress/flate": true,
"compress/gzip": true,
"compress/lzw": true,
"compress/zlib": true,
"container/heap": true,
"container/list": true,
"container/ring": true,
"crypto": true,
"crypto/aes": true,
"crypto/cipher": true,
"crypto/des": true,
"crypto/dsa": true,
"crypto/ecdsa": true,
"crypto/elliptic": true,
"crypto/hmac": true,
"crypto/md5": true,
"crypto/rand": true,
"crypto/rc4": true,
"crypto/rsa": true,
"crypto/sha1": true,
"crypto/sha256": true,
"crypto/sha512": true,
"crypto/subtle": true,
"crypto/tls": true,
"crypto/x509": true,
"crypto/x509/pkix": true,
"database/sql": true,
"database/sql/driver": true,
"debug/dwarf": true,
"debug/elf": true,
"debug/gosym": true,
"debug/macho": true,
"debug/pe": true,
"encoding/ascii85": true,
"encoding/asn1": true,
"encoding/base32": true,
"encoding/base64": true,
"encoding/binary": true,
"encoding/csv": true,
"encoding/gob": true,
"encoding/hex": true,
"encoding/json": true,
"encoding/pem": true,
"encoding/xml": true,
"errors": true,
"expvar": true,
"flag": true,
"fmt": true,
"go/ast": true,
"go/build": true,
"go/doc": true,
"go/format": true,
"go/parser": true,
"go/printer": true,
"go/scanner": true,
"go/token": true,
"hash": true,
"hash/adler32": true,
"hash/crc32": true,
"hash/crc64": true,
"hash/fnv": true,
"html": true,
"html/template": true,
"image": true,
"image/color": true,
"image/draw": true,
"image/gif": true,
"image/jpeg": true,
"image/png": true,
"index/suffixarray": true,
"io": true,
"io/ioutil": true,
"log": true,
"log/syslog": true,
"math": true,
"math/big": true,
"math/cmplx": true,
"math/rand": true,
"mime": true,
"mime/multipart": true,
"net": true,
"net/http": true,
"net/http/cgi": true,
"net/http/cookiejar": true,
"net/http/fcgi": true,
"net/http/httptest": true,
"net/http/httputil": true,
"net/http/pprof": true,
"net/mail": true,
"net/rpc": true,
"net/rpc/jsonrpc": true,
"net/smtp": true,
"net/textproto": true,
"net/url": true,
"os": true,
"os/exec": true,
"os/signal": true,
"os/user": true,
"path": true,
"path/filepath": true,
"reflect": true,
"regexp": true,
"regexp/syntax": true,
"runtime": true,
"runtime/cgo": true,
"runtime/debug": true,
"runtime/pprof": true,
"runtime/race": true,
"sort": true,
"strconv": true,
"strings": true,
"sync": true,
"sync/atomic": true,
"syscall": true,
"testing": true,
"testing/iotest": true,
"testing/quick": true,
"text/scanner": true,
"text/tabwriter": true,
"text/template": true,
"text/template/parse": true,
"time": true,
"unicode": true,
"unicode/utf16": true,
"unicode/utf8": true,
"unsafe": true,
}
// IsGoRepoPath returns true if package is from standard library.
func IsGoRepoPath(importPath string) bool {
return standardPath[importPath]
}
func CheckNodeValue(v string) string {
if len(v) == 0 {
return "<UTD>"
}
return v
}

71
doc/utils_test.go

@ -0,0 +1,71 @@
// Copyright 2013-2014 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 (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
var VcsTestPairs = map[string]bool{
"/.hg": true,
"/.git": true,
"/.svn": true,
"/.vendor": false,
}
func Test_isVcsPath(t *testing.T) {
Convey("Test if the path is belonging to VCS", t, func() {
for name, expect := range VcsTestPairs {
So(isVcsPath(name), ShouldEqual, expect)
}
})
}
func TestGetDirsInfo(t *testing.T) {
Convey("Get directory's information that exist", t, func() {
dis, err := GetDirsInfo(".")
So(err, ShouldBeNil)
So(len(dis), ShouldEqual, 13)
})
Convey("Get directory's information does not exist", t, func() {
dis, err := GetDirsInfo("./404")
So(err, ShouldBeNil)
So(len(dis), ShouldEqual, 0)
})
}
var GoStdTestPairs = map[string]bool{
"net/http": true,
"fmt": true,
"github.com/gpmgo/gopm": false,
"github.com/Unknwon/com": false,
}
func TestIsGoRepoPath(t *testing.T) {
Convey("Test if the path is belonging to Go STD", t, func() {
for name, expect := range GoStdTestPairs {
So(IsGoRepoPath(name), ShouldEqual, expect)
}
})
}
func TestGetImports(t *testing.T) {
Convey("Get package that are imported", t, func() {
So(len(GetImports(".", "github.com/gpmgo/gopm/docs", false)), ShouldEqual, 4)
})
}

307
doc/vcs.go

@ -0,0 +1,307 @@
// Copyright 2013-2014 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/xml"
"errors"
"io"
"net/http"
"os"
"path"
"regexp"
"strings"
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/log"
)
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 lsremoteRe = regexp.MustCompile(`(?m)^([0-9a-f]{40})\s+refs/(?:tags|heads)/(.+)$`)
var defaultTags = map[string]string{"git": "master", "hg": "default"}
func bestTag(tags map[string]string, defaultTag string) (string, string, error) {
if commit, ok := tags[defaultTag]; ok {
return defaultTag, commit, nil
}
return "", "", com.NotFoundError{"Tag or branch not found."}
}
// PureDownload downloads package without version control.
func PureDownload(nod *Node, installRepoPath string, ctx *cli.Context) ([]string, error) {
for _, s := range services {
if s.get == nil || !strings.HasPrefix(nod.DownloadURL, s.prefix) {
continue
}
m := s.pattern.FindStringSubmatch(nod.DownloadURL)
if m == nil {
if s.prefix != "" {
return nil, errors.New("Cannot match package service prefix by given path")
}
continue
}
match := map[string]string{"importPath": nod.DownloadURL}
for i, n := range s.pattern.SubexpNames() {
if n != "" {
match[n] = m[i]
}
}
return s.get(HttpClient, match, installRepoPath, nod, ctx)
}
log.Log("Cannot match any service, getting dynamic...")
return getDynamic(HttpClient, nod, installRepoPath, ctx)
}
func getDynamic(client *http.Client, nod *Node, installRepoPath string, ctx *cli.Context) ([]string, error) {
match, err := fetchMeta(client, nod.ImportPath)
if err != nil {
return nil, err
}
if match["projectRoot"] != nod.ImportPath {
rootMatch, err := fetchMeta(client, match["projectRoot"])
if err != nil {
return nil, err
}
if rootMatch["projectRoot"] != match["projectRoot"] {
return nil, com.NotFoundError{"Project root mismatch."}
}
}
nod.DownloadURL = com.Expand("{repo}{dir}", match)
return PureDownload(nod, installRepoPath, ctx)
}
func fetchMeta(client *http.Client, importPath string) (map[string]string, error) {
uri := importPath
if !strings.Contains(uri, "/") {
// Add slash for root of domain.
uri = uri + "/"
}
uri = uri + "?go-get=1"
scheme := "https"
resp, err := client.Get(scheme + "://" + uri)
if err != nil || resp.StatusCode != 200 {
if err == nil {
resp.Body.Close()
}
scheme = "http"
resp, err = client.Get(scheme + "://" + uri)
if err != nil {
return nil, &com.RemoteError{strings.SplitN(importPath, "/", 2)[0], err}
}
}
defer resp.Body.Close()
return parseMeta(scheme, importPath, resp.Body)
}
func attrValue(attrs []xml.Attr, name string) string {
for _, a := range attrs {
if strings.EqualFold(a.Name.Local, name) {
return a.Value
}
}
return ""
}
func parseMeta(scheme, importPath string, r io.Reader) (map[string]string, error) {
var match map[string]string
d := xml.NewDecoder(r)
d.Strict = false
metaScan:
for {
t, tokenErr := d.Token()
if tokenErr != nil {
break metaScan
}
switch t := t.(type) {
case xml.EndElement:
if strings.EqualFold(t.Name.Local, "head") {
break metaScan
}
case xml.StartElement:
if strings.EqualFold(t.Name.Local, "body") {
break metaScan
}
if !strings.EqualFold(t.Name.Local, "meta") ||
attrValue(t.Attr, "name") != "go-import" {
continue metaScan
}
f := strings.Fields(attrValue(t.Attr, "content"))
if len(f) != 3 ||
!strings.HasPrefix(importPath, f[0]) ||
!(len(importPath) == len(f[0]) || importPath[len(f[0])] == '/') {
continue metaScan
}
if match != nil {
return nil, com.NotFoundError{"More than one <meta> found at " + scheme + "://" + importPath}
}
projectRoot, vcs, repo := f[0], f[1], f[2]
repo = strings.TrimSuffix(repo, "."+vcs)
i := strings.Index(repo, "://")
if i < 0 {
return nil, com.NotFoundError{"Bad repo URL in <meta>."}
}
proto := repo[:i]
repo = repo[i+len("://"):]
match = map[string]string{
// Used in getVCSDoc, same as vcsPattern matches.
"importPath": importPath,
"repo": repo,
"vcs": vcs,
"dir": importPath[len(projectRoot):],
// Used in getVCSDoc
"scheme": proto,
// Used in getDynamic.
"projectRoot": projectRoot,
"projectName": path.Base(projectRoot),
"projectURL": scheme + "://" + projectRoot,
}
}
}
if match == nil {
return nil, com.NotFoundError{"<meta> not found."}
}
return match, nil
}
func getImports(rootPath string, match map[string]string, cmdFlags map[string]bool, nod *Node) (imports []string) {
dirs, err := GetDirsInfo(rootPath)
if err != nil {
log.Error("", "Fail to get directory's information")
log.Fatal("", err.Error())
}
for _, d := range dirs {
if d.IsDir() && !(!cmdFlags["-e"] && strings.Contains(d.Name(), "example")) {
absPath := rootPath + d.Name() + "/"
importPkgs, err := CheckImports(absPath, match["importPath"], nod)
if err != nil {
return nil
}
imports = append(imports, importPkgs...)
}
}
return imports
}
// checkImports checks package denpendencies.
func CheckImports(absPath, importPath string, nod *Node) (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") {
data, err := com.ReadFile(absPath + fi.Name())
if err != nil {
return nil, err
}
files = append(files, &source{
name: fi.Name(),
data: data,
})
}
}
// Check if has Go source files.
if len(files) > 0 {
w := &walker{ImportPath: importPath}
importPkgs, err = w.build(files, nod)
if err != nil {
return nil, err
}
}
return importPkgs, err
}

239
doc/walker.go

@ -0,0 +1,239 @@
// 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"
"go/ast"
"go/build"
"go/doc"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"path"
"runtime"
"strings"
"unicode"
"unicode/utf8"
"github.com/gpmgo/gopm/log"
)
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, nod *Node) ([]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 {
log.Warn("walker: %s", err.Error())
return nil, nil
}
}
// 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)
}
}
apkg, _ := ast.NewPackage(w.fset, files, simpleImporter, nil)
mode := doc.Mode(0)
if w.ImportPath == "builtin" {
mode |= doc.AllDecls
}
pdoc := doc.New(apkg, w.ImportPath, mode)
if nod != nil {
nod.Synopsis = Synopsis(pdoc.Doc)
if i := strings.Index(nod.Synopsis, "\n"); i > -1 {
nod.Synopsis = nod.Synopsis[:i]
}
}
return imports, err
}
var badSynopsisPrefixes = []string{
"Autogenerated by Thrift Compiler",
"Automatically generated ",
"Auto-generated by ",
"Copyright ",
"COPYRIGHT ",
`THE SOFTWARE IS PROVIDED "AS IS"`,
"TODO: ",
"vim:",
}
// Synopsis extracts the first sentence from s. All runs of whitespace are
// replaced by a single space.
func Synopsis(s string) string {
parts := strings.SplitN(s, "\n\n", 2)
s = parts[0]
var buf []byte
const (
other = iota
period
space
)
last := space
Loop:
for i := 0; i < len(s); i++ {
b := s[i]
switch b {
case ' ', '\t', '\r', '\n':
switch last {
case period:
break Loop
case other:
buf = append(buf, ' ')
last = space
}
case '.':
last = period
buf = append(buf, b)
default:
last = other
buf = append(buf, b)
}
}
// Ensure that synopsis fits an App Engine datastore text property.
const m = 400
if len(buf) > m {
buf = buf[:m]
if i := bytes.LastIndex(buf, []byte{' '}); i >= 0 {
buf = buf[:i]
}
buf = append(buf, " ..."...)
}
s = string(buf)
r, n := utf8.DecodeRuneInString(s)
if n < 0 || unicode.IsPunct(r) || unicode.IsSymbol(r) {
// ignore Markdown headings, editor settings, Go build constraints, and * in poorly formatted block comments.
s = ""
} else {
for _, prefix := range badSynopsisPrefixes {
if strings.HasPrefix(s, prefix) {
s = ""
break
}
}
}
return s
}

67
gopm.go

@ -0,0 +1,67 @@
// Copyright 2013-2014 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.
// gopm(Go Package Manager) is a Go package manage tool for searching, installing, updating and sharing your packages in Go.
package main
import (
"os"
"runtime"
"github.com/codegangsta/cli"
"github.com/gpmgo/gopm/cmd"
)
// +build go1.1
// Test that go1.1 tag above is included in builds. main.go refers to this definition.
const go11tag = true
const APP_VER = "0.6.5.0320"
// cmd.CmdTest,
// cmd.CmdSearch,
// cmdClean,
// cmdDoc,
// cmdEnv,
// cmdFix,
// cmdList,
// cmdTool,
// cmdVet,
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
}
func main() {
app := cli.NewApp()
app.Name = "gopm"
app.Usage = "Go Package Manager"
app.Version = APP_VER
app.Commands = []cli.Command{
cmd.CmdGet,
cmd.CmdBin,
cmd.CmdGen,
cmd.CmdRun,
cmd.CmdBuild,
cmd.CmdInstall,
cmd.CmdUpdate,
cmd.CmdConfig,
}
app.Flags = append(app.Flags, []cli.Flag{
cli.BoolFlag{"noterm", "disable color output"},
}...)
app.Run(os.Args)
}

121
log/log.go

@ -0,0 +1,121 @@
// 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.
// +build !windows
// Package log provides npm-like style log output.
package log
import (
"fmt"
"os"
"github.com/aybabtme/color/brush"
)
func Error(hl, msg string) {
if PureMode {
errorP(hl, msg)
}
if len(hl) > 0 {
hl = " " + brush.Red(hl).String()
}
fmt.Printf("gopm %s%s %s\n", brush.Red("ERR!"), hl, msg)
}
func Fatal(hl, msg string) {
if PureMode {
fatal(hl, msg)
}
Error(hl, msg)
os.Exit(2)
}
func Warn(format string, args ...interface{}) {
if PureMode {
warn(format, args...)
return
}
fmt.Printf("gopm %s %s\n", brush.Purple("WARN"),
fmt.Sprintf(format, args...))
}
func Log(format string, args ...interface{}) {
if PureMode {
log(format, args...)
return
}
if !Verbose {
return
}
fmt.Printf("gopm %s %s\n", brush.White("INFO"),
fmt.Sprintf(format, args...))
}
func Trace(format string, args ...interface{}) {
if PureMode {
trace(format, args...)
return
}
if !Verbose {
return
}
fmt.Printf("gopm %s %s\n", brush.Blue("TRAC"),
fmt.Sprintf(format, args...))
}
func Success(title, hl, msg string) {
if PureMode {
success(title, hl, msg)
return
}
if !Verbose {
return
}
if len(hl) > 0 {
hl = " " + brush.Green(hl).String()
}
fmt.Printf("gopm %s%s %s\n", brush.Green(title), hl, msg)
}
func Message(hl, msg string) {
if PureMode {
message(hl, msg)
return
}
if !Verbose {
return
}
if len(hl) > 0 {
hl = " " + brush.Yellow(hl).String()
}
fmt.Printf("gopm %s%s %s\n", brush.Yellow("MSG!"), hl, msg)
}
func Help(format string, args ...interface{}) {
if PureMode {
help(format, args...)
}
fmt.Printf("gopm %s %s\n", brush.Cyan("HELP"),
fmt.Sprintf(format, args...))
os.Exit(2)
}

80
log/logP.go

@ -0,0 +1,80 @@
// 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 log
import (
"fmt"
"os"
)
var (
PureMode = false
Verbose = false
)
func errorP(hl, msg string) {
if len(hl) > 0 {
hl = " " + hl
}
fmt.Printf("gopm ERR!%s %s\n", hl, msg)
}
func fatal(hl, msg string) {
errorP(hl, msg)
os.Exit(2)
}
func warn(format string, args ...interface{}) {
fmt.Printf("gopm WARN %s\n", fmt.Sprintf(format, args...))
}
func log(format string, args ...interface{}) {
if !Verbose {
return
}
fmt.Printf("gopm INFO %s\n", fmt.Sprintf(format, args...))
}
func trace(format string, args ...interface{}) {
if !Verbose {
return
}
fmt.Printf("gopm TRAC %s\n", fmt.Sprintf(format, args...))
}
func success(title, hl, msg string) {
if !Verbose {
return
}
if len(hl) > 0 {
hl = " " + hl
}
fmt.Printf("gopm %s%s %s\n", title, hl, msg)
}
func message(hl, msg string) {
if !Verbose {
return
}
if len(hl) > 0 {
hl = " " + hl
}
fmt.Printf("gopm MSG!%s %s\n", hl, msg)
}
func help(format string, args ...interface{}) {
fmt.Printf("gopm HELP %s\n", fmt.Sprintf(format, args...))
os.Exit(2)
}

48
log/log_windows.go

@ -0,0 +1,48 @@
// 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 log provides npm-like style log output.
package log
func Error(hl, msg string) {
errorP(hl, msg)
}
func Fatal(hl, msg string) {
fatal(hl, msg)
}
func Warn(format string, args ...interface{}) {
warn(format, args...)
}
func Log(format string, args ...interface{}) {
log(format, args...)
}
func Trace(format string, args ...interface{}) {
trace(format, args...)
}
func Success(title, hl, msg string) {
success(title, hl, msg)
}
func Message(hl, msg string) {
message(hl, msg)
}
func Help(format string, args ...interface{}) {
help(format, args...)
}
Loading…
Cancel
Save