devcam fixv,hook: docs, bugfix, cleanup.

Change-Id: Ie5f9f9ea62c13221d6b9c9913ef953ab39fb7914
This commit is contained in:
mpl 2015-08-03 19:23:58 +02:00
parent c9027c2c7f
commit 9a24acca6c
3 changed files with 76 additions and 65 deletions

View File

@ -26,6 +26,7 @@ import (
"os/signal"
pathpkg "path"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
@ -153,9 +154,9 @@ func handleSignals(camliProc *os.Process) {
func checkCamliSrcRoot() {
args := flag.Args()
// TODO(mpl): we should probably get rid of that limitation someday.
if len(args) > 0 && args[0] == "review" ||
if len(args) > 0 && (args[0] == "review" ||
args[0] == "hook" ||
args[0] == "fixv" {
args[0] == "fixv") {
// exception for devcam review, which does its own check.
return
}
@ -172,6 +173,26 @@ func checkCamliSrcRoot() {
camliSrcRoot = cwd
}
func repoRoot() (string, error) {
dir, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("could not get current directory: %v", err)
}
rootlen := 1
if runtime.GOOS == "windows" {
rootlen += len(filepath.VolumeName(dir))
}
for {
if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
return dir, nil
}
if len(dir) == rootlen && dir[rootlen-1] == filepath.Separator {
return "", fmt.Errorf(".git not found. Rerun from within the Camlistore source tree.")
}
dir = filepath.Dir(dir)
}
}
func selfModTime() (time.Time, error) {
var modTime time.Time
devcamBin, err := osutil.SelfPath()

View File

@ -59,7 +59,18 @@ func init() {
}
func (c *fixvCmd) Usage() {
fmt.Fprintf(cmdmain.Stderr, "Usage: devcam [globalopts] fixv [args...]\n")
cmdmain.Errorf("Usage: devcam [globalopts] fixv [args...]\n")
}
func (c *fixvCmd) Describe() string {
return "Check, and optionally fix, import statements in vendored files."
}
func (c *fixvCmd) Examples() []string {
return []string{
"-w # automatically fix the imports in the vendored files from the git staging area",
"/foo/bar.go # assume /foo/bar.go is vendored, and check if it needs to have its import fixed",
}
}
func (c *fixvCmd) RunCommand(args []string) error {
@ -72,12 +83,19 @@ func (c *fixvCmd) run(args []string) (tofix []string, err error) {
if len(args) != 0 {
vendoredFiles = args
} else {
repo := repoRoot()
repo, err := repoRoot()
if err != nil {
return nil, err
}
if !strings.HasSuffix(repo, string(filepath.Separator)) {
repo += string(filepath.Separator)
}
vendoredFiles = addRoot(repo, filter(isVendored, nonBlankLines(cmdOutput("git", "diff-index", "--name-only", "--diff-filter=ACM", "--cached", "HEAD", "--"))))
out, err := cmdOutputDirErr(".", "git", "diff-index", "--name-only", "--diff-filter=ACM", "--cached", "HEAD", "--")
if err != nil {
return nil, err
}
vendoredFiles = addRoot(repo, filter(isVendored, nonBlankLines(out)))
if len(vendoredFiles) == 0 {
return nil, nil
}
@ -92,7 +110,7 @@ func (c *fixvCmd) run(args []string) (tofix []string, err error) {
continue
}
if !c.fix {
fmt.Fprintf(cmdmain.Stderr, "%v imports need fixing\n", filename)
cmdmain.Errorf("%v imports need fixing\n", filename)
tofix = append(tofix, filename)
continue
}
@ -103,7 +121,7 @@ func (c *fixvCmd) run(args []string) (tofix []string, err error) {
if err := ioutil.WriteFile(filename, data, 0600); err != nil {
return nil, fmt.Errorf("failed to write modified file %v: %v", filename, err)
}
fmt.Fprintf(cmdmain.Stderr, "%v imports now fixed\n", filename)
cmdmain.Errorf("%v imports now fixed\n", filename)
}
if !c.fix && len(tofix) > 0 {
return tofix, errImportsNeedsFixing

View File

@ -26,7 +26,6 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"sort"
"strings"
@ -39,8 +38,12 @@ var hookFiles = []string{
}
func (c *hookCmd) installHook() error {
root, err := repoRoot()
if err != nil {
return err
}
for _, hookFile := range hookFiles {
filename := filepath.Join(repoRoot(), hookPath+hookFile)
filename := filepath.Join(root, hookPath+hookFile)
hookContent := fmt.Sprintf(hookScript, hookFile)
// If hook file exists, assume it is okay.
_, err := os.Stat(filename)
@ -73,27 +76,32 @@ exec devcam hook %s "$@"
type hookCmd struct {
verbose bool
fix bool // disabled for now
debug bool
}
func init() {
cmdmain.RegisterCommand("hook", func(flags *flag.FlagSet) cmdmain.CommandRunner {
cmd := &hookCmd{}
flags.BoolVar(&cmd.verbose, "verbose", false, "Be verbose.")
flags.BoolVar(&cmd.debug, "debug", false, "Arguments after the hook name are files that will be used as input to the hook, instead of the hook using the staging area.")
// TODO(mpl): "-w" flag to run gofmt -w and devcam fixv -w. for now just print instruction.
// flags.BoolVar(&cmd.fix, "w", false, "Perform appropriate fixes, for hooks like pre-commit.")
return cmd
})
}
// TODO(mpl): more docs, examples. Also doc in website to tell ppl to use it.
func (c *hookCmd) Usage() {
printf("Usage: devcam [globalopts] hook [[hook-name] [args...]]\n")
}
func (c *hookCmd) Examples() []string {
return []string{
"# install the hooks (if needed)",
"pre-commit # install the hooks (if needed), then run the pre-commit hook",
}
}
func (c *hookCmd) Describe() string {
return "Install git hooks for Camlistore, and if given, run the hook given as argument. Currently available hooks are: " + strings.TrimSuffix(strings.Join(hookFiles, ", "), ",") + "."
}
func (c *hookCmd) RunCommand(args []string) error {
if err := c.installHook(); err != nil {
return err
@ -105,9 +113,8 @@ func (c *hookCmd) RunCommand(args []string) error {
case "pre-commit":
if err := c.hookPreCommit(args[1:]); err != nil {
printf("You can override these checks with 'git commit --no-verify'\n")
// TODO(mpl): make sure that by exiting "early" we're not skipping some post-RunCommand
// stuff controlled by cmdmain.Main
os.Exit(1)
cmdmain.ExitWithFailure = true
return err
}
}
return nil
@ -140,7 +147,7 @@ func (c *hookCmd) hookGofmt() error {
files, err := c.runGofmt()
if err != nil {
printf("gofmt reported errors:\n\t%v\n", strings.Replace(strings.TrimSpace(err.Error()), "\n", "\n\t", -1))
printf("gofmt hook reported errors:\n\t%v\n", strings.Replace(strings.TrimSpace(err.Error()), "\n", "\n\t", -1))
return errors.New("gofmt errors")
}
if len(files) == 0 {
@ -167,7 +174,7 @@ func (c *hookCmd) hookTrailingSpace() error {
func (c *hookCmd) hookVendoredImports(args []string) error {
tofix, err := (&fixvCmd{
verbose: c.verbose,
fix: c.fix,
fix: false,
}).run(args)
if err != nil {
if err == errImportsNeedsFixing {
@ -183,12 +190,19 @@ func (c *hookCmd) hookVendoredImports(args []string) error {
// runGofmt runs the external gofmt command over the local version of staged files.
// It returns the files that need gofmting.
func (c *hookCmd) runGofmt() (files []string, err error) {
repo := repoRoot()
repo, err := repoRoot()
if err != nil {
return nil, err
}
if !strings.HasSuffix(repo, string(filepath.Separator)) {
repo += string(filepath.Separator)
}
indexFiles := addRoot(repo, filter(gofmtRequired, nonBlankLines(cmdOutput("git", "diff-index", "--name-only", "--diff-filter=ACM", "--cached", "HEAD", "--"))))
out, err := cmdOutputDirErr(".", "git", "diff-index", "--name-only", "--diff-filter=ACM", "--cached", "HEAD", "--")
if err != nil {
return nil, err
}
indexFiles := addRoot(repo, filter(gofmtRequired, nonBlankLines(out)))
if len(indexFiles) == 0 {
return
}
@ -225,32 +239,7 @@ func (c *hookCmd) runGofmt() (files []string, err error) {
}
func printf(format string, args ...interface{}) {
fmt.Fprintf(cmdmain.Stderr, format, args...)
}
func dief(format string, args ...interface{}) {
printf(format, args...)
os.Exit(1)
}
func repoRoot() string {
dir, err := os.Getwd()
if err != nil {
dief("could not get current directory: %v", err)
}
rootlen := 1
if runtime.GOOS == "windows" {
rootlen += len(filepath.VolumeName(dir))
}
for {
if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
return dir
}
if len(dir) == rootlen && dir[rootlen-1] == filepath.Separator {
dief(".git not found. Rerun from within the Camlistore source tree.")
}
dir = filepath.Dir(dir)
}
cmdmain.Errorf(format, args...)
}
func addRoot(root string, list []string) []string {
@ -315,23 +304,6 @@ func (c *hookCmd) verbosef(format string, args ...interface{}) {
}
}
// cmdOutput runs the command line, returning its output.
// If the command cannot be run or does not exit successfully,
// cmdOutput dies.
//
// NOTE: cmdOutput must be used only to run commands that read state,
// not for commands that make changes. Commands that make changes
// should be run using runDirErr so that the -v and -n flags apply to them.
func cmdOutput(command string, args ...string) string {
out, err := cmdOutputDirErr(".", command, args...)
if err != nil {
printf("%v\n", err)
// TODO(mpl): maybe not die. see other comment about cmdmain.Main.
os.Exit(1)
}
return out
}
// cmdOutputDirErr runs the command line in dir, returning its output
// and any error results.
//