From 7ca67a94479d1e44cb52cf43a4753b5be975dd7a Mon Sep 17 00:00:00 2001 From: Evsyukov Denis Date: Sat, 10 Feb 2024 11:37:22 +0300 Subject: [PATCH 1/5] fix: toolchain not available After updating golang to 1.22 we have error: ``` go: download go1.22 for darwin/arm64: toolchain not available ``` https://github.com/golang/go/issues/65568 Just set full version for golang to fix this issue --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f58b1b4..e5eb0d7 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/kevincobain2000/gobrew -go 1.22 +go 1.22.0 require ( github.com/Masterminds/semver v1.5.0 From b1cdfd1174acc5bd8482f5ecdae7b6835dbb46a1 Mon Sep 17 00:00:00 2001 From: Evsyukov Denis Date: Sat, 10 Feb 2024 11:49:41 +0300 Subject: [PATCH 2/5] fix: add build tag to windows cmd goland added this automatically :) --- cmd/gobrew/main_windows.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/gobrew/main_windows.go b/cmd/gobrew/main_windows.go index f0f084c..98bc320 100644 --- a/cmd/gobrew/main_windows.go +++ b/cmd/gobrew/main_windows.go @@ -1,3 +1,5 @@ +//go:build windows + package main const usageMsg = ` From 0c15c1a15e101dd392859d3c08cbe99489e76f07 Mon Sep 17 00:00:00 2001 From: Evsyukov Denis Date: Sat, 10 Feb 2024 11:50:47 +0300 Subject: [PATCH 3/5] feat: add golangci-lint to build Using golangci-link makes it easier to check the code and avoid a lot of problems --- .github/workflows/golangci-lint.yml | 27 +++++++++++++++++++++++++++ .golangci.yaml | 26 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .golangci.yaml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..97182ba --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,27 @@ +name: golangci-lint +on: + push: + branches: + - '**' + pull_request: + branches: + - '**' + workflow_dispatch: + +permissions: + contents: read +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - + uses: actions/checkout@v4 + - + uses: kevincobain2000/action-gobrew@v2 + with: + version: latest + - + uses: golangci/golangci-lint-action@v4 + with: + version: latest \ No newline at end of file diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..888131a --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,26 @@ +linters: + # Disable all linters. + # Default: false + disable-all: true + # Enable specific linter + # https://golangci-lint.run/usage/linters/#enabled-by-default + enable: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - bodyclose + - dupl + - errorlint + - exportloopref + - goconst + - gocritic + - gocyclo + - goprintffuncname + - gosec + - prealloc + - predeclared + - revive + - stylecheck + - whitespace From 72726e7e6df9534fa19db9b4a6d2c17e3ff6b81e Mon Sep 17 00:00:00 2001 From: Evsyukov Denis Date: Sat, 10 Feb 2024 21:16:17 +0300 Subject: [PATCH 4/5] fix: linters warnings --- .golangci.yaml | 2 -- cmd/gobrew/main.go | 12 ++++++------ gobrew.go | 47 +++++++++++++++++++++++++--------------------- gobrew_test.go | 16 ++++++++-------- helpers.go | 31 +++++++++++++++--------------- helpers_test.go | 1 - 6 files changed, 56 insertions(+), 53 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 888131a..fe58133 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -10,7 +10,6 @@ linters: - govet - ineffassign - staticcheck - - bodyclose - dupl - errorlint - exportloopref @@ -20,7 +19,6 @@ linters: - goprintffuncname - gosec - prealloc - - predeclared - revive - stylecheck - whitespace diff --git a/cmd/gobrew/main.go b/cmd/gobrew/main.go index 0d348de..5046f0f 100644 --- a/cmd/gobrew/main.go +++ b/cmd/gobrew/main.go @@ -67,7 +67,7 @@ func init() { // Check if the major version is 1 and the minor version is 21 or greater if majorVersionNum == 1 && minorVersionNum >= 21 { // Modify the versionArg to include ".0" - versionArg = versionArg + ".0" + versionArg += ".0" } } } @@ -88,10 +88,10 @@ func main() { config := gobrew.Config{ RootDir: rootDir, - RegistryPathUrl: registryPath, - GobrewDownloadUrl: gobrew.DownloadUrl, - GobrewTags: gobrew.TagsApi, - GobrewVersionsUrl: gobrew.VersionsUrl, + RegistryPathURL: registryPath, + GobrewDownloadURL: gobrew.DownloadURL, + GobrewTags: gobrew.TagsAPI, + GobrewVersionsURL: gobrew.VersionsURL, } gb := gobrew.NewGoBrew(config) @@ -108,7 +108,7 @@ func main() { gb.ListRemoteVersions(true) case "install": gb.Install(versionArg) - if gb.CurrentVersion() == "None" { + if gb.CurrentVersion() == gobrew.NoneVersion { gb.Use(versionArg) } case "use": diff --git a/gobrew.go b/gobrew.go index a8fba2e..90e9bb5 100644 --- a/gobrew.go +++ b/gobrew.go @@ -20,9 +20,14 @@ import ( const ( goBrewDir string = ".gobrew" DefaultRegistryPath string = "https://go.dev/dl/" - DownloadUrl string = "https://github.com/kevincobain2000/gobrew/releases/latest/download/" - TagsApi = "https://raw.githubusercontent.com/kevincobain2000/gobrew/json/golang-tags.json" - VersionsUrl string = "https://api.github.com/repos/kevincobain2000/gobrew/releases/latest" + DownloadURL string = "https://github.com/kevincobain2000/gobrew/releases/latest/download/" + TagsAPI = "https://raw.githubusercontent.com/kevincobain2000/gobrew/json/golang-tags.json" + VersionsURL string = "https://api.github.com/repos/kevincobain2000/gobrew/releases/latest" +) + +const ( + NoneVersion = "None" + ProgramName = "gobrew" ) // check GoBrew implement is Command interface @@ -31,7 +36,7 @@ var _ Command = (*GoBrew)(nil) // Command ... type Command interface { ListVersions() - ListRemoteVersions(print bool) map[string][]string + ListRemoteVersions(bool) map[string][]string CurrentVersion() string Uninstall(version string) Install(version string) string @@ -55,10 +60,10 @@ type GoBrew struct { type Config struct { RootDir string - RegistryPathUrl string - GobrewDownloadUrl string + RegistryPathURL string + GobrewDownloadURL string GobrewTags string - GobrewVersionsUrl string + GobrewVersionsURL string } // NewGoBrew instance @@ -85,7 +90,7 @@ func (gb *GoBrew) Interactive(ask bool) { latestVersion := gb.getLatestVersion() latestMajorVersion := extractMajorVersion(latestVersion) - modVersion := "None" + modVersion := NoneVersion if gb.hasModFile() { modVersion = gb.getModVersion() modVersion = extractMajorVersion(modVersion) @@ -93,11 +98,11 @@ func (gb *GoBrew) Interactive(ask bool) { fmt.Println() - if currentVersion == "None" { + if currentVersion == NoneVersion { color.Warnln("GO Installed Version", ".......", currentVersion) } else { var labels []string - if modVersion != "None" && currentMajorVersion != modVersion { + if modVersion != NoneVersion && currentMajorVersion != modVersion { labels = append(labels, "not same as go.mod") } if currentVersion != latestVersion { @@ -111,7 +116,7 @@ func (gb *GoBrew) Interactive(ask bool) { color.Successln("GO Installed Version", ".......", currentVersion+label) } - if modVersion != "None" && latestMajorVersion != modVersion { + if modVersion != NoneVersion && latestMajorVersion != modVersion { label := " " + color.FgYellow.Render("(not latest)") color.Successln("GO go.mod Version", " .......", modVersion+label) } else { @@ -121,7 +126,7 @@ func (gb *GoBrew) Interactive(ask bool) { color.Successln("GO Latest Version", " .......", latestVersion) fmt.Println() - if currentVersion == "None" { + if currentVersion == NoneVersion { color.Warnln("GO is not installed.") c := true if ask { @@ -133,7 +138,7 @@ func (gb *GoBrew) Interactive(ask bool) { return } - if modVersion != "None" && currentMajorVersion != modVersion { + if modVersion != NoneVersion && currentMajorVersion != modVersion { color.Warnf("GO Installed Version (%s) and go.mod Version (%s) are different.\n", currentMajorVersion, modVersion) c := true if ask { @@ -268,12 +273,12 @@ func (gb *GoBrew) ListRemoteVersions(print bool) map[string][]string { func (gb *GoBrew) CurrentVersion() string { fp, err := evalSymlinks(gb.currentBinDir) if err != nil { - return "None" + return NoneVersion } version := strings.TrimSuffix(fp, filepath.Join("go", "bin")) version = filepath.Base(version) if version == "." { - return "None" + return NoneVersion } return version } @@ -294,7 +299,7 @@ func (gb *GoBrew) Uninstall(version string) { // Install the given version of go func (gb *GoBrew) Install(version string) string { - if version == "" || version == "None" { + if version == "" || version == NoneVersion { color.Errorln("[Error] No version provided") os.Exit(1) } @@ -337,11 +342,11 @@ func (gb *GoBrew) Upgrade(currentVersion string) { return } - mkdirTemp, _ := os.MkdirTemp("", "gobrew") - tmpFile := filepath.Join(mkdirTemp, "gobrew"+fileExt) - downloadUrl, _ := url.JoinPath(gb.GobrewDownloadUrl, "gobrew-"+gb.getArch()+fileExt) + mkdirTemp, _ := os.MkdirTemp("", ProgramName) + tmpFile := filepath.Join(mkdirTemp, ProgramName+fileExt) + downloadURL, _ := url.JoinPath(gb.GobrewDownloadURL, "gobrew-"+gb.getArch()+fileExt) utils.CheckError( - utils.DownloadWithProgress(downloadUrl, "gobrew"+fileExt, mkdirTemp), + utils.DownloadWithProgress(downloadURL, ProgramName+fileExt, mkdirTemp), "[Error] Download GoBrew failed") source, err := os.Open(tmpFile) @@ -351,7 +356,7 @@ func (gb *GoBrew) Upgrade(currentVersion string) { utils.CheckError(os.Remove(source.Name()), "==> [Error] Cannot remove tmp file:") }(source) - goBrewFile := filepath.Join(gb.installDir, "bin", "gobrew"+fileExt) + goBrewFile := filepath.Join(gb.installDir, "bin", ProgramName+fileExt) removeFile(goBrewFile) destination, err := os.Create(goBrewFile) utils.CheckError(err, "==> [Error] Cannot open file") diff --git a/gobrew_test.go b/gobrew_test.go index eb6c487..6b01ecd 100644 --- a/gobrew_test.go +++ b/gobrew_test.go @@ -13,13 +13,13 @@ import ( func setupGobrew(t *testing.T, ts *httptest.Server) GoBrew { tags, _ := url.JoinPath(ts.URL, "golang-tags.json") - versionUrl, _ := url.JoinPath(ts.URL, "latest") + versionURL, _ := url.JoinPath(ts.URL, "latest") config := Config{ RootDir: t.TempDir(), - RegistryPathUrl: ts.URL, - GobrewDownloadUrl: ts.URL, + RegistryPathURL: ts.URL, + GobrewDownloadURL: ts.URL, GobrewTags: tags, - GobrewVersionsUrl: versionUrl, + GobrewVersionsURL: versionURL, } gb := NewGoBrew(config) return gb @@ -59,7 +59,7 @@ func TestUpgrade(t *testing.T) { binaryDir := filepath.Join(gb.installDir, "bin") _ = os.MkdirAll(binaryDir, os.ModePerm) - baseName := "gobrew" + fileExt + baseName := ProgramName + fileExt binaryFile := filepath.Join(binaryDir, baseName) if oldFile, err := os.Create(binaryFile); err == nil { @@ -84,7 +84,7 @@ func TestDoNotUpgradeLatestVersion(t *testing.T) { binaryDir := filepath.Join(gb.installDir, "bin") _ = os.MkdirAll(binaryDir, os.ModePerm) - baseName := "gobrew" + fileExt + baseName := ProgramName + fileExt binaryFile := filepath.Join(binaryDir, baseName) currentVersion := gb.getGobrewVersion() @@ -109,7 +109,7 @@ func TestInteractive(t *testing.T) { currentVersion := gb.CurrentVersion() latestVersion := gb.getLatestVersion() - assert.Equal(t, "None", currentVersion) + assert.Equal(t, NoneVersion, currentVersion) assert.NotEqual(t, currentVersion, latestVersion) gb.Interactive(false) @@ -148,7 +148,7 @@ func TestGoBrew_CurrentVersion(t *testing.T) { ts := httptest.NewServer(http.FileServer(http.Dir("testdata"))) defer ts.Close() gb := setupGobrew(t, ts) - assert.Equal(t, true, gb.CurrentVersion() == "None") + assert.Equal(t, true, gb.CurrentVersion() == NoneVersion) gb.Install("1.19") gb.Use("1.19") assert.Equal(t, true, gb.CurrentVersion() == "1.19") diff --git a/helpers.go b/helpers.go index 52e66e9..320686f 100644 --- a/helpers.go +++ b/helpers.go @@ -72,7 +72,7 @@ func (gb *GoBrew) getGroupedVersion(versions []string, print bool) map[string][] sort.Sort(semver.Collection(versionsSemantic)) // match 1.0.0 or 2.0.0 - reTopVersion, _ := regexp.Compile("[0-9]+.0.0") + reTopVersion := regexp.MustCompile("[0-9]+.0.0") for _, versionSemantic := range versionsSemantic { maxPerLine := 0 @@ -101,7 +101,6 @@ func (gb *GoBrew) getGroupedVersion(versions []string, print bool) map[string][] if v, err := semver.NewVersion(r); err == nil { groupedVersionsSemantic = append(groupedVersionsSemantic, v) } - } // sort semantic versions sort.Sort(semver.Collection(groupedVersionsSemantic)) @@ -169,7 +168,7 @@ func (gb *GoBrew) cleanDownloadsDir() { } func (gb *GoBrew) judgeVersion(version string) string { - judgedVersion := "None" + judgedVersion := NoneVersion rcBetaOk := false reRcOrBeta := regexp.MustCompile("beta.*|rc.*") @@ -194,7 +193,7 @@ func (gb *GoBrew) judgeVersion(version string) string { modVersion := gb.getModVersion() // if modVersion is like 1.19, 1.20, 1.21 then appened @latest to it if strings.Count(modVersion, ".") == 1 { - modVersion = modVersion + "@latest" + modVersion += "@latest" } return gb.judgeVersion(modVersion) } @@ -214,7 +213,7 @@ func (gb *GoBrew) judgeVersion(version string) string { } } if len(versionsSemantic) == 0 { - return "None" + return NoneVersion } // sort semantic versions @@ -225,7 +224,7 @@ func (gb *GoBrew) judgeVersion(version string) string { // get last element if version == "dev-latest" { if len(judgedVersions) == 0 { - return "None" + return NoneVersion } // Filter versions containing "rc" or "beta" filteredVersions := gb.filterVersions(judgedVersions, []string{"rc", "beta"}) @@ -252,7 +251,7 @@ func (gb *GoBrew) judgeVersion(version string) string { return gb.judgeVersion(latest) } - if judgedVersion != "None" { + if judgedVersion != NoneVersion { // check if judgedVersion is in the groupedVersions if _, ok := groupedVersions[judgedVersion]; ok { // get last item in the groupedVersions excluding rc and beta @@ -274,7 +273,7 @@ func (gb *GoBrew) judgeVersion(version string) string { } func (gb *GoBrew) hasModFile() bool { - modFilePath := filepath.Join("go.mod") + modFilePath := "go.mod" _, err := os.Stat(modFilePath) if err == nil { return true @@ -285,14 +284,15 @@ func (gb *GoBrew) hasModFile() bool { return false } +// nolint:gocritic // read go.mod file and extract version // Do not use go to get the version as go list -m -f '{{.GoVersion}}' // Because go might not be installed func (gb *GoBrew) getModVersion() string { - modFilePath := filepath.Join("go.mod") + modFilePath := "go.mod" modFile, err := os.Open(modFilePath) if err != nil { - return "None" + return NoneVersion } defer func(modFile *os.File) { _ = modFile.Close() @@ -310,7 +310,7 @@ func (gb *GoBrew) getModVersion() string { color.Errorln(err) os.Exit(1) } - return "None" + return NoneVersion } func (gb *GoBrew) mkDirs(version string) { @@ -342,10 +342,10 @@ func (gb *GoBrew) filterVersions(versions []string, contains []string) []string func (gb *GoBrew) downloadAndExtract(version string) { tarName := "go" + version + "." + gb.getArch() + tarNameExt - downloadURL, _ := url.JoinPath(gb.RegistryPathUrl, tarName) + downloadURL, _ := url.JoinPath(gb.RegistryPathURL, tarName) color.Infoln("==> [Info] Downloading from:", downloadURL) - dstDownloadDir := filepath.Join(gb.downloadsDir) + dstDownloadDir := gb.downloadsDir color.Infoln("==> [Info] Downloading to:", dstDownloadDir) err := utils.DownloadWithProgress(downloadURL, tarName, dstDownloadDir) @@ -385,7 +385,7 @@ func (gb *GoBrew) changeSymblinkGo(version string) { } func (gb *GoBrew) getGobrewVersion() string { - data := doRequest(gb.GobrewVersionsUrl) + data := doRequest(gb.GobrewVersionsURL) if len(data) == 0 { return "" } @@ -421,12 +421,13 @@ func (gb *GoBrew) getGolangVersions() (result []string) { return } +// nolint:gocritic func doRequest(url string) (data []byte) { client := &http.Client{} request, err := http.NewRequest("GET", url, nil) utils.CheckError(err, "==> [Error] Cannot create request") - request.Header.Set("User-Agent", "gobrew") + request.Header.Set("User-Agent", ProgramName) response, err := client.Do(request) utils.CheckError(err, "==> [Error] Cannot get response") diff --git a/helpers_test.go b/helpers_test.go index a26e05b..38f491c 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -62,7 +62,6 @@ func TestJudgeVersion(t *testing.T) { gb := setupGobrew(t, ts) version := gb.judgeVersion(test.version) assert.Equal(t, test.wantVersion, version) - }) } t.Log("test finished") From 8efdf7d6f4f13784c1b4aeb24abf900685947265 Mon Sep 17 00:00:00 2001 From: Evsyukov Denis Date: Sat, 10 Feb 2024 22:01:01 +0300 Subject: [PATCH 5/5] fix: a more specific definition of exclusion for gocritic --- helpers.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/helpers.go b/helpers.go index 320686f..d564450 100644 --- a/helpers.go +++ b/helpers.go @@ -284,7 +284,6 @@ func (gb *GoBrew) hasModFile() bool { return false } -// nolint:gocritic // read go.mod file and extract version // Do not use go to get the version as go list -m -f '{{.GoVersion}}' // Because go might not be installed @@ -308,7 +307,7 @@ func (gb *GoBrew) getModVersion() string { if err = scanner.Err(); err != nil { color.Errorln(err) - os.Exit(1) + os.Exit(1) // nolint:gocritic } return NoneVersion } @@ -421,7 +420,6 @@ func (gb *GoBrew) getGolangVersions() (result []string) { return } -// nolint:gocritic func doRequest(url string) (data []byte) { client := &http.Client{} request, err := http.NewRequest("GET", url, nil) @@ -439,12 +437,12 @@ func doRequest(url string) (data []byte) { if response.StatusCode == http.StatusTooManyRequests || response.StatusCode == http.StatusForbidden { color.Errorln("==> [Error] Rate limit exhausted") - os.Exit(1) + os.Exit(1) // nolint:gocritic } if response.StatusCode != http.StatusOK { color.Errorln("==> [Error] Cannot read response:", response.Status) - os.Exit(1) + os.Exit(1) // nolint:gocritic } data, err = io.ReadAll(response.Body)