1
0
mirror of https://github.com/helm/chart-releaser.git synced 2026-02-05 18:45:29 +01:00
Files
chart-releaser/pkg/github/github.go
2019-02-11 06:51:54 +01:00

228 lines
6.6 KiB
Go

// Copyright The Helm Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package github
import (
"context"
"net/http"
"os"
"path/filepath"
"time"
"github.com/Songmu/retry"
"github.com/pkg/errors"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
)
// GitHub contains the functions necessary for interacting with GitHub release
// objects
type GitHub interface {
GetRepository(ctx context.Context) (*github.Repository, error)
CreateRelease(ctx context.Context, req *github.RepositoryRelease) (*github.RepositoryRelease, error)
GetRelease(ctx context.Context, tag string) (*github.RepositoryRelease, error)
EditRelease(ctx context.Context, releaseID int64, req *github.RepositoryRelease) (*github.RepositoryRelease, error)
ListReleases(ctx context.Context) ([]*github.RepositoryRelease, error)
UploadAsset(ctx context.Context, releaseID int64, filename string) (*github.ReleaseAsset, error)
ListAssets(ctx context.Context, releaseID int64) ([]*github.ReleaseAsset, error)
}
// Client is the client for interacting with the GitHub API
type Client struct {
Owner, Repo string
*github.Client
}
// NewGitHubClient creates and initializes a new GitHubClient
func NewGitHubClient(owner, repo, token string) (GitHub, error) {
var client *github.Client
if token != "" {
ts := oauth2.StaticTokenSource(&oauth2.Token{
AccessToken: token,
})
tc := oauth2.NewClient(context.TODO(), ts)
client = github.NewClient(tc)
} else {
client = github.NewClient(nil)
}
return &Client{
Owner: owner,
Repo: repo,
Client: client,
}, nil
}
// GetRepository fetches a repository
func (c *Client) GetRepository(ctx context.Context) (*github.Repository, error) {
repo, res, err := c.Repositories.Get(context.TODO(), c.Owner, c.Repo)
if err != nil {
if res.StatusCode == http.StatusNotFound {
return nil, nil
}
panic(err)
}
return repo, nil
}
// CreateRelease creates a new release object in the GitHub API
func (c *Client) CreateRelease(ctx context.Context, req *github.RepositoryRelease) (*github.RepositoryRelease, error) {
release, res, err := c.Repositories.CreateRelease(context.TODO(), c.Owner, c.Repo, req)
if err != nil {
return nil, errors.Wrap(err, "failed to create a release")
}
if res.StatusCode != http.StatusCreated {
return nil, errors.Errorf("create release: invalid status: %s", res.Status)
}
return release, nil
}
// GetRelease queries the GitHub API for a specified release object
func (c *Client) GetRelease(ctx context.Context, tag string) (*github.RepositoryRelease, error) {
// Check Release whether already exists or not
release, res, err := c.Repositories.GetReleaseByTag(context.TODO(), c.Owner, c.Repo, tag)
if err != nil {
if res == nil {
return nil, errors.Wrapf(err, "failed to get release tag: %s", tag)
}
// TODO(tcnksm): Handle invalid token
if res.StatusCode != http.StatusNotFound {
return nil, errors.Wrapf(err,
"get release tag: invalid status: %s", res.Status)
}
return nil, nil
}
return release, nil
}
// EditRelease edit a release object within the GitHub API
func (c *Client) EditRelease(ctx context.Context, releaseID int64, req *github.RepositoryRelease) (*github.RepositoryRelease, error) {
var release *github.RepositoryRelease
err := retry.Retry(3, 3*time.Second, func() error {
var (
res *github.Response
err error
)
release, res, err = c.Repositories.EditRelease(context.TODO(), c.Owner, c.Repo, releaseID, req)
if err != nil {
return errors.Wrapf(err, "failed to edit release: %d", releaseID)
}
if res.StatusCode != http.StatusOK {
return errors.Errorf("edit release: invalid status: %s", res.Status)
}
return nil
})
return release, err
}
// ListReleases lists Releases given a repository
func (c *Client) ListReleases(ctx context.Context) ([]*github.RepositoryRelease, error) {
result := []*github.RepositoryRelease{}
page := 1
for {
assets, res, err := c.Repositories.ListReleases(context.TODO(), c.Owner, c.Repo, &github.ListOptions{Page: page})
if err != nil {
return nil, errors.Wrap(err, "failed to list releases")
}
if res.StatusCode != http.StatusOK {
return nil, errors.Errorf("list repository releases: invalid status code: %s", res.Status)
}
result = append(result, assets...)
if res.NextPage <= page {
break
}
page = res.NextPage
}
return result, nil
}
// UploadAsset uploads specified assets to a given release object
func (c *Client) UploadAsset(ctx context.Context, releaseID int64, filename string) (*github.ReleaseAsset, error) {
filename, err := filepath.Abs(filename)
if err != nil {
return nil, errors.Wrap(err, "failed to get abs path")
}
f, err := os.Open(filename)
if err != nil {
return nil, errors.Wrap(err, "failed to open file")
}
opts := &github.UploadOptions{
// Use base name by default
Name: filepath.Base(filename),
}
var asset *github.ReleaseAsset
err = retry.Retry(3, 3*time.Second, func() error {
var (
res *github.Response
err error
)
asset, res, err = c.Repositories.UploadReleaseAsset(context.TODO(), c.Owner, c.Repo, releaseID, opts, f)
if err != nil {
return errors.Wrapf(err, "failed to upload release asset: %s", filename)
}
switch res.StatusCode {
case http.StatusCreated:
return nil
case 422:
return errors.Errorf(
"upload release asset: invalid status code: %s",
"422 (this is probably because the asset already uploaded)")
default:
return errors.Errorf(
"upload release asset: invalid status code: %s", res.Status)
}
})
return asset, err
}
// ListAssets lists assets associated with a given release
func (c *Client) ListAssets(ctx context.Context, releaseID int64) ([]*github.ReleaseAsset, error) {
result := []*github.ReleaseAsset{}
page := 1
for {
assets, res, err := c.Repositories.ListReleaseAssets(context.TODO(), c.Owner, c.Repo, releaseID, &github.ListOptions{Page: page})
if err != nil {
return nil, errors.Wrap(err, "failed to list assets")
}
if res.StatusCode != http.StatusOK {
return nil, errors.Errorf("list release assets: invalid status code: %s", res.Status)
}
result = append(result, assets...)
if res.NextPage <= page {
break
}
page = res.NextPage
}
return result, nil
}