Skip to content

Commit

Permalink
switch to go
Browse files Browse the repository at this point in the history
  • Loading branch information
cpunion committed Nov 27, 2024
1 parent 9f4d376 commit a0d70b7
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 1 deletion.
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ runs:
architecture: ${{ inputs.architecture }}

- name: 'Setup Go+'
run: node $GITHUB_ACTION_PATH/dist/index.js
run: go run .
shell: bash
env:
INPUT_GOP_VERSION: ${{ inputs.gop-version }}
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/goplus/setup-goplus

go 1.18
9 changes: 9 additions & 0 deletions index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

import "github.com/goplus/setup-goplus/install"

func main() {
if err := install.InstallGop(); err != nil {
panic(err)
}
}
296 changes: 296 additions & 0 deletions install/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
package install

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strings"
)

const GoplusRepo = "https://github.com/goplus/gop.git"

// InstallGop is the main function for installing gop

Check warning on line 15 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L15

Comment should end in a period (godot)
func InstallGop() error {

Check warning on line 16 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L16

exported: func name will be used as install.InstallGop by other packages, and that stutters; consider calling this Gop (revive)
versionSpec, err := resolveVersionInput()
if err != nil {
return err
}

tagVersions, err := fetchTags()
if err != nil {
return err
}

// Filter and sort valid versions
var validVersions []string
for _, v := range tagVersions {
if isValidVersion(v) {
validVersions = append(validVersions, v)
}
}
sortVersions(validVersions)

var version string
if versionSpec == "" || versionSpec == "latest" {

Check warning on line 37 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L37

`if versionSpec == "" || versionSpec == "latest"` has complex nested blocks (complexity: 6) (nestif)
version = validVersions[0]
warning(fmt.Sprintf("No gop-version specified, using latest version: %s", version))

Check warning on line 39 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L39

fmt.Sprintf can be replaced with string concatenation (perfsprint)
} else {
version = maxSatisfying(validVersions, versionSpec)
if version == "" {
warning(fmt.Sprintf("No gop-version found that satisfies '%s', trying branches...", versionSpec))
branches, err := fetchBranches()
if err != nil {
return err
}
if !contains(branches, versionSpec) {
return fmt.Errorf("no gop-version found that satisfies '%s' in branches or tags", versionSpec)

Check warning on line 49 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L49

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"no gop-version found that satisfies '%s' in branches or tags\", versionSpec)" (err113)
}
}
}

var checkoutVersion string
if version != "" {
info(fmt.Sprintf("Selected version %s by spec %s", version, versionSpec))
checkoutVersion = "v" + version
setOutput("gop-version-verified", "true")
} else {
warning(fmt.Sprintf("Unable to find a version that satisfies the version spec '%s', trying branches...", versionSpec))
checkoutVersion = versionSpec
setOutput("gop-version-verified", "false")
}

gopDir, err := cloneBranchOrTag(checkoutVersion)
if err != nil {
return err
}

if err := install(gopDir); err != nil {
return err
}

if version != "" {
if err := checkVersion(version); err != nil {
return err
}
}

gopVersion, err := getGopVersion()
if err != nil {
return err
}
setOutput("gop-version", gopVersion)

return nil
}

func sortVersions(versions []string) {
sort.Sort(sort.Reverse(semverSort(versions)))
}

type semverSort []string

func (s semverSort) Len() int { return len(s) }
func (s semverSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s semverSort) Less(i, j int) bool { return compareVersions(s[i], s[j]) }

func compareVersions(a, b string) bool {
aParts := strings.Split(a, ".")
bParts := strings.Split(b, ".")
for i := 0; i < len(aParts) && i < len(bParts); i++ {
if aParts[i] != bParts[i] {
return aParts[i] < bParts[i]
}
}
return len(aParts) < len(bParts)
}

func isValidVersion(version string) bool {
match, _ := regexp.MatchString(`^\d+\.\d+\.\d+$`, version)
return match
}

func maxSatisfying(versions []string, spec string) string {
for _, v := range versions {
if strings.HasPrefix(v, spec) {
return v
}
}
return ""
}

func cloneBranchOrTag(versionSpec string) (string, error) {
workDir := filepath.Join(os.Getenv("HOME"), "workdir")
if err := os.RemoveAll(workDir); err != nil {
return "", err
}
if err := os.MkdirAll(workDir, 0755); err != nil {
return "", err
}

info(fmt.Sprintf("Cloning gop %s to %s ...", versionSpec, workDir))
cmd := exec.Command("git", "clone", "--depth", "1", "--branch", versionSpec, GoplusRepo)
cmd.Dir = workDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return "", err
}
info("gop cloned")
return filepath.Join(workDir, "gop"), nil
}

