func main()

in cmd/sync-tags/main.go [70:371]


func main() {
	// repository flags used when the repository is not k8s.io/kubernetes
	commitMsgTag := flag.String("commit-message-tag", "Kubernetes-commit", "the git commit message tag used to point back to source commits")
	sourceRemote := flag.String("source-remote", "", "the source repo remote (e.g. upstream")
	sourceBranch := flag.String("source-branch", "", "the source repo branch (not qualified, just the name; defaults to equal <branch>)")
	prefix := flag.String("prefix", "kubernetes-", "a string to put in front of upstream tags")
	pushScriptPath := flag.String("push-script", "", "git-push command(s) are appended to this file to push the new tags to the origin remote")
	dependencies := flag.String("dependencies", "", "comma-separated list of repo:branch pairs of dependencies")
	skipFetch := flag.Bool("skip-fetch", false, "skip fetching tags")
	mappingOutputFile := flag.String("mapping-output-file", "", "a file name to write the source->dest hash mapping to ({{.Tag}} is substituted with the tag name, {{.Branch}} with the local branch name)")
	publishSemverTags := flag.Bool("publish-v0-semver", false, "publish v0.x.y tag at destination repo for v1.x.y tag at the source repo")

	flag.Usage = Usage
	flag.Parse()

	if *sourceRemote == "" {
		glog.Fatalf("source-remote cannot be empty")
	}

	if *sourceBranch == "" {
		glog.Fatalf("source-branch cannot be empty")
	}

	var dependentRepos []string
	if len(*dependencies) > 0 {
		for _, pair := range strings.Split(*dependencies, ",") {
			ps := strings.Split(pair, ":")
			dependentRepos = append(dependentRepos, ps[0])
		}
	}

	// open repo at "."
	r, err := gogit.PlainOpen(".")
	if err != nil {
		glog.Fatalf("Failed to open repo at .: %v", err)
	}

	h, err := r.Head()
	if err != nil {
		glog.Fatalf("Failed to get HEAD: %v", err)
	}
	localBranch := h.Name().Short()
	if localBranch == "" {
		glog.Fatalf("Failed to get current branch.")
	}

	// get first-parent commit list of upstream branch
	srcUpdateBranch, err := r.ResolveRevision(plumbing.Revision(fmt.Sprintf("refs/remotes/%s/%s", *sourceRemote, *sourceBranch)))
	if err != nil {
		glog.Fatalf("Failed to open upstream branch %s: %v", *sourceBranch, err)
	}
	srcHead, err := cache.CommitObject(r, *srcUpdateBranch)
	if err != nil {
		glog.Fatalf("Failed to open upstream branch %s head: %v", *sourceBranch, err)
	}
	srcFirstParents, err := git.FirstParentList(r, srcHead)
	if err != nil {
		glog.Fatalf("Failed to get upstream branch %s first-parent list: %v", *sourceBranch, err)
	}

	// delete remote tags locally
	if !*skipFetch {
		fmt.Printf("Removing all local copies of origin and %s tags.\n", *sourceRemote)
		if err := removeRemoteTags(r, "origin", *sourceRemote); err != nil {
			glog.Fatalf("Failed to iterate through tags: %v", err)
		}
	}

	// get upstream tags
	if !*skipFetch {
		fmt.Printf("Fetching tags from remote %q.\n", *sourceRemote)
		err = fetchTags(r, *sourceRemote)
		if err != nil {
			glog.Fatalf("Failed to fetch tags for %q: %v", *sourceRemote, err)
		}
	}
	srcTagCommits, err := remoteTags(r, *sourceRemote)
	if err != nil {
		glog.Fatalf("Failed to iterate through %s tags: %v", *sourceRemote, err)
	}

	// get all origin tags
	if !*skipFetch {
		fmt.Printf("Fetching tags from remote %q.\n", "origin")
		err = fetchTags(r, "origin")
		if err != nil {
			glog.Fatalf("Failed to fetch tags for %q: %v", "origin", err)
		}
	}
	bTagCommits, err := remoteTags(r, "origin")
	if err != nil {
		glog.Fatalf("Failed to iterate through origin tags: %v", err)
	}

	// filter tags by source branch
	srcFirstParentCommits := map[string]struct{}{}
	for _, kc := range srcFirstParents {
		srcFirstParentCommits[kc.Hash.String()] = struct{}{}
	}
	for name, kh := range srcTagCommits {
		// ignore non-annotated tags
		tag, err := r.TagObject(kh)
		if err != nil {
			delete(srcTagCommits, name)
			continue
		}

		// delete tag not on the source branch
		if _, ok := srcFirstParentCommits[tag.Target.String()]; !ok {
			delete(srcTagCommits, name)
		}
	}

	var sourceCommitsToDstCommits map[plumbing.Hash]plumbing.Hash

	mappingFilesWritten := map[string]bool{}

	// create or update tags from srcTagCommits as local tags with the given prefix
	createdTags := []string{}
	for name, kh := range srcTagCommits {
		bName := name
		if *prefix != "" {
			bName = *prefix + name[1:] // remove the v
		}

		var (
			semverTag        = ""
			publishSemverTag = false
		)
		// if we are publishing semver tags
		if *publishSemverTags {
			// and this is a valid v1... semver tag
			if _, semverErr := semver.Parse(name[1:]); semverErr == nil && strings.HasPrefix(name, "v1.") {
				publishSemverTag = true
				semverTag = "v0." + strings.TrimPrefix(name, "v1.") // replace v1.x.y with v0.x.y
			}
		}

		// ignore non-annotated tags
		tag, err := r.TagObject(kh)
		if err != nil {
			continue
		}

		// ignore old tags
		if tag.Tagger.When.Before(time.Date(2017, 9, 1, 0, 0, 0, 0, time.UTC)) {
			// TODO: Fix or remove
			// fmt.Printf("Ignoring old tag origin/%s from %v\n", bName, tag.Tagger.When)
			continue
		}

		// skip if either tag exists at origin
		_, nonSemverTagAtOrigin := bTagCommits[bName]
		_, semverTagAtOrigin := bTagCommits[semverTag]
		if nonSemverTagAtOrigin || (publishSemverTag && semverTagAtOrigin) {
			continue
		}

		// if any of the tag exists locally,
		// delete the tags, clear the cache and recreate them
		if tagExists(bName) {
			commit, commitTime, err := taggedCommitHashAndTime(r, bName)
			if err != nil {
				glog.Fatalf("Failed to get tag %s: %v", bName, err)
			}
			rev := commit.String()
			pseudoVersion := fmt.Sprintf("v0.0.0-%s-%s", commitTime.UTC().Format("20060102150405"), rev[:12])

			fmt.Printf("Clearing cache for local tag %s.\n", pseudoVersion)
			if err := cleanCacheForTag(pseudoVersion); err != nil {
				glog.Fatalf("Failed to clean go mod cache for %s: %v", pseudoVersion, err)
			}

			if err := deleteTag(bName); err != nil {
				glog.Fatalf("Failed to delete tag %s: %v", bName, err)
			}
		}

		if publishSemverTag && tagExists(semverTag) {
			fmt.Printf("Clearing cache for local tag %s.\n", semverTag)
			if err := cleanCacheForTag(semverTag); err != nil {
				glog.Fatalf("Failed to clean go mod cache for %s: %v", semverTag, err)
			}
			if err := deleteTag(semverTag); err != nil {
				glog.Fatalf("Failed to delete tag %s: %v", semverTag, err)
			}
		}

		// lazily compute kube commit map
		if sourceCommitsToDstCommits == nil {
			bRevision, err := r.ResolveRevision(plumbing.Revision(fmt.Sprintf("refs/heads/%s", localBranch)))
			if err != nil {
				glog.Fatalf("Failed to open branch %s: %v", localBranch, err)
			}
			fmt.Printf("Computing mapping from kube commits to the local branch %q at %s because %q seems to be relevant.\n", localBranch, bRevision.String(), bName)
			bHeadCommit, err := cache.CommitObject(r, *bRevision)
			if err != nil {
				glog.Fatalf("Failed to open branch %s head: %v", localBranch, err)
			}
			bFirstParents, err := git.FirstParentList(r, bHeadCommit)
			if err != nil {
				glog.Fatalf("Failed to get branch %s first-parent list: %v", localBranch, err)
			}
			sourceCommitsToDstCommits, err = git.SourceCommitToDstCommits(r, *commitMsgTag, bFirstParents, srcFirstParents)
			if err != nil {
				glog.Fatalf("Failed to map upstream branch %s to HEAD: %v", *sourceBranch, err)
			}
		}

		// map kube commit to local branch
		bh, found := sourceCommitsToDstCommits[tag.Target]
		if !found {
			// this means that the tag is not on the current source branch
			continue
		}

		// store source->dest hash mapping for debugging
		if *mappingOutputFile != "" {
			fname := mappingOutputFileName(*mappingOutputFile, localBranch, bName)
			if !mappingFilesWritten[fname] {
				fmt.Printf("Writing source->dest hash mapping to %q\n", fname)
				f, err := os.Create(fname)
				if err != nil {
					glog.Fatal(f)
				}
				if err := writeKubeCommitMapping(f, sourceCommitsToDstCommits, srcFirstParents); err != nil {
					glog.Fatal(err)
				}
				defer f.Close()

				mappingFilesWritten[fname] = true
			}
		}

		if len(dependentRepos) > 0 {
			wt := checkoutBranchTagCommit(r, bh, dependentRepos)

			// update go.mod to point to actual tagged version in the dependencies. This version might differ
			// from the one currently in go.mod because the other repo could have gotten more commit for this
			// tag, but this repo didn't. Compare https://github.com/kubernetes/publishing-bot/issues/12 for details.
			var changed bool
			_, err = os.Stat("go.mod")
			if err == nil {
				if publishSemverTag {
					changed = updateGoMod(semverTag, dependentRepos, true)
				} else {
					changed = updateGoMod(bName, dependentRepos, false)
				}
			}

			if changed {
				if publishSemverTag {
					bh = createCommitToFixDeps(wt, semverTag)
				} else {
					bh = createCommitToFixDeps(wt, bName)
				}
			}
		}

		// create semver annotated tag
		if publishSemverTag {
			fmt.Printf("Tagging %v as %q.\n", bh, semverTag)
			err = createAnnotatedTag(bh, semverTag, tag.Tagger.When, dedent.Dedent(fmt.Sprintf(`
			Kubernetes release %s

			Based on https://github.com/kubernetes/kubernetes/releases/tag/%s
			`, name, name)))
			if err != nil {
				glog.Fatalf("Failed to create tag %q: %v", semverTag, err)
			}
			createdTags = append(createdTags, semverTag)
		}

		// create non-semver prefixed annotated tag
		fmt.Printf("Tagging %v as %q.\n", bh, bName)
		err = createAnnotatedTag(bh, bName, tag.Tagger.When, dedent.Dedent(fmt.Sprintf(`
				Kubernetes release %s

				Based on https://github.com/kubernetes/kubernetes/releases/tag/%s
				`, name, name)))
		if err != nil {
			glog.Fatalf("Failed to create tag %q: %v", bName, err)
		}
		createdTags = append(createdTags, bName)
	}

	// write push command for new tags
	// we use git push --atomic because it treats
	// any existing releases which have only non-semver tags as no-ops
	// and both semver and non-semver tags are targeted in a single operation
	if *pushScriptPath != "" && len(createdTags) > 0 {
		pushScript, err := os.OpenFile(*pushScriptPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755)
		if err != nil {
			glog.Fatalf("Failed to open push-script %q for appending: %v", *pushScriptPath, err)
		}
		defer pushScript.Close()
		_, err = pushScript.WriteString(fmt.Sprintf("git push --atomic origin %s\n", "refs/tags/"+strings.Join(createdTags, " refs/tags/")))
		if err != nil {
			glog.Fatalf("Failed to write to push-script %q: %q", *pushScriptPath, err)
		}
	}
}