Skip to content

Commit

Permalink
initial implementation for install, exec, go commands
Browse files Browse the repository at this point in the history
  • Loading branch information
petejkim committed Jun 18, 2014
1 parent 09f0feb commit 2a6f534
Show file tree
Hide file tree
Showing 10 changed files with 486 additions and 0 deletions.
72 changes: 72 additions & 0 deletions .gitignore
@@ -0,0 +1,72 @@
.vendor

# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
*.test

# Folders
_obj/
_test/
bin/
_vendor/
build/

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe

*.swp
**.orig
.env

# OSX
.DS_Store
._*
.Spotlight-V100
.Trashes

# Linux
*~
.directory

# Windows
Thumbs.db
Desktop.ini

# RubyMine
.idea/

# TextMate
*.tmproj
*.tmproject
tmtags

# Vim
.*.sw[a-z]
*.un~
Session.vim
tags

# Emacs
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
.elc
auto-save-list
tramp
.\#*

# Vagrant
.vagrant
3 changes: 3 additions & 0 deletions Goopfile
@@ -0,0 +1,3 @@
github.com/onsi/ginkgo
github.com/onsi/gomega
code.google.com/p/go.tools/go/vcs
3 changes: 3 additions & 0 deletions Goopfile.lock
@@ -0,0 +1,3 @@
github.com/onsi/ginkgo #12446bbcc74950944c897e32ea99cc8d058f9c92
github.com/onsi/gomega #036273e6f643552b16fe7a6464afd2ae0324992e
code.google.com/p/go.tools/go/vcs #c5e1d3cb5e8e8c1c7c0738642555d7669d1d41e0
25 changes: 25 additions & 0 deletions Makefile
@@ -0,0 +1,25 @@
NO_COLOR=\033[0m
OK_COLOR=\033[32;01m
ERROR_COLOR=\033[31;01m
WARN_COLOR=\033[33;01m
BIN_NAME=goop

all: build

build:
@mkdir -p build/
@echo "$(OK_COLOR)==> Installing dependencies$(NO_COLOR)"
go get -d -v ./...
@echo "$(OK_COLOR)==> Building$(NO_COLOR)"
go install -x ./...
cp $(GOPATH)/bin/$(BIN_NAME) build/$(BIN_NAME)

format:
go fmt ./...

test:
@echo "$(OK_COLOR)==> Testing...$(NO_COLOR)"
@go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs -n1 go get -d
@ginkgo -r -trace -keepGoing

.PHONY: all build format test
21 changes: 21 additions & 0 deletions README.md
@@ -1,6 +1,27 @@
goop
====

A dependency manager for Go (golang), inspired by Bundler. It is different from other dependency managers in that it does not force you to mess with your `GOPATH`.

1. Create `Goopfile`. Revision reference (e.g. Git SHA hash) is optional, but recommended. Prefix hash with `#`. (This is to futureproof the file format.)

Example:
```
github.com/mattn/go-sqlite3
github.com/gorilla/context #14f550f51af52180c2eefed15e5fd18d63c0a64a
github.com/gorilla/mux #854d482e26505d59549690719cbc009f04042c2e
```

2. Run `goop install`. This will install packages under a subdirectory called `.vendor` and create `Goopfile.lock`, recording exact versions used for each package. Subsequent `goop install` runs will install the versions specified in `Goopfile.lock`. You should check this file in to your source version control.

3. Run commands using `goop exec`, for example `goop exec go run main.go`. This will set correct `GOPATH` env var before executing your command, without clobbering it globally. For convenience, you do not need type `exec` keyword for `go` commands. (e.g. `goop go test`)

4. Run `goop update` to ignore exisiting `Goopfile.lock`, and update to latest versions of packages, as specified in `Goopfile`.

#### Caveat

Goop currently only supports Git and Mercurial. Support for Git and Mercurial should cover 99% of the cases, but you are welcome to make a pull request that adds support for Subversion and Bazaar.

- - -
Copyright (c) 2014 Irrational Industries, Inc. d.b.a. Nitrous.IO.<br>
This software is licensed under the [MIT License](http://github.com/nitrous-io/goop/raw/master/LICENSE).
8 changes: 8 additions & 0 deletions colors/colors.go
@@ -0,0 +1,8 @@
package colors

const (
Reset = "\033[0m"
OK = "\033[0;32m"
Error = "\033[0;31m"
Warn = "\033[0;33m"
)
175 changes: 175 additions & 0 deletions goop/goop.go
@@ -0,0 +1,175 @@
package goop

import (
"fmt"
"io"
"os"
"os/exec"
"path"
"strings"

"code.google.com/p/go.tools/go/vcs"

"github.com/nitrous-io/goop/colors"
"github.com/nitrous-io/goop/parser"
)

type UnsupportedVCSError struct {
VCS string
}

func (e *UnsupportedVCSError) Error() string {
return fmt.Sprintf("goop: %s is not supported.", e.VCS)
}

type Goop struct {
dir string
stdin io.Reader
stdout io.Writer
stderr io.Writer
}

func NewGoop(dir string, stdin io.Reader, stdout io.Writer, stderr io.Writer) *Goop {
return &Goop{dir: dir, stdout: stdout, stderr: stderr}
}

func (g *Goop) Env() []string {
sysEnv := os.Environ()
env := make([]string, len(sysEnv))
copy(env, sysEnv)
gopathPatched, pathPatched := false, false

for i, e := range env {
if !gopathPatched && strings.HasPrefix(e, "GOPATH=") {
env[i] = fmt.Sprintf("GOPATH=%s", g.vendorDir())
gopathPatched = true
} else if !pathPatched && strings.HasPrefix(e, "PATH=") {
env[i] = fmt.Sprintf("PATH=%s:%s", path.Join(g.vendorDir(), "bin"), e[5:])
pathPatched = true
}
if gopathPatched && pathPatched {
break
}
}
return env
}

func (g *Goop) Exec(name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.Env = g.Env()
cmd.Stdin = g.stdin
cmd.Stdout = g.stdout
cmd.Stderr = g.stderr
return cmd.Run()
}

func (g *Goop) Install() error {
f, err := os.Open(path.Join(g.dir, "Goopfile"))
if err != nil {
return err
}
defer f.Close()

deps, err := parser.Parse(f)
if err != nil {
return err
}

for _, dep := range deps {
g.stdout.Write([]byte(colors.OK + "=> Installing " + dep.Pkg + "..." + colors.Reset + "\n"))
err = g.Exec("go", "get", "-v", dep.Pkg)
if err != nil {
return err
}

repo, err := vcs.RepoRootForImportPath(dep.Pkg, true)
if err != nil {
return err
}

pkgPath := path.Join(g.vendorDir(), "src", repo.Root)

if dep.Rev == "" {
rev, err := g.currentRev(repo.VCS.Cmd, pkgPath)
if err != nil {
return err
}
dep.Rev = rev
continue
}

err = g.checkout(repo.VCS.Cmd, pkgPath, dep.Rev)
if err != nil {
return err
}
}

lf, err := os.Create(path.Join(g.dir, "Goopfile.lock"))
defer lf.Close()

for _, dep := range deps {
_, err = lf.WriteString(fmt.Sprintf("%s #%s\n", dep.Pkg, dep.Rev))
if err != nil {
return err
}
}

return nil
}

func (g *Goop) vendorDir() string {
return path.Join(g.dir, ".vendor")
}

func (g *Goop) currentRev(vcsCmd string, path string) (string, error) {
switch vcsCmd {
case "git":
cmd := exec.Command("git", "rev-parse", "--verify", "HEAD")
cmd.Dir = path
cmd.Stderr = g.stderr
rev, err := cmd.Output()
if err != nil {
return "", err
} else {
return strings.TrimSpace(string(rev)), err
}
case "hg":
cmd := exec.Command("hg", "log", "-r", ".", "--template", "{node}")
cmd.Dir = path
cmd.Stderr = g.stderr
rev, err := cmd.Output()
if err != nil {
return "", err
} else {
return strings.TrimSpace(string(rev)), err
}
}
return "", &UnsupportedVCSError{VCS: vcsCmd}
}

func (g *Goop) checkout(vcsCmd string, path string, tag string) error {
switch vcsCmd {
case "git":
err := g.execInPath(path, "git", "fetch")
if err != nil {
return err
}
return g.execInPath(path, "git", "checkout", tag)
case "hg":
err := g.execInPath(path, "hg", "pull")
if err != nil {
return err
}
return g.execInPath(path, "hg", "update", tag)
}
return &UnsupportedVCSError{VCS: vcsCmd}
}

func (g *Goop) execInPath(path string, name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.Dir = path
cmd.Stdin = g.stdin
cmd.Stdout = g.stdout
cmd.Stderr = g.stderr
return cmd.Run()
}
32 changes: 32 additions & 0 deletions main.go
@@ -0,0 +1,32 @@
package main

import (
"os"
"path"

"github.com/nitrous-io/goop/colors"
"github.com/nitrous-io/goop/goop"
)

func main() {
name := path.Base(os.Args[0])

pwd, err := os.Getwd()
if err != nil {
os.Stderr.WriteString(colors.Error + name + ": error - failed to determine present working directory!" + colors.Reset + "\n")
}

g := goop.NewGoop(path.Join(pwd), os.Stdin, os.Stdout, os.Stderr)

switch os.Args[1] {
case "install":
err = g.Install()
case "exec":
err = g.Exec(os.Args[2], os.Args[3:]...)
case "go":
err = g.Exec("go", os.Args[2:]...)
}
if err != nil {
os.Stderr.WriteString(colors.Error + name + ": error - " + err.Error() + colors.Reset + "\n")
}
}

0 comments on commit 2a6f534

Please sign in to comment.