Skip to content

Commit

Permalink
fix: avoid unnecessary git checks (#339)
Browse files Browse the repository at this point in the history
Co-authored-by: Tiago Cesar Katcipis <tiagokatcipis@gmail.com>
  • Loading branch information
i4ki and katcipis committed May 11, 2022
1 parent 6591957 commit c9a486b
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 125 deletions.
91 changes: 52 additions & 39 deletions cmd/terramate/cli/cli.go
Expand Up @@ -319,53 +319,17 @@ func (c *cli) run() {

c.checkVersion()

if c.parsedArgs.Changed {
logger.Trace().Msg("`Changed` flag was set.")

logger.Trace().Msg("Create new git wrapper.")

git, err := newGit(c.root(), true)
if err != nil {
log.Fatal().
Err(err).
Msg("creating git wrapper.")
}

logger.Trace().Msg("Check git default branch was updated.")

if err := c.prj.checkLocalDefaultIsUpdated(git); err != nil {
log.Fatal().
Err(err).
Msg("checking git default branch was updated.")
}
}

logger.Debug().Msg("Handle command.")

switch c.ctx.Command() {
case "list":
log.Trace().
Str("actionContext", "cli()").
Msg("Print list of stacks.")
c.printStacks()
case "run":
logger.Debug().Msg("Handle `run` command.")

if len(c.parsedArgs.Run.Command) == 0 {
log.Fatal().Msg("no command specified")
}
fallthrough
log.Fatal().Msg("no command specified")
case "run <cmd>":
logger.Debug().Msg("Handle `run <cmd>` command.")

c.runOnStacks()
case "generate":
report := generate.Do(c.root(), c.wd())
c.log(report.String())

if report.HasFailures() {
os.Exit(1)
}
c.generate()
case "experimental globals":
c.printStacksGlobals()
case "experimental metadata":
Expand All @@ -383,6 +347,45 @@ func (c *cli) run() {
}
}

func (c *cli) checkGit() {
logger := log.With().
Str("action", "checkGit()").
Logger()

logger.Trace().Msg("Check git default remote.")

if err := c.prj.checkDefaultRemote(); err != nil {
log.Fatal().
Err(err).
Msg("Checking git default remote.")
}

if c.parsedArgs.GitChangeBase != "" {
c.prj.baseRef = c.parsedArgs.GitChangeBase
} else {
c.prj.baseRef = c.prj.defaultBaseRef()
}

if c.parsedArgs.Changed {
logger.Trace().Msg("Change detection is on: check git default branch was updated")

if err := c.prj.checkLocalDefaultIsUpdated(); err != nil {
log.Fatal().
Err(err).
Msg("checking git default branch was updated.")
}
}
}

func (c *cli) generate() {
report := generate.Do(c.root(), c.wd())
c.log(report.String())

if report.HasFailures() {
os.Exit(1)
}
}

func (c *cli) initStack(dirs []string) {
var errmsgs []string

Expand Down Expand Up @@ -469,6 +472,10 @@ func (c *cli) printStacks() {
Str("action", "printStacks()").
Logger()

if c.parsedArgs.Changed {
c.checkGit()
}

if c.parsedArgs.List.Why && !c.parsedArgs.Changed {
logger.Fatal().
Msg("the --why flag must be used together with --changed")
Expand Down Expand Up @@ -814,6 +821,12 @@ func (c *cli) runOnStacks() {
Str("workingDir", c.wd()).
Logger()

c.checkGit()

if len(c.parsedArgs.Run.Command) == 0 {
logger.Fatal().Msgf("run expects a cmd")
}

stacks, err := c.computeSelectedStacks(true)
if err != nil {
logger.Fatal().
Expand Down Expand Up @@ -1035,7 +1048,6 @@ func lookupProject(wd string) (prj project, found bool, err error) {
logger.Trace().Msg("Get root of git repo.")

gitdir, err := gw.Root()

if err == nil {
logger.Trace().Msg("Get absolute path of git directory.")

Expand Down Expand Up @@ -1068,6 +1080,7 @@ func lookupProject(wd string) (prj project, found bool, err error) {
prj.isRepo = true
prj.rootcfg = cfg
prj.root = root
prj.git.wrapper = gw

return prj, true, nil
}
Expand Down
141 changes: 60 additions & 81 deletions cmd/terramate/cli/project.go
Expand Up @@ -31,72 +31,97 @@ type project struct {
baseRef string

git struct {
headCommitID string
localDefaultBranchCommitID string
remoteDefaultBranchCommitID string
wrapper *git.Git
headCommit string
localDefaultBranchCommit string
remoteDefaultBranchCommit string
}
}

func (p project) gitcfg() *hcl.GitConfig {
return p.rootcfg.Terramate.RootConfig.Git
}

func (p *project) parseLocalDefaultBranch(g *git.Git) error {
func (p *project) localDefaultBranchCommit() string {
if p.git.localDefaultBranchCommit != "" {
return p.git.localDefaultBranchCommit
}
logger := log.With().
Str("action", "localDefaultBranchCommit()").
Logger()

gitcfg := p.gitcfg()
refName := gitcfg.DefaultRemote + "/" + gitcfg.DefaultBranch
val, err := g.RevParse(refName)
val, err := p.git.wrapper.RevParse(refName)
if err != nil {
return err
logger.Fatal().Err(err).Send()
}

p.git.localDefaultBranchCommitID = val
return nil
p.git.localDefaultBranchCommit = val
return val
}

func (p *project) parseHead(g *git.Git) error {
val, err := g.RevParse("HEAD")
func (p *project) headCommit() string {
if p.git.headCommit != "" {
return p.git.headCommit
}

logger := log.With().
Str("action", "headCommit()").
Logger()

val, err := p.git.wrapper.RevParse("HEAD")
if err != nil {
return err
logger.Fatal().Err(err).Send()
}

p.git.headCommitID = val
return nil
p.git.headCommit = val
return val
}

func (p *project) parseRemoteDefaultBranch(g *git.Git) error {
func (p *project) remoteDefaultCommit() string {
if p.git.remoteDefaultBranchCommit != "" {
return p.git.remoteDefaultBranchCommit
}

logger := log.With().
Str("action", "remoteDefaultCommit()").
Logger()

gitcfg := p.gitcfg()
remoteRef, err := g.FetchRemoteRev(gitcfg.DefaultRemote, gitcfg.DefaultBranch)
remoteRef, err := p.git.wrapper.FetchRemoteRev(gitcfg.DefaultRemote, gitcfg.DefaultBranch)
if err != nil {
return fmt.Errorf("fetching remote commit of %s/%s: %v",
gitcfg.DefaultRemote, gitcfg.DefaultBranch,
err,
)
logger.Fatal().Err(
fmt.Errorf("fetching remote commit of %s/%s: %v",
gitcfg.DefaultRemote, gitcfg.DefaultBranch,
err,
)).Send()
}

p.git.remoteDefaultBranchCommitID = remoteRef.CommitID
return nil
p.git.remoteDefaultBranchCommit = remoteRef.CommitID
return p.git.remoteDefaultBranchCommit
}

func (p *project) isDefaultBranch(g *git.Git) bool {
func (p *project) isDefaultBranch() bool {
git := p.gitcfg()
branch, err := g.CurrentBranch()
branch, err := p.git.wrapper.CurrentBranch()
if err != nil {
// WHY?
// The current branch name (the symbolic-ref of the HEAD) is not always
// available, in this case we naively check if HEAD == local origin/main.
// This case usually happens in the git setup of CIs.
return p.git.localDefaultBranchCommitID == p.git.headCommitID
return p.localDefaultBranchCommit() == p.headCommit()
}

return branch == git.DefaultBranch
}

// defaultBaseRef returns the baseRef for the current git environment.
func (p *project) defaultBaseRef(g *git.Git) string {
func (p *project) defaultBaseRef() string {
git := p.gitcfg()
if p.isDefaultBranch(g) &&
p.git.remoteDefaultBranchCommitID == p.git.headCommitID {
_, err := g.RevParse(git.DefaultBranchBaseRef)
if p.isDefaultBranch() &&
p.remoteDefaultCommit() == p.headCommit() {
_, err := p.git.wrapper.RevParse(git.DefaultBranchBaseRef)
if err == nil {
return git.DefaultBranchBaseRef
}
Expand Down Expand Up @@ -146,55 +171,17 @@ func (p *project) setDefaults(parsedArgs *cliSpec) error {
gitOpt.DefaultRemote = defaultRemote
}

if p.isRepo {
logger.Trace().Msg("Create new git wrapper.")

gw, err := newGit(p.wd, false)
if err != nil {
return err
}

logger.Trace().Msg("Check git default remote.")

if err := p.checkDefaultRemote(gw); err != nil {
log.Fatal().
Err(err).
Msg("Checking git default remote.")
}

err = p.parseLocalDefaultBranch(gw)
if err != nil {
return err
}

err = p.parseRemoteDefaultBranch(gw)
if err != nil {
return err
}

err = p.parseHead(gw)
if err != nil {
return err
}

if parsedArgs.GitChangeBase != "" {
p.baseRef = parsedArgs.GitChangeBase
} else {
p.baseRef = p.defaultBaseRef(gw)
}
}

return nil
}

func (p project) checkDefaultRemote(g *git.Git) error {
func (p project) checkDefaultRemote() error {
logger := log.With().
Str("action", "checkDefaultRemote()").
Logger()

logger.Trace().Msg("Get list of configured git remotes.")

remotes, err := g.Remotes()
remotes, err := p.git.wrapper.Remotes()
if err != nil {
return fmt.Errorf("checking if remote %q exists: %v", defaultRemote, err)
}
Expand Down Expand Up @@ -232,28 +219,20 @@ func (p project) checkDefaultRemote(g *git.Git) error {
)
}

func (p *project) checkLocalDefaultIsUpdated(g *git.Git) error {
func (p *project) checkLocalDefaultIsUpdated() error {
logger := log.With().
Str("action", "checkLocalDefaultIsUpdated()").
Str("workingDir", p.wd).
Logger()

logger.Trace().Msg("Create new git wrapper.")

gw, err := newGit(p.wd, false)
if err != nil {
return err
}

if !p.isDefaultBranch(gw) {
if !p.isDefaultBranch() {
return nil
}

gitcfg := p.gitcfg()

logger.Trace().Msg("Fetch remote reference.")

mergeBaseCommitID, err := g.MergeBase(p.git.headCommitID, p.git.remoteDefaultBranchCommitID)
gitcfg := p.gitcfg()
mergeBaseCommitID, err := p.git.wrapper.MergeBase(p.headCommit(), p.remoteDefaultCommit())
if err != nil {
return fmt.Errorf(
"the reference %s/%s is not reachable from HEAD: %w",
Expand All @@ -263,7 +242,7 @@ func (p *project) checkLocalDefaultIsUpdated(g *git.Git) error {
)
}

if mergeBaseCommitID != p.git.remoteDefaultBranchCommitID {
if mergeBaseCommitID != p.remoteDefaultCommit() {
return errors.E(
ErrOutdatedLocalRev,
"remote %s/%s != HEAD",
Expand Down
7 changes: 2 additions & 5 deletions cmd/terramate/cli/project_test.go
Expand Up @@ -19,7 +19,6 @@ import (

"github.com/madlambda/spells/assert"
"github.com/mineiros-io/terramate/errors"
"github.com/mineiros-io/terramate/test"
"github.com/mineiros-io/terramate/test/sandbox"
"github.com/rs/zerolog"
)
Expand Down Expand Up @@ -54,16 +53,14 @@ func TestLocalDefaultIsOutdated(t *testing.T) {
git.CheckoutNew("main")

prj, foundRoot, err := lookupProject(s.RootDir())

assert.NoError(t, err)

if !foundRoot {
t.Fatal("unable to find root")
}

assert.NoError(t, prj.setDefaults(&cliSpec{}))

g := test.NewGitWrapper(t, s.RootDir(), []string{})
assert.IsError(t, prj.checkLocalDefaultIsUpdated(g), errors.E(ErrOutdatedLocalRev))
assert.IsError(t, prj.checkLocalDefaultIsUpdated(), errors.E(ErrOutdatedLocalRev))
}

func init() {
Expand Down

0 comments on commit c9a486b

Please sign in to comment.