// cmdRunHook runs the checks in a git repository. // // Use a precise "stash, run checks, unstash" to ensure that the check is // properly run on the data in the index. func (a *application) cmdRunHook(repo scm.Repo, mode string, noUpdate bool) error { switch checks.Mode(mode) { case checks.PreCommit: return a.runPreCommit(repo) case checks.PrePush: return a.runPrePush(repo) case checks.ContinuousIntegration: // Always runs all tests on CI. change, err := repo.Between(scm.Current, scm.Initial, a.config.IgnorePatterns) if err != nil { return err } mode := []checks.Mode{checks.ContinuousIntegration} // This is a special case, some users want reproducible builds and in this // case they do not want any external reference and want to enforce // noUpdate, but many people may not care (yet). So default to fetching but // it can be overriden. var prereqReady sync.WaitGroup errCh := make(chan error, 1) prereqReady.Add(1) go func() { defer prereqReady.Done() errCh <- a.cmdInstallPrereq(repo, mode, noUpdate) }() err = a.runChecks(change, mode, &prereqReady) if err2 := <-errCh; err2 != nil { return err2 } return err default: return errors.New("unsupported hook type for run-hook") } }
func runPreCommit(repo scm.Repo, config *checks.Config) error { // First, stash index and work dir, keeping only the to-be-committed changes // in the working directory. stashed, err := repo.Stash() if err != nil { return err } // Run the checks. var change scm.Change change, err = repo.Between(scm.Current, repo.HEAD(), config.IgnorePatterns) if change != nil { err = runChecks(config, change, []checks.Mode{checks.PreCommit}, &sync.WaitGroup{}) } // If stashed is false, everything was in the index so no stashing was needed. if stashed { if err2 := repo.Restore(); err == nil { err = err2 } } return err }
func (a *application) runPreCommit(repo scm.Repo) error { // First, stash index and work dir, keeping only the to-be-committed changes // in the working directory. // TODO(maruel): When running for an git commit --amend run, use HEAD~1. stashed, err := repo.Stash() if err != nil { return err } // Run the checks. var change scm.Change change, err = repo.Between(scm.Current, scm.Head, a.config.IgnorePatterns) if change != nil { err = a.runChecks(change, []checks.Mode{checks.PreCommit}, &sync.WaitGroup{}) } // If stashed is false, everything was in the index so no stashing was needed. if stashed { if err2 := repo.Restore(); err == nil { err = err2 } } return err }
func (a *application) runPrePush(repo scm.Repo) (err error) { previous := scm.Head // Will be "" if the current checkout was detached. previousRef := repo.Ref(scm.Head) curr := previous stashed := false defer func() { if curr != previous { p := previousRef if p == "" { p = string(previous) } if err2 := repo.Checkout(p); err == nil { err = err2 } } if stashed { if err2 := repo.Restore(); err == nil { err = err2 } } }() bio := bufio.NewReader(os.Stdin) line := "" triedToStash := false for { if line, err = bio.ReadString('\n'); err != nil { break } matches := rePrePush.FindStringSubmatch(line[:len(line)-1]) if len(matches) != 5 { return fmt.Errorf("unexpected stdin for pre-push: %q", line) } from := scm.Commit(matches[4]) to := scm.Commit(matches[2]) if to == gitNilCommit { // It's being deleted. continue } if to != curr { // Stash, checkout, run tests. if !triedToStash { // Only try to stash once. triedToStash = true if stashed, err = repo.Stash(); err != nil { return } } curr = to if err = repo.Checkout(string(to)); err != nil { return } } if from == gitNilCommit { from = scm.Initial } change, err := repo.Between(to, from, a.config.IgnorePatterns) if err != nil { return err } if err = a.runChecks(change, []checks.Mode{checks.PrePush}, &sync.WaitGroup{}); err != nil { return err } } if err == io.EOF { err = nil } return }