Migrated to 0.5 structure and updated documentation
This commit is contained in:
parent
187cb0b1f2
commit
c097b9bbfa
40
.drone.yml
40
.drone.yml
@ -1,39 +1,29 @@
|
||||
build:
|
||||
image: golang:1.5
|
||||
workspace:
|
||||
base: /go
|
||||
|
||||
pipeline:
|
||||
test:
|
||||
image: golang:1.6
|
||||
environment:
|
||||
- CGO_ENABLED=0
|
||||
commands:
|
||||
- make deps
|
||||
- make vet
|
||||
- make build
|
||||
- make test
|
||||
- go vet
|
||||
- go test -cover -coverprofile=coverage.out
|
||||
- go build -ldflags "-s -w -X main.build=$DRONE_BUILD_NUMBER" -a -tags netgo
|
||||
|
||||
publish:
|
||||
coverage:
|
||||
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
1
.drone.yml.sig
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9nbwoKcGlwZWxpbmU6CiAgdGVzdDoKICAgIGltYWdlOiBnb2xhbmc6MS42CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBDR09fRU5BQkxFRD0wCiAgICBjb21tYW5kczoKICAgICAgLSBnbyB2ZXQKICAgICAgLSBnbyB0ZXN0IC1jb3ZlciAtY292ZXJwcm9maWxlPWNvdmVyYWdlLm91dAogICAgICAtIGdvIGJ1aWxkIC1sZGZsYWdzICItcyAtdyAtWCBtYWluLmJ1aWxkPSREUk9ORV9CVUlMRF9OVU1CRVIiIC1hIC10YWdzIG5ldGdvCgogIGxhdGVzdDoKICAgIGltYWdlOiBkb2NrZXIKICAgIHJlcG86IHBsdWdpbnMvZ2l0aHViLXJlbGVhc2UKICAgIHRhZ3M6IFsgImxhdGVzdCIsICIxLjAiLCAiMSIgXQogICAgd2hlbjoKICAgICAgYnJhbmNoOiBtYXN0ZXIKICAgICAgZXZlbnQ6IHB1c2gKCnBsdWdpbjoKICBuYW1lOiBHaXRIdWIgUmVsZWFzZQogIGRlc2M6IFB1Ymxpc2ggZmlsZXMgYW5kIGFydGlmYWN0cyB0byBHaXRIdWIgUmVsZWFzZXMKICB0eXBlOiBwdWJsaXNoCiAgaW1hZ2U6IHBsdWdpbnMvZ2l0aHViLXJlbGVhc2UKICBsYWJlbHM6CiAgICAtIGdpdGh1YgogICAgLSByZWxlYXNlCg.5FZW1Auk18CljY9LW5P82r9RM_spPCYMYg82PFj2irM
|
71
DOCS.md
71
DOCS.md
@ -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
|
||||
```
|
||||
|
@ -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
132
README.md
@ -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
|
||||
```
|
||||
|
38
checksum.go
38
checksum.go
@ -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
364
main.go
@ -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
134
plugin.go
Normal 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
125
release.go
Normal 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
|
||||
}
|
14
types.go
14
types.go
@ -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
103
utils.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user