func UpdateGitHubPage()

in pkg/announce/github_page.go [175:343]


func UpdateGitHubPage(opts *GitHubPageOptions) (err error) {
	token := os.Getenv(github.TokenEnvKey)
	if token == "" {
		return errors.New("cannot update release page without a GitHub token")
	}

	gh := github.New()
	releaseVerb := "Posting"
	semver, err := util.TagStringToSemver(opts.Tag)
	if err != nil {
		return errors.Wrap(err, "parsing semver from tag")
	}

	// Determine if this is a prerelase
	// // [[ "$FLAGS_type" == official ]] && prerelease="false"
	isPrerelease := false
	if len(semver.Pre) > 0 {
		isPrerelease = true
	}

	// Process the specified assets
	releaseAssets, err := processAssetFiles(opts.AssetFiles)
	if err != nil {
		return errors.Wrap(err, "processing the asset file list")
	}

	// Substitution struct for the template
	subs := struct {
		Substitutions map[string]string
		Assets        []map[string]string
	}{
		Substitutions: opts.Substitutions,
		Assets:        releaseAssets,
	}

	// If we have a release notes file defined and set a substitution
	// entry for its contents
	if opts.ReleaseNotesFile != "" {
		rnData, err := os.ReadFile(opts.ReleaseNotesFile)
		if err != nil {
			return errors.Wrap(err, "reading release notes file")
		}
		subs.Substitutions["ReleaseNotes"] = string(rnData)
	}

	// Open the template file (if a custom)
	templateText := ghPageBody
	if opts.PageTemplate != "" {
		logrus.Debugf("Using custom page template %s", opts.PageTemplate)
		templateText = opts.PageTemplate
	}
	// Parse the template we will use to build the release page
	tmpl, err := template.New("GitHubPage").Parse(templateText)
	if err != nil {
		return errors.Wrap(err, "parsing github page template")
	}

	// Run the template to verify the output.
	output := new(bytes.Buffer)
	err = tmpl.Execute(output, subs)
	if err != nil {
		return errors.Wrap(err, "executing page template")
	}

	// If we are in mock, we write it to stdout and exit. All checks
	// performed to the repo are skipped as the tag may not exist yet.
	if !opts.NoMock {
		logrus.Info("Mock mode, outputting the release page")
		_, err := os.Stdout.Write(output.Bytes())
		return errors.Wrap(err, "writing github page to stdout")
	}

	// Check to see that a tag exists.
	// non-draft release posts to github create a tag.  We don't want to
	// create any tags on the repo this way. The tag should already exist
	// as a result of the release process.
	tagFound, err := gh.TagExists(opts.Owner, opts.Repo, opts.Tag)
	if err != nil {
		return errors.Wrap(err, "checking if the tag already exists in GitHub")
	}
	if !tagFound {
		logrus.Warnf("The %s tag doesn't exist yet on GitHub.", opts.Tag)
		logrus.Warnf("That can't be good.")
		logrus.Warnf("We certainly cannot publish a release without a tag.")
		return errors.New("tag not found while trying to publish release page")
	}

	// Get the release we are looking for
	releases, err := gh.Releases(opts.Owner, opts.Repo, true)
	if err != nil {
		return errors.Wrap(err, "listing the repositories releases")
	}

	// Does the release exist yet?
	var releaseID int64
	commitish := ""
	for _, release := range releases {
		if release.GetTagName() == opts.Tag {
			releaseID = release.GetID()
			commitish = release.GetTargetCommitish()
		}
	}

	if releaseID != 0 {
		logrus.Warnf("The %s is already published on github.", opts.Tag)
		if !opts.UpdateIfReleaseExists {
			return errors.New("release " + opts.Tag + " already exists. Left intact")
		}
		logrus.Infof("Using release id %d to update existing release.", releaseID)
		releaseVerb = "Updating"
	}

	// Post release data
	logrus.Infof("%s the %s release on github...", releaseVerb, opts.Tag)

	// Call GitHub to set the release page
	release, err := gh.UpdateReleasePage(
		opts.Owner, opts.Repo, releaseID,
		opts.Tag, commitish, opts.Name, output.String(),
		opts.Draft, isPrerelease,
	)
	if err != nil {
		return errors.Wrap(err, "updating the release on GitHub")
	}

	// Releases often take a bit of time to show up in the API
	// after creating the page. If the release does not appear
	// in the API right away , sleep 3 secs and retry 3 times.
	for checkAttempts := 3; checkAttempts >= 0; checkAttempts-- {
		releaseFound := false
		releases, err = gh.Releases(opts.Owner, opts.Repo, true)
		if err != nil {
			return errors.Wrap(err, "listing releases in repository")
		}
		// Check if the page shows up in the API
		for _, testRelease := range releases {
			if testRelease.GetID() == release.GetID() {
				releaseFound = true
				break
			}
		}
		if releaseFound {
			break
		}

		if checkAttempts == 0 {
			return errors.New("release not found, even when call to github was successful")
		}
		logrus.Info("Release page not yet returned by the GitHub API, sleeping and retrying")
		time.Sleep(3 * time.Second)
	}

	// Delete any assets reviously uploaded
	if err := deleteReleaseAssets(gh, opts.Owner, opts.Repo, release.GetID()); err != nil {
		return errors.Wrap(err, "deleting the existing release assets")
	}

	// publish binary
	for _, assetData := range releaseAssets {
		logrus.Infof("Uploading %s as release asset", assetData["realpath"])
		asset, err := gh.UploadReleaseAsset(opts.Owner, opts.Repo, release.GetID(), assetData["rawpath"])
		if err != nil {
			return errors.Wrapf(err, "uploading %s to the release", assetData["realpath"])
		}
		logrus.Info("Successfully uploaded asset #", asset.GetID())
	}
	logrus.Infof("Release %s published on GitHub", opts.Tag)
	return nil
}