forked from tcnksm/go-latest
/
latest.go
152 lines (122 loc) · 3.56 KB
/
latest.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
go-latest is pacakge to check a provided version is latest from various sources.
http://github.com/tcnksm/go-latest
package main
import (
"github.com/tcnksm/go-latest"
)
githubTag := &latest.GithubTag{
Owner: "tcnksm",
Repository: "ghr"
}
res, _ := latest.Check("0.1.0",githubTag)
if res.Outdated {
fmt.Printf("version 0.1.0 is out of date, you can upgrade to %s", res.Current)
}
*/
package latest
import (
"fmt"
"os"
"sort"
"github.com/hashicorp/go-version"
)
// EnvGoLatestDisable is environmental variable to disable go-latest
// execution.
const EnvGoLatestDisable = "GOLATEST_DISABLE"
// Source is the interface that every version information source must implement.
type Source interface {
// Validate is called before Fetch in Check.
// Source may need to have various information like URL or product name,
// so it is used for check each variables are correctly set.
// If it is failed, Fetch() will not executed.
Validate() error
// Fetch is called in Check to fetch information from remote sources.
// After fetching, it will convert it into common expression (FetchResponse)
Fetch() (*FetchResponse, error)
}
// FetchResponse the commom response of Fetch request.
type FetchResponse struct {
Versions []*version.Version
Malformeds []string
Meta *Meta
}
// Meta is meta information from Fetch request.
type Meta struct {
Message string
URL string
}
// CheckResponse is a response for a Check request.
type CheckResponse struct {
// Current is current latest version on source.
Current string
// Outdate is true when target version is less than Curernt on source.
Outdated bool
// Latest is true when target version is equal to Current on source.
Latest bool
// New is true when target version is greater than Current on source.
New bool
// Malformed store versions or tags which can not be parsed as
// Semantic versioning (not compared with target).
Malformeds []string
// Meta is meta information from source.
Meta *Meta
}
// Check fetches last version information from its source
// and compares with target and return result (CheckResponse).
func Check(s Source, target string) (*CheckResponse, error) {
if os.Getenv(EnvGoLatestDisable) != "" {
return &CheckResponse{}, nil
}
// Convert target to *version.Version
targetV, err := version.NewVersion(target)
if err != nil {
return nil, fmt.Errorf("failed to parse %s, %s", target, err.Error())
}
// Validate source
if err = s.Validate(); err != nil {
return nil, err
}
fr, err := s.Fetch()
if err != nil {
return nil, err
}
// Source must has at leaset one version information
versions := fr.Versions
if len(fr.Versions) == 0 {
return nil, fmt.Errorf("no version to compare")
}
sort.Sort(version.Collection(versions))
currentV := versions[len(versions)-1]
var outdated, latest, new bool
if targetV.LessThan(currentV) {
outdated = true
}
// If target = current, target is `latest`
if targetV.Equal(currentV) {
latest = true
}
// If target > current, target is `latest` and `new`
if targetV.GreaterThan(currentV) {
latest, new = true, true
}
return &CheckResponse{
Current: currentV.String(),
Outdated: outdated,
Latest: latest,
New: new,
Malformeds: fr.Malformeds,
Meta: fr.Meta,
}, nil
}
// newFetchResponse is constructor of FetchResponse. This is only for
// implement your own Source
func newFetchResponse() *FetchResponse {
var versions []*version.Version
var malformeds []string
return &FetchResponse{
Versions: versions,
Malformeds: malformeds,
Meta: &Meta{},
}
}