mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 21:19:28 -06:00
4ab3bd6f7e
* CI: Allow overwriting of existing GitHub release assets This closes #63698 * Use c for *cli.Context in publishgithub.go
192 lines
5.6 KiB
Go
192 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/google/go-github/github"
|
|
"github.com/urfave/cli/v2"
|
|
"golang.org/x/oauth2"
|
|
|
|
"github.com/grafana/grafana/pkg/build/config"
|
|
)
|
|
|
|
type githubRepositoryService interface {
|
|
GetReleaseByTag(ctx context.Context, owner string, repo string, tag string) (*github.RepositoryRelease, *github.Response, error)
|
|
CreateRelease(ctx context.Context, owner string, repo string, release *github.RepositoryRelease) (*github.RepositoryRelease, *github.Response, error)
|
|
UploadReleaseAsset(ctx context.Context, owner string, repo string, id int64, opt *github.UploadOptions, file *os.File) (*github.ReleaseAsset, *github.Response, error)
|
|
DeleteReleaseAsset(ctx context.Context, owner string, repo string, id int64) (*github.Response, error)
|
|
ListReleaseAssets(ctx context.Context, owner string, repo string, id int64, opt *github.ListOptions) ([]*github.ReleaseAsset, *github.Response, error)
|
|
}
|
|
|
|
type githubRepo struct {
|
|
owner string
|
|
name string
|
|
}
|
|
|
|
type publishGithubFlags struct {
|
|
create bool
|
|
dryRun bool
|
|
tag string
|
|
repo *githubRepo
|
|
artifactPath string
|
|
}
|
|
|
|
var (
|
|
newGithubClient = githubRepositoryClient
|
|
errTokenIsEmpty = errors.New("the environment variable GH_TOKEN must be set")
|
|
errTagIsEmpty = errors.New(`failed to retrieve release tag from metadata, use "--tag" to set it manually`)
|
|
errReleaseNotFound = errors.New(`release not found, use "--create" to create the release`)
|
|
)
|
|
|
|
func PublishGithub(c *cli.Context) error {
|
|
ctx := c.Context
|
|
token := os.Getenv("GH_TOKEN")
|
|
f, err := getPublishGithubFlags(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if f.tag == "" {
|
|
return errTagIsEmpty
|
|
}
|
|
|
|
if token == "" {
|
|
return errTokenIsEmpty
|
|
}
|
|
|
|
if f.dryRun {
|
|
return runPublishGithubDryRun(f, token, c)
|
|
}
|
|
|
|
client := newGithubClient(ctx, token)
|
|
release, res, err := client.GetReleaseByTag(ctx, f.repo.owner, f.repo.name, f.tag)
|
|
if err != nil && res.StatusCode != 404 {
|
|
return err
|
|
}
|
|
|
|
if release == nil {
|
|
if f.create {
|
|
release, _, err = client.CreateRelease(ctx, f.repo.owner, f.repo.name, &github.RepositoryRelease{TagName: &f.tag})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return errReleaseNotFound
|
|
}
|
|
}
|
|
|
|
artifactName := path.Base(f.artifactPath)
|
|
file, err := os.Open(f.artifactPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
assetsPage := 1
|
|
foundAsset := false
|
|
for {
|
|
assets, resp, err := client.ListReleaseAssets(ctx, f.repo.owner, f.repo.name, *release.ID, &github.ListOptions{
|
|
Page: assetsPage,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, asset := range assets {
|
|
if asset.GetName() == artifactName {
|
|
fmt.Printf("Found existing artifact with the name '%s'. Deleting that now.\n", artifactName)
|
|
if _, err := client.DeleteReleaseAsset(ctx, f.repo.owner, f.repo.name, asset.GetID()); err != nil {
|
|
return fmt.Errorf("failed to delete already existing asset: %w", err)
|
|
}
|
|
foundAsset = true
|
|
break
|
|
}
|
|
}
|
|
if resp.NextPage <= assetsPage || foundAsset {
|
|
break
|
|
}
|
|
assetsPage = resp.NextPage
|
|
}
|
|
|
|
asset, _, err := client.UploadReleaseAsset(ctx, f.repo.owner, f.repo.name, *release.ID, &github.UploadOptions{Name: artifactName}, file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("Asset '%s' uploaded to release '%s' on repository '%s/%s'\nDownload: %s\n", *asset.Name, f.tag, f.repo.owner, f.repo.name, *asset.BrowserDownloadURL)
|
|
return nil
|
|
}
|
|
|
|
func githubRepositoryClient(ctx context.Context, token string) githubRepositoryService {
|
|
ts := oauth2.StaticTokenSource(
|
|
&oauth2.Token{AccessToken: token},
|
|
)
|
|
tc := oauth2.NewClient(ctx, ts)
|
|
|
|
client := github.NewClient(tc)
|
|
return client.Repositories
|
|
}
|
|
|
|
func getPublishGithubFlags(c *cli.Context) (*publishGithubFlags, error) {
|
|
metadata, err := config.GenerateMetadata(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tag := c.Value("tag").(string)
|
|
if tag == "" && metadata.GrafanaVersion != "" {
|
|
tag = fmt.Sprintf("v%s", metadata.GrafanaVersion)
|
|
}
|
|
fullRepo := c.Value("repo").(string)
|
|
dryRun := c.Value("dry-run").(bool)
|
|
owner := strings.Split(fullRepo, "/")[0]
|
|
name := strings.Split(fullRepo, "/")[1]
|
|
create := c.Value("create").(bool)
|
|
artifactPath := c.Value("path").(string)
|
|
if artifactPath == "" {
|
|
artifactPath = fmt.Sprintf("grafana-enterprise2-%s-amd64.img", metadata.GrafanaVersion)
|
|
fmt.Printf("path argument is not provided, resolving to default %s...\n", artifactPath)
|
|
}
|
|
return &publishGithubFlags{
|
|
artifactPath: artifactPath,
|
|
create: create,
|
|
dryRun: dryRun,
|
|
tag: tag,
|
|
repo: &githubRepo{
|
|
owner: owner,
|
|
name: name,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func runPublishGithubDryRun(f *publishGithubFlags, token string, c *cli.Context) error {
|
|
client := newGithubClient(c.Context, token)
|
|
fmt.Println("Dry-Run: Retrieving release on repository by tag")
|
|
release, res, err := client.GetReleaseByTag(c.Context, f.repo.owner, f.repo.name, f.tag)
|
|
if err != nil && res.StatusCode != 404 {
|
|
fmt.Println("Dry-Run: Github communication error:\n", err)
|
|
return nil
|
|
}
|
|
|
|
if release == nil {
|
|
if f.create {
|
|
fmt.Println("Dry-Run: Release doesn't exist and --create is enabled, so it would try to create the release")
|
|
} else {
|
|
fmt.Println("Dry-Run: Release doesn't exist and --create is disabled, so it would fail with error")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
artifactName := path.Base(f.artifactPath)
|
|
fmt.Printf("Dry-Run: Opening file for release: %s\n", f.artifactPath)
|
|
_, err = os.Open(f.artifactPath)
|
|
if err != nil {
|
|
fmt.Println("Dry-Run: Error opening file\n", err)
|
|
return nil
|
|
}
|
|
|
|
fmt.Printf("Dry-Run: Would upload asset '%s' to release '%s' on repo '%s/%s' and return download URL if successful\n", artifactName, f.tag, f.repo.owner, f.repo.name)
|
|
return nil
|
|
}
|