Skip to content

Commit

Permalink
feat: git change detection works on any commit
Browse files Browse the repository at this point in the history
  • Loading branch information
snakster committed Dec 29, 2023
1 parent d549213 commit 9b7088b
Show file tree
Hide file tree
Showing 6 changed files with 904 additions and 70 deletions.
24 changes: 9 additions & 15 deletions cmd/terramate/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,22 +627,16 @@ func (c *cli) setupGit() {
return
}

remoteCheckFailed := false

if err := c.prj.checkDefaultRemote(); err != nil {
if c.prj.git.remoteConfigured {
fatal(err, "checking git default remote")
} else {
remoteCheckFailed = true
}
}

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

Expand Down Expand Up @@ -763,7 +757,7 @@ func (c *cli) triggerStackByFilter() {
fatal(errors.E("trigger command expects either a stack path or the --experimental-status flag"))
}

mgr := stack.NewManager(c.cfg(), c.prj.baseRef)
mgr := stack.NewManager(c.cfg(), c.prj.baseRev)
status := parseStatusFilter(c.parsedArgs.Experimental.Trigger.ExperimentalStatus)
stacksReport, err := c.listStacks(mgr, false, status)
if err != nil {
Expand Down Expand Up @@ -1311,7 +1305,7 @@ func (c *cli) printStacks() {
log.Fatal().Msg("the --why flag must be used together with --changed")
}

mgr := stack.NewManager(c.cfg(), c.prj.baseRef)
mgr := stack.NewManager(c.cfg(), c.prj.baseRev)

status := parseStatusFilter(c.parsedArgs.List.ExperimentalStatus)
report, err := c.listStacks(mgr, c.parsedArgs.Changed, status)
Expand Down Expand Up @@ -1351,7 +1345,7 @@ func parseStatusFilter(strStatus string) cloudstack.FilterStatus {
}

func (c *cli) printRunEnv() {
mgr := stack.NewManager(c.cfg(), c.prj.baseRef)
mgr := stack.NewManager(c.cfg(), c.prj.baseRev)
report, err := c.listStacks(mgr, c.parsedArgs.Changed, cloudstack.NoFilter)
if err != nil {
fatal(err, "listing stacks")
Expand Down Expand Up @@ -1579,7 +1573,7 @@ func (c *cli) generateDebug() {
}

func (c *cli) printStacksGlobals() {
mgr := stack.NewManager(c.cfg(), c.prj.baseRef)
mgr := stack.NewManager(c.cfg(), c.prj.baseRev)
report, err := c.listStacks(mgr, c.parsedArgs.Changed, cloudstack.NoFilter)
if err != nil {
fatal(err, "listing stacks globals: listing stacks")
Expand Down Expand Up @@ -1613,7 +1607,7 @@ func (c *cli) printMetadata() {
Str("action", "cli.printMetadata()").
Logger()

mgr := stack.NewManager(c.cfg(), c.prj.baseRef)
mgr := stack.NewManager(c.cfg(), c.prj.baseRev)
report, err := c.listStacks(mgr, c.parsedArgs.Changed, cloudstack.NoFilter)
if err != nil {
fatal(err, "loading metadata: listing stacks")
Expand Down Expand Up @@ -1677,7 +1671,7 @@ func (c *cli) checkGenCode() bool {
}

func (c *cli) ensureStackID() {
mgr := stack.NewManager(c.cfg(), c.prj.baseRef)
mgr := stack.NewManager(c.cfg(), c.prj.baseRev)
report, err := c.listStacks(mgr, false, cloudstack.NoFilter)
if err != nil {
fatal(err, "listing stacks")
Expand Down Expand Up @@ -1922,7 +1916,7 @@ func (c *cli) friendlyFmtDir(dir string) (string, bool) {
}

func (c *cli) computeSelectedStacks(ensureCleanRepo bool) (config.List[*config.SortableStack], error) {
mgr := stack.NewManager(c.cfg(), c.prj.baseRef)
mgr := stack.NewManager(c.cfg(), c.prj.baseRev)

report, err := c.listStacks(mgr, c.parsedArgs.Changed, cloudstack.NoFilter)
if err != nil {
Expand Down
89 changes: 34 additions & 55 deletions cmd/terramate/cli/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type project struct {
wd string
isRepo bool
root config.Root
baseRef string
baseRev string
normalizedRepo string

git struct {
Expand Down Expand Up @@ -63,25 +63,6 @@ func (p *project) prettyRepo() string {
return p.normalizedRepo
}

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 := p.git.wrapper.RevParse(refName)
if err != nil {
logger.Fatal().Err(err).Send()
}

p.git.localDefaultBranchCommit = val
return val
}

func (p *project) isGitFeaturesEnabled() bool {
return p.isRepo && p.hasCommit()
}
Expand Down Expand Up @@ -137,50 +118,48 @@ func (p *project) remoteDefaultCommit() string {
return p.git.remoteDefaultBranchCommit
}

func (p *project) isDefaultBranch() bool {
git := p.gitcfg()
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.localDefaultBranchCommit() == p.headCommit()
// selectChangeBase returns the revision used for change comparison based on the current Git state.
func (p *project) selectChangeBase() string {
gitcfg := p.gitcfg()
gw := p.git.wrapper

defaultBranchRev, _ := gw.RevParse(gitcfg.DefaultBranch)
if defaultBranchRev == "" {
// There's no default branch available, so we can't look for a common parent with it.
return gitcfg.DefaultBranchBaseRef
}

return branch == git.DefaultBranch
}
branch, _ := gw.CurrentBranch()

// defaultBaseRef returns the baseRef for the current git environment.
func (p *project) defaultBaseRef() string {
git := p.gitcfg()
if p.isDefaultBranch() &&
p.remoteDefaultCommit() == p.headCommit() {
_, err := p.git.wrapper.RevParse(git.DefaultBranchBaseRef)
if err == nil {
return git.DefaultBranchBaseRef
// Either we are on a branch or at a detached HEAD.
if branch != "" {
if branch == gitcfg.DefaultBranch {
// We are at the tip of the default branch -> latest default commit.
return gitcfg.DefaultBranchBaseRef
}
} else {
headRev, _ := gw.RevParse("HEAD")
isDetachedDefaultBranchTip := headRev == defaultBranchRev
if isDetachedDefaultBranchTip {
// We are at the latest commit of the default branch.
return gitcfg.DefaultBranchBaseRef
}
}

return p.defaultBranchRef()
}

// defaultLocalBaseRef returns the baseRef in case there's no remote setup.
func (p *project) defaultLocalBaseRef() string {
git := p.gitcfg()
if p.isDefaultBranch() {
_, err := p.git.wrapper.RevParse(git.DefaultBranchBaseRef)
if err == nil {
return git.DefaultBranchBaseRef
isDefaultBranchAncestor, _ := gw.IsFirstParentAncestor("HEAD", defaultBranchRev)
if isDefaultBranchAncestor {
// We are at an older commit of the default branch.
return gitcfg.DefaultBranchBaseRef
}
}

return git.DefaultBranch
}
commonParentWithDefaultBranch, _ := gw.FindNearestCommonParent(defaultBranchRev, "HEAD")
if commonParentWithDefaultBranch != "" {
// We have a nearest common parent with the default branch. Similar to the historic merge base.
return commonParentWithDefaultBranch
}

func (p project) defaultBranchRef() string {
git := p.gitcfg()
return git.DefaultRemote + "/" + git.DefaultBranch
// Fall back to default. Should never happen unless running on an isolated commit.
return gitcfg.DefaultBranchBaseRef
}

func (p *project) setDefaults() error {
Expand Down
Loading

0 comments on commit 9b7088b

Please sign in to comment.