Skip to content

Commit

Permalink
Add new flag --replace
Browse files Browse the repository at this point in the history
  • Loading branch information
tcnksm committed Oct 8, 2014
1 parent 318ec51 commit d148aed
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 9 deletions.
51 changes: 51 additions & 0 deletions asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
)

type Assets struct {
Name string `json:"name"`
AssetId int `json:"id"`
}

type DeleteTarget Assets

const (
// GET /repos/:owner/:repo/releases/:id/assets
LIST_ASSET_URL = "https://api.github.com/repos/%s/%s/releases/%d/assets"
)

func listAssetsURL(info *Info) string {
return fmt.Sprintf(LIST_ASSET_URL, info.OwnerName, info.RepoName, info.ID)
}

// DeleteTargets extract asset IDs which is already uploaded
// It decide `already uploaded` based on filename to upload
func SearchDeleteTargets(r io.Reader, uploads []string) ([]DeleteTarget, error) {

targets := []DeleteTarget{}
body, err := ioutil.ReadAll(r)
if err != nil {
return targets, err
}

var assetsUploaded []Assets
err = json.Unmarshal(body, &assetsUploaded)
if err != nil {
return targets, err
}

for _, upload := range uploads {
for _, asset := range assetsUploaded {
if upload == asset.Name {
targets = append(targets,
DeleteTarget{Name: asset.Name, AssetId: asset.AssetId})
}
}
}
return targets, nil
}
61 changes: 61 additions & 0 deletions asset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
. "github.com/onsi/gomega"
"strings"
"testing"
)

func TestListAssetURL(t *testing.T) {
RegisterTestingT(t)

info := &Info{
OwnerName: "tcnksm",
RepoName: "ghr",
ID: 1234,
}

url := listAssetsURL(info)
Expect(url).To(Equal("https://api.github.com/repos/tcnksm/ghr/releases/1234/assets"))
}

func TestExtractDeleteTarget(t *testing.T) {
RegisterTestingT(t)

uploads := []string{"example1.zip", "example3.zip"}

json := `
[
{
"url": "https://api.github.com/repos/octocat/Hello-World/releases/assets/1",
"browser_download_url": "https://github.com/octocat/Hello-World/releases/download/v1.0.0/example1.zip",
"id": 1,
"name": "example1.zip",
"label": "short description",
"state": "uploaded"
},
{
"url": "https://api.github.com/repos/octocat/Hello-World/releases/assets/2",
"browser_download_url": "https://github.com/octocat/Hello-World/releases/download/v1.0.0/example2.zip",
"id": 2,
"name": "example2.zip",
"label": "short description",
"state": "uploaded"
},
{
"url": "https://api.github.com/repos/octocat/Hello-World/releases/assets/3",
"browser_download_url": "https://github.com/octocat/Hello-World/releases/download/v1.0.0/example3.zip",
"id": 3,
"name": "example3.zip",
"label": "short description",
"state": "uploaded"
}
]
`
targets, err := SearchDeleteTargets(strings.NewReader(json), uploads)
Expect(err).NotTo(HaveOccurred())
Expect(targets).To(
Equal([]DeleteTarget{
{Name: "example1.zip", AssetId: 1},
{Name: "example3.zip", AssetId: 3}}))
}
7 changes: 7 additions & 0 deletions delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ import (
const (
// DELETE /repos/:owner/:repo/releases/:id
DELETE_RELEASE_URL = "https://api.github.com/repos/%s/%s/releases/%d"

// DELETE /repos/:owner/:repo/releases/assets/:id
DELETE_ASSET_URL = "https://api.github.com/repos/%s/%s/releases/assets/%d"
)

func deleteReleaseURL(info *Info) string {
return fmt.Sprintf(DELETE_RELEASE_URL, info.OwnerName, info.RepoName, info.ID)
}

func deleteAssetURL(info *Info, assetId int) string {
return fmt.Sprintf(DELETE_ASSET_URL, info.OwnerName, info.RepoName, assetId)
}
14 changes: 13 additions & 1 deletion delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"testing"
)

