mirror of
https://github.com/jimeh/build-emacs-for-macos.git
synced 2026-02-19 06:06:40 +00:00
243 lines
5.0 KiB
Go
243 lines
5.0 KiB
Go
package release
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/google/go-github/v35/github"
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/jimeh/build-emacs-for-macos/pkg/gh"
|
|
"github.com/jimeh/build-emacs-for-macos/pkg/repository"
|
|
)
|
|
|
|
type releaseType int
|
|
|
|
// Release type constants
|
|
const (
|
|
Normal releaseType = iota
|
|
Draft
|
|
Prerelease
|
|
)
|
|
|
|
type PublishOptions struct {
|
|
// Repository is the GitHub repository to publish the release on.
|
|
Repository *repository.Repository
|
|
|
|
// CommitRef is the git commit ref to create the release tag on.
|
|
CommitRef string
|
|
|
|
// ReleaseName is the name of the git tag to create the release on.
|
|
ReleaseName string
|
|
|
|
// ReleaseTitle is the title of the release, if not specified ReleaseName
|
|
// will be used.
|
|
ReleaseTitle string
|
|
|
|
// ReleaseType is the type of release to create (normal, prerelease, or
|
|
// draft)
|
|
ReleaseType releaseType
|
|
|
|
// AssetFiles is a list of files which must all exist in the release for
|
|
// the check to pass.
|
|
AssetFiles []string
|
|
|
|
// GitHubToken is the OAuth token used to talk to the GitHub API.
|
|
GithubToken string
|
|
}
|
|
|
|
//nolint:funlen,gocyclo
|
|
// Publish creates and publishes a GitHub release.
|
|
func Publish(ctx context.Context, opts *PublishOptions) error {
|
|
logger := hclog.FromContext(ctx).Named("release")
|
|
gh := gh.New(ctx, opts.GithubToken)
|
|
|
|
files, err := publishFileList(opts.AssetFiles)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tagName := opts.ReleaseName
|
|
name := opts.ReleaseTitle
|
|
if name == "" {
|
|
name = tagName
|
|
}
|
|
|
|
prerelease := opts.ReleaseType == Prerelease
|
|
draft := opts.ReleaseType == Draft
|
|
|
|
logger.Info("checking release", "tag", tagName)
|
|
release, resp, err := gh.Repositories.GetReleaseByTag(
|
|
ctx, opts.Repository.Owner(), opts.Repository.Name(), tagName,
|
|
)
|
|
if err != nil {
|
|
if resp.StatusCode != http.StatusNotFound {
|
|
return err
|
|
}
|
|
|
|
logger.Info("creating release", "tag", tagName, "name", name)
|
|
|
|
release, _, err = gh.Repositories.CreateRelease(
|
|
ctx, opts.Repository.Owner(), opts.Repository.Name(),
|
|
&github.RepositoryRelease{
|
|
Name: &name,
|
|
TagName: &tagName,
|
|
TargetCommitish: &opts.CommitRef,
|
|
Prerelease: boolPtr(false),
|
|
Draft: boolPtr(true),
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for _, fileName := range files {
|
|
fileIO, err2 := os.Open(fileName)
|
|
if err2 != nil {
|
|
return err2
|
|
}
|
|
defer fileIO.Close()
|
|
|
|
fileInfo, err2 := fileIO.Stat()
|
|
if err2 != nil {
|
|
return err2
|
|
}
|
|
|
|
fileBaseName := filepath.Base(fileName)
|
|
assetExists := false
|
|
|
|
for _, a := range release.Assets {
|
|
if a.GetName() != fileBaseName {
|
|
continue
|
|
}
|
|
|
|
if a.GetSize() == int(fileInfo.Size()) {
|
|
logger.Info("asset exists with correct size",
|
|
"file", fileBaseName,
|
|
"local_size", byteCountIEC(fileInfo.Size()),
|
|
"remote_size", byteCountIEC(int64(a.GetSize())),
|
|
)
|
|
assetExists = true
|
|
} else {
|
|
_, err = gh.Repositories.DeleteReleaseAsset(
|
|
ctx, opts.Repository.Owner(), opts.Repository.Name(),
|
|
a.GetID(),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
logger.Info(
|
|
"deleted asset with wrong size", "file", fileBaseName,
|
|
)
|
|
}
|
|
}
|
|
|
|
if !assetExists {
|
|
logger.Info("uploading asset",
|
|
"file", fileBaseName,
|
|
"size", byteCountIEC(fileInfo.Size()),
|
|
)
|
|
_, _, err2 = gh.Repositories.UploadReleaseAsset(
|
|
ctx, opts.Repository.Owner(), opts.Repository.Name(),
|
|
release.GetID(),
|
|
&github.UploadOptions{Name: fileBaseName},
|
|
fileIO,
|
|
)
|
|
if err2 != nil {
|
|
return err2
|
|
}
|
|
}
|
|
}
|
|
|
|
changed := false
|
|
if release.GetName() != name {
|
|
release.Name = &name
|
|
changed = true
|
|
}
|
|
|
|
if release.GetDraft() != draft {
|
|
release.Draft = &draft
|
|
changed = true
|
|
}
|
|
|
|
if !draft && release.GetPrerelease() != prerelease {
|
|
release.Prerelease = &prerelease
|
|
changed = true
|
|
}
|
|
|
|
if changed {
|
|
release, _, err = gh.Repositories.EditRelease(
|
|
ctx, opts.Repository.Owner(), opts.Repository.Name(),
|
|
release.GetID(), release,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
logger.Info("release created", "url", release.GetHTMLURL())
|
|
|
|
return nil
|
|
}
|
|
|
|
func publishFileList(files []string) ([]string, error) {
|
|
var output []string
|
|
for _, file := range files {
|
|
var err error
|
|
file, err = filepath.Abs(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stat, err := os.Stat(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !stat.Mode().IsRegular() {
|
|
return nil, fmt.Errorf("\"%s\" is not a file", file)
|
|
}
|
|
|
|
output = append(output, file)
|
|
sumFile := file + ".sha256"
|
|
|
|
_, err = os.Stat(sumFile)
|
|
fmt.Printf("err: %+v\n", err)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
continue
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
output = append(output, sumFile)
|
|
}
|
|
|
|
return output, nil
|
|
}
|
|
|
|
func byteCountIEC(b int64) string {
|
|
const unit = 1024
|
|
if b < unit {
|
|
if b == 1 {
|
|
return fmt.Sprintf("%d byte", b)
|
|
}
|
|
|
|
return fmt.Sprintf("%d bytes", b)
|
|
}
|
|
div, exp := int64(unit), 0
|
|
for n := b / unit; n >= unit; n /= unit {
|
|
div *= unit
|
|
exp++
|
|
}
|
|
|
|
return fmt.Sprintf("%.1f %ciB",
|
|
float64(b)/float64(div), "KMGTPE"[exp])
|
|
}
|
|
|
|
func boolPtr(v bool) *bool {
|
|
return &v
|
|
}
|