1
0
mirror of https://github.com/strongdm/comply synced 2026-03-04 17:46:17 +00:00

chore: migrate to go modules

This commit is contained in:
Carlos C
2019-07-14 13:51:10 -03:00
parent 87e8266f1b
commit c303b68201
125 changed files with 7334 additions and 2443 deletions

26
vendor/github.com/aktau/github-release/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,26 @@
github-release
go-app
bin/
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe

20
vendor/github.com/aktau/github-release/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Nicolas Hillegeer
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

83
vendor/github.com/aktau/github-release/Makefile generated vendored Normal file
View File

@@ -0,0 +1,83 @@
LAST_TAG := $(shell git describe --abbrev=0 --tags)
USER := aktau
EXECUTABLE := github-release
# only include the amd64 binaries, otherwise the github release will become
# too big
UNIX_EXECUTABLES := \
darwin/amd64/$(EXECUTABLE) \
freebsd/amd64/$(EXECUTABLE) \
linux/amd64/$(EXECUTABLE)
WIN_EXECUTABLES := \
windows/amd64/$(EXECUTABLE).exe
COMPRESSED_EXECUTABLES=$(UNIX_EXECUTABLES:%=%.tar.bz2) $(WIN_EXECUTABLES:%.exe=%.zip)
COMPRESSED_EXECUTABLE_TARGETS=$(COMPRESSED_EXECUTABLES:%=bin/%)
UPLOAD_CMD = bin/tmp/$(EXECUTABLE) upload -u $(USER) -r $(EXECUTABLE) -t $(LAST_TAG) -n $(subst /,-,$(FILE)) -f bin/$(FILE)
all: $(EXECUTABLE)
# the executable used to perform the upload, dogfooding and all...
bin/tmp/$(EXECUTABLE):
go build -o "$@"
# arm
bin/linux/arm/5/$(EXECUTABLE):
GOARM=5 GOARCH=arm GOOS=linux go build -o "$@"
bin/linux/arm/7/$(EXECUTABLE):
GOARM=7 GOARCH=arm GOOS=linux go build -o "$@"
# 386
bin/darwin/386/$(EXECUTABLE):
GOARCH=386 GOOS=darwin go build -o "$@"
bin/linux/386/$(EXECUTABLE):
GOARCH=386 GOOS=linux go build -o "$@"
bin/windows/386/$(EXECUTABLE):
GOARCH=386 GOOS=windows go build -o "$@"
# amd64
bin/freebsd/amd64/$(EXECUTABLE):
GOARCH=amd64 GOOS=freebsd go build -o "$@"
bin/darwin/amd64/$(EXECUTABLE):
GOARCH=amd64 GOOS=darwin go build -o "$@"
bin/linux/amd64/$(EXECUTABLE):
GOARCH=amd64 GOOS=linux go build -o "$@"
bin/windows/amd64/$(EXECUTABLE).exe:
GOARCH=amd64 GOOS=windows go build -o "$@"
# compressed artifacts, makes a huge difference (Go executable is ~9MB,
# after compressing ~2MB)
%.tar.bz2: %
tar -jcvf "$<.tar.bz2" "$<"
%.zip: %.exe
zip "$@" "$<"
# git tag -a v$(RELEASE) -m 'release $(RELEASE)'
release: clean
$(MAKE) bin/tmp/$(EXECUTABLE) $(COMPRESSED_EXECUTABLE_TARGETS)
git push && git push --tags
git log --format=%B $(LAST_TAG) -1 | \
bin/tmp/$(EXECUTABLE) release -u $(USER) -r $(EXECUTABLE) \
-t $(LAST_TAG) -n $(LAST_TAG) -d - || true
$(foreach FILE,$(COMPRESSED_EXECUTABLES),$(UPLOAD_CMD);)
# install and/or update all dependencies, run this from the project directory
# go get -u ./...
# go test -i ./
dep:
go list -f '{{join .Deps "\n"}}' | xargs go list -e -f '{{if not .Standard}}{{.ImportPath}}{{end}}' | xargs go get -u
$(EXECUTABLE): dep
go build -o "$@"
install:
go install
clean:
rm go-app || true
rm $(EXECUTABLE) || true
rm -rf bin/
.PHONY: clean release dep install

132
vendor/github.com/aktau/github-release/README.md generated vendored Normal file
View File

