From bc3a56ab104f7e6dc0721b8c8cd697dc4ed94efe Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 26 Jun 2024 08:19:01 +0200 Subject: [PATCH] dev: clean gen_github_action_config (#4847) --- scripts/gen_github_action_config/go.mod | 2 +- scripts/gen_github_action_config/go.sum | 4 +- scripts/gen_github_action_config/main.go | 284 ++++++++++++----------- 3 files changed, 156 insertions(+), 134 deletions(-) diff --git a/scripts/gen_github_action_config/go.mod b/scripts/gen_github_action_config/go.mod index c8f2a21f..cd40b6b8 100644 --- a/scripts/gen_github_action_config/go.mod +++ b/scripts/gen_github_action_config/go.mod @@ -3,7 +3,7 @@ module github.com/golangci/golangci-lint/scripts/gen_github_action_config go 1.21 require ( - github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc + github.com/shurcooL/githubv4 v0.0.0-20240429030203-be2daab69064 golang.org/x/oauth2 v0.21.0 ) diff --git a/scripts/gen_github_action_config/go.sum b/scripts/gen_github_action_config/go.sum index b6c12dce..0bd98dea 100644 --- a/scripts/gen_github_action_config/go.sum +++ b/scripts/gen_github_action_config/go.sum @@ -1,7 +1,7 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc h1:vH0NQbIDk+mJLvBliNGfcQgUmhlniWBDXC79oRxfZA0= -github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= +github.com/shurcooL/githubv4 v0.0.0-20240429030203-be2daab69064 h1:RCQBSFx5JrsbHltqTtJ+kN3U0Y3a/N/GlVdmRSoxzyE= +github.com/shurcooL/githubv4 v0.0.0-20240429030203-be2daab69064/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= diff --git a/scripts/gen_github_action_config/main.go b/scripts/gen_github_action_config/main.go index 809e8943..fea468c4 100644 --- a/scripts/gen_github_action_config/main.go +++ b/scripts/gen_github_action_config/main.go @@ -14,39 +14,7 @@ import ( "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 -} +const noPatch = -1 type logInfo struct { Warning string `json:",omitempty"` @@ -72,16 +40,19 @@ type version struct { 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 { +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 } @@ -89,100 +60,6 @@ func (v *version) isAfterOrEq(vv *version) bool { 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} - - latestVersion := version{} - latestVersionConfig := versionConfig{} - 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, - } - if maxPatchVersion.isAfterOrEq(&latestVersion) { - latestVersion = maxPatchVersion - latestVersionConfig.TargetVersion = maxPatchVersion.String() - latestVersionConfig.AssetURL = assetURL - } - } - minorVersionToConfig["latest"] = latestVersionConfig - - return &actionConfig{MinorVersionToConfig: minorVersionToConfig}, nil -} - type release struct { TagName string ReleaseAssets struct { @@ -194,14 +71,51 @@ type releaseAsset struct { DownloadURL string } +func main() { + if err := generate(context.Background()); err != nil { + log.Fatal(err) + } +} + +func generate(ctx context.Context) error { + if len(os.Args) != 2 { + return fmt.Errorf("usage: go run .../main.go out-path.json") + } + + 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) + } + + 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 +} + 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(ctx, src) - client := githubv4.NewClient(httpClient) + + client := githubv4.NewClient(oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{AccessToken: githubToken}))) var q struct { Repository struct { @@ -227,13 +141,121 @@ func fetchAllReleases(ctx context.Context) ([]release, error) { 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 } + +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} + + latestVersion := version{} + latestVersionConfig := versionConfig{} + + 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, + } + + if maxPatchVersion.isAfterOrEq(&latestVersion) { + latestVersion = maxPatchVersion + latestVersionConfig.TargetVersion = maxPatchVersion.String() + latestVersionConfig.AssetURL = assetURL + } + } + + minorVersionToConfig["latest"] = latestVersionConfig + + return &actionConfig{MinorVersionToConfig: minorVersionToConfig}, nil +} + +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 parseVersion(s string) (*version, error) { + const vPrefix = "v" + if !strings.HasPrefix(s, vPrefix) { + return nil, fmt.Errorf("version %q should start with %q", s, vPrefix) + } + + parts := strings.Split(strings.TrimPrefix(s, vPrefix), ".") + + var nums []int + for _, part := range parts { + num, err := strconv.Atoi(part) + if err != nil { + return nil, fmt.Errorf("failed to parse version %q: %w", s, err) + } + + nums = append(nums, num) + } + + switch len(nums) { + case 2: + return &version{major: nums[0], minor: nums[1], patch: noPatch}, nil + case 3: + return &version{major: nums[0], minor: nums[1], patch: nums[2]}, nil + default: + return nil, fmt.Errorf("invalid version format: %s", s) + } +}