gitea-release/main.go
2016-02-21 11:06:40 +01:00

278 lines
6.5 KiB
Go

package main
import (
"fmt"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"github.com/drone/drone-go/drone"
"github.com/drone/drone-go/plugin"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
)
var (
buildCommit string
)
func main() {
fmt.Printf("Drone GitHub Release Plugin built from %s\n", buildCommit)
workspace := drone.Workspace{}
repo := drone.Repo{}
build := drone.Build{}
vargs := Params{}
plugin.Param("workspace", &workspace)
plugin.Param("repo", &repo)
plugin.Param("build", &build)
plugin.Param("vargs", &vargs)
plugin.MustParse()
if build.Event != "tag" {
fmt.Printf("The GitHub Release plugin is only available for tags\n")
os.Exit(0)
}
if vargs.FileExists == "" {
vargs.FileExists = "overwrite"
}
if !fileExistsValues[vargs.FileExists] {
fmt.Printf("invalid value for file_exists: use [empty], overwrite, skip or fail")
}
if vargs.BaseURL == "" {
vargs.BaseURL = "https://api.github.com/"
} else if !strings.HasSuffix(vargs.BaseURL, "/") {
vargs.BaseURL = vargs.BaseURL + "/"
}
if vargs.UploadURL == "" {
vargs.UploadURL = "https://uploads.github.com/"
} else if !strings.HasSuffix(vargs.UploadURL, "/") {
vargs.UploadURL = vargs.UploadURL + "/"
}
if vargs.APIKey == "" {
fmt.Printf("You must provide an API key\n")
os.Exit(1)
}
if workspace.Path != "" {
os.Chdir(workspace.Path)
}
var files []string
for _, glob := range vargs.Files.Slice() {
globed, err := filepath.Glob(glob)
if err != nil {
fmt.Printf("Failed to glob %s\n", glob)
os.Exit(1)
}
if globed != nil {
files = append(files, globed...)
}
}
if vargs.Checksum.Len() > 0 {
var err error
files, err = writeChecksums(files, vargs.Checksum.Slice())
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
baseURL, err := url.Parse(vargs.BaseURL)
if err != nil {
fmt.Printf("Failed to parse base URL\n")
os.Exit(1)
}
uploadURL, err := url.Parse(vargs.UploadURL)
if err != nil {
fmt.Printf("Failed to parse upload URL\n")
os.Exit(1)
}
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: vargs.APIKey})
tc := oauth2.NewClient(oauth2.NoContext, ts)
client := github.NewClient(tc)
client.BaseURL = baseURL
client.UploadURL = uploadURL
rc := releaseClient{
Client: client,
Owner: repo.Owner,
Repo: repo.Name,
Tag: filepath.Base(build.Ref),
Draft: vargs.Draft,
FileExists: vargs.FileExists,
}
release, err := rc.buildRelease()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if err := rc.uploadFiles(*release.ID, files); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
var fileExistsValues = map[string]bool{
"overwrite": true,
"fail": true,
"skip": true,
}
// Release holds ties the drone env data and github client together.
type releaseClient struct {
*github.Client
Owner string
Repo string
Tag string
Draft bool
FileExists string
}
func (rc *releaseClient) buildRelease() (*github.RepositoryRelease, error) {
// first attempt to get a release by that tag
release, err := rc.getRelease()
if err != nil && release == nil {
fmt.Println(err)
} else if release != nil {
return release, nil
}
// if no release was found by that tag, create a new one
release, err = rc.newRelease()
if err != nil {
return nil, fmt.Errorf("Failed to retrieve or create a release: %s", err)
}
return release, nil
}
func (rc *releaseClient) getRelease() (*github.RepositoryRelease, error) {
release, _, err := rc.Client.Repositories.GetReleaseByTag(rc.Owner, rc.Repo, rc.Tag)
if err != nil {
return nil, fmt.Errorf("Release %s not found", rc.Tag)
}
fmt.Printf("Successfully retrieved %s release\n", rc.Tag)
return release, nil
}
func (rc *releaseClient) newRelease() (*github.RepositoryRelease, error) {
rr := &github.RepositoryRelease{
TagName: github.String(rc.Tag),
Draft: &rc.Draft,
}
release, _, err := rc.Client.Repositories.CreateRelease(rc.Owner, rc.Repo, rr)
if err != nil {
return nil, fmt.Errorf("Failed to create release: %s", err)
}
fmt.Printf("Successfully created %s release\n", rc.Tag)
return release, nil
}
func (rc *releaseClient) uploadFiles(id int, files []string) error {
assets, _, err := rc.Client.Repositories.ListReleaseAssets(rc.Owner, rc.Repo, id, &github.ListOptions{})
if err != nil {
return fmt.Errorf("Failed to fetch existing assets: %s", err)
}
var uploadFiles []string
files:
for _, file := range files {
for _, asset := range assets {
if *asset.Name == path.Base(file) {
switch rc.FileExists {
case "overwrite":
// do nothing
case "fail":
return fmt.Errorf("Asset file %s already exists", path.Base(file))
case "skip":
fmt.Printf("Skipping pre-existing %s artifact\n", *asset.Name)
continue files
default:
return fmt.Errorf("Internal error, unkown file_exist value %s", rc.FileExists)
}
}
}
uploadFiles = append(uploadFiles, file)
}
for _, file := range uploadFiles {
handle, err := os.Open(file)
if err != nil {
return fmt.Errorf("Failed to read %s artifact: %s", file, err)
}
for _, asset := range assets {
if *asset.Name == path.Base(file) {
if _, err := rc.Client.Repositories.DeleteReleaseAsset(rc.Owner, rc.Repo, *asset.ID); err != nil {
return fmt.Errorf("Failed to delete %s artifact: %s", file, err)
}
fmt.Printf("Successfully deleted old %s artifact\n", *asset.Name)
}
}
uo := &github.UploadOptions{Name: path.Base(file)}
if _, _, err = rc.Client.Repositories.UploadReleaseAsset(rc.Owner, rc.Repo, id, uo, handle); err != nil {
return fmt.Errorf("Failed to upload %s artifact: %s", file, err)
}
fmt.Printf("Successfully uploaded %s artifact\n", file)
}
return nil
}
func writeChecksums(files, methods []string) ([]string, error) {
checksums := make(map[string][]string)
for _, method := range methods {
for _, file := range files {
handle, err := os.Open(file)
if err != nil {
return nil, fmt.Errorf("Failed to read %s artifact: %s", file, err)
}
hash, err := checksum(handle, method)
if err != nil {
return nil, err
}
checksums[method] = append(checksums[method], hash, file)
}
}
for method, results := range checksums {
filename := method + "sum.txt"
f, err := os.Create(filename)
if err != nil {
return nil, err
}
for i := 0; i < len(results); i += 2 {
hash := results[i]
file := results[i+1]
if _, err := f.WriteString(fmt.Sprintf("%s %s\n", hash, file)); err != nil {
return nil, err
}
}
files = append(files, filename)
}
return files, nil
}