@@ -0,0 +1,132 @@
github-release
==============
A small commandline app written in Go that allows you to easily create
and delete releases of your projects on Github. In addition it allows
you to attach files to those releases.
It interacts with the [github releases API](http://developer.github.com/v3/repos/releases).
Though it's entirely possibly to [do all these things with
cURL](https://github.com/blog/1645-releases-api-preview), It's not
really that user-friendly. For example, you need to first query the API
to find the id of the release you want, before you can upload an
artifact. `github-release` takes care of those little details.
It might still be a bit rough around the edges, pull requests are
welcome!
**NOTE**: I've been made aware of the existence of the
[gothub](https://github.com/itchio/gothub) fork. Since I have very little
time to work on the project and have been a really bad maintainer, I suggest
checking it out to see if your issues have been solved there.
How to install
==============
If you don't have the Go toolset installed, and you don't want to, but
still want to use the app, you can download binaries for your platform
on the [releases
page](https://github.com/aktau/github-release/releases/latest). Yes, that's
dogfooding, check the makefile!
If you have Go installed, you can just do:
```sh
go get github.com/aktau/github-release
```
This will automatically download, compile and install the app.
After that you should have a `github-release` executable in your
`$GOPATH/bin`.
How to use
==========
**NOTE**: for these examples I've [created a github
token](https://help.github.com/articles/creating-an-access-token-for-command-line-use)
and set it as the env variable `GITHUB_TOKEN`. `github-release` will
automatically pick it up from the environment so that you don't have to
pass it as an argument.
```sh
# set your token
export GITHUB_TOKEN=...
# check the help
$ github-release --help
# make your tag and upload
$ git tag ... && git push --tags
# check the current tags and existing releases of the repo
$ github-release info -u aktau -r gofinance
git tags:
- v0.1.0 (commit: https://api.github.com/repos/aktau/gofinance/commits/f562727ce83ce8971a8569a1879219e41d56a756)
releases:
- v0.1.0, name: 'hoary ungar', description: 'something something dark side 2', id: 166740, tagged: 29/01/2014 at 14:27, published: 30/01/2014 at 16:20, draft: ✔, prerelease: ✗
- artifact: github.go, downloads: 0, state: uploaded, type: application/octet-stream, size: 1.9KB, id: 68616
# create a formal release
$ github-release release \
--user aktau \
--repo gofinance \
--tag v0.1.0 \
--name "the wolf of source street" \
--description "Not a movie, contrary to popular opinion. Still, my first release!" \
--pre-release
# you've made a mistake, but you can edit the release without
# having to delete it first (this also means you can edit without having
# to upload your files again)
$ github-release edit \
--user aktau \
--repo gofinance \
--tag v0.1.0 \
--name "Highlander II: The Quickening" \
--description "This is the actual description!"
# upload a file, for example the OSX/AMD64 binary of my gofinance app
$ github-release upload \
--user aktau \
--repo gofinance \
--tag v0.1.0 \
--name "gofinance-osx-amd64" \
--file bin/darwin/amd64/gofinance
# upload other files...
$ github-release upload ...
# you're not happy with it, so delete it
$ github-release delete \
--user aktau \
--repo gofinance \
--tag v0.1.0
```
GitHub Enterprise Support
=========================
You can point to a different GitHub API endpoint via the environment variable ```GITHUB_API```:
```
export GITHUB_API=http://github.company.com/api/v3
```
Used libraries
==============
| Package | Description | License |
| ------------------------------------------------------------------------ | ------------------- | ------- |
| [github.com/dustin/go-humanize](https://github.com/dustin/go-humanize) | humanize file sizes | MIT |
| [github.com/tomnomnom/linkheader](https://github.com/tomnomnom/linkheader) | GH API pagination | MIT |
| [github.com/voxelbrain/goptions](https://github.com/voxelbrain/goptions) | option parsing | BSD |
Todo
====
- Check if an artifact is already uploaded before starting a new upload
Copyright
=========
Copyright (c) 2014, Nicolas Hillegeer. All rights reserved.

32
vendor/github.com/aktau/github-release/assets.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package main
import (
"time"
)
const (
ASSET_DOWNLOAD_URI = "/repos/%s/%s/releases/assets/%d"
)
type Asset struct {
Url string `json:"url"`
Id int `json:"id"`
Name string `json:"name"`
ContentType string `json:"content_type"`
State string `json:"state"`
Size uint64 `json:"size"`
Downloads uint64 `json:"download_count"`
Created time.Time `json:"created_at"`
Published time.Time `json:"published_at"`
}
// findAssetID returns the asset ID if name can be found in assets,
// otherwise returns -1.
func findAssetID(assets []Asset, name string) int {
for _, asset := range assets {
if asset.Name == name {
return asset.Id
}
}
return -1
}

448
vendor/github.com/aktau/github-release/cmd.go generated vendored Normal file
View File

@@ -0,0 +1,448 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strconv"
"github.com/aktau/github-release/github"
)
func infocmd(opt Options) error {
user := nvls(opt.Info.User, EnvUser)
repo := nvls(opt.Info.Repo, EnvRepo)
token := nvls(opt.Info.Token, EnvToken)
tag := opt.Info.Tag
if user == "" || repo == "" {
return fmt.Errorf("user and repo need to be passed as arguments")
}
// Find regular git tags.
foundTags, err := Tags(user, repo, token)
if err != nil {
return fmt.Errorf("could not fetch tags, %v", err)
}
if len(foundTags) == 0 {
return fmt.Errorf("no tags available for %v/%v", user, repo)
}
tags := foundTags[:0]
for _, t := range foundTags {
// If the user only requested one tag, filter out the rest.
if tag == "" || t.Name == tag {
tags = append(tags, t)
}
}
renderer := renderInfoText
if opt.Info.JSON {
renderer = renderInfoJSON
}
// List releases + assets.
var releases []Release
if tag == "" {
// Get all releases.
vprintf("%v/%v: getting information for all releases\n", user, repo)
releases, err = Releases(user, repo, token)
if err != nil {
return err
}
} else {
// Get only one release.
vprintf("%v/%v/%v: getting information for the release\n", user, repo, tag)
release, err := ReleaseOfTag(user, repo, tag, token)
if err != nil {
return err
}
releases = []Release{*release}
}
return renderer(tags, releases)
}
func renderInfoText(tags []Tag, releases []Release) error {
fmt.Println("tags:")
for _, tag := range tags {
fmt.Println("-", &tag)
}
fmt.Println("releases:")
for _, release := range releases {
fmt.Println("-", &release)
}
return nil
}
func renderInfoJSON(tags []Tag, releases []Release) error {
out := struct {
Tags []Tag
Releases []Release
}{
Tags: tags,
Releases: releases,
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(&out)
}
func uploadcmd(opt Options) error {
user := nvls(opt.Upload.User, EnvUser)
repo := nvls(opt.Upload.Repo, EnvRepo)
token := nvls(opt.Upload.Token, EnvToken)
tag := opt.Upload.Tag
name := opt.Upload.Name
label := opt.Upload.Label
file := opt.Upload.File
vprintln("uploading...")
if file == nil {
return fmt.Errorf("provided file was not valid")
}
defer file.Close()
if err := ValidateCredentials(user, repo, token, tag); err != nil {
return err
}
// Find the release corresponding to the entered tag, if any.
rel, err := ReleaseOfTag(user, repo, tag, token)
if err != nil {
return err
}
// If asked to replace, first delete the existing asset, if any.
if assetID := findAssetID(rel.Assets, name); opt.Upload.Replace && assetID != -1 {
URL := nvls(EnvApiEndpoint, github.DefaultBaseURL) +
fmt.Sprintf(ASSET_DOWNLOAD_URI, user, repo, assetID)
resp, err := github.DoAuthRequest("DELETE", URL, "application/json", token, nil, nil)
if err != nil || resp.StatusCode != http.StatusNoContent {
return fmt.Errorf("could not replace asset %s (ID: %d), deletion failed (error: %v, status: %s)",
name, assetID, err, resp.Status)
}
}
v := url.Values{}
v.Set("name", name)
if label != "" {
v.Set("label", label)
}
url := rel.CleanUploadUrl() + "?" + v.Encode()
resp, err := github.DoAuthRequest("POST", url, "application/octet-stream",
token, nil, file)
if err != nil {
return fmt.Errorf("can't create upload request to %v, %v", url, err)
}
defer resp.Body.Close()
vprintln("RESPONSE:", resp)
if resp.StatusCode != http.StatusCreated {
if msg, err := ToMessage(resp.Body); err == nil {
return fmt.Errorf("could not upload, status code (%v), %v",
resp.Status, msg)
}
return fmt.Errorf("could not upload, status code (%v)", resp.Status)
}
if VERBOSITY != 0 {
vprintf("BODY: ")
if _, err := io.Copy(os.Stderr, resp.Body); err != nil {
return fmt.Errorf("while reading response, %v", err)
}
}
return nil
}
func downloadcmd(opt Options) error {
user := nvls(opt.Download.User, EnvUser)
repo := nvls(opt.Download.Repo, EnvRepo)
token := nvls(opt.Download.Token, EnvToken)
tag := opt.Download.Tag
name := opt.Download.Name
latest := opt.Download.Latest
vprintln("downloading...")
if err := ValidateTarget(user, repo, tag, latest); err != nil {
return err
}
// Find the release corresponding to the entered tag, if any.
var rel *Release
var err error
if latest {
rel, err = LatestRelease(user, repo, token)
} else {
rel, err = ReleaseOfTag(user, repo, tag, token)
}
if err != nil {
return err
}
assetID := findAssetID(rel.Assets, name)
if assetID == -1 {
return fmt.Errorf("coud not find asset named %s", name)
}
var resp *http.Response
if token == "" {
// Use the regular github.com site it we don't have a token.
resp, err = http.Get("https://github.com" + fmt.Sprintf("/%s/%s/releases/download/%s/%s", user, repo, tag, name))
} else {
url := nvls(EnvApiEndpoint, github.DefaultBaseURL) + fmt.Sprintf(ASSET_DOWNLOAD_URI, user, repo, assetID)
resp, err = github.DoAuthRequest("GET", url, "", token, map[string]string{
"Accept": "application/octet-stream",
}, nil)
}
if err != nil {
return fmt.Errorf("could not fetch releases, %v", err)
}
defer resp.Body.Close()
vprintln("GET", resp.Request.URL, "->", resp)
contentLength, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("github did not respond with 200 OK but with %v", resp.Status)
}
out := os.Stdout // Pipe the asset to stdout by default.
if isCharDevice(out) {
// If stdout is a char device, assume it's a TTY (terminal). In this
// case, don't pipe th easset to stdout, but create it as a file in
// the current working folder.
if out, err = os.Create(name); err != nil {
return fmt.Errorf("could not create file %s", name)
}
defer out.Close()
}
return mustCopyN(out, resp.Body, contentLength)
}
// mustCopyN attempts to copy exactly N bytes, if this fails, an error is
// returned.
func mustCopyN(w io.Writer, r io.Reader, n int64) error {
an, err := io.Copy(w, r)
if an != n {
return fmt.Errorf("data did not match content length %d != %d", an, n)
}
return err
}
func ValidateTarget(user, repo, tag string, latest bool) error {
if user == "" {
return fmt.Errorf("empty user")
}
if repo == "" {
return fmt.Errorf("empty repo")
}
if tag == "" && !latest {
return fmt.Errorf("empty tag")
}
return nil
}
func ValidateCredentials(user, repo, token, tag string) error {
if err := ValidateTarget(user, repo, tag, false); err != nil {
return err
}
if token == "" {
return fmt.Errorf("empty token")
}
return nil
}
func releasecmd(opt Options) error {
cmdopt := opt.Release
user := nvls(cmdopt.User, EnvUser)
repo := nvls(cmdopt.Repo, EnvRepo)
token := nvls(cmdopt.Token, EnvToken)
tag := cmdopt.Tag
name := nvls(cmdopt.Name, tag)
desc := nvls(cmdopt.Desc, tag)
target := nvls(cmdopt.Target)
draft := cmdopt.Draft
prerelease := cmdopt.Prerelease
vprintln("releasing...")
if err := ValidateCredentials(user, repo, token, tag); err != nil {
return err
}
// Check if we need to read the description from stdin.
if desc == "-" {
b, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return fmt.Errorf("could not read description from stdin: %v", err)
}
desc = string(b)
}
params := ReleaseCreate{
TagName: tag,
TargetCommitish: target,
Name: name,
Body: desc,
Draft: draft,
Prerelease: prerelease,
}
/* encode params as json */
payload, err := json.Marshal(params)
if err != nil {
return fmt.Errorf("can't encode release creation params, %v", err)
}
reader := bytes.NewReader(payload)
URL := nvls(EnvApiEndpoint, github.DefaultBaseURL) + fmt.Sprintf("/repos/%s/%s/releases", user, repo)
resp, err := github.DoAuthRequest("POST", URL, "application/json", token, nil, reader)
if err != nil {
return fmt.Errorf("while submitting %v, %v", string(payload), err)
}
defer resp.Body.Close()
vprintln("RESPONSE:", resp)
if resp.StatusCode != http.StatusCreated {
if resp.StatusCode == 422 {
return fmt.Errorf("github returned %v (this is probably because the release already exists)",
resp.Status)
}
return fmt.Errorf("github returned %v", resp.Status)
}
if VERBOSITY != 0 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("error while reading response, %v", err)
}
vprintln("BODY:", string(body))
}
return nil
}
func editcmd(opt Options) error {
cmdopt := opt.Edit
user := nvls(cmdopt.User, EnvUser)
repo := nvls(cmdopt.Repo, EnvRepo)
token := nvls(cmdopt.Token, EnvToken)
tag := cmdopt.Tag
name := nvls(cmdopt.Name, tag)
desc := nvls(cmdopt.Desc, tag)
draft := cmdopt.Draft
prerelease := cmdopt.Prerelease
vprintln("editing...")
if err := ValidateCredentials(user, repo, token, tag); err != nil {
return err
}
id, err := IdOfTag(user, repo, tag, token)
if err != nil {
return err
}
vprintf("release %v has id %v\n", tag, id)
// Check if we need to read the description from stdin.
if desc == "-" {
b, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return fmt.Errorf("could not read description from stdin: %v", err)
}
desc = string(b)
}
/* the release create struct works for editing releases as well */
params := ReleaseCreate{
TagName: tag,
Name: name,
Body: desc,
Draft: draft,
Prerelease: prerelease,
}
/* encode the parameters as JSON, as required by the github API */
payload, err := json.Marshal(params)
if err != nil {
return fmt.Errorf("can't encode release creation params, %v", err)
}
URL := nvls(EnvApiEndpoint, github.DefaultBaseURL) + fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id)
resp, err := github.DoAuthRequest("PATCH", URL, "application/json", token, nil, bytes.NewReader(payload))
if err != nil {
return fmt.Errorf("while submitting %v, %v", string(payload), err)
}
defer resp.Body.Close()
vprintln("RESPONSE:", resp)
if resp.StatusCode != http.StatusOK {
if resp.StatusCode == 422 {
return fmt.Errorf("github returned %v (this is probably because the release already exists)",
resp.Status)
}
return fmt.Errorf("github returned unexpected status code %v", resp.Status)
}
if VERBOSITY != 0 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("error while reading response, %v", err)
}
vprintln("BODY:", string(body))
}
return nil
}
func deletecmd(opt Options) error {
user, repo, token, tag := nvls(opt.Delete.User, EnvUser),
nvls(opt.Delete.Repo, EnvRepo),
nvls(opt.Delete.Token, EnvToken),
opt.Delete.Tag
vprintln("deleting...")
id, err := IdOfTag(user, repo, tag, token)
if err != nil {
return err
}
vprintf("release %v has id %v\n", tag, id)
baseURL := nvls(EnvApiEndpoint, github.DefaultBaseURL)
resp, err := github.DoAuthRequest("DELETE", baseURL+fmt.Sprintf("/repos/%s/%s/releases/%d",
user, repo, id), "application/json", token, nil, nil)
if err != nil {
return fmt.Errorf("release deletion failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusNoContent {
return fmt.Errorf("could not delete the release corresponding to tag %s on repo %s/%s",
tag, user, repo)
}
return nil
}

6
vendor/github.com/aktau/github-release/commit.go generated vendored Normal file
View File

@@ -0,0 +1,6 @@
package main
type Commit struct {
Sha string `json:"sha"`
Url string `json:"url"`
}

42
vendor/github.com/aktau/github-release/error.go generated vendored Normal file
View File

@@ -0,0 +1,42 @@
package main
import (
"encoding/json"
"fmt"
"io"
"strings"
)
/* usually when something goes wrong, github sends something like this back */
type Message struct {
Message string `json:"message"`
Errors []GithubError `json:"errors"`
}
type GithubError struct {
Resource string `json:"resource"`
Code string `json:"code"`
Field string `json:"field"`
}
/* transforms a stream into a Message, if it's valid json */
func ToMessage(r io.Reader) (*Message, error) {
var msg Message
if err := json.NewDecoder(r).Decode(&msg); err != nil {
return nil, err
}
return &msg, nil
}
func (m *Message) String() string {
str := fmt.Sprintf("msg: %v, errors: ", m.Message)
errstr := make([]string, len(m.Errors))
for idx, err := range m.Errors {
errstr[idx] = fmt.Sprintf("[field: %v, code: %v]",
err.Field, err.Code)
}
return str + strings.Join(errstr, ", ")
}

View File

@@ -0,0 +1,130 @@
package main
import (
"fmt"
"os"
"github.com/aktau/github-release/github"
"github.com/voxelbrain/goptions"
)
const GH_URL = "https://github.com"
type Options struct {
Help goptions.Help `goptions:"-h, --help, description='Show this help'"`
Verbosity []bool `goptions:"-v, --verbose, description='Be verbose'"`
Quiet bool `goptions:"-q, --quiet, description='Do not print anything, even errors (except if --verbose is specified)'"`
Version bool `goptions:"--version, description='Print version'"`
goptions.Verbs
Download struct {
Token string `goptions:"-s, --security-token, description='Github token ($GITHUB_TOKEN if set). required if repo is private.'"`
User string `goptions:"-u, --user, description='Github repo user or organisation (required if $GITHUB_USER not set)'"`
Repo string `goptions:"-r, --repo, description='Github repo (required if $GITHUB_REPO not set)'"`
Latest bool `goptions:"-l, --latest, description='Download latest release (required if tag is not specified)',mutexgroup='input'"`
Tag string `goptions:"-t, --tag, description='Git tag to download from (required if latest is not specified)', mutexgroup='input',obligatory"`
Name string `goptions:"-n, --name, description='Name of the file', obligatory"`
} `goptions:"download"`
Upload struct {
Token string `goptions:"-s, --security-token, description='Github token (required if $GITHUB_TOKEN not set)'"`
User string `goptions:"-u, --user, description='Github repo user or organisation (required if $GITHUB_USER not set)'"`
Repo string `goptions:"-r, --repo, description='Github repo (required if $GITHUB_REPO not set)'"`
Tag string `goptions:"-t, --tag, description='Git tag to upload to', obligatory"`
Name string `goptions:"-n, --name, description='Name of the file', obligatory"`
Label string `goptions:"-l, --label, description='Label (description) of the file'"`
File *os.File `goptions:"-f, --file, description='File to upload (use - for stdin)', rdonly, obligatory"`
Replace bool `goptions:"-R, --replace, description='Replace asset with same name if it already exists (WARNING: not atomic, failure to upload will remove the original asset too)'"`
} `goptions:"upload"`
Release struct {
Token string `goptions:"-s, --security-token, description='Github token (required if $GITHUB_TOKEN not set)'"`
User string `goptions:"-u, --user, description='Github repo user or organisation (required if $GITHUB_USER not set)'"`
Repo string `goptions:"-r, --repo, description='Github repo (required if $GITHUB_REPO not set)'"`
Tag string `goptions:"-t, --tag, obligatory, description='Git tag to create a release from'"`
Name string `goptions:"-n, --name, description='Name of the release (defaults to tag)'"`
Desc string `goptions:"-d, --description, description='Release description, use - for reading a description from stdin (defaults to tag)'"`
Target string `goptions:"-c, --target, description='Commit SHA or branch to create release of (defaults to the repository default branch)'"`
Draft bool `goptions:"--draft, description='The release is a draft'"`
Prerelease bool `goptions:"-p, --pre-release, description='The release is a pre-release'"`
} `goptions:"release"`
Edit struct {
Token string `goptions:"-s, --security-token, description='Github token (required if $GITHUB_TOKEN not set)'"`
User string `goptions:"-u, --user, description='Github repo user or organisation (required if $GITHUB_USER not set)'"`
Repo string `goptions:"-r, --repo, description='Github repo (required if $GITHUB_REPO not set)'"`
Tag string `goptions:"-t, --tag, obligatory, description='Git tag to edit the release of'"`
Name string `goptions:"-n, --name, description='New name of the release (defaults to tag)'"`
Desc string `goptions:"-d, --description, description='New release description, use - for reading a description from stdin (defaults to tag)'"`
Draft bool `goptions:"--draft, description='The release is a draft'"`
Prerelease bool `goptions:"-p, --pre-release, description='The release is a pre-release'"`
} `goptions:"edit"`
Delete struct {
Token string `goptions:"-s, --security-token, description='Github token (required if $GITHUB_TOKEN not set)'"`
User string `goptions:"-u, --user, description='Github repo user or organisation (required if $GITHUB_USER not set)'"`
Repo string `goptions:"-r, --repo, description='Github repo (required if $GITHUB_REPO not set)'"`
Tag string `goptions:"-t, --tag, obligatory, description='Git tag of release to delete'"`
} `goptions:"delete"`
Info struct {
Token string `goptions:"-s, --security-token, description='Github token ($GITHUB_TOKEN if set). required if repo is private.'"`
User string `goptions:"-u, --user, description='Github repo user or organisation (required if $GITHUB_USER not set)'"`
Repo string `goptions:"-r, --repo, description='Github repo (required if $GITHUB_REPO not set)'"`
Tag string `goptions:"-t, --tag, description='Git tag to query (optional)'"`
JSON bool `goptions:"-j, --json, description='Emit info as JSON instead of text'"`
} `goptions:"info"`
}
type Command func(Options) error
var commands = map[goptions.Verbs]Command{
"download": downloadcmd,
"upload": uploadcmd,
"release": releasecmd,
"edit": editcmd,
"delete": deletecmd,
"info": infocmd,
}
var (
VERBOSITY = 0
)
var (
EnvToken string
EnvUser string
EnvRepo string
EnvApiEndpoint string
)
func init() {
EnvToken = os.Getenv("GITHUB_TOKEN")
EnvUser = os.Getenv("GITHUB_USER")
EnvRepo = os.Getenv("GITHUB_REPO")
EnvApiEndpoint = os.Getenv("GITHUB_API")
}
func main() {
options := Options{}
goptions.ParseAndFail(&options)
if options.Version {
fmt.Printf("github-release v%s\n", VERSION)
return
}
if len(options.Verbs) == 0 {
goptions.PrintHelp()
return
}
VERBOSITY = len(options.Verbosity)
github.VERBOSITY = VERBOSITY
if cmd, found := commands[options.Verbs]; found {
err := cmd(options)
if err != nil {
if !options.Quiet {
fmt.Fprintln(os.Stderr, "error:", err)
}
os.Exit(1)
}
}
}

22
vendor/github.com/aktau/github-release/github/debug.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
package github
import (
"fmt"
"os"
)
func vprintln(a ...interface{}) (int, error) {
if VERBOSITY > 0 {
return fmt.Fprintln(os.Stderr, a...)
}
return 0, nil
}
func vprintf(format string, a ...interface{}) (int, error) {
if VERBOSITY > 0 {
return fmt.Fprintf(os.Stderr, format, a...)
}
return 0, nil
}

79
vendor/github.com/aktau/github-release/github/file.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
package github
import (
"bytes"
"errors"
"fmt"
"io"
"os"
)
func GetFileSize(f *os.File) (int64, error) {
/* first try stat */
off, err := fsizeStat(f)
if err != nil {
/* if that fails, try seek */
return fsizeSeek(f)
}
return off, nil
}
func fsizeStat(f *os.File) (int64, error) {
fi, err := f.Stat()
if err != nil {
return 0, err
}
return fi.Size(), nil
}
func fsizeSeek(f *os.File) (int64, error) {
off, err := f.Seek(0, 2)
if err != nil {
return 0, fmt.Errorf("seeking did not work, stdin is not" +
"supported yet because github doesn't support chunking" +
"requests (and I haven't implemented detecting stdin and" +
"buffering yet")
}
_, err = f.Seek(0, 0)
if err != nil {
return 0, fmt.Errorf("could not seek back in the file")
}
return off, nil
}
// materializeFile takes a physical file or stream (named pipe, user input,
// ...) and returns an io.Reader and the number of bytes that can be read
// from it.
func materializeFile(f *os.File) (io.Reader, int64, error) {
fi, err := f.Stat()
if err != nil {
return nil, 0, err
}
// If the file is actually a char device (like user typed input)
// or a named pipe (like a streamed in file), buffer it up.
//
// When uploading a file, you need to either explicitly set the
// Content-Length header or send a chunked request. Since the
// github upload server doesn't accept chunked encoding, we have
// to set the size of the file manually. Since a stream doesn't have a
// predefined length, it's read entirely into a byte buffer.
if fi.Mode()&(os.ModeCharDevice|os.ModeNamedPipe) == 1 {
vprintln("input was a stream, buffering up")
var buf bytes.Buffer
n, err := buf.ReadFrom(f)
if err != nil {
return nil, 0, errors.New("req: could not buffer up input stream: " + err.Error())
}
return &buf, n, err
}
// We know the os.File is most likely an actual file now.
n, err := GetFileSize(f)
return f, n, err
}

277
vendor/github.com/aktau/github-release/github/github.go generated vendored Normal file
View File

@@ -0,0 +1,277 @@
// Package github is a mini-library for querying the GitHub v3 API that
// takes care of authentication (with tokens only) and pagination.
package github
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"reflect"
"github.com/tomnomnom/linkheader"
)
const DefaultBaseURL = "https://api.github.com"
// Set to values > 0 to control verbosity, for debugging.
var VERBOSITY = 0
// DoAuthRequest ...
//
// TODO: This function is amazingly ugly (separate headers, token, no API
// URL constructions, et cetera).
func DoAuthRequest(method, url, mime, token string, headers map[string]string, body io.Reader) (*http.Response, error) {
req, err := newAuthRequest(method, url, mime, token, headers, body)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}
// Client collects a few options that can be set when contacting the GitHub
// API, such as authorization tokens. Methods called on Client will supply
// these options when calling the API.
type Client struct {
Token string // Github API token, used when set.
BaseURL string // Github API URL, defaults to DefaultBaseURL if unset.
}
// Get fetches uri (relative URL) from the GitHub API and unmarshals the
// response into v. It takes care of pagination transparantly.
func (c Client) Get(uri string, v interface{}) error {
if c.BaseURL == "" {
c.BaseURL = DefaultBaseURL
}
rc, err := c.getPaginated(uri)
if err != nil {
return err
}
defer rc.Close()
var r io.Reader = rc
if VERBOSITY > 0 {
vprintln("BODY:")
r = io.TeeReader(rc, os.Stderr)
}
// Github may return paginated responses. If so, githubGetPaginated will
// return a reader which yields the concatenation of all pages. These
// reponses are _separate_ JSON arrays. Standard json.Unmarshal() or
// json.Decoder.Decode() will not have the expected result when
// unmarshalling into v. For example, a 2-page response:
//
// 1. [{...}, {...}, {...}]
// 2. [{...}]
//
// If v is a slice type, we'd like to decode the four objects from the
// two pages into a single slice. However, if we just use
// json.Decoder.Decode(), that won't work. v will be overridden each
// time.
//
// For this reason, we use two very ugly things.
//
// 1. We analyze v with reflect to see if it's a slice.
// 2. If so, we use the json.Decoder token API and reflection to
// dynamically add new elements into the slice, ignoring the
// boundaries between JSON arrays.
//
// This is a lot of work, and feels very stupid. An alternative would be
// removing the outermost ][ in the intermediate responses, which would
// be even more finnicky. Another alternative would be to explicitly
// expose a pagination API, forcing clients of this code to deal with
// it. That's how the go-github library does it. But why solve a problem
// sensibly if one can power through it with reflection (half-joking)?
sl := reflect.Indirect(reflect.ValueOf(v)) // Get the reflect.Value of the slice so we can append to it.
t := sl.Type()
if t.Kind() != reflect.Slice {
// Not a slice, not going to handle special pagination JSON stream
// semantics since it likely wouldn't work properly anyway. If this
// is a non-paginated stream, it should work.
return json.NewDecoder(r).Decode(v)
}
t = t.Elem() // Extract the type of the slice's elements.
// Use streaming Token API to append all elements of the JSON stream
// arrays (pagination) to the slice.
for dec := json.NewDecoder(r); ; {
tok, err := dec.Token()
if err != nil {
if err == io.EOF {
return nil // Natural end of the JSON stream.
}
return err
}
vprintf("TOKEN %T: %v\n", tok, tok)
// Check for tokens until we get an opening array brace. If we're
// not in an array, we can't decode an array element later, which
// would result in an error.
if tok != json.Delim('[') {
continue
}
// Read the array, appending all elements to the slice.
for dec.More() {
it := reflect.New(t) // Interface to a valid pointer to an object of the same type as the slice elements.
vprintf("OBJECT %T: %v\n", it, it)
if err := dec.Decode(it.Interface()); err != nil {
return err
}
sl.Set(reflect.Append(sl, it.Elem()))
}
}
}
// getPaginated returns a reader that yields the concatenation of the
// paginated responses to a query (URI).
//
// TODO: Rework the API so we can cleanly append per_page=100 as a URL
// parameter.
func (c Client) getPaginated(uri string) (io.ReadCloser, error) {
// Parse the passed-in URI to make sure we don't lose any values when
// setting our own params.
u, err := url.Parse(c.BaseURL + uri)
if err != nil {
return nil, err
}
v := u.Query()
v.Set("per_page", "100") // The default is 30, this makes it less likely for Github to rate-limit us.
if c.Token != "" {
v.Set("access_token", c.Token)
}
u.RawQuery = v.Encode()
resp, err := http.Get(u.String())
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("expected '200 OK' but received '%v' (url: %s)", resp.Status, resp.Request.URL)
}
vprintln("GET (top-level)", resp.Request.URL, "->", resp)
// If the HTTP response is paginated, it will contain a Link header.
links := linkheader.Parse(resp.Header.Get("Link"))
if len(links) == 0 {
return resp.Body, nil // No pagination.
}
// In this case, fetch all pages and concatenate them.
r, w := io.Pipe()
done := make(chan struct{}) // Backpressure from the pipe writer.
responses := make(chan *http.Response, 5) // Allow 5 concurrent HTTP requests.
responses <- resp
// URL fetcher goroutine. Fetches paginated responses until no more
// pages can be found. Closes the write end of the pipe if fetching a
// page fails.
go func() {
defer close(responses) // Signal that no more requests are coming.
for len(links) > 0 {
URL := nextLink(links)
if URL == "" {
return // We're done.
}
resp, err := http.Get(URL)
links = linkheader.Parse(resp.Header.Get("Link"))
if err != nil {
w.CloseWithError(err)
return
}
select {
case <-done:
return // The body concatenator goroutine signals it has stopped.
case responses <- resp: // Schedule the request body to be written to the pipe.
}
}
}()
// Body concatenator goroutine. Writes each response into the pipe
// sequentially. Closes the write end of the pipe if the HTTP status is
// not 200 or the body can't be read.
go func() {
defer func() {
// Drain channel and close bodies, stop leaks.
for resp := range responses {
resp.Body.Close()
}
}()
defer close(done) // Signal that we're done writing all requests, or an error occurred.
for resp := range responses {
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
w.CloseWithError(fmt.Errorf("expected '200 OK' but received '%v' (url: %s)", resp.Status, resp.Request.URL))
return
}
_, err := io.Copy(w, resp.Body)
resp.Body.Close()
if err != nil {
vprintln("error: io.Copy: ", err)
w.CloseWithError(err)
return
}
}
w.Close()
}()
return r, nil
}
// Create a new request that sends the auth token.
func newAuthRequest(method, url, mime, token string, headers map[string]string, body io.Reader) (*http.Request, error) {
vprintln("creating request:", method, url, mime, token)
var n int64 // content length
var err error
if f, ok := body.(*os.File); ok {
// Retrieve the content-length and buffer up if necessary.
body, n, err = materializeFile(f)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
// net/http automatically does this if req.Body is of type
// (bytes.Reader|bytes.Buffer|strings.Reader). Sadly, we also need to
// handle *os.File.
if n != 0 {
vprintln("setting content-length to", n)
req.ContentLength = n
}
if mime != "" {
req.Header.Set("Content-Type", mime)
}
req.Header.Set("Authorization", "token "+token)
for k, v := range headers {
req.Header.Set(k, v)
}
return req, nil
}
// nextLink returns the HTTP header Link annotated with 'next', "" otherwise.
func nextLink(links linkheader.Links) string {
for _, link := range links {
if link.Rel == "next" && link.URL != "" {
return link.URL
}
}
return ""
}

137
vendor/github.com/aktau/github-release/releases.go generated vendored Normal file
View File

@@ -0,0 +1,137 @@
package main
import (
"fmt"
"strings"
"time"
"github.com/aktau/github-release/github"
"github.com/dustin/go-humanize"
)
const (
RELEASE_LIST_URI = "/repos/%s/%s/releases"
RELEASE_LATEST_URI = "/repos/%s/%s/releases/latest"
RELEASE_DATE_FORMAT = "02/01/2006 at 15:04"
)
type Release struct {
Url string `json:"url"`
PageUrl string `json:"html_url"`
UploadUrl string `json:"upload_url"`
Id int `json:"id"`
Name string `json:"name"`
Description string `json:"body"`
TagName string `json:"tag_name"`
Draft bool `json:"draft"`
Prerelease bool `json:"prerelease"`
Created *time.Time `json:"created_at"`
Published *time.Time `json:"published_at"`
Assets []Asset `json:"assets"`
}
func (r *Release) CleanUploadUrl() string {
bracket := strings.Index(r.UploadUrl, "{")
if bracket == -1 {
return r.UploadUrl
}
return r.UploadUrl[0:bracket]
}
func (r *Release) String() string {
str := make([]string, len(r.Assets)+1)
str[0] = fmt.Sprintf(
"%s, name: '%s', description: '%s', id: %d, tagged: %s, published: %s, draft: %v, prerelease: %v",
r.TagName, r.Name, r.Description, r.Id,
timeFmtOr(r.Created, RELEASE_DATE_FORMAT, ""),
timeFmtOr(r.Published, RELEASE_DATE_FORMAT, ""),
Mark(r.Draft), Mark(r.Prerelease))
for idx, asset := range r.Assets {
str[idx+1] = fmt.Sprintf(" - artifact: %s, downloads: %d, state: %s, type: %s, size: %s, id: %d",
asset.Name, asset.Downloads, asset.State, asset.ContentType,
humanize.Bytes(asset.Size), asset.Id)
}
return strings.Join(str, "\n")
}
type ReleaseCreate struct {
TagName string `json:"tag_name"`
TargetCommitish string `json:"target_commitish,omitempty"`
Name string `json:"name"`
Body string `json:"body"`
Draft bool `json:"draft"`
Prerelease bool `json:"prerelease"`
}
func Releases(user, repo, token string) ([]Release, error) {
var releases []Release
err := github.Client{Token: token, BaseURL: EnvApiEndpoint}.Get(fmt.Sprintf(RELEASE_LIST_URI, user, repo), &releases)
if err != nil {
return nil, err
}
return releases, nil
}
func latestReleaseApi(user, repo, token string) (*Release, error) {
var release Release
return &release, github.Client{Token: token, BaseURL: EnvApiEndpoint}.Get(fmt.Sprintf(RELEASE_LATEST_URI, user, repo), &release)
}
func LatestRelease(user, repo, token string) (*Release, error) {
// If latestReleaseApi DOESN'T give an error, return the release.
if latestRelease, err := latestReleaseApi(user, repo, token); err == nil {
return latestRelease, nil
}
// The enterprise api doesnt support the latest release endpoint. Get
// all releases and compare the published date to get the latest.
releases, err := Releases(user, repo, token)
if err != nil {
return nil, err
}
var latestRelIndex = -1
maxDate := time.Time{}
for i, release := range releases {
if relDate := *release.Published; relDate.After(maxDate) {
maxDate = relDate
latestRelIndex = i
}
}
if latestRelIndex == -1 {
return nil, fmt.Errorf("could not find the latest release")
}
vprintln("Scanning ", len(releases), "releases, latest release is", releases[latestRelIndex])
return &releases[latestRelIndex], nil
}
func ReleaseOfTag(user, repo, tag, token string) (*Release, error) {
releases, err := Releases(user, repo, token)
if err != nil {
return nil, err
}
for _, release := range releases {
if release.TagName == tag {
return &release, nil
}
}
return nil, fmt.Errorf("could not find the release corresponding to tag %s", tag)
}
/* find the release-id of the specified tag */
func IdOfTag(user, repo, tag, token string) (int, error) {
release, err := ReleaseOfTag(user, repo, tag, token)
if err != nil {
return 0, err
}
return release.Id, nil
}

28
vendor/github.com/aktau/github-release/tags.go generated vendored Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
"fmt"
"github.com/aktau/github-release/github"
)
const (
TAGS_URI = "/repos/%s/%s/tags"
)
type Tag struct {
Name string `json:"name"`
Commit Commit `json:"commit"`
ZipBallUrl string `json:"zipball_url"`
TarBallUrl string `json:"tarball_url"`
}
func (t *Tag) String() string {
return t.Name + " (commit: " + t.Commit.Url + ")"
}
// Get the tags associated with a repo.
func Tags(user, repo, token string) ([]Tag, error) {
var tags []Tag
return tags, github.Client{Token: token, BaseURL: EnvApiEndpoint}.Get(fmt.Sprintf(TAGS_URI, user, repo), &tags)
}

