@@ -14,39 +14,7 @@ import (
1414 "golang.org/x/oauth2"
1515)
1616
17- func main () {
18- if err := generate (context .Background ()); err != nil {
19- log .Fatal (err )
20- }
21- }
22-
23- func generate (ctx context.Context ) error {
24- allReleases , err := fetchAllReleases (ctx )
25- if err != nil {
26- return fmt .Errorf ("failed to fetch all releases: %w" , err )
27- }
28-
29- cfg , err := buildConfig (allReleases )
30- if err != nil {
31- return fmt .Errorf ("failed to build config: %w" , err )
32- }
33-
34- if len (os .Args ) != 2 { //nolint:gomnd
35- return fmt .Errorf ("usage: go run .../main.go out-path.json" )
36- }
37- outFile , err := os .Create (os .Args [1 ])
38- if err != nil {
39- return fmt .Errorf ("failed to create output config file: %w" , err )
40- }
41- defer outFile .Close ()
42- enc := json .NewEncoder (outFile )
43- enc .SetIndent ("" , " " )
44- if err = enc .Encode (cfg ); err != nil {
45- return fmt .Errorf ("failed to json encode config: %w" , err )
46- }
47-
48- return nil
49- }
17+ const noPatch = - 1
5018
5119type logInfo struct {
5220 Warning string `json:",omitempty"`
@@ -72,79 +40,142 @@ type version struct {
7240
7341func (v version ) String () string {
7442 ret := fmt .Sprintf ("v%d.%d" , v .major , v .minor )
43+
7544 if v .patch != noPatch {
7645 ret += fmt .Sprintf (".%d" , v .patch )
7746 }
47+
7848 return ret
7949}
8050
81- func (v * version ) isAfterOrEq (vv * version ) bool {
51+ func (v version ) isAfterOrEq (vv * version ) bool {
8252 if v .major != vv .major {
8353 return v .major >= vv .major
8454 }
55+
8556 if v .minor != vv .minor {
8657 return v .minor >= vv .minor
8758 }
8859
8960 return v .patch >= vv .patch
9061}
9162
92- const noPatch = - 1
63+ type release struct {
64+ TagName string
65+ ReleaseAssets struct {
66+ Nodes []releaseAsset
67+ } `graphql:"releaseAssets(first: 50)"`
68+ }
9369
94- func parseVersion (s string ) (* version , error ) {
95- const vPrefix = "v"
96- if ! strings .HasPrefix (s , vPrefix ) {
97- return nil , fmt .Errorf ("version should start with %q" , vPrefix )
70+ type releaseAsset struct {
71+ DownloadURL string
72+ }
73+
74+ func main () {
75+ if err := generate (context .Background ()); err != nil {
76+ log .Fatal (err )
9877 }
99- s = strings . TrimPrefix ( s , vPrefix )
78+ }
10079
101- parts := strings .Split (s , "." )
80+ func generate (ctx context.Context ) error {
81+ if len (os .Args ) != 2 {
82+ return fmt .Errorf ("usage: go run .../main.go out-path.json" )
83+ }
10284
103- var nums []int
104- for _ , part := range parts {
105- num , err := strconv .Atoi (part )
106- if err != nil {
107- return nil , fmt .Errorf ("failed to parse version part: %w" , err )
108- }
109- nums = append (nums , num )
85+ allReleases , err := fetchAllReleases (ctx )
86+ if err != nil {
87+ return fmt .Errorf ("failed to fetch all releases: %w" , err )
11088 }
11189
112- if len (nums ) == 2 { //nolint:gomnd
113- return & version {major : nums [0 ], minor : nums [1 ], patch : noPatch }, nil
90+ cfg , err := buildConfig (allReleases )
91+ if err != nil {
92+ return fmt .Errorf ("failed to build config: %w" , err )
11493 }
115- if len (nums ) == 3 { //nolint:gomnd
116- return & version {major : nums [0 ], minor : nums [1 ], patch : nums [2 ]}, nil
94+
95+ outFile , err := os .Create (os .Args [1 ])
96+ if err != nil {
97+ return fmt .Errorf ("failed to create output config file: %w" , err )
11798 }
11899
119- return nil , errors .New ("invalid version format" )
100+ defer outFile .Close ()
101+
102+ enc := json .NewEncoder (outFile )
103+ enc .SetIndent ("" , " " )
104+
105+ if err = enc .Encode (cfg ); err != nil {
106+ return fmt .Errorf ("failed to json encode config: %w" , err )
107+ }
108+
109+ return nil
120110}
121111
122- func findLinuxAssetURL (ver * version , releaseAssets []releaseAsset ) (string , error ) {
123- pattern := fmt .Sprintf ("golangci-lint-%d.%d.%d-linux-amd64.tar.gz" , ver .major , ver .minor , ver .patch )
124- for _ , relAsset := range releaseAssets {
125- if strings .HasSuffix (relAsset .DownloadURL , pattern ) {
126- return relAsset .DownloadURL , nil
112+ func fetchAllReleases (ctx context.Context ) ([]release , error ) {
113+ githubToken := os .Getenv ("GITHUB_TOKEN" )
114+ if githubToken == "" {
115+ return nil , errors .New ("no GITHUB_TOKEN environment variable" )
116+ }
117+
118+ client := githubv4 .NewClient (oauth2 .NewClient (ctx , oauth2 .StaticTokenSource (& oauth2.Token {AccessToken : githubToken })))
119+
120+ var q struct {
121+ Repository struct {
122+ Releases struct {
123+ Nodes []release
124+ PageInfo struct {
125+ EndCursor githubv4.String
126+ HasNextPage bool
127+ }
128+ } `graphql:"releases(first: 100, orderBy: { field: CREATED_AT, direction: DESC }, after: $releasesCursor)"`
129+ } `graphql:"repository(owner: $owner, name: $name)"`
130+ }
131+
132+ vars := map [string ]any {
133+ "owner" : githubv4 .String ("golangci" ),
134+ "name" : githubv4 .String ("golangci-lint" ),
135+ "releasesCursor" : (* githubv4 .String )(nil ),
136+ }
137+
138+ var allReleases []release
139+ for {
140+ err := client .Query (ctx , & q , vars )
141+ if err != nil {
142+ return nil , fmt .Errorf ("failed to fetch releases page from GitHub: %w" , err )
127143 }
144+
145+ releases := q .Repository .Releases
146+ allReleases = append (allReleases , releases .Nodes ... )
147+
148+ if ! releases .PageInfo .HasNextPage {
149+ break
150+ }
151+
152+ vars ["releasesCursor" ] = githubv4 .NewString (releases .PageInfo .EndCursor )
128153 }
129- return "" , fmt .Errorf ("no matched asset url for pattern %q" , pattern )
154+
155+ return allReleases , nil
130156}
131157
132158func buildConfig (releases []release ) (* actionConfig , error ) {
133159 versionToRelease := map [version ]release {}
160+
134161 for _ , rel := range releases {
135162 ver , err := parseVersion (rel .TagName )
136163 if err != nil {
137164 return nil , fmt .Errorf ("failed to parse release %s version: %w" , rel .TagName , err )
138165 }
166+
139167 if _ , ok := versionToRelease [* ver ]; ok {
140168 return nil , fmt .Errorf ("duplicate release %s" , rel .TagName )
141169 }
170+
142171 versionToRelease [* ver ] = rel
143172 }
144173
145174 maxPatchReleases := map [string ]version {}
175+
146176 for ver := range versionToRelease {
147177 key := fmt .Sprintf ("v%d.%d" , ver .major , ver .minor )
178+
148179 if mapVer , ok := maxPatchReleases [key ]; ! ok || ver .isAfterOrEq (& mapVer ) {
149180 maxPatchReleases [key ] = ver
150181 }
@@ -155,6 +186,7 @@ func buildConfig(releases []release) (*actionConfig, error) {
155186
156187 latestVersion := version {}
157188 latestVersionConfig := versionConfig {}
189+
158190 for minorVersionedStr , maxPatchVersion := range maxPatchReleases {
159191 if ! maxPatchVersion .isAfterOrEq (& minAllowedVersion ) {
160192 minorVersionToConfig [minorVersionedStr ] = versionConfig {
@@ -163,77 +195,67 @@ func buildConfig(releases []release) (*actionConfig, error) {
163195 }
164196 continue
165197 }
198+
166199 maxPatchVersion := maxPatchVersion
200+
167201 assetURL , err := findLinuxAssetURL (& maxPatchVersion , versionToRelease [maxPatchVersion ].ReleaseAssets .Nodes )
168202 if err != nil {
169203 return nil , fmt .Errorf ("failed to find linux asset url for release %s: %w" , maxPatchVersion , err )
170204 }
205+
171206 minorVersionToConfig [minorVersionedStr ] = versionConfig {
172207 TargetVersion : maxPatchVersion .String (),
173208 AssetURL : assetURL ,
174209 }
210+
175211 if maxPatchVersion .isAfterOrEq (& latestVersion ) {
176212 latestVersion = maxPatchVersion
177213 latestVersionConfig .TargetVersion = maxPatchVersion .String ()
178214 latestVersionConfig .AssetURL = assetURL
179215 }
180216 }
217+
181218 minorVersionToConfig ["latest" ] = latestVersionConfig
182219
183220 return & actionConfig {MinorVersionToConfig : minorVersionToConfig }, nil
184221}
185222
186- type release struct {
187- TagName string
188- ReleaseAssets struct {
189- Nodes []releaseAsset
190- } `graphql:"releaseAssets(first: 50)"`
191- }
192-
193- type releaseAsset struct {
194- DownloadURL string
195- }
223+ func findLinuxAssetURL (ver * version , releaseAssets []releaseAsset ) (string , error ) {
224+ pattern := fmt .Sprintf ("golangci-lint-%d.%d.%d-linux-amd64.tar.gz" , ver .major , ver .minor , ver .patch )
196225
197- func fetchAllReleases ( ctx context. Context ) ([] release , error ) {
198- githubToken := os . Getenv ( "GITHUB_TOKEN" )
199- if githubToken == "" {
200- return nil , errors . New ( "no GITHUB_TOKEN environment variable" )
226+ for _ , relAsset := range releaseAssets {
227+ if strings . HasSuffix ( relAsset . DownloadURL , pattern ) {
228+ return relAsset . DownloadURL , nil
229+ }
201230 }
202- src := oauth2 .StaticTokenSource (& oauth2.Token {AccessToken : githubToken })
203- httpClient := oauth2 .NewClient (ctx , src )
204- client := githubv4 .NewClient (httpClient )
205231
206- var q struct {
207- Repository struct {
208- Releases struct {
209- Nodes []release
210- PageInfo struct {
211- EndCursor githubv4.String
212- HasNextPage bool
213- }
214- } `graphql:"releases(first: 100, orderBy: { field: CREATED_AT, direction: DESC }, after: $releasesCursor)"`
215- } `graphql:"repository(owner: $owner, name: $name)"`
216- }
232+ return "" , fmt .Errorf ("no matched asset url for pattern %q" , pattern )
233+ }
217234
218- vars := map [ string ] any {
219- "owner" : githubv4 . String ( "golangci" ),
220- "name" : githubv4 . String ( "golangci-lint" ),
221- "releasesCursor" : ( * githubv4 . String )( nil ),
235+ func parseVersion ( s string ) ( * version , error ) {
236+ const vPrefix = "v"
237+ if ! strings . HasPrefix ( s , vPrefix ) {
238+ return nil , fmt . Errorf ( "version %q should start with %q" , s , vPrefix )
222239 }
223240
224- var allReleases []release
225- for {
226- err := client .Query (ctx , & q , vars )
241+ parts := strings .Split (strings .TrimPrefix (s , vPrefix ), "." )
242+
243+ var nums []int
244+ for _ , part := range parts {
245+ num , err := strconv .Atoi (part )
227246 if err != nil {
228- return nil , fmt .Errorf ("failed to fetch releases page from GitHub : %w" , err )
247+ return nil , fmt .Errorf ("failed to parse version %q : %w" , s , err )
229248 }
230- releases := q .Repository .Releases
231- allReleases = append (allReleases , releases .Nodes ... )
232- if ! releases .PageInfo .HasNextPage {
233- break
234- }
235- vars ["releasesCursor" ] = githubv4 .NewString (releases .PageInfo .EndCursor )
249+
250+ nums = append (nums , num )
236251 }
237252
238- return allReleases , nil
253+ switch len (nums ) {
254+ case 2 :
255+ return & version {major : nums [0 ], minor : nums [1 ], patch : noPatch }, nil
256+ case 3 :
257+ return & version {major : nums [0 ], minor : nums [1 ], patch : nums [2 ]}, nil
258+ default :
259+ return nil , fmt .Errorf ("invalid version format: %s" , s )
260+ }
239261}
0 commit comments