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
}