dev: generate assets/github-action-config.json ()

It will be used by GitHub action `golangci-lint-action`.

Relates: 
This commit is contained in:
Isaev Denis 2020-05-09 15:30:54 +03:00 committed by GitHub
parent 3c2aa58dc5
commit 55cd9f78a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 367 additions and 8 deletions
scripts/gen_github_action_config

View file

@ -0,0 +1,10 @@
module github.com/golangci/golangci-lint/scripts/gen_github_action_config
go 1.13
require (
github.com/shurcooL/githubv4 v0.0.0-20200414012201-bbc966b061dd
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f // indirect
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
)

View file

@ -0,0 +1,22 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/shurcooL/githubv4 v0.0.0-20200414012201-bbc966b061dd h1:EwtC+kDj8s9OKiaStPZtTv3neldOyr98AXIxvmn3Gss=
github.com/shurcooL/githubv4 v0.0.0-20200414012201-bbc966b061dd/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f h1:tygelZueB1EtXkPI6mQ4o9DQ0+FKW41hTbunoXZCTqk=
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

View file

@ -0,0 +1,231 @@
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"
"github.com/shurcooL/githubv4"
"golang.org/x/oauth2"
)
func main() {
if err := generate(context.Background()); err != nil {
log.Fatal(err)
}
}
func generate(ctx context.Context) error {
allReleases, err := fetchAllReleases(ctx)
if err != nil {
return fmt.Errorf("failed to fetch all releases: %w", err)
}
cfg, err := buildConfig(allReleases)
if err != nil {
return fmt.Errorf("failed to build config: %w", err)
}
if len(os.Args) != 2 { //nolint:gomnd
return fmt.Errorf("usage: go run .../main.go out-path.json")
}
outFile, err := os.Create(os.Args[1])
if err != nil {
return fmt.Errorf("failed to create output config file: %w", err)
}
defer outFile.Close()
enc := json.NewEncoder(outFile)
enc.SetIndent("", " ")
if err = enc.Encode(cfg); err != nil {
return fmt.Errorf("failed to json encode config: %w", err)
}
return nil
}
type logInfo struct {
Warning string `json:",omitempty"`
Info string `json:",omitempty"`
}
type versionConfig struct {
Error string `json:",omitempty"`
Log *logInfo `json:",omitempty"`
TargetVersion string `json:",omitempty"`
AssetURL string `json:",omitempty"`
}
type actionConfig struct {
MinorVersionToConfig map[string]versionConfig
}
type version struct {
major, minor, patch int
}
func (v version) String() string {
ret := fmt.Sprintf("v%d.%d", v.major, v.minor)
if v.patch != noPatch {
ret += fmt.Sprintf(".%d", v.patch)
}
return ret
}
func (v *version) isAfterOrEq(vv *version) bool {
if v.major != vv.major {
return v.major >= vv.major
}
if v.minor != vv.minor {
return v.minor >= vv.minor
}
return v.patch >= vv.patch
}
const noPatch = -1
func parseVersion(s string) (*version, error) {
const vPrefix = "v"
if !strings.HasPrefix(s, vPrefix) {
return nil, fmt.Errorf("version should start with %q", vPrefix)
}
s = strings.TrimPrefix(s, vPrefix)
parts := strings.Split(s, ".")
var nums []int
for _, part := range parts {
num, err := strconv.Atoi(part)
if err != nil {
return nil, fmt.Errorf("failed to parse version part: %w", err)
}
nums = append(nums, num)
}
if len(nums) == 2 { //nolint:gomnd
return &version{major: nums[0], minor: nums[1], patch: noPatch}, nil
}
if len(nums) == 3 { //nolint:gomnd
return &version{major: nums[0], minor: nums[1], patch: nums[2]}, nil
}
return nil, errors.New("invalid version format")
}
func findLinuxAssetURL(ver *version, releaseAssets []releaseAsset) (string, error) {
pattern := fmt.Sprintf("golangci-lint-%d.%d.%d-linux-amd64.tar.gz", ver.major, ver.minor, ver.patch)
for _, relAsset := range releaseAssets {
if strings.HasSuffix(relAsset.DownloadURL, pattern) {
return relAsset.DownloadURL, nil
}
}
return "", fmt.Errorf("no matched asset url for pattern %q", pattern)
}
func buildConfig(releases []release) (*actionConfig, error) {
versionToRelease := map[version]release{}
for _, rel := range releases {
ver, err := parseVersion(rel.TagName)
if err != nil {
return nil, fmt.Errorf("failed to parse release %s version: %w", rel.TagName, err)
}
if _, ok := versionToRelease[*ver]; ok {
return nil, fmt.Errorf("duplicate release %s", rel.TagName)
}
versionToRelease[*ver] = rel
}
maxPatchReleases := map[string]version{}
for ver := range versionToRelease {
key := fmt.Sprintf("v%d.%d", ver.major, ver.minor)
if mapVer, ok := maxPatchReleases[key]; !ok || ver.isAfterOrEq(&mapVer) {
maxPatchReleases[key] = ver
}
}
minorVersionToConfig := map[string]versionConfig{}
minAllowedVersion := version{major: 1, minor: 14, patch: 0}
for minorVersionedStr, maxPatchVersion := range maxPatchReleases {
if !maxPatchVersion.isAfterOrEq(&minAllowedVersion) {
minorVersionToConfig[minorVersionedStr] = versionConfig{
Error: fmt.Sprintf("golangci-lint version '%s' isn't supported: we support only %s and later versions",
minorVersionedStr, minAllowedVersion),
}
continue
}
maxPatchVersion := maxPatchVersion
assetURL, err := findLinuxAssetURL(&maxPatchVersion, versionToRelease[maxPatchVersion].ReleaseAssets.Nodes)
if err != nil {
return nil, fmt.Errorf("failed to find linux asset url for release %s: %w", maxPatchVersion, err)
}
minorVersionToConfig[minorVersionedStr] = versionConfig{
TargetVersion: maxPatchVersion.String(),
AssetURL: assetURL,
}
}
return &actionConfig{MinorVersionToConfig: minorVersionToConfig}, nil
}
type release struct {
TagName string
ReleaseAssets struct {
Nodes []releaseAsset
} `graphql:"releaseAssets(first: 50)"`
}
type releaseAsset struct {
DownloadURL string
}
func fetchAllReleases(ctx context.Context) ([]release, error) {
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken == "" {
return nil, errors.New("no GITHUB_TOKEN environment variable")
}
src := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken})
httpClient := oauth2.NewClient(context.Background(), src)
client := githubv4.NewClient(httpClient)
var q struct {
Repository struct {
Releases struct {
Nodes []release
PageInfo struct {
EndCursor githubv4.String
HasNextPage bool
}
} `graphql:"releases(first: 100, after: $releasesCursor)"`
} `graphql:"repository(owner: $owner, name: $name)"`
}
vars := map[string]interface{}{
"owner": githubv4.String("golangci"),
"name": githubv4.String("golangci-lint"),
"releasesCursor": (*githubv4.String)(nil),
}
var allReleases []release
for {
err := client.Query(ctx, &q, vars)
if err != nil {
return nil, fmt.Errorf("failed to fetch releases page from github: %w", err)
}
releases := q.Repository.Releases
allReleases = append(allReleases, releases.Nodes...)
if !releases.PageInfo.HasNextPage {
break
}
vars["releasesCursor"] = githubv4.NewString(releases.PageInfo.EndCursor)
}
return allReleases, nil
}