Skip to content

Commit fad5acc

Browse files
committed
Cleaner go version pinning
1 parent 8a3236b commit fad5acc

1 file changed

Lines changed: 38 additions & 50 deletions

File tree

pkg/workflows/artifacts/utils.go

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"os"
77
"os/exec"
8-
"path/filepath"
98
"strings"
109
)
1110

@@ -81,10 +80,10 @@ func GetBuildCmd(inputFile string, outputFile string, rootFolder string) *exec.C
8180
inputFile,
8281
)
8382
env := append(os.Environ(), "GOOS=wasip1", "GOARCH=wasm", "CGO_ENABLED=0")
84-
// Pin GOTOOLCHAIN to the version declared in the nearest go.mod so the
85-
// compiled WASM is reproducible across machines whose local Go differs
86-
// from go.mod (GOTOOLCHAIN=auto uses the local Go when it's newer).
87-
if toolchain := goToolchainFromMod(rootFolder); toolchain != "" {
83+
// Pin GOTOOLCHAIN so the compiled WASM is reproducible. Prefer the
84+
// configured GOTOOLCHAIN; when it's unset, fall back to the version
85+
// declared in the module's go.mod (the local Go version wouldn't pin).
86+
if toolchain := goToolchain(rootFolder); toolchain != "" {
8887
env = append(env, "GOTOOLCHAIN="+toolchain)
8988
}
9089
buildCmd.Env = env
@@ -95,13 +94,37 @@ func GetBuildCmd(inputFile string, outputFile string, rootFolder string) *exec.C
9594
return buildCmd
9695
}
9796

98-
// goToolchainFromMod returns a GOTOOLCHAIN value (e.g. "go1.26.2") derived
99-
// from the nearest go.mod walking up from startDir. Prefers the toolchain
100-
// directive when present, falling back to the go directive. Returns "" when
101-
// no go.mod or version can be determined.
102-
func goToolchainFromMod(startDir string) string {
103-
goModPath := findNearestGoMod(startDir)
104-
if goModPath == "" {
97+
// goToolchain returns a GOTOOLCHAIN value (e.g. "go1.26.2") to pin the build
98+
// to. It prefers the configured `go env GOTOOLCHAIN`; when that is unset (e.g.
99+
// "auto") it falls back to the go version declared in the module's go.mod. The
100+
// local Go version is not used as a fallback because it would not pin a
101+
// reproducible toolchain. Returns "" when nothing can be determined.
102+
func goToolchain(dir string) string {
103+
if v := goEnv(dir, "GOTOOLCHAIN"); v != "" && v != "auto" {
104+
return v
105+
}
106+
return goToolchainFromMod(dir)
107+
}
108+
109+
// goEnv runs `go env <name>` in dir and returns the trimmed value, or "" on error.
110+
func goEnv(dir, name string) string {
111+
cmd := exec.Command("go", "env", name)
112+
cmd.Dir = dir
113+
out, err := cmd.Output()
114+
if err != nil {
115+
return ""
116+
}
117+
return strings.TrimSpace(string(out))
118+
}
119+
120+
// goToolchainFromMod returns a GOTOOLCHAIN value derived from the go directive
121+
// in the module's go.mod, located via `go env GOMOD`. Returns "" when no go.mod
122+
// or go version can be determined.
123+
func goToolchainFromMod(dir string) string {
124+
goModPath := goEnv(dir, "GOMOD")
125+
// `go env GOMOD` returns "" outside a module and os.DevNull when modules
126+
// are disabled (GO111MODULE=off); neither is a real go.mod file.
127+
if goModPath == "" || goModPath == os.DevNull {
105128
return ""
106129
}
107130
f, err := os.Open(goModPath)
@@ -110,47 +133,12 @@ func goToolchainFromMod(startDir string) string {
110133
}
111134
defer f.Close()
112135

113-
var goVersion, toolchain string
114136
scanner := bufio.NewScanner(f)
115137
for scanner.Scan() {
116-
line := strings.TrimSpace(scanner.Text())
117-
if line == "" || strings.HasPrefix(line, "//") {
118-
continue
119-
}
120-
fields := strings.Fields(line)
121-
if len(fields) < 2 {
122-
continue
123-
}
124-
switch fields[0] {
125-
case "go":
126-
goVersion = fields[1]
127-
case "toolchain":
128-
toolchain = fields[1]
138+
fields := strings.Fields(scanner.Text())
139+
if len(fields) == 2 && fields[0] == "go" {
140+
return "go" + fields[1]
129141
}
130142
}
131-
if toolchain != "" {
132-
return toolchain
133-
}
134-
if goVersion != "" {
135-
return "go" + goVersion
136-
}
137143
return ""
138144
}
139-
140-
func findNearestGoMod(startDir string) string {
141-
dir, err := filepath.Abs(startDir)
142-
if err != nil {
143-
return ""
144-
}
145-
for {
146-
candidate := filepath.Join(dir, "go.mod")
147-
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
148-
return candidate
149-
}
150-
parent := filepath.Dir(dir)
151-
if parent == dir {
152-
return ""
153-
}
154-
dir = parent
155-
}
156-
}

0 commit comments

Comments
 (0)