func()

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
}