Skip to content

Commit 3a08c55

Browse files
authored
add a 'check go version' command to RPC (#2542)
1 parent 7955bf6 commit 3a08c55

File tree

6 files changed

+147
-35
lines changed

6 files changed

+147
-35
lines changed

frontend/app/store/wshclientapi.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class RpcApiType {
4242
return client.wshRpcCall("captureblockscreenshot", data, opts);
4343
}
4444

45+
// command "checkgoversion" [call]
46+
CheckGoVersionCommand(client: WshClient, opts?: RpcOpts): Promise<CommandCheckGoVersionRtnData> {
47+
return client.wshRpcCall("checkgoversion", null, opts);
48+
}
49+
4550
// command "connconnect" [call]
4651
ConnConnectCommand(client: WshClient, data: ConnRequest, opts?: RpcOpts): Promise<void> {
4752
return client.wshRpcCall("connconnect", data, opts);

frontend/types/gotypes.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ declare global {
173173
blockid: string;
174174
};
175175

176+
// wshrpc.CommandCheckGoVersionRtnData
177+
type CommandCheckGoVersionRtnData = {
178+
gostatus: string;
179+
gopath: string;
180+
goversion: string;
181+
errorstring?: string;
182+
};
183+
176184
// wshrpc.CommandControllerAppendOutputData
177185
type CommandControllerAppendOutputData = {
178186
blockid: string;

pkg/wshrpc/wshclient/wshclient.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ func CaptureBlockScreenshotCommand(w *wshutil.WshRpc, data wshrpc.CommandCapture
5959
return resp, err
6060
}
6161

62+
// command "checkgoversion", wshserver.CheckGoVersionCommand
63+
func CheckGoVersionCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) (*wshrpc.CommandCheckGoVersionRtnData, error) {
64+
resp, err := sendRpcRequestCallHelper[*wshrpc.CommandCheckGoVersionRtnData](w, "checkgoversion", nil, opts)
65+
return resp, err
66+
}
67+
6268
// command "connconnect", wshserver.ConnConnectCommand
6369
func ConnConnectCommand(w *wshutil.WshRpc, data wshrpc.ConnRequest, opts *wshrpc.RpcOpts) error {
6470
_, err := sendRpcRequestCallHelper[any](w, "connconnect", data, opts)

pkg/wshrpc/wshrpctypes.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ const (
167167
Command_StartBuilder = "startbuilder"
168168
Command_GetBuilderStatus = "getbuilderstatus"
169169
Command_GetBuilderOutput = "getbuilderoutput"
170+
Command_CheckGoVersion = "checkgoversion"
170171

171172
// electron
172173
Command_ElectronEncrypt = "electronencrypt"
@@ -335,6 +336,7 @@ type WshRpcInterface interface {
335336
StartBuilderCommand(ctx context.Context, data CommandStartBuilderData) error
336337
GetBuilderStatusCommand(ctx context.Context, builderId string) (*BuilderStatusData, error)
337338
GetBuilderOutputCommand(ctx context.Context, builderId string) ([]string, error)
339+
CheckGoVersionCommand(ctx context.Context) (*CommandCheckGoVersionRtnData, error)
338340

339341
// proc
340342
VDomRenderCommand(ctx context.Context, data vdom.VDomFrontendUpdate) chan RespOrErrorUnion[*vdom.VDomBackendUpdate]
@@ -1018,6 +1020,13 @@ type BuilderStatusData struct {
10181020
Version int `json:"version"`
10191021
}
10201022

1023+
type CommandCheckGoVersionRtnData struct {
1024+
GoStatus string `json:"gostatus"`
1025+
GoPath string `json:"gopath"`
1026+
GoVersion string `json:"goversion"`
1027+
ErrorString string `json:"errorstring,omitempty"`
1028+
}
1029+
10211030
type CommandElectronEncryptData struct {
10221031
PlainText string `json:"plaintext"`
10231032
}

pkg/wshrpc/wshserver/wshserver.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import (
5757
"github.com/wavetermdev/waveterm/pkg/wsl"
5858
"github.com/wavetermdev/waveterm/pkg/wslconn"
5959
"github.com/wavetermdev/waveterm/pkg/wstore"
60+
"github.com/wavetermdev/waveterm/tsunami/build"
6061
)
6162

6263
var InvalidWslDistroNames = []string{"docker-desktop", "docker-desktop-data"}
@@ -1073,6 +1074,21 @@ func (ws *WshServer) GetBuilderOutputCommand(ctx context.Context, builderId stri
10731074
return bc.GetOutput(), nil
10741075
}
10751076

1077+
func (ws *WshServer) CheckGoVersionCommand(ctx context.Context) (*wshrpc.CommandCheckGoVersionRtnData, error) {
1078+
watcher := wconfig.GetWatcher()
1079+
fullConfig := watcher.GetFullConfig()
1080+
goPath := fullConfig.Settings.TsunamiGoPath
1081+
1082+
result := build.CheckGoVersion(goPath)
1083+
1084+
return &wshrpc.CommandCheckGoVersionRtnData{
1085+
GoStatus: result.GoStatus,
1086+
GoPath: result.GoPath,
1087+
GoVersion: result.GoVersion,
1088+
ErrorString: result.ErrorString,
1089+
}, nil
1090+
}
1091+
10761092
func (ws *WshServer) RecordTEventCommand(ctx context.Context, data telemetrydata.TEvent) error {
10771093
err := telemetry.RecordTEvent(ctx, &data)
10781094
if err != nil {

tsunami/build/build.go

Lines changed: 103 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ func (opts BuildOpts) getNodePath() string {
119119
return "node"
120120
}
121121

122+
type GoVersionCheckResult struct {
123+
GoStatus string
124+
GoPath string
125+
GoVersion string
126+
ErrorString string
127+
}
128+
122129
func FindGoExecutable() (string, error) {
123130
// First try the standard PATH lookup
124131
if goPath, err := exec.LookPath("go"); err == nil {
@@ -156,72 +163,133 @@ func FindGoExecutable() (string, error) {
156163
return "", fmt.Errorf("go command not found in PATH or common installation locations")
157164
}
158165

159-
func verifyEnvironment(verbose bool, opts BuildOpts) (*BuildEnv, error) {
160-
oc := opts.OutputCapture
161-
162-
if opts.SdkVersion == "" && opts.SdkReplacePath == "" {
163-
return nil, fmt.Errorf("either SdkVersion or SdkReplacePath must be set")
164-
}
165-
166-
if opts.SdkVersion != "" {
167-
versionRegex := regexp.MustCompile(`^v\d+\.\d+\.\d+`)
168-
if !versionRegex.MatchString(opts.SdkVersion) {
169-
return nil, fmt.Errorf("SdkVersion must be in semantic version format (e.g., v0.0.0), got: %s", opts.SdkVersion)
170-
}
171-
}
172-
166+
func CheckGoVersion(customGoPath string) GoVersionCheckResult {
173167
var goPath string
174168
var err error
175169

176-
if opts.GoPath != "" {
177-
goPath = opts.GoPath
178-
if verbose {
179-
oc.Printf("Using custom go path: %s", opts.GoPath)
180-
}
170+
if customGoPath != "" {
171+
goPath = customGoPath
181172
} else {
182173
goPath, err = FindGoExecutable()
183174
if err != nil {
184-
return nil, fmt.Errorf("go command not found: %w", err)
185-
}
186-
if verbose {
187-
oc.Printf("Using go path: %s", goPath)
175+
return GoVersionCheckResult{
176+
GoStatus: "notfound",
177+
GoPath: "",
178+
GoVersion: "",
179+
ErrorString: "",
180+
}
188181
}
189182
}
190183

191-
// Run go version command
192184
cmd := exec.Command(goPath, "version")
193185
output, err := cmd.Output()
194186
if err != nil {
195-
return nil, fmt.Errorf("failed to run 'go version': %w", err)
187+
return GoVersionCheckResult{
188+
GoStatus: "error",
189+
GoPath: goPath,
190+
GoVersion: "",
191+
ErrorString: fmt.Sprintf("failed to run 'go version': %v", err),
192+
}
196193
}
197194

198-
// Parse go version output and check for 1.22+
199195
versionStr := strings.TrimSpace(string(output))
200-
if verbose {
201-
oc.Printf("Found %s", versionStr)
202-
}
203196

204-
// Extract version like "go1.22.0" from output
205197
versionRegex := regexp.MustCompile(`go(1\.\d+)`)
206198
matches := versionRegex.FindStringSubmatch(versionStr)
207199
if len(matches) < 2 {
208-
return nil, fmt.Errorf("unable to parse go version from: %s", versionStr)
200+
return GoVersionCheckResult{
201+
GoStatus: "error",
202+
GoPath: goPath,
203+
GoVersion: versionStr,
204+
ErrorString: fmt.Sprintf("unable to parse go version from: %s", versionStr),
205+
}
209206
}
210207

211208
goVersion := matches[1]
212209

213-
// Check if version is 1.22+
214210
minorRegex := regexp.MustCompile(`1\.(\d+)`)
215211
minorMatches := minorRegex.FindStringSubmatch(goVersion)
216212
if len(minorMatches) < 2 {
217-
return nil, fmt.Errorf("unable to parse minor version from: %s", goVersion)
213+
return GoVersionCheckResult{
214+
GoStatus: "error",
215+
GoPath: goPath,
216+
GoVersion: versionStr,
217+
ErrorString: fmt.Sprintf("unable to parse minor version from: %s", goVersion),
218+
}
218219
}
219220

220221
minor, err := strconv.Atoi(minorMatches[1])
221-
if err != nil || minor < MinSupportedGoMinorVersion {
222-
return nil, fmt.Errorf("go version 1.%d or higher required, found: %s", MinSupportedGoMinorVersion, versionStr)
222+
if err != nil {
223+
return GoVersionCheckResult{
224+
GoStatus: "error",
225+
GoPath: goPath,
226+
GoVersion: versionStr,
227+
ErrorString: fmt.Sprintf("failed to parse minor version: %v", err),
228+
}
229+
}
230+
231+
if minor < MinSupportedGoMinorVersion {
232+
return GoVersionCheckResult{
233+
GoStatus: "badversion",
234+
GoPath: goPath,
235+
GoVersion: versionStr,
236+
ErrorString: "",
237+
}
223238
}
224239

240+
return GoVersionCheckResult{
241+
GoStatus: "ok",
242+
GoPath: goPath,
243+
GoVersion: versionStr,
244+
ErrorString: "",
245+
}
246+
}
247+
248+
func verifyEnvironment(verbose bool, opts BuildOpts) (*BuildEnv, error) {
249+
oc := opts.OutputCapture
250+
251+
if opts.SdkVersion == "" && opts.SdkReplacePath == "" {
252+
return nil, fmt.Errorf("either SdkVersion or SdkReplacePath must be set")
253+
}
254+
255+
if opts.SdkVersion != "" {
256+
versionRegex := regexp.MustCompile(`^v\d+\.\d+\.\d+`)
257+
if !versionRegex.MatchString(opts.SdkVersion) {
258+
return nil, fmt.Errorf("SdkVersion must be in semantic version format (e.g., v0.0.0), got: %s", opts.SdkVersion)
259+
}
260+
}
261+
262+
result := CheckGoVersion(opts.GoPath)
263+
264+
switch result.GoStatus {
265+
case "notfound":
266+
return nil, fmt.Errorf("go command not found")
267+
case "badversion":
268+
return nil, fmt.Errorf("go version 1.%d or higher required, found: %s", MinSupportedGoMinorVersion, result.GoVersion)
269+
case "error":
270+
return nil, fmt.Errorf("%s", result.ErrorString)
271+
case "ok":
272+
if verbose {
273+
if opts.GoPath != "" {
274+
oc.Printf("Using custom go path: %s", result.GoPath)
275+
} else {
276+
oc.Printf("Using go path: %s", result.GoPath)
277+
}
278+
oc.Printf("Found %s", result.GoVersion)
279+
}
280+
default:
281+
return nil, fmt.Errorf("unexpected go status: %s", result.GoStatus)
282+
}
283+
284+
versionRegex := regexp.MustCompile(`go(1\.\d+)`)
285+
matches := versionRegex.FindStringSubmatch(result.GoVersion)
286+
if len(matches) < 2 {
287+
return nil, fmt.Errorf("unable to parse go version from: %s", result.GoVersion)
288+
}
289+
goVersion := matches[1]
290+
291+
var err error
292+
225293
// Check if node is available
226294
if opts.NodePath != "" {
227295
// Custom node path specified - verify it's absolute and executable

0 commit comments

Comments
 (0)