Migrated to 0.5 structure and updated documentation

This commit is contained in:
Thomas Boerger 2016-08-26 10:38:22 +02:00
parent 187cb0b1f2
commit c097b9bbfa
No known key found for this signature in database
GPG Key ID: 5A388F55283960B6
11 changed files with 572 additions and 463 deletions

View File

@ -1,39 +1,29 @@
build:
image: golang:1.5
environment:
- CGO_ENABLED=0
commands:
- make deps
- make vet
- make build
- make test
workspace:
base: /go
publish:
coverage:
pipeline:
test:
image: golang:1.6
environment:
- CGO_ENABLED=0
commands:
- go vet
- go test -cover -coverprofile=coverage.out
- go build -ldflags "-s -w -X main.build=$DRONE_BUILD_NUMBER" -a -tags netgo
latest:
image: docker
repo: plugins/github-release
tags: [ "latest", "1.0", "1" ]
when:
branch: master
docker:
username: $$DOCKER_USER
password: $$DOCKER_PASS
email: $$DOCKER_EMAIL
repo: plugins/drone-github-release
tag: latest
when:
branch: master
docker:
username: $$DOCKER_USER
password: $$DOCKER_PASS
email: $$DOCKER_EMAIL
repo: plugins/drone-github-release
tag: develop
when:
branch: develop
event: push
plugin:
name: GitHub Release
desc: Publish files and artifacts to GitHub Releases
type: publish
image: plugins/drone-github-release
image: plugins/github-release
labels:
- github
- release

1
.drone.yml.sig Normal file
View File

@ -0,0 +1 @@
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9nbwoKcGlwZWxpbmU6CiAgdGVzdDoKICAgIGltYWdlOiBnb2xhbmc6MS42CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBDR09fRU5BQkxFRD0wCiAgICBjb21tYW5kczoKICAgICAgLSBnbyB2ZXQKICAgICAgLSBnbyB0ZXN0IC1jb3ZlciAtY292ZXJwcm9maWxlPWNvdmVyYWdlLm91dAogICAgICAtIGdvIGJ1aWxkIC1sZGZsYWdzICItcyAtdyAtWCBtYWluLmJ1aWxkPSREUk9ORV9CVUlMRF9OVU1CRVIiIC1hIC10YWdzIG5ldGdvCgogIGxhdGVzdDoKICAgIGltYWdlOiBkb2NrZXIKICAgIHJlcG86IHBsdWdpbnMvZ2l0aHViLXJlbGVhc2UKICAgIHRhZ3M6IFsgImxhdGVzdCIsICIxLjAiLCAiMSIgXQogICAgd2hlbjoKICAgICAgYnJhbmNoOiBtYXN0ZXIKICAgICAgZXZlbnQ6IHB1c2gKCnBsdWdpbjoKICBuYW1lOiBHaXRIdWIgUmVsZWFzZQogIGRlc2M6IFB1Ymxpc2ggZmlsZXMgYW5kIGFydGlmYWN0cyB0byBHaXRIdWIgUmVsZWFzZXMKICB0eXBlOiBwdWJsaXNoCiAgaW1hZ2U6IHBsdWdpbnMvZ2l0aHViLXJlbGVhc2UKICBsYWJlbHM6CiAgICAtIGdpdGh1YgogICAgLSByZWxlYXNlCg.5FZW1Auk18CljY9LW5P82r9RM_spPCYMYg82PFj2irM

71
DOCS.md
View File

