├── .buildkite ├── pipeline.yml └── steps │ └── build-binary.sh ├── .github └── dependabot.yml ├── .gitignore ├── Dockerfile ├── LICENSE.txt ├── Readme.md ├── docker-compose.yml ├── go.mod ├── go.sum └── main.go /.buildkite/pipeline.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: ":mac: amd64" 3 | command: .buildkite/steps/build-binary.sh darwin amd64 4 | 5 | - name: ":linux: amd64" 6 | command: .buildkite/steps/build-binary.sh linux amd64 7 | -------------------------------------------------------------------------------- /.buildkite/steps/build-binary.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu 3 | 4 | export GOOS=$1 5 | export GOARCH=$2 6 | export DISTFILE="dist/github-release-${GOOS}-${GOARCH}" 7 | 8 | go_version="1.9.2" 9 | go_pkg="github.com/buildkite/github-release" 10 | 11 | rm -rf dist 12 | mkdir -p dist 13 | 14 | go_build_in_docker() { 15 | docker run \ 16 | -v "${PWD}:/go/src/${go_pkg}" \ 17 | -w "/go/src/${go_pkg}" \ 18 | -e "GOOS=${GOOS}" -e "GOARCH=${GOARCH}" -e "CGO_ENABLED=0" \ 19 | --rm "golang:${go_version}" \ 20 | go build "$@" 21 | } 22 | 23 | echo "+++ Building github-release for $GOOS/$GOARCH with golang:${go_version} :golang:" 24 | 25 | go_build_in_docker -a -tags netgo -ldflags '-w' -o "${DISTFILE}" main.go 26 | file "${DISTFILE}" 27 | 28 | chmod +x "${DISTFILE}" 29 | echo "👍 ${DISTFILE}" 30 | 31 | buildkite-agent artifact upload "${DISTFILE}" 32 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: gomod 5 | directory: / 6 | schedule: 7 | interval: weekly 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | pkg 3 | dist 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.9.2 AS build-env 2 | 3 | ENV CGO_ENABLED=0 \ 4 | GOOS=linux \ 5 | GOARCH=amd64 6 | 7 | WORKDIR /go/src/github.com/buildkite/github-release 8 | ADD . /go/src/github.com/buildkite/github-release 9 | RUN go build -a -tags netgo -ldflags '-w' -o /bin/github-release 10 | 11 | FROM scratch 12 | COPY --from=build-env /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 13 | COPY --from=build-env /bin/github-release /github-release 14 | ENTRYPOINT ["/github-release"] 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015 Keith Pitt, Buildkite Pty Ltd 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # github-release 2 | 3 | > [!WARNING] 4 | > `github-release` is deprecated. We recommend creating GitHub releases with [the `gh` CLI tool](https://github.com/cli/cli). 5 | 6 | `github-release` is a utility to create GitHub releases and upload packages. 7 | 8 | ### Usage 9 | 10 | ```bash 11 | $ github-release "v1.0" pkg/*.tar.gz --commit "branch-or-sha" \ 12 | --tag "1-0-0-stable" \ 13 | --prerelease \ 14 | --github-repository "your/repo" \ 15 | --github-access-token [..] 16 | ``` 17 | 18 | Environment variables can also be used: 19 | 20 | ```bash 21 | $ export GITHUB_RELEASE_ACCESS_TOKEN="..." 22 | $ export GITHUB_RELEASE_REPOSITORY="..." 23 | $ export GITHUB_RELEASE_TAG="..." 24 | $ export GITHUB_RELEASE_COMMIT="..." 25 | $ export GITHUB_RELEASE_PRERELEASE="..." 26 | $ github-release "v1.0" pkg/*.tar.gz 27 | ``` 28 | 29 | For the GitHub access token, you can use a [personal access token](https://github.com/settings/applications#personal-access-tokens) 30 | 31 | ### Development 32 | 33 | ``` 34 | git clone git@github.com:buildkite/github-release.git 35 | cd github-release 36 | direnv allow 37 | go run main.go --help 38 | ``` 39 | 40 | ### Sponsor 41 | 42 | This project is developed and maintained by [Buildkite](https://buildkite.com) 43 | 44 | ### Contributing 45 | 46 | 1. Fork it 47 | 2. Create your feature branch (`git checkout -b my-new-feature`) 48 | 3. Commit your changes (`git commit -am 'Add some feature'`) 49 | 4. Push to the branch (`git push origin my-new-feature`) 50 | 5. Create new Pull Request 51 | 52 | ### Copyright 53 | 54 | Copyright (c) 2015 Keith Pitt, Tim Lucas, Buildkite Pty Ltd. See LICENSE for details. 55 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | github-release: 5 | image: buildkite/github-release 6 | build: . 7 | environment: 8 | - GITHUB_RELEASE_ACCESS_TOKEN 9 | - GITHUB_RELEASE_REPOSITORY 10 | - GITHUB_RELEASE_TAG 11 | - GITHUB_RELEASE_COMMIT 12 | - GITHUB_RELEASE_PRERELEASE 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/buildkite/github-release 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/google/go-github v8.0.1-0.20170604030111-7a51fb928f52+incompatible 7 | github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect 8 | github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f 9 | github.com/stretchr/testify v1.8.4 // indirect 10 | golang.org/x/oauth2 v0.0.0-20170517174439-f047394b6d14 11 | google.golang.org/appengine v1.6.8 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 5 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 6 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 7 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 8 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 9 | github.com/google/go-github v8.0.1-0.20170604030111-7a51fb928f52+incompatible h1:XfCpf6Ak5XPaZpv2rlCqfBuKoFQH5SvXvpQIiV1H7H4= 10 | github.com/google/go-github v8.0.1-0.20170604030111-7a51fb928f52+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 11 | github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= 12 | github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 13 | github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f h1:I6mXuorHlvwNDFelz7a+j0HaGYSzX7+Gq60DqLVypfc= 14 | github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w= 15 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 16 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 19 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 20 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 21 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 22 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 23 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 24 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 25 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 26 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 27 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 28 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 29 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 30 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= 31 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 32 | golang.org/x/oauth2 v0.0.0-20170517174439-f047394b6d14 h1:5fvqvN1Glau4dT9f9CReJ/5DQdgl50EfDOzX7jBecZg= 33 | golang.org/x/oauth2 v0.0.0-20170517174439-f047394b6d14/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 34 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 35 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 36 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 37 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 38 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 39 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 40 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 41 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 42 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 43 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 44 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 45 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 46 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 47 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 48 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 49 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 50 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 51 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 52 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 53 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= 54 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 55 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 56 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 57 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 58 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 59 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 60 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 61 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 62 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 63 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "reflect" 11 | "strings" 12 | 13 | "github.com/google/go-github/github" 14 | "github.com/oleiade/reflections" 15 | "golang.org/x/oauth2" 16 | ) 17 | 18 | var commandLineName = "github-release" 19 | 20 | var commandLineVersion = "1.0" 21 | 22 | var commandLineUsage = `github-release is a utility to create GitHub releases and upload packages. 23 | 24 | Usage: 25 | $ github-release "v1.0" pkg/*.tar.gz --commit "branch-or-sha" \ # defaults to master 26 | --tag "1-0-0-stable" \ # defaults to the name of the release 27 | --prerelease \ # defaults to false 28 | --github-repository "your/repo" \ 29 | --github-access-token [..] 30 | 31 | Environment variables can also be used: 32 | 33 | $ export GITHUB_RELEASE_ACCESS_TOKEN="..." 34 | $ export GITHUB_RELEASE_REPOSITORY="..." 35 | $ export GITHUB_RELEASE_TAG="..." 36 | $ export GITHUB_RELEASE_COMMIT="..." 37 | $ export GITHUB_RELEASE_PRERELEASE="..." 38 | $ github-release "v1.0" pkg/*.tar.gz 39 | 40 | Version: 41 | $ github-release --version 42 | 43 | Help: 44 | $ github-release --help 45 | 46 | See https://github.com/buildkite/github-release and the GitHub 47 | create release documentation https://developer.github.com/v3/repos/releases/#create-a-release 48 | for more information.` 49 | 50 | type commandLineOptions struct { 51 | GithubAccessToken string `flag:"github-access-token" env:"GITHUB_RELEASE_ACCESS_TOKEN" required:"true"` 52 | GithubRepository string `flag:"github-repository" env:"GITHUB_RELEASE_REPOSITORY" required:"true"` 53 | Tag string `flag:"tag" env:"GITHUB_RELEASE_TAG"` 54 | Commit string `flag:"commit" env:"GITHUB_RELEASE_COMMIT"` 55 | Prerelease bool `flag:"prerelease" env:"GITHUB_RELEASE_PRERELEASE"` 56 | } 57 | 58 | // tokenSource is an oauth2.TokenSource which returns a static access token 59 | type tokenSource struct { 60 | token *oauth2.Token 61 | } 62 | 63 | // Token implements the oauth2.TokenSource interface 64 | func (t *tokenSource) Token() (*oauth2.Token, error) { 65 | return t.token, nil 66 | } 67 | 68 | func main() { 69 | if len(os.Args) == 1 { 70 | exitAndError("invalid usage") 71 | } 72 | 73 | // Are they checking version? 74 | if os.Args[1] == "--version" { 75 | fmt.Printf("%s version %s\n", commandLineName, commandLineVersion) 76 | os.Exit(0) 77 | } 78 | 79 | // Collect the release assets from the command line 80 | releaseAssets := collectReleaseAssets(os.Args[1:]) 81 | 82 | // Parse our command line options 83 | options := commandLineOptions{} 84 | parseArgs(&options, os.Args) 85 | 86 | // Grab our release name. If it starts with a '--', then they haven't 87 | // entered one. 88 | releaseName := os.Args[1] 89 | if strings.HasPrefix(releaseName, "--") { 90 | exitAndError("invalid usage") 91 | } 92 | 93 | // fmt.Printf("name: %s\n", releaseName) 94 | // fmt.Printf("assets: %s\n", releaseAssets) 95 | // fmt.Printf("options: %s\n", options.GithubAccessToken) 96 | 97 | // Finally do the release 98 | release(releaseName, releaseAssets, &options) 99 | } 100 | 101 | // Finds the assets from the argument list by looping over every argument, 102 | // and checking if it's a file. 103 | func collectReleaseAssets(args []string) (files []string) { 104 | for i := 0; i < len(args); i++ { 105 | arg := args[i] 106 | 107 | // Check if the file exists 108 | if _, err := os.Stat(arg); err == nil { 109 | files = append(files, arg) 110 | } 111 | 112 | // If the arg is an option, we've gone to far 113 | if strings.HasPrefix(arg, "--") { 114 | break 115 | } 116 | } 117 | 118 | return 119 | } 120 | 121 | // Reflects on the values in the commandLineOptions structure, and fills it 122 | // with values either from the command line, or from the ENV. 123 | func parseArgs(opts *commandLineOptions, args []string) { 124 | // Create a flag set for args 125 | flags := flag.NewFlagSet(commandLineName, flag.ExitOnError) 126 | 127 | // Get the fields for the strucutre 128 | fields, _ := reflections.Fields(opts) 129 | 130 | // Loop through each field of the structure, and define a flag for it 131 | for i := 0; i < len(fields); i++ { 132 | fieldName := fields[i] 133 | flagName, _ := reflections.GetFieldTag(opts, fieldName, "flag") 134 | fieldKind, _ := reflections.GetFieldKind(opts, fieldName) 135 | 136 | if fieldKind == reflect.String { 137 | flags.String(flagName, "", "") 138 | } else if fieldKind == reflect.Bool { 139 | flags.Bool(flagName, false, "") 140 | } else { 141 | exitAndError(fmt.Sprintf("Could not create flag for %s", fieldName)) 142 | } 143 | } 144 | 145 | // Define our custom usage text 146 | flags.Usage = func() { 147 | fmt.Printf("%s\n", commandLineUsage) 148 | } 149 | 150 | // Search the args until we find a "--", which signifies the user has started 151 | // defining options. 152 | var argumentFlags []string 153 | started := false 154 | for i := 0; i < len(args); i++ { 155 | if strings.HasPrefix(args[i], "--") { 156 | started = true 157 | } 158 | 159 | if started { 160 | argumentFlags = append(argumentFlags, args[i]) 161 | } 162 | } 163 | 164 | // Now parse the flags 165 | flags.Parse(argumentFlags) 166 | 167 | // Now the flag set has been populated with values, retrieve them and 168 | // set them (or use the ENV variable if empty) 169 | for i := 0; i < len(fields); i++ { 170 | fieldName := fields[i] 171 | fieldKind, _ := reflections.GetFieldKind(opts, fieldName) 172 | 173 | // Grab the flags we need 174 | flagName, _ := reflections.GetFieldTag(opts, fieldName, "flag") 175 | envName, _ := reflections.GetFieldTag(opts, fieldName, "env") 176 | required, _ := reflections.GetFieldTag(opts, fieldName, "required") 177 | 178 | // Grab the value from the flag 179 | value := flags.Lookup(flagName).Value.String() 180 | 181 | // If the value of the flag is empty, try the ENV 182 | if value == "" { 183 | value = os.Getenv(envName) 184 | } 185 | 186 | // Do validation 187 | if required == "true" && value == "" { 188 | exitAndError(fmt.Sprintf("missing %s", flagName)) 189 | } 190 | 191 | // Set the value in the right way 192 | if fieldKind == reflect.String { 193 | reflections.SetField(opts, fieldName, value) 194 | } else if fieldKind == reflect.Bool { 195 | // The bool is converted to a string above 196 | if value == "true" { 197 | reflections.SetField(opts, fieldName, true) 198 | } else { 199 | reflections.SetField(opts, fieldName, false) 200 | } 201 | } else { 202 | exitAndError(fmt.Sprintf("Could not set value of %s", fieldName)) 203 | } 204 | } 205 | } 206 | 207 | func exitAndError(message interface{}) { 208 | fmt.Printf("%s: %s\nSee '%s --help'\n", commandLineName, message, commandLineName) 209 | os.Exit(1) 210 | } 211 | 212 | func release(releaseName string, releaseAssets []string, options *commandLineOptions) { 213 | if options.Prerelease { 214 | log.Printf("Creating prerelease %s for repository: %s", releaseName, options.GithubRepository) 215 | } else { 216 | log.Printf("Creating release %s for repository: %s", releaseName, options.GithubRepository) 217 | } 218 | 219 | // Split the repository into two parts (owner and repository) 220 | repositoryParts := strings.Split(options.GithubRepository, "/") 221 | if len(repositoryParts) != 2 { 222 | exitAndError("github-repository is in the wrong format") 223 | } 224 | 225 | // If no tag was provided, use the name of the release 226 | tagName := options.Tag 227 | if tagName == "" { 228 | tagName = releaseName 229 | } 230 | 231 | // log.Printf("%s", repos) 232 | // log.Printf("name: %s", releaseName) 233 | // log.Printf("assets: %s", releaseAssets) 234 | // log.Printf("commit: %s", options.Commit) 235 | // log.Printf("tag: %s", tag) 236 | 237 | // Create an oAuth Token Source 238 | ts := &tokenSource{ 239 | &oauth2.Token{AccessToken: options.GithubAccessToken}, 240 | } 241 | 242 | // New oAuth client 243 | tc := oauth2.NewClient(oauth2.NoContext, ts) 244 | 245 | // Github Client 246 | client := github.NewClient(tc) 247 | 248 | ctx := context.TODO() 249 | 250 | // Create an object that represents the release 251 | release := &github.RepositoryRelease{ 252 | Name: &releaseName, 253 | TagName: &tagName, 254 | TargetCommitish: &options.Commit, 255 | Prerelease: &options.Prerelease, 256 | } 257 | 258 | // Create the GitHub release 259 | createdRelease, _, err := client.Repositories.CreateRelease(ctx, repositoryParts[0], repositoryParts[1], release) 260 | if err != nil { 261 | log.Fatalf("Failed to create release (%T %v)", err, err) 262 | } 263 | 264 | // log.Printf("DEBUG: %s", github.Stringify(createdRelease)) 265 | 266 | // Start uploading the assets 267 | for i := 0; i < len(releaseAssets); i++ { 268 | fileName := releaseAssets[i] 269 | 270 | file, err := os.Open(fileName) 271 | if err != nil { 272 | log.Fatalf("Could not open file \"%s\" (%T %v)", fileName, err, err) 273 | } 274 | 275 | releaseAssetOptions := &github.UploadOptions{Name: filepath.Base(fileName)} 276 | createdReleaseAsset, _, err := client.Repositories.UploadReleaseAsset(ctx,repositoryParts[0], repositoryParts[1], *createdRelease.ID, releaseAssetOptions, file) 277 | if err != nil { 278 | log.Fatalf("Failed to upload asset \"%s\" (%T %v)", fileName, err, err) 279 | } 280 | 281 | log.Printf("Successfully uploaded asset: %s", github.Stringify(createdReleaseAsset.URL)) 282 | } 283 | 284 | log.Printf("Successfully created release: %s", github.Stringify(createdRelease.HTMLURL)) 285 | } 286 | --------------------------------------------------------------------------------