forked from deanishe/awgo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
github.go
137 lines (122 loc) · 3.6 KB
/
github.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright (c) 2018 Dean Jackson <[email protected]>
// MIT Licence - http://opensource.org/licenses/MIT
package update
import (
"encoding/json"
"fmt"
"log"
"net/url"
"strings"
aw "github.com/deanishe/awgo"
)
const (
ghBaseURL = "https://api.github.com/repos/"
)
// GitHub is a Workflow Option. It sets a Workflow Updater for the specified GitHub repo.
// Repo name should be of the form "username/repo", e.g. "deanishe/alfred-ssh".
func GitHub(repo string) aw.Option {
return func(wf *aw.Workflow) aw.Option {
u, _ := New(wf, &GitHubReleaser{Repo: repo})
return aw.Update(u)(wf)
}
}
// GitHubReleaser updates from a GitHub repo's releases. Repo should be in
// the form "username/reponame", e.g. "deanishe/alfred-ssh". Releases
// are marked as pre-releases based on the "This is a pre-release"
// checkbox on the website, *not* the version number/tag.
type GitHubReleaser struct {
Repo string // Repo name in form username/repo
releases []*Release // GitHub releases for Repo
}
// Releases implements Releaser. Returns a slice of available releases that
// contain an .alfredworkflow file.
func (gh *GitHubReleaser) Releases() ([]*Release, error) {
if gh.releases == nil {
gh.releases = []*Release{}
// rels := []*Release{}
js, err := getURL(gh.url())
if err != nil {
log.Printf("Error fetching GitHub releases: %s", err)
return nil, err
}
// log.Printf("%d bytes of JSON", len(js))
rels, err := parseGitHubReleases(js)
if err != nil {
log.Printf("Error parsing GitHub releases: %s", err)
return nil, err
}
gh.releases = rels
}
log.Printf("%d release(s) in repo %s", len(gh.releases), gh.Repo)
return gh.releases, nil
}
func (gh *GitHubReleaser) url() *url.URL {
u, _ := url.Parse(fmt.Sprintf("%s%s/releases", ghBaseURL, gh.Repo))
return u
}
// ghRelease is the data model for GitHub releases JSON.
type ghRelease struct {
Name string `json:"name"`
Prerelease bool `json:"prerelease"`
Assets []*ghAsset `json:"assets"`
Tag string `json:"tag_name"`
}
// ghAsset is the data model for GitHub releases JSON.
type ghAsset struct {
Name string `json:"name"`
URL string `json:"browser_download_url"`
}
// parseGitHubReleases parses GitHub releases JSON.
func parseGitHubReleases(js []byte) ([]*Release, error) {
ghrels := []*ghRelease{}
rels := []*Release{}
if err := json.Unmarshal(js, &ghrels); err != nil {
return nil, err
}
for _, ghr := range ghrels {
r, err := ghReleaseToRelease(ghr)
if err != nil {
log.Printf("invalid release: %s", err)
} else {
rels = append(rels, r)
}
}
return rels, nil
}
func ghReleaseToRelease(ghr *ghRelease) (*Release, error) {
rel := &Release{Prerelease: ghr.Prerelease}
// Check version
v, err := NewSemVer(ghr.Tag)
if err != nil {
return nil, fmt.Errorf("invalid version/tag %q: %s", ghr.Tag, err)
}
rel.Version = v
// Check files (assets)
assets := []*ghAsset{}
assets3 := []*ghAsset{} // .alfred3workflow files
for _, gha := range ghr.Assets {
if strings.HasSuffix(gha.Name, ".alfredworkflow") {
assets = append(assets, gha)
} else if strings.HasSuffix(gha.Name, ".alfred3workflow") {
assets3 = append(assets3, gha)
}
}
// Prefer .alfred3workflow files if present
if len(assets3) > 0 {
assets = assets3
}
// Reject bad releases
if len(assets) > 1 {
return nil, fmt.Errorf("multiple (%d) workflow files in release %s", len(assets), ghr.Tag)
}
if len(assets) == 0 {
return nil, fmt.Errorf("no workflow files in release %s", ghr.Tag)
}
rel.Filename = assets[0].Name
u, err := url.Parse(assets[0].URL)
if err != nil {
return nil, err
}
rel.URL = u
return rel, nil
}