@ -1,30 +1,61 @@
Use this plugin for publishing files and artifacts to GitHub releases. You
can override the default configuration with the following parameters:
Use this plugin for publishing files and artifacts to GitHub releases. Be aware
that you can use this plugin only for tags, GitHub doesn't support the release
of branches.
* `api_key` - GitHub oauth token with public_repo or repo permission
* `files` - Files to upload to GitHub Release, globs are allowed
* `file_exists` - What to do if an file asset already exists, supported values: **overwrite** (default), **skip** and **fail**
* `checksum` - Checksum takes hash methods to include in your GitHub release for the files specified. Supported hash methods include md5, sha1, sha256, sha512, adler32, and crc32.
* `draft` - create a draft release if set to true
* `base_url` - GitHub base URL, only required for GHE
* `upload_url` - GitHub upload URL, only required for GHE
## Config
Sample configuration:
The following parameters are used to configure the plugin:
```yaml
publish:
github_release:
api_key: my_github_api_key
files: dist/*
checksum: sha1
* **api_key** - GitHub oauth token with public_repo or repo permission
* **files** - files to upload to GitHub Release, globs are allowed
* **file_exists** - what to do if an file asset already exists, supported values: **overwrite** (default), **skip** and **fail**
* **checksum** - checksum takes hash methods to include in your GitHub release for the files specified. Supported hash methods include md5, sha1, sha256, sha512, adler32, and crc32.
* **draft** - create a draft release if set to true
* **base_url** - GitHub base URL, only required for GHE
* **upload_url** - GitHub upload URL, only required for GHE
The following secret values can be set to configure the plugin.
* **GITHUB_RELEASE_API_KEY** - corresponds to **api_key**
* **GITHUB_RELEASE_BASE_URL** - corresponds to **base_url**
* **GITHUB_RELEASE_UPLOAD_URL** - corresponds to **upload_url**
It is highly recommended to put the **GITHUB_RELEASE_API_KEY** into a secret so
it is not exposed to users. This can be done using the drone-cli.
```bash
drone secret add --image=plugins/github-release \
octocat/hello-world GITHUB_RELEASE_API_KEY my_github_api_key
```
or
Then sign the YAML file after all secrets are added.
```bash
drone sign octocat/hello-world
```
See [secrets](http://readme.drone.io/0.5/usage/secrets/) for additional
information on secrets
## Examples
The following is a sample configuration in your .drone.yml file:
```yaml
publish:
pipeline:
github_release:
api_key: my_github_api_key
image: plugins/github-release
files: dist/*
when:
event: tag
```
An example for generating checksums and upload additional files:
```yaml
pipeline:
github_release:
image: plugins/github-release
files:
- dist/*
- bin/binary.exe
@ -35,4 +66,6 @@ publish:
- sha512
- adler32
- crc32
when:
event: tag
```

View File

@ -1,9 +1,4 @@
# Docker image for the Drone GitHub Release plugin
#
# cd $GOPATH/src/github.com/drone-plugins/drone-github-release
# make deps build docker
FROM alpine:3.3
FROM alpine:3.4
RUN apk update && \
apk add \

132
README.md
View File

@ -1,117 +1,53 @@
# drone-github-release
[![Build Status](http://beta.drone.io/api/badges/drone-plugins/drone-github-release/status.svg)](http://beta.drone.io/drone-plugins/drone-github-release)
[![Coverage Status](https://aircover.co/badges/drone-plugins/drone-github-release/coverage.svg)](https://aircover.co/drone-plugins/drone-github-release)
[![](https://badge.imagelayers.io/plugins/drone-github-release:latest.svg)](https://imagelayers.io/?images=plugins/drone-github-release:latest 'Get your own badge on imagelayers.io')
[![Go Doc](https://godoc.org/github.com/drone-plugins/drone-github-release?status.svg)](http://godoc.org/github.com/drone-plugins/drone-github-release)
[![Go Report](https://goreportcard.com/badge/github.com/drone-plugins/drone-github-release)](https://goreportcard.com/report/github.com/drone-plugins/drone-github-release)
[![Join the chat at https://gitter.im/drone/drone](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/drone/drone)
Drone plugin to publish files and artifacts to GitHub Release. For the usage information and a listing of the available options please take a look at [the docs](DOCS.md).
Drone plugin to publish files and artifacts to GitHub Release. For the usage
information and a listing of the available options please take a look at
[the docs](DOCS.md).
## Binary
## Build
Build the binary using `make`:
Build the binary with the following commands:
```
make deps build
```
### Example
```sh
./drone-github-release <<EOF
{
"repo": {
"clone_url": "git://github.com/drone/drone",
"owner": "drone",
"name": "drone",
"full_name": "drone/drone"
},
"system": {
"link_url": "https://beta.drone.io"
},
"build": {
"number": 22,
"status": "success",
"started_at": 1421029603,
"finished_at": 1421029813,
"message": "Update the Readme",
"author": "johnsmith",
"author_email": "john.smith@gmail.com"
"event": "push",
"branch": "master",
"commit": "436b7a6e2abaddfd35740527353e78a227ddcb2c",
"ref": "refs/heads/master"
},
"workspace": {
"root": "/drone/src",
"path": "/drone/src/github.com/drone/drone"
},
"vargs": {
"api_key": "your_api_key",
"files": [
"dist/*.txt",
"dist/other-file"
],
"checksum": [
"sha1",
"sha256",
"sha512"
]
}
}
EOF
go build
go test
```
## Docker
Build the container using `make`:
Build the docker image with the following commands:
```
make deps docker
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo
docker build --rm=true -t plugins/github-release .
```
### Example
Please note incorrectly building the image for the correct x64 linux and with
GCO disabled will result in an error when running the Docker image:
```
docker: Error response from daemon: Container command
'/bin/drone-github-release' not found or does not exist..
```
## Usage
Execute from the working directory:
```sh
docker run -i plugins/drone-github-release <<EOF
{
"repo": {
"clone_url": "git://github.com/drone/drone",
"owner": "drone",
"name": "drone",
"full_name": "drone/drone"
},
"system": {
"link_url": "https://beta.drone.io"
},
"build": {
"number": 22,
"status": "success",
"started_at": 1421029603,
"finished_at": 1421029813,
"message": "Update the Readme",
"author": "johnsmith",
"author_email": "john.smith@gmail.com"
"event": "push",
"branch": "master",
"commit": "436b7a6e2abaddfd35740527353e78a227ddcb2c",
"ref": "refs/heads/master"
},
"workspace": {
"root": "/drone/src",
"path": "/drone/src/github.com/drone/drone"
},
"vargs": {
"api_key": "your_api_key",
"files": [
"dist/*.txt",
"dist/other-file"
],
"checksum": [
"sha1",
"sha256",
"sha512"
]
}
}
EOF
docker run --rm \
-e DRONE_BUILD_EVENT=tag \
-e DRONE_REPO_OWNER=octocat \
-e DRONE_REPO_NAME=foo \
-e DRONE_COMMIT_REF=refs/heads/master \
-e PLUGIN_API_KEY=${HOME}/.ssh/id_rsa \
-e PLUGIN_FILES=master \
-v $(pwd):$(pwd) \
-w $(pwd) \
plugins/github-release
```

View File

@ -1,38 +0,0 @@
package main
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash/adler32"
"hash/crc32"
"io"
"io/ioutil"
"strconv"
)
func checksum(r io.Reader, method string) (string, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return "", err
}
switch method {
case "md5":
return fmt.Sprintf("%x", md5.Sum(b)), nil
case "sha1":
return fmt.Sprintf("%x", sha1.Sum(b)), nil
case "sha256":
return fmt.Sprintf("%x", sha256.Sum256(b)), nil
case "sha512":
return fmt.Sprintf("%x", sha512.Sum512(b)), nil
case "adler32":
return strconv.FormatUint(uint64(adler32.Checksum(b)), 10), nil
case "crc32":
return strconv.FormatUint(uint64(crc32.ChecksumIEEE(b)), 10), nil
default:
return "", fmt.Errorf("hashing method %s is not supported", method)
}
}

364
main.go
View File

@ -2,276 +2,120 @@ 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"
"github.com/Sirupsen/logrus"
"github.com/joho/godotenv"
"github.com/urfave/cli"
)
var (
buildCommit string
)
var build = "0" // build number set at compile-time
func main() {
fmt.Printf("Drone GitHub Release Plugin built from %s\n", buildCommit)
app := cli.NewApp()
app.Name = "github-release plugin"
app.Usage = "github-release plugin"
app.Action = run
app.Version = fmt.Sprintf("1.0.%s", build)
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "api-key",
Usage: "api key to access github api",
EnvVar: "PLUGIN_API_KEY,GITHUB_RELEASE_API_KEY",
},
cli.StringSliceFlag{
Name: "files",
Usage: "list of files to upload",
EnvVar: "PLUGIN_FILES,GITHUB_RELEASE_FILES",
},
cli.StringFlag{
Name: "file-exists",
Value: "overwrite",
Usage: "what to do if file already exist",
EnvVar: "PLUGIN_FILE_EXISTS,GITHUB_RELEASE_FILE_EXISTS",
},
cli.StringSliceFlag{
Name: "checksum",
Usage: "generate specific checksums",
EnvVar: "PLUGIN_CHECKSUM,GITHUB_RELEASE_CHECKSUM",
},
cli.BoolFlag{
Name: "draft",
Usage: "create a draft release",
EnvVar: "PLUGIN_DRAFT,GITHUB_RELEASE_DRAFT",
},
cli.StringFlag{
Name: "base-url",
Value: "https://api.github.com/",
Usage: "api url, needs to be changed for ghe",
EnvVar: "PLUGIN_BASE_URL,GITHUB_RELEASE_BASE_URL",
},
cli.StringFlag{
Name: "upload-url",
Value: "https://uploads.github.com/",
Usage: "upload url, needs to be changed for ghe",
EnvVar: "PLUGIN_UPLOAD_URL,GITHUB_RELEASE_UPLOAD_URL",
},
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)
cli.StringFlag{
Name: "repo.owner",
Usage: "repository owner",
EnvVar: "DRONE_REPO_OWNER",
},
cli.StringFlag{
Name: "repo.name",
Usage: "repository name",
EnvVar: "DRONE_REPO_NAME",
},
cli.StringFlag{
Name: "build.event",
Value: "push",
Usage: "build event",
EnvVar: "DRONE_BUILD_EVENT",
},
cli.StringFlag{
Name: "commit.ref",
Value: "refs/heads/master",
Usage: "git commit ref",
EnvVar: "DRONE_COMMIT_REF",
},
cli.StringFlag{
Name: "env-file",
Usage: "source env file",
},
}
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)
if err := app.Run(os.Args); err != nil {
logrus.Fatal(err)
}
}
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
func run(c *cli.Context) error {
if c.String("env-file") != "" {
_ = godotenv.Load(c.String("env-file"))
}
plugin := Plugin{
Repo: Repo{
Owner: c.String("repo.owner"),
Name: c.String("repo.name"),
},
Build: Build{
Event: c.String("build.event"),
},
Commit: Commit{
Ref: c.String("commit.sha"),
},
Config: Config{
APIKey: c.String("api-key"),
Files: c.StringSlice("api-key"),
FileExists: c.String("file-exists"),
Checksum: c.StringSlice("checksum"),
Draft: c.Bool("draft"),
BaseURL: c.String("base-url"),
UploadURL: c.String("upload-url"),
},
}
return plugin.Exec()
}

134
plugin.go Normal file
View File

@ -0,0 +1,134 @@
package main
import (
"fmt"
"net/url"
"path/filepath"
"strings"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
)
type (
Repo struct {
Owner string
Name string
}
Build struct {
Event string
}
Commit struct {
Ref string
}
Config struct {
APIKey string
Files []string
FileExists string
Checksum []string
Draft bool
BaseURL string
UploadURL string
}
Plugin struct {
Repo Repo
Build Build
Commit Commit
Config Config
}
)
func (p Plugin) Exec() error {
var (
files []string
)
if p.Build.Event != "tag" {
return fmt.Errorf("The GitHub Release plugin is only available for tags")
}
if p.Config.APIKey == "" {
return fmt.Errorf("You must provide an API key")
}
if !fileExistsValues[p.Config.FileExists] {
return fmt.Errorf("Invalid value for file_exists")
}
if !strings.HasSuffix(p.Config.BaseURL, "/") {
p.Config.BaseURL = p.Config.BaseURL + "/"
}
if !strings.HasSuffix(p.Config.UploadURL, "/") {
p.Config.UploadURL = p.Config.UploadURL + "/"
}
for _, glob := range p.Config.Files {
globed, err := filepath.Glob(glob)
if err != nil {
return fmt.Errorf("Failed to glob %s. %s", glob, err)
}
if globed != nil {
files = append(files, globed...)
}
}
if len(p.Config.Checksum) > 0 {
var (
err error
)
files, err = writeChecksums(files, p.Config.Checksum)
if err != nil {
return fmt.Errorf("Failed to write checksums. %s", err)
}
}
baseURL, err := url.Parse(p.Config.BaseURL)
if err != nil {
return fmt.Errorf("Failed to parse base URL. %s", err)
}
uploadURL, err := url.Parse(p.Config.UploadURL)
if err != nil {
return fmt.Errorf("Failed to parse upload URL. %s", err)
}
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: p.Config.APIKey})
tc := oauth2.NewClient(oauth2.NoContext, ts)
client := github.NewClient(tc)
client.BaseURL = baseURL
client.UploadURL = uploadURL
rc := releaseClient{
Client: client,
Owner: p.Repo.Owner,
Repo: p.Repo.Name,
Tag: filepath.Base(p.Commit.Ref),
Draft: p.Config.Draft,
FileExists: p.Config.FileExists,
}
release, err := rc.buildRelease()
if err != nil {
return fmt.Errorf("Failed to create the release. %s", err)
}
if err := rc.uploadFiles(*release.ID, files); err != nil {
return fmt.Errorf("Failed to upload the files. %s", err)
}
return nil
}

125
release.go Normal file
View File

@ -0,0 +1,125 @@
package main
import (
"fmt"
"os"
"path"
"github.com/google/go-github/github"
)
// 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
}

View File

@ -1,14 +0,0 @@
package main
import "github.com/drone/drone-go/drone"
// Params are the parameters that the GitHub Release plugin can parse.
type Params struct {
BaseURL string `json:"base_url"`
UploadURL string `json:"upload_url"`
APIKey string `json:"api_key"`
Files drone.StringSlice `json:"files"`
Checksum drone.StringSlice `json:"checksum"`
Draft bool `json:"draft"`
FileExists string `json:"file_exists"`
}

103
utils.go Normal file
View File

@ -0,0 +1,103 @@
package main
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash/adler32"
"hash/crc32"
"io"
"io/ioutil"
"os"
"os/exec"
"strconv"
"strings"
)
var (
fileExistsValues = map[string]bool{
"overwrite": true,
"fail": true,
"skip": true,
}
)
func execute(cmd *exec.Cmd) error {
fmt.Println("+", strings.Join(cmd.Args, " "))
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
return cmd.Run()
}
func checksum(r io.Reader, method string) (string, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return "", err
}
switch method {
case "md5":
return fmt.Sprintf("%x", md5.Sum(b)), nil
case "sha1":
return fmt.Sprintf("%x", sha1.Sum(b)), nil
case "sha256":
return fmt.Sprintf("%x", sha256.Sum256(b)), nil
case "sha512":
return fmt.Sprintf("%x", sha512.Sum512(b)), nil
case "adler32":
return strconv.FormatUint(uint64(adler32.Checksum(b)), 10), nil
case "crc32":
return strconv.FormatUint(uint64(crc32.ChecksumIEEE(b)), 10), nil
}
return "", fmt.Errorf("Hashing method %s is not supported", method)
}
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
}