9
vendor/github.com/aktau/github-release/term.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
package main
func Mark(ok bool) string {
if ok {
return "✔"
} else {
return "✗"
}
}

52
vendor/github.com/aktau/github-release/util.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
package main
import (
"fmt"
"os"
"time"
)
/* oracle nvl, return first non-empty string */
func nvls(xs ...string) string {
for _, s := range xs {
if s != "" {
return s
}
}
return ""
}
func vprintln(a ...interface{}) (int, error) {
if VERBOSITY > 0 {
return fmt.Fprintln(os.Stderr, a...)
}
return 0, nil
}
func vprintf(format string, a ...interface{}) (int, error) {
if VERBOSITY > 0 {
return fmt.Fprintf(os.Stderr, format, a...)
}
return 0, nil
}
// formats time `t` as `fmt` if it is not nil, otherwise returns `def`
func timeFmtOr(t *time.Time, fmt, def string) string {
if t == nil {
return def
}
return t.Format(fmt)
}
// isCharDevice returns true if f is a character device (panics if f can't
// be stat'ed).
func isCharDevice(f *os.File) bool {
stat, err := f.Stat()
if err != nil {
panic(err)
}
return (stat.Mode() & os.ModeCharDevice) != 0
}

3
vendor/github.com/aktau/github-release/version.go generated vendored Normal file
View File

@@ -0,0 +1,3 @@
package main
const VERSION = "0.7.2"