func()

in pkg/anago/stage.go [403:546]


func (d *DefaultStage) TagRepository() error {
	repo, err := d.impl.OpenRepo(gitRoot)
	if err != nil {
		return errors.Wrap(err, "open Kubernetes repository")
	}

	for _, version := range d.state.versions.Ordered() {
		logrus.Infof("Preparing version %s", version)

		// Ensure that the tag not already exists
		if _, err := d.impl.RevParseTag(repo, version); err == nil {
			return errors.Errorf("tag %s already exists", version)
		}

		// Usually the build version contains a commit we can reference. If
		// not, because the build version is exactly a tag, then we fallback to
		// that tag.
		commit := d.options.BuildVersion
		if len(d.state.semverBuildVersion.Build) > 0 {
			commit = d.state.semverBuildVersion.Build[0]
		}

		if d.state.createReleaseBranch {
			logrus.Infof("Creating release branch %s", d.options.ReleaseBranch)

			if version == d.state.versions.Prime() {
				logrus.Infof("Version %s is the prime version", version)
				logrus.Infof(
					"Creating release branch %s from commit %s",
					d.options.ReleaseBranch, commit,
				)
				if err := d.impl.Checkout(
					repo, "-b", d.options.ReleaseBranch, commit,
				); err != nil {
					return errors.Wrap(err, "create new release branch")
				}
			} else {
				logrus.Infof(
					"Version %s is not the prime, checking out %s branch",
					version, git.DefaultBranch,
				)
				if err := d.impl.Checkout(repo, git.DefaultBranch); err != nil {
					return errors.Wrapf(err, "checkout %s branch", git.DefaultBranch)
				}
			}
		} else {
			logrus.Infof("Checking out branch %s", d.options.ReleaseBranch)
			if err := d.impl.Checkout(repo, d.options.ReleaseBranch); err != nil {
				return errors.Wrapf(err, "checking out branch %s", d.options.ReleaseBranch)
			}
		}

		// `branch == ""` in case we checked out a commit directly, which is
		// then in detached head state.
		branch, err := d.impl.CurrentBranch(repo)
		if err != nil {
			return errors.Wrap(err, "get current branch")
		}
		if branch != "" {
			logrus.Infof("Current branch is %s", branch)
		}

		// For release branches, we create an empty release commit to avoid
		// potential ambiguous 'git describe' logic between the official
		// release, 'x.y.z' and the next beta of that release branch,
		// 'x.y.(z+1)-beta.0'.
		//
		// We avoid doing this empty release commit on 'master', as:
		//   - there is a potential for branch conflicts as upstream/master
		//     moves ahead
		//   - we're checking out a git ref, as opposed to a branch, which
		//     means the tag will detached from 'upstream/master'
		//
		// A side-effect of the tag being detached from 'master' is the primary
		// build job (ci-kubernetes-build) will build as the previous alpha,
		// instead of the assumed tag. This causes the next anago run against
		// 'master' to fail due to an old build version.
		//
		// Example: 'v1.18.0-alpha.2.663+df908c3aad70be'
		//          (should instead be:
		//			 'v1.18.0-alpha.3.<commits-since-tag>+<commit-ish>')
		//
		// ref:
		//   - https://github.com/kubernetes/release/issues/1020
		//   - https://github.com/kubernetes/release/pull/1030
		//   - https://github.com/kubernetes/release/issues/1080
		//   - https://github.com/kubernetes/kubernetes/pull/88074

		// When tagging a release branch, always create an empty commit:
		if strings.HasPrefix(branch, "release-") {
			logrus.Infof("Creating empty release commit for tag %s", version)
			if err := d.impl.CommitEmpty(
				repo,
				fmt.Sprintf("Release commit for Kubernetes %s", version),
			); err != nil {
				return errors.Wrap(err, "create empty release commit")
			}
		}

		// If we are on master/main we do not create an empty commit,
		// but we detach the head at the specified commit to avoid having
		// commits merged between the BuildVersion commit and the tag:
		if branch != "" && !strings.HasPrefix(branch, "release-") {
			logrus.Infof("Detaching HEAD at commit %s to create tag %s", commit, version)
			if err := d.impl.Checkout(repo, commit); err != nil {
				return errors.Wrap(err, "checkout release commit")
			}
		}

		// If a custom ref is provided, try to merge it into the release
		// branch.
		ref := release.GetK8sRef()
		if ref != release.DefaultK8sRef {
			logrus.Infof("Merging custom ref: %s", ref)
			if err := d.impl.Merge(repo, git.Remotify(ref)); err != nil {
				return errors.Wrap(err, "merge k8s ref")
			}
		}

		// Tag the repository:
		logrus.Infof("Tagging version %s", version)
		if err := d.impl.Tag(
			repo,
			version,
			fmt.Sprintf(
				"Kubernetes %s release %s", d.options.ReleaseType, version,
			),
		); err != nil {
			return errors.Wrap(err, "tag version")
		}

		// if we are working on master/main at this point, we are in
		// detached HEAD state. So we checkout the branch again.
		// The next stage (build) will checkout the branch it needs, but
		// let's not end this step with a detached HEAD
		if branch != "" && !strings.HasPrefix(branch, "release-") {
			logrus.Infof("Checking out %s to reattach HEAD", d.options.ReleaseBranch)
			if err := d.impl.Checkout(repo, d.options.ReleaseBranch); err != nil {
				return errors.Wrapf(err, "checking out branch %s", d.options.ReleaseBranch)
			}
		}
	}
	return nil
}