func TestDeleteURL(t *testing.T) {
func TestDeleteReleaseURL(t *testing.T) {
RegisterTestingT(t)

info := &Info{
Expand All @@ -17,3 +17,15 @@ func TestDeleteURL(t *testing.T) {
url := deleteReleaseURL(info)
Expect(url).To(Equal("https://api.github.com/repos/taichi/tool/releases/123"))
}

func TestDeleteAssetURL(t *testing.T) {
RegisterTestingT(t)

info := &Info{
OwnerName: "taichi",
RepoName: "tool",
}

url := deleteAssetURL(info, 12345)
Expect(url).To(Equal("https://api.github.com/repos/taichi/tool/releases/assets/12345"))
}
52 changes: 44 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var (
repo = flag.String([]string{"r", "-repository"}, "", "Repository name")
token = flag.String([]string{"t", "-token"}, "", "Github API Token")
parallel = flag.Int([]string{"p", "--parallel"}, -1, "Parallelization factor")
flReplace = flag.Bool([]string{"-replace"}, false, "Replace asset if target is already uploaded")
flDelete = flag.Bool([]string{"-delete"}, false, "Delete release if it exists")
flDraft = flag.Bool([]string{"-draft"}, false, "Create unpublised release")
flPrerelease = flag.Bool([]string{"-prerelease"}, false, "Create prerelease")
Expand Down Expand Up @@ -58,6 +59,16 @@ func artifacts(path string) ([]string, error) {
return files, nil
}

func artifactNames(artifacts []string) []string {
names := []string{}
for _, artifact := range artifacts {
if f, err := os.Stat(artifact); err == nil {
names = append(names, f.Name())
}
}
return names
}

func main() {
// call ghrMain in a separate function
// so that it can use defer and have them
Expand Down Expand Up @@ -92,12 +103,6 @@ func ghrMain() int {
tag := flag.Arg(0)
inputPath := flag.Arg(1)

// Limit amount of parallelism
// by number of logic CPU
if *parallel <= 0 {
*parallel = runtime.NumCPU()
}

if *token == "" {
*token = os.Getenv("GITHUB_TOKEN")
if *token == "" {
Expand Down Expand Up @@ -173,6 +178,12 @@ func ghrMain() int {
return 1
}

// Limit amount of parallelism
// by number of logic CPU
if *parallel <= 0 {
*parallel = runtime.NumCPU()
}

// Use CPU efficiently
cpu := runtime.NumCPU()
runtime.GOMAXPROCS(cpu)
Expand All @@ -181,6 +192,30 @@ func ghrMain() int {
var wg sync.WaitGroup
errors := make([]string, 0)
semaphore := make(chan int, *parallel)

if *flReplace {
deleteTargets, err := GetDeleteTargets(info, artifactNames(files))
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
}
for _, target := range deleteTargets {
wg.Add(1)
go func(target DeleteTarget) {
defer wg.Done()
semaphore <- 1
fmt.Fprintf(os.Stderr, "--> %15s is already exists, delete it\n", target.Name)
if err := DeleteAsset(info, target.AssetId); err != nil {
errorLock.Lock()
defer errorLock.Unlock()
errors = append(errors,
fmt.Sprintf("deleting %s error: %s", target.Name, err))
}
<-semaphore
}(target)
}
}
wg.Wait()

for _, path := range files {
wg.Add(1)
go func(path string) {
Expand All @@ -198,7 +233,7 @@ func ghrMain() int {
errorLock.Lock()
defer errorLock.Unlock()
errors = append(errors,
fmt.Sprintf("%s error: %s", path, err))
fmt.Sprintf("upload %s error: %s", path, err))
}
<-semaphore
}(path)
Expand Down Expand Up @@ -226,6 +261,7 @@ Options:
-t, --token Github API Token
-r, --repository Github repository name
-p, --parallel=-1 Amount of parallelism, defaults to number of CPUs
 --replace Replace asset if target already exists
 --delete Delete release if same version exists
--draft Create unpublised release
--prerelease Create prerelease
Expand All @@ -235,6 +271,6 @@ Options:
Example:
$ ghr v1.0.0 dist/
$ ghr --delete v1.0.0 dist/
$ ghr --replace v1.0.0 dist/
$ ghr v1.0.2 dist/tool.zip
`
49 changes: 49 additions & 0 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,30 @@ func GetReleaseID(info *Info) (int, error) {
return SearchIDByTag(res.Body, info.TagName)
}

func GetDeleteTargets(info *Info, uploads []string) ([]DeleteTarget, error) {
requestURL := listAssetsURL(info)
debug(requestURL)

req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return []DeleteTarget{}, err
}

res, err := http.DefaultClient.Do(req)
if err != nil {
return []DeleteTarget{}, err
}
debug(res.Status)

err = checkStatusOK(res.StatusCode, res.Status)
if err != nil {
return []DeleteTarget{}, err
}

defer res.Body.Close()
return SearchDeleteTargets(res.Body, uploads)
}

func DeleteRelease(info *Info) error {
requestURL := deleteReleaseURL(info)
debug(requestURL)
Expand All @@ -74,6 +98,31 @@ func DeleteRelease(info *Info) error {
return nil
}

func DeleteAsset(info *Info, assetId int) error {
requestURL := deleteAssetURL(info, assetId)
debug(requestURL)

req, err := http.NewRequest("DELETE", requestURL, nil)
if err != nil {
return err
}

req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/vnd.github.v3+json")
req.Header.Add("Authorization", fmt.Sprintf("token %s", info.Token))

res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
debug(res.Status)

if res.StatusCode != http.StatusNoContent {
return fmt.Errorf("Github returned %s\n", res.Status)
}
return nil
}

func CreateNewRelease(info *Info) (int, error) {

requestURL := releaseURL(info)
Expand Down

0 comments on commit d148aed

Please sign in to comment.