// cmdInstall first calls cmdInstallPrereq() then install the // .git/hooks/pre-commit and pre-push hooks. // // Silently ignore installing the hooks when running under a CI. In // particular, circleci.com doesn't create the directory .git/hooks. func (a *application) cmdInstall(repo scm.ReadOnlyRepo, modes []checks.Mode, noUpdate bool, prereqReady *sync.WaitGroup) (err error) { errCh := make(chan error, 1) go func() { defer prereqReady.Done() errCh <- a.cmdInstallPrereq(repo, modes, noUpdate) }() defer func() { if err2 := <-errCh; err == nil { err = err2 } }() if checks.IsContinuousIntegration() { log.Printf("Running under CI; not installing hooks") return nil } log.Printf("Installing hooks") hookDir, err2 := repo.HookPath() if err2 != nil { return err2 } for _, t := range []string{"pre-commit", "pre-push"} { // Always remove hook first if it exists, in case it's a symlink. p := filepath.Join(hookDir, t) _ = os.Remove(p) if err = ioutil.WriteFile(p, []byte(fmt.Sprintf(hookContent, t)), 0777); err != nil { return err } } log.Printf("Installation done") return nil }
// mainImpl implements pcg. func mainImpl() error { a := application{} exec, args := os.Args[0], os.Args[1:] var commands, flags []string for i, arg := range args { if strings.HasPrefix(arg, "-") { flags = args[i:] break } commands = append(commands, arg) } if len(commands) == 0 { if checks.IsContinuousIntegration() { commands = []string{"run-hook", "continuous-integration"} } else { commands = []string{"installrun"} } } fs := flag.NewFlagSet(exec, flag.ExitOnError) verboseFlag := fs.Bool("v", checks.IsContinuousIntegration() || os.Getenv("VERBOSE") != "", "enables verbose logging output") allFlag := fs.Bool("a", false, "runs checks as if all files had been modified") againstFlag := fs.String("r", "", "runs checks on files modified since this revision, as evaluated by your scm repo") noUpdateFlag := fs.Bool("n", false, "disallow using go get even if a prerequisite is missing; bail out instead") configPathFlag := fs.String("c", "pre-commit-go.yml", "file name of the config to load") modeFlag := fs.String("m", "", "comma separated list of modes to process; default depends on the command") fs.IntVar(&a.maxConcurrent, "C", 0, "maximum number of concurrent processes") fs.Parse(flags) if *allFlag { if *againstFlag != "" { return errors.New("-a can't be used with -r") } *againstFlag = string(scm.Initial) } log.SetFlags(log.Lmicroseconds) if !*verboseFlag { log.SetOutput(ioutil.Discard) } modes, err := processModes(*modeFlag) if err != nil { return err } cwd, err := os.Getwd() if err != nil { return err } repo, err := scm.GetRepo(cwd, "") if err != nil { return err } var configPath string configPath, a.config = loadConfig(repo, *configPathFlag) log.Printf("config: %s", configPath) if a.maxConcurrent > 0 { log.Printf("using %d maximum concurrent goroutines", a.maxConcurrent) a.config.MaxConcurrent = a.maxConcurrent } switch cmd := commands[0]; cmd { case "help", "-help", "-h": cmd = "help" if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if *noUpdateFlag != false { return fmt.Errorf("-n can't be used with %s", cmd) } if *configPathFlag != "pre-commit-go.yml" { return fmt.Errorf("-m can't be used with %s", cmd) } if *modeFlag != "" { return fmt.Errorf("-m can't be used with %s", cmd) } b := &bytes.Buffer{} fs.SetOutput(b) fs.PrintDefaults() return a.cmdHelp(repo, b.String()) case "info": if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if *noUpdateFlag != false { return fmt.Errorf("-n can't be used with %s", cmd) } return a.cmdInfo(repo, modes, configPath) case "install", "i": cmd = "install" if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if len(modes) == 0 { modes = checks.AllModes } var prereqReady sync.WaitGroup prereqReady.Add(1) return a.cmdInstall(repo, modes, *noUpdateFlag, &prereqReady) case "installrun": if len(modes) == 0 { modes = []checks.Mode{checks.PrePush} } // Start running all checks that do not have a prerequisite before // installation is completed. var prereqReady sync.WaitGroup prereqReady.Add(1) errCh := make(chan error, 1) go func() { errCh <- a.cmdInstall(repo, modes, *noUpdateFlag, &prereqReady) }() err := a.cmdRun(repo, modes, *againstFlag, &prereqReady) if err2 := <-errCh; err2 != nil { return err2 } return err case "prereq", "p": cmd = "prereq" if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if len(modes) == 0 { modes = checks.AllModes } return a.cmdInstallPrereq(repo, modes, *noUpdateFlag) case "run", "r": cmd = "run" if *noUpdateFlag != false { return fmt.Errorf("-n can't be used with %s", cmd) } if len(modes) == 0 { modes = []checks.Mode{checks.PrePush} } return a.cmdRun(repo, modes, *againstFlag, &sync.WaitGroup{}) case "run-hook": if modes != nil { return fmt.Errorf("-m can't be used with %s", cmd) } if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if len(commands) < 2 { return errors.New("run-hook is only meant to be used by hooks") } return a.cmdRunHook(repo, commands[1], *noUpdateFlag) case "version": if modes != nil { return fmt.Errorf("-m can't be used with %s", cmd) } if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if *noUpdateFlag != false { return fmt.Errorf("-n can't be used with %s", cmd) } fmt.Println(version) return nil case "writeconfig", "w": if modes != nil { return fmt.Errorf("-m can't be used with %s", cmd) } if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } // Note that in that case, configPath is ignored and not overritten. return a.cmdWriteConfig(repo, *configPathFlag) default: return fmt.Errorf("unknown command %q, try 'help'", cmd) } }
// mainImpl implements pcg. func mainImpl() error { if len(os.Args) == 1 { if checks.IsContinuousIntegration() { os.Args = append(os.Args, "run-hook", "continuous-integration") } else { os.Args = append(os.Args, "installrun") } } cmd := os.Args[1] copy(os.Args[1:], os.Args[2:]) os.Args = os.Args[:len(os.Args)-1] verboseFlag := flag.Bool("v", checks.IsContinuousIntegration() || os.Getenv("VERBOSE") != "", "enables verbose logging output") allFlag := flag.Bool("a", false, "runs checks as if all files had been modified") againstFlag := flag.String("r", "", "runs checks on files modified since this revision, as evaluated by your scm repo") noUpdateFlag := flag.Bool("n", false, "disallow using go get even if a prerequisite is missing; bail out instead") configPathFlag := flag.String("c", "pre-commit-go.yml", "file name of the config to load") modeFlag := flag.String("m", "", "coma separated list of modes to process; default depends on the command") flag.Parse() if *allFlag { if *againstFlag != "" { return errors.New("-a can't be used with -r") } *againstFlag = string(scm.GitInitialCommit) } log.SetFlags(log.Lmicroseconds) if !*verboseFlag { log.SetOutput(ioutil.Discard) } modes, err := processModes(*modeFlag) if err != nil { return err } cwd, err := os.Getwd() if err != nil { return err } repo, err := scm.GetRepo(cwd, "") if err != nil { return err } configPath, config := loadConfig(repo, *configPathFlag) log.Printf("config: %s", configPath) switch cmd { case "help", "-help", "-h": cmd = "help" if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if *noUpdateFlag != false { return fmt.Errorf("-n can't be used with %s", cmd) } if *configPathFlag != "pre-commit-go.yml" { return fmt.Errorf("-m can't be used with %s", cmd) } if *modeFlag != "" { return fmt.Errorf("-m can't be used with %s", cmd) } b := &bytes.Buffer{} flag.CommandLine.SetOutput(b) flag.CommandLine.PrintDefaults() return cmdHelp(repo, config, b.String()) case "info": if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if *noUpdateFlag != false { return fmt.Errorf("-n can't be used with %s", cmd) } return cmdInfo(repo, config, modes, configPath) case "install", "i": cmd = "install" if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if len(modes) == 0 { modes = checks.AllModes } var prereqReady sync.WaitGroup prereqReady.Add(1) return cmdInstall(repo, config, modes, *noUpdateFlag, &prereqReady) case "installrun": if len(modes) == 0 { modes = []checks.Mode{checks.PrePush} } // Start running all checks that do not have a prerequisite before // installation is completed. var prereqReady sync.WaitGroup prereqReady.Add(1) errCh := make(chan error, 1) go func() { errCh <- cmdInstall(repo, config, modes, *noUpdateFlag, &prereqReady) }() err := cmdRun(repo, config, modes, *againstFlag, &prereqReady) if err2 := <-errCh; err2 != nil { return err2 } return err case "prereq", "p": cmd = "prereq" if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if len(modes) == 0 { modes = checks.AllModes } return cmdInstallPrereq(repo, config, modes, *noUpdateFlag) case "run", "r": cmd = "run" if *noUpdateFlag != false { return fmt.Errorf("-n can't be used with %s", cmd) } if len(modes) == 0 { modes = []checks.Mode{checks.PrePush} } return cmdRun(repo, config, modes, *againstFlag, &sync.WaitGroup{}) case "run-hook": if modes != nil { return fmt.Errorf("-m can't be used with %s", cmd) } if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if flag.NArg() != 1 { return errors.New("run-hook is only meant to be used by hooks") } return cmdRunHook(repo, config, flag.Arg(0), *noUpdateFlag) case "version": if modes != nil { return fmt.Errorf("-m can't be used with %s", cmd) } if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } if *noUpdateFlag != false { return fmt.Errorf("-n can't be used with %s", cmd) } fmt.Println(version) return nil case "writeconfig", "w": if modes != nil { return fmt.Errorf("-m can't be used with %s", cmd) } if *allFlag != false { return fmt.Errorf("-a can't be used with %s", cmd) } if *againstFlag != "" { return fmt.Errorf("-r can't be used with %s", cmd) } // Note that in that case, configPath is ignored and not overritten. return cmdWriteConfig(repo, config, *configPathFlag) default: return errors.New("unknown command, try 'help'") } }