in pkg/fastforward/fastforward.go [75:228]
func (f *FastForward) Run() (err error) {
if f.options.Submit {
logrus.Info("Submitting GCB job")
options := gcb.NewDefaultOptions()
options.FastForward = true
options.NoMock = f.options.NoMock
options.Stream = true
return f.Submit(options)
}
repo, err := f.prepareRepo()
if err != nil {
return errors.Wrap(err, "prepare repository")
}
if !f.options.NoMock {
logrus.Info("Using dry mode, which does not modify any remote content")
f.RepoSetDry(repo)
}
branch := f.options.Branch
if branch == "" {
logrus.Info("No release branch specified, finding the latest")
branch, err = f.RepoLatestReleaseBranch(repo)
if err != nil {
return errors.Wrap(err, "finding latest release branch")
}
logrus.Infof("Found latest release branch: %s", branch)
notRequired, err := f.noFastForwardRequired(repo, branch)
if err != nil {
return errors.Wrap(err, "check if fast forward is required")
}
if notRequired {
logrus.Infof(
"Fast forward not required because final tag already exists for latest release branch %s",
branch,
)
return nil
}
} else {
logrus.Infof("Checking if %q is a release branch", branch)
if isReleaseBranch := f.IsReleaseBranch(branch); !isReleaseBranch {
return errors.Errorf("%s is not a release branch", branch)
}
logrus.Info("Checking if branch is available on the default remote")
branchExists, err := f.RepoHasRemoteBranch(repo, branch)
if err != nil {
return errors.Wrap(err, "checking if branch exists on the default remote")
}
if !branchExists {
return errors.New("branch does not exist on the default remote")
}
}
if f.options.Cleanup {
defer func() {
if err := f.RepoCleanup(repo); err != nil {
logrus.Errorf("Repo cleanup failed: %v", err)
}
}()
} else {
// Restore the currently checked out branch afterwards
currentBranch, err := f.RepoCurrentBranch(repo)
if err != nil {
return errors.Wrap(err, "unable to retrieve current branch")
}
defer func() {
if err := f.RepoCheckout(repo, currentBranch); err != nil {
logrus.Errorf("Unable to restore branch %s: %v", currentBranch, err)
}
}()
}
logrus.Info("Checking out release branch")
if err := f.RepoCheckout(repo, branch); err != nil {
return errors.Wrapf(err, "checking out branch %s", branch)
}
logrus.Infof("Finding merge base between %q and %q", git.DefaultBranch, branch)
mergeBase, err := f.RepoMergeBase(repo, git.DefaultBranch, branch)
if err != nil {
return errors.Wrap(err, "find merge base")
}
// Verify the tags
mainTag, err := f.RepoDescribe(
repo,
git.NewDescribeOptions().
WithRevision(git.Remotify(git.DefaultBranch)).
WithAbbrev(0).
WithTags(),
)
if err != nil {
return errors.Wrap(err, "describe latest main tag")
}
mergeBaseTag, err := f.RepoDescribe(
repo,
git.NewDescribeOptions().
WithRevision(mergeBase).
WithAbbrev(0).
WithTags(),
)
if err != nil {
return errors.Wrap(err, "describe latest merge base tag")
}
logrus.Infof("Merge base tag is: %s", mergeBaseTag)
if mainTag != mergeBaseTag {
return errors.Errorf(
"unable to fast forward: tag %q does not match %q",
mainTag, mergeBaseTag,
)
}
logrus.Infof("Verified that the latest tag on the main branch is the same as the merge base tag")
releaseRev, err := f.RepoHead(repo)
if err != nil {
return errors.Wrap(err, "get release rev")
}
logrus.Infof("Latest release branch revision is %s", releaseRev)
logrus.Info("Merging main branch changes into release branch")
if err := f.RepoMerge(repo, f.options.MainRef); err != nil {
return errors.Wrap(err, "merge main ref")
}
headRev, err := f.RepoHead(repo)
if err != nil {
return errors.Wrap(err, "get HEAD rev")
}
prepushMessage(f.RepoDir(repo), branch, f.options.MainRef, releaseRev, headRev)
pushUpstream := false
if f.options.NonInteractive {
pushUpstream = true
} else {
_, pushUpstream, err = f.Ask(pushUpstreamQuestion, "yes", 3)
if err != nil {
return errors.Wrap(err, "ask upstream question")
}
}
if pushUpstream {
logrus.Infof("Pushing %s branch", branch)
if err := f.RepoPush(repo, branch); err != nil {
return errors.Wrap(err, "push to repo")
}
}
return nil
}