From 70ca6255f4a1d82766a31206d6f511044555be34 Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Mon, 23 Nov 2015 01:07:57 +0100 Subject: [PATCH] Implemented initial version based on google/go-github --- .drone.sec | 0 .drone.yml | 31 +++ .gitignore | 2 + DOCS.md | 17 ++ Dockerfile | 10 + LICENSE | 1 - README.md | 47 ++++- glide.yaml | 13 ++ logo.svg | 6 + main.go | 262 ++++++++++++++++++++++++ vendor/github.com/drone/drone-go | 1 + vendor/github.com/google/go-github | 1 + vendor/github.com/google/go-querystring | 1 + vendor/golang.org/x/net | 1 + vendor/golang.org/x/oauth2 | 1 + 15 files changed, 392 insertions(+), 2 deletions(-) create mode 100644 .drone.sec create mode 100644 .drone.yml create mode 100644 DOCS.md create mode 100644 Dockerfile create mode 100644 glide.yaml create mode 100644 logo.svg create mode 100644 main.go create mode 160000 vendor/github.com/drone/drone-go create mode 160000 vendor/github.com/google/go-github create mode 160000 vendor/github.com/google/go-querystring create mode 160000 vendor/golang.org/x/net create mode 160000 vendor/golang.org/x/oauth2 diff --git a/.drone.sec b/.drone.sec new file mode 100644 index 0000000..e69de29 diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..24a0ce9 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,31 @@ +build: + image: golang:1.5 + environment: + - GO15VENDOREXPERIMENT=1 + - GOOS=linux + - GOARCH=amd64 + - CGO_ENABLED=0 + commands: + - go get + - go build + - go test + +publish: + docker: + username: drone + password: $$DOCKER_PASS + email: $$DOCKER_EMAIL + repo: plugins/drone-github-release + when: + branch: master + +plugin: + name: GitHub Release + desc: Publish files and artifacts to GitHub releases + type: publish + image: plugins/drone-github-release + labels: + - publish + - github + - release + - dpl diff --git a/.gitignore b/.gitignore index daf913b..5dc0f42 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ _testmain.go *.exe *.test *.prof + +drone-github-release diff --git a/DOCS.md b/DOCS.md new file mode 100644 index 0000000..a88acb9 --- /dev/null +++ b/DOCS.md @@ -0,0 +1,17 @@ +Use this plugin for publishing files and artifacts to GitHub releases. You +can override the default configuration with the following parameters: + +* `api_key` - GitHub oauth token with public_repo or repo permission +* `files` - Files to upload to GitHub Release; globs are allowed +* `base_url` - GitHub base URL; only required for GHE +* `upload_url` - GitHub upload URL; only required for GHE + +Sample configuration: + +```yaml +publish: + github_release: + api_key: my_github_api_key + files: + - dist/* +``` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d8ba038 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +# Docker image for the Drone GitHub Release plugin +# +# cd $GOPATH/src/github.com/drone-plugins/drone-github-release +# GO15VENDOREXPERIMENT=1 CGO_ENABLED=0 go build -a -tags netgo +# docker build --rm=true -t plugins/drone-github-release . + +FROM alpine:3.2 +RUN apk add -U ca-certificates git && rm -rf /var/cache/apk/* +ADD drone-github-release /bin/ +ENTRYPOINT ["/bin/drone-github-release"] diff --git a/LICENSE b/LICENSE index 8f71f43..8dada3e 100644 --- a/LICENSE +++ b/LICENSE @@ -199,4 +199,3 @@ 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. - diff --git a/README.md b/README.md index 94f4ced..db799be 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,47 @@ # drone-github-release -https://github.com/travis-ci/dpl/blob/master/README.md?#github-releases + +Drone plugin for publishing GitHub releases + +## Usage + +Publish a release: + +``` +./drone-github-release < + + + + + \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..797bcec --- /dev/null +++ b/main.go @@ -0,0 +1,262 @@ +package main + +import ( + "errors" + "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" +) + +type Params struct { + BaseUrl string `json:"base_url"` + UploadUrl string `json:"upload_url"` + APIKey string `json:"api_key"` + Files []string `json:"files"` +} + +func main() { + r := drone.Repo{} + b := drone.Build{} + w := drone.Workspace{} + v := Params{} + + plugin.Param("repo", &r) + plugin.Param("build", &b) + plugin.Param("workspace", &w) + plugin.Param("vargs", &v) + + plugin.MustParse() + + if b.Event != "tag" { + fmt.Printf("The GitHub Release plugin is only available for tags\n") + os.Exit(0) + + return + } + + if len(v.BaseUrl) == 0 { + v.BaseUrl = "https://api.github.com/" + } else { + if !strings.HasSuffix(v.BaseUrl, "/") { + v.BaseUrl = v.BaseUrl + "/" + } + } + + if len(v.UploadUrl) == 0 { + v.UploadUrl = "https://uploads.github.com/" + } else { + if !strings.HasSuffix(v.UploadUrl, "/") { + v.UploadUrl = v.UploadUrl + "/" + } + } + + if len(v.APIKey) == 0 { + fmt.Printf("You must provide an API key\n") + os.Exit(1) + + return + } + + files := make([]string, 0) + + for _, glob := range v.Files { + globed, err := filepath.Glob(glob) + + if err != nil { + fmt.Printf("Failed to glob %s\n", glob) + os.Exit(1) + + return + } + + if globed != nil { + files = append(files, globed...) + } + } + + baseUrl, err := url.Parse(v.BaseUrl) + + if err != nil { + fmt.Printf("Failed to parse base URL\n") + os.Exit(1) + + return + } + + uploadUrl, err := url.Parse(v.UploadUrl) + + if err != nil { + fmt.Printf("Failed to parse upload URL\n") + os.Exit(1) + + return + } + + ts := oauth2.StaticTokenSource( + &oauth2.Token{ + AccessToken: v.APIKey, + }) + + tc := oauth2.NewClient( + oauth2.NoContext, + ts) + + client := github.NewClient(tc) + client.BaseURL = baseUrl + client.UploadURL = uploadUrl + + release, releaseErr := retrieveRelease( + client, + r.Owner, + r.Name, + filepath.Base(b.Ref)) + + if releaseErr != nil { + fmt.Println(releaseErr) + os.Exit(1) + + return + } + + uploadErr := appendFiles( + client, + r.Owner, + r.Name, + *release.ID, + files) + + if uploadErr != nil { + fmt.Println(uploadErr) + os.Exit(1) + + return + } +} + +func prepareRelease(client *github.Client, owner string, repo string, tag string) (*github.RepositoryRelease, error) { + var release *github.RepositoryRelease + var releaseErr error + + release, releaseErr = retrieveRelease( + client, + owner, + repo, + tag) + + if releaseErr != nil { + return nil, releaseErr + } + + if release != nil { + return release, nil + } + + release, releaseErr = createRelease( + client, + owner, + repo, + tag) + + if releaseErr != nil { + return nil, releaseErr + } + + if release != nil { + return release, nil + } + + return nil, errors.New( + "Failed to retrieve or create a release") +} + +func retrieveRelease(client *github.Client, owner string, repo string, tag string) (*github.RepositoryRelease, error) { + release, _, err := client.Repositories.GetReleaseByTag( + owner, + repo, + tag) + + if err != nil { + return nil, errors.New( + "Failed to retrieve release") + } + + fmt.Printf("Successfully retrieved %s release\n", tag) + return release, nil +} + +func createRelease(client *github.Client, owner string, repo string, tag string) (*github.RepositoryRelease, error) { + release, _, err := client.Repositories.CreateRelease( + owner, + repo, + &github.RepositoryRelease{TagName: github.String(tag)}) + + if err != nil { + return nil, errors.New( + "Failed to create release") + } + + fmt.Printf("Successfully created %s release\n", tag) + return release, nil +} + +func appendFiles(client *github.Client, owner string, repo string, id int, files []string) error { + assets, _, err := client.Repositories.ListReleaseAssets( + owner, + repo, + id, + &github.ListOptions{}) + + if err != nil { + return errors.New( + "Failed to fetch existing assets") + } + + for _, file := range files { + handle, err := os.Open(file) + + if err != nil { + return errors.New( + fmt.Sprintf("Failed to read %s artifact", file)) + } + + for _, asset := range assets { + if *asset.Name == path.Base(file) { + _, deleteErr := client.Repositories.DeleteReleaseAsset( + owner, + repo, + *asset.ID) + + if deleteErr != nil { + return errors.New( + fmt.Sprintf("Failed to delete %s artifact", file)) + } else { + fmt.Printf("Successfully deleted old %s artifact\n", *asset.Name) + } + } + } + + _, _, uploadErr := client.Repositories.UploadReleaseAsset( + owner, + repo, + id, + &github.UploadOptions{Name: path.Base(file)}, + handle) + + if uploadErr != nil { + return errors.New( + fmt.Sprintf("Failed to upload %s artifact", file)) + } else { + fmt.Printf("Successfully uploaded %s artifact\n", file) + } + } + + return nil +} diff --git a/vendor/github.com/drone/drone-go b/vendor/github.com/drone/drone-go new file mode 160000 index 0000000..085fdbd --- /dev/null +++ b/vendor/github.com/drone/drone-go @@ -0,0 +1 @@ +Subproject commit 085fdbd71700fb1a27fdc60b09689ab92cee8ceb diff --git a/vendor/github.com/google/go-github b/vendor/github.com/google/go-github new file mode 160000 index 0000000..47f2593 --- /dev/null +++ b/vendor/github.com/google/go-github @@ -0,0 +1 @@ +Subproject commit 47f2593dad1971eec2f0a0971aa007fef5edbc50 diff --git a/vendor/github.com/google/go-querystring b/vendor/github.com/google/go-querystring new file mode 160000 index 0000000..2a60fc2 --- /dev/null +++ b/vendor/github.com/google/go-querystring @@ -0,0 +1 @@ +Subproject commit 2a60fc2ba6c19de80291203597d752e9ba58e4c0 diff --git a/vendor/golang.org/x/net b/vendor/golang.org/x/net new file mode 160000 index 0000000..4f2fc6c --- /dev/null +++ b/vendor/golang.org/x/net @@ -0,0 +1 @@ +Subproject commit 4f2fc6c1e69d41baf187332ee08fbd2b296f21ed diff --git a/vendor/golang.org/x/oauth2 b/vendor/golang.org/x/oauth2 new file mode 160000 index 0000000..442624c --- /dev/null +++ b/vendor/golang.org/x/oauth2 @@ -0,0 +1 @@ +Subproject commit 442624c9ec9243441e83b374a9e22ac549b5c51d