Skip to content
This repository has been archived by the owner on Mar 5, 2022. It is now read-only.

Commit

Permalink
Version v1.2.0 (#16)
Browse files Browse the repository at this point in the history
Version 1.2.0

* Stop using a cache (#12)

The cache was well-intentioned, but caused lots of tricky bugs. It could
get polluted with bad versions of dependencies, for example, and its
existence was always hard to justify.

* Fix gometalinter (#15)

Fixes retool to support github.com/alecthomas/gometalinter. The 
`retool do` command now appends the `_tools` directory to `GOPATH`
when it runs subcommands, and it sets `GOBIN` to `_tools/bin`. In 
addition, when syncing, retool will preserve the linters vendored by 
gometalinter in its `_linters` directory, so `retool do gometalinter` will
work without hassle.
  • Loading branch information
spenczar authored Mar 24, 2017
1 parent 96e8fc9 commit d6b4af0
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 57 deletions.
23 changes: 23 additions & 0 deletions clean.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ func clean(pkgs []string) {
return nil
}

// If the path is a directory that's specially marked for preservation, keep
// it and all its contents.
if info.IsDir() && preserveDirectory(path) {
return filepath.SkipDir
}

// If the folder is a kept package or a parent, don't delete it and keep recursing
for p := range deps {
if strings.HasPrefix(p, pkg) {
Expand Down Expand Up @@ -175,3 +181,20 @@ func isLegalFile(filename string) bool {
}
return false
}

// List of directories that should be completely preserved if they are present.
var preservedDirectories = []string{
// gometalinter vendors its own linters and relies on this directory's
// existence. See issue #7.
filepath.Join("github.com", "alecthomas", "gometalinter", "_linters"),
}

// checks whether path is in the list of preserved directories.
func preserveDirectory(path string) bool {
for _, d := range preservedDirectories {
if strings.HasSuffix(path, d) {
return true
}
}
return false
}
23 changes: 18 additions & 5 deletions do.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ import (
"strings"
)

func setPath() (unset func()) {
func setPath() error {
prevpath := os.Getenv("PATH")
newPath := filepath.Join(toolDirPath, "bin") + string(os.PathListSeparator) + prevpath
_ = os.Setenv("PATH", newPath)
return func() {
_ = os.Setenv("PATH", prevpath)
return os.Setenv("PATH", newPath)
}

func setGoEnv() error {
newGoBin := filepath.Join(toolDirPath, "bin")
if err := os.Setenv("GOBIN", newGoBin); err != nil {
return err
}

prevGoPath := os.Getenv("GOPATH")
newGoPath := toolDirPath + string(os.PathListSeparator) + prevGoPath
return os.Setenv("GOPATH", newGoPath)
}

func do() {
Expand All @@ -22,7 +30,12 @@ func do() {
fatal("no command passed to retool do", nil)
}

defer setPath()()
if err := setPath(); err != nil {
fatal("unable to set PATH", err)
}
if err := setGoEnv(); err != nil {
fatal("unable to set up go environment variables", err)
}

cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = os.Stdin
Expand Down
9 changes: 2 additions & 7 deletions input.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func printUsageAndExit(command string, exitCode int) {
os.Exit(exitCode)
}

const usage = `usage: retool (add | remove | upgrade | sync | do | clean | build | help)
const usage = `usage: retool (add | remove | upgrade | sync | do | build | help)
use retool with a subcommand:
Expand All @@ -124,7 +124,6 @@ upgrade will upgrade a tool
sync will synchronize your _tools with tools.json, downloading if necessary
build will compile all the tools in _tools
do will run stuff using your installed tools
clean will delete the repo cache stored at ~/.retool
help [command] will describe a command in more detail
version will print the installed version of retool
Expand Down Expand Up @@ -187,11 +186,7 @@ That works too.

const cleanUsage = `usage: retool clean
retool clean will delete the repo cache stored at ~/.retool
This is just
rm -rf ~/.retool
That works too.
retool clean has no effect, but still exists for compatibility.
`

const buildUsage = `usage: retool build
Expand Down
25 changes: 2 additions & 23 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,9 @@ import (
"flag"
"fmt"
"os"
"os/user"
"path/filepath"
)

const version = "v1.1.0"

var cacheDir = ""

func init() {
u, err := user.Current()
if err == nil && u.HomeDir != "" {
cacheDir = filepath.Join(u.HomeDir, ".retool")
} else {
cwd, err := os.Getwd()
if err == nil {
cacheDir = filepath.Join(cwd, ".retool")
} else {
cacheDir = ".retool"
}
}
}
const version = "v1.2.0"

func main() {
flag.Parse()
Expand Down Expand Up @@ -69,10 +51,7 @@ func main() {
s.sync()
do()
case "clean":
err = os.RemoveAll(cacheDir)
if err != nil {
fatal("Failure during clean", err)
}
log("the clean subcommand is deprecated and has no effect")
default:
fatal(fmt.Sprintf("unknown cmd %q", cmd), nil)
}
Expand Down
68 changes: 67 additions & 1 deletion retool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
Expand Down Expand Up @@ -86,6 +87,19 @@ func TestRetool(t *testing.T) {
runRetoolCmd(t, dir, retool, "add", "github.com/spenczar/retool_test_app", "origin/has_dep")
})

t.Run("clean", func(t *testing.T) {
// Clean should be a noop, but kept around for compatibility
cmd := exec.Command(retool, "clean")
_, err := cmd.Output()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
t.Fatalf("expected no errors when using retool clean, have this:\n%s", string(exitErr.Stderr))
} else {
t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
}
}
})

t.Run("do", func(t *testing.T) {
dir, cleanup := setupTempDir(t)
defer cleanup()
Expand All @@ -96,6 +110,58 @@ func TestRetool(t *testing.T) {
t.Errorf("have=%q, want=%q", output, want)
}
})

t.Run("upgrade", func(t *testing.T) {
dir, cleanup := setupTempDir(t)
defer cleanup()
runRetoolCmd(t, dir, retool, "add", "github.com/twitchtv/retool", "v1.0.1")
runRetoolCmd(t, dir, retool, "upgrade", "github.com/twitchtv/retool", "v1.0.3")
out := runRetoolCmd(t, dir, retool, "do", "retool", "version")
if want := "retool v1.0.3"; string(out) != want {
t.Errorf("have=%q, want=%q", string(out), want)
}
})
t.Run("gometalinter exemption", func(t *testing.T) {
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("unable to make temp dir: %s", err)
}
defer func() {
_ = os.RemoveAll(dir)
}()

runRetoolCmd(t, dir, retool, "add", "github.com/alecthomas/gometalinter", "origin/master")
runRetoolCmd(t, dir, retool, "do", "gometalinter", "--install")

// Create a dummy go file so gometalinter runs. If we don't do this,
// gometalinter will exit without doing any work, and we'll get a false
// positive.
//
// The file will be removed with the deferred os.RemoveAll(dir) call, no
// need to remove it here.
f, err := os.Create(filepath.Join(dir, "main.go"))
if err != nil {
t.Fatalf("unable to create file for gometalinter to run against: %s", err)
}
defer func() {
if closeErr := f.Close(); closeErr != nil {
t.Errorf("unable to close gometalinter test file: %s", closeErr)
}
}()
_, err = io.WriteString(f, `package main
func main() {}`)
if err != nil {
t.Fatalf("unable to write gometalinter test file: %s", err)
}

// If gometalinter can't find its tools, it will exit with code 2.
runRetoolCmd(t, dir, retool, "do", "gometalinter", ".")

// Make sure gometalinter installs to the tool directory, not to the global
// GOPATH.
assertBinInstalled(t, dir, "structcheck")
})
}

func runRetoolCmd(t *testing.T, dir, retool string, args ...string) (output string) {
Expand All @@ -105,7 +171,7 @@ func runRetoolCmd(t *testing.T, dir, retool string, args ...string) (output stri
out, err := cmd.Output()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
t.Fatalf("command %q failed: %s", "retool "+strings.Join(cmd.Args[1:], " "), string(exitErr.Stderr))
t.Fatalf("command %q failed, stderr:\n%s\n\nstdout:%s", "retool "+strings.Join(cmd.Args[1:], " "), string(exitErr.Stderr), string(out))
} else {
t.Fatalf("unexpected err when running %q: %q", strings.Join(cmd.Args, " "), err)
}
Expand Down
14 changes: 2 additions & 12 deletions sync.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package main

import (
"os/exec"
"path/filepath"
)
import "os/exec"

func (s spec) sync() {
m := getManifest()
Expand All @@ -23,21 +20,14 @@ func (s spec) sync() {
fatal("failed to ensure tool dir", err)
}

// Download everything to cache
// Download everything to tool directory
for _, t := range s.Tools {
err = download(t)
if err != nil {
fatalExec("failed to sync "+t.Repository, err)
}
}

// Copy the cache into the tools directory
cmd = exec.Command("cp", "-R", filepath.Join(cacheDir, "src"), filepath.Join(toolDirPath, "src"))
_, err = cmd.Output()
if err != nil {
fatalExec("failed to copy data from cache ", err)
}

// Install the packages
s.build()

Expand Down
12 changes: 3 additions & 9 deletions tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type tool struct {
}

func (t *tool) path() string {
return filepath.Join(cacheDir, "src", t.Repository)
return filepath.Join(toolDirPath, "src", t.Repository)
}

func (t *tool) executable() string {
Expand Down Expand Up @@ -48,15 +48,9 @@ func setEnvVar(cmd *exec.Cmd, key, val string) {
}

func get(t *tool) error {
// If the repo is already downloaded to the cache, then we can exit early
if _, err := os.Stat(filepath.Join(cacheDir, "src", t.Repository)); err == nil {
log(t.Repository + " already exists, skipping 'get' step")
return nil
}

log("downloading " + t.Repository)
cmd := exec.Command("go", "get", "-d", t.Repository)
setEnvVar(cmd, "GOPATH", cacheDir)
setEnvVar(cmd, "GOPATH", toolDirPath)
_, err := cmd.Output()
if err != nil {
return errors.Wrap(err, "failed to 'go get' tool")
Expand Down Expand Up @@ -114,7 +108,7 @@ func setVersion(t *tool) error {

// Re-run 'go get' in case the new version has a different set of dependencies.
cmd = exec.Command("go", "get", "-d", t.Repository)
setEnvVar(cmd, "GOPATH", cacheDir)
setEnvVar(cmd, "GOPATH", toolDirPath)
_, err = cmd.Output()
if err != nil {
return errors.Wrap(err, "failed to 'go get' tool")
Expand Down

0 comments on commit d6b4af0

Please sign in to comment.