func install(gopDir string) error {
info(fmt.Sprintf("Installing gop %s ...", gopDir))
binDir := filepath.Join(os.Getenv("HOME"), "bin")
cmd := exec.Command("go", "run", "cmd/make.go", "-install")
cmd.Dir = gopDir
cmd.Env = append(os.Environ(), "GOBIN="+binDir)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return err
}

addToPath(binDir)
info("gop installed")
return nil
}

func checkVersion(versionSpec string) error {
info(fmt.Sprintf("Testing gop %s ...", versionSpec))
actualVersion, err := getGopVersion()
if err != nil {
return err
}
if actualVersion != versionSpec {
return fmt.Errorf("installed gop version %s does not match expected version %s", actualVersion, versionSpec)

Check warning on line 169 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L169

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"installed gop version %s does not match expected version %s\", actualVersion, versionSpec)" (err113)
}
info(fmt.Sprintf("Installed gop version %s", actualVersion))

Check warning on line 171 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L171

fmt.Sprintf can be replaced with string concatenation (perfsprint)
return nil
}

func getGopVersion() (string, error) {
cmd := exec.Command("gop", "env", "GOPVERSION")
out, err := cmd.Output()
if err != nil {
return "", err
}
return strings.TrimPrefix(strings.TrimSpace(string(out)), "v"), nil
}

func fetchTags() ([]string, error) {
cmd := exec.Command("git", "-c", "versionsort.suffix=-", "ls-remote", "--tags", "--sort=v:refname", GoplusRepo)
out, err := cmd.Output()
if err != nil {
return nil, err
}

var versions []string

Check warning on line 191 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L191

Consider pre-allocating `versions` (prealloc)
for _, line := range strings.Split(string(out), "\n") {
if line == "" {
continue
}
parts := strings.Split(line, "\t")
version := strings.TrimPrefix(strings.TrimPrefix(parts[1], "refs/tags/"), "v")
versions = append(versions, version)
}
return versions, nil
}

func fetchBranches() ([]string, error) {
cmd := exec.Command("git", "-c", "versionsort.suffix=-", "ls-remote", "--heads", "--sort=v:refname", GoplusRepo)
out, err := cmd.Output()
if err != nil {
return nil, err
}

var branches []string

Check warning on line 210 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L210

Consider pre-allocating `branches` (prealloc)
for _, line := range strings.Split(string(out), "\n") {
if line == "" {
continue
}
parts := strings.Split(line, "\t")
branch := strings.TrimPrefix(parts[1], "refs/heads/")
branches = append(branches, branch)
}
return branches, nil
}

func resolveVersionInput() (string, error) {
version := os.Getenv("INPUT_GOP_VERSION")
versionFile := os.Getenv("INPUT_GOP_VERSION_FILE")

if version != "" && versionFile != "" {
warning("Both gop-version and gop-version-file inputs are specified, only gop-version will be used")
return version, nil
}

if version != "" {
return version, nil
}

if versionFile != "" {
if _, err := os.Stat(versionFile); os.IsNotExist(err) {
return "", fmt.Errorf("the specified gop version file at: %s does not exist", versionFile)

Check warning on line 237 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L237

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"the specified gop version file at: %s does not exist\", versionFile)" (err113)
}
return parseGopVersionFile(versionFile)
}

return "", nil
}

func parseGopVersionFile(versionFilePath string) (string, error) {
content, err := os.ReadFile(versionFilePath)
if err != nil {
return "", err
}

filename := filepath.Base(versionFilePath)
if filename == "gop.mod" || filename == "gop.work" {
re := regexp.MustCompile(`^gop (\d+(\.\d+)*)`)
match := re.FindSubmatch(content)
if match != nil {
return string(match[1]), nil
}
return "", nil
}

return strings.TrimSpace(string(content)), nil
}

func setOutput(name, value string) {
if outputFile := os.Getenv("GITHUB_OUTPUT"); outputFile != "" {
f, _ := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
fmt.Fprintf(f, "%s=%s\n", name, value)
f.Close()
}
}

func addToPath(path string) {
if pathFile := os.Getenv("GITHUB_PATH"); pathFile != "" {
f, _ := os.OpenFile(pathFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
fmt.Fprintln(f, path)
f.Close()
}
os.Setenv("PATH", path+":"+os.Getenv("PATH"))
}

func info(msg string) {
fmt.Println(msg)

Check warning on line 282 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L282

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
}

func warning(msg string) {
fmt.Printf("::warning::%s\n", msg)

Check warning on line 286 in install/install.go

View check run for this annotation

qiniu-x / golangci-lint

install/install.go#L286

use of `fmt.Printf` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
}

func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}

0 comments on commit a0d70b7

Please sign in to comment.