From 6b8cb079739cf0c016c21944ab2d12219c0937d5 Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Sat, 2 May 2026 10:05:09 +1000 Subject: [PATCH] Add Wails CLI install/detection helper Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/wails/cli.go | 41 +++++++++++++++++++ internal/wails/cli_test.go | 83 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 internal/wails/cli.go create mode 100644 internal/wails/cli_test.go diff --git a/internal/wails/cli.go b/internal/wails/cli.go new file mode 100644 index 0000000..0d2ca44 --- /dev/null +++ b/internal/wails/cli.go @@ -0,0 +1,41 @@ +package wails + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/leonmika/wails-release/internal/runner" +) + +var cliVersionRE = regexp.MustCompile(`v\d+\.\d+\.\d+`) + +// ParseCLIVersion extracts the version from `wails -v` output. +func ParseCLIVersion(out []byte) (string, error) { + m := cliVersionRE.FindString(strings.TrimSpace(string(out))) + if m == "" { + return "", fmt.Errorf("could not parse Wails CLI version from %q", string(out)) + } + return m, nil +} + +// EnsureCLI installs the Wails CLI at `want` (e.g. "v2.11.0") if it is not +// already installed at exactly that version. +func EnsureCLI(ctx context.Context, r runner.Runner, want string) error { + out, err := r.Run(ctx, runner.Spec{Name: "wails", Args: []string{"-v"}}) + if err == nil { + got, perr := ParseCLIVersion(out) + if perr == nil && got == want { + return nil + } + } + _, err = r.Run(ctx, runner.Spec{ + Name: "go", + Args: []string{"install", "github.com/wailsapp/wails/v2/cmd/wails@" + want}, + }) + if err != nil { + return fmt.Errorf("install wails CLI %s: %w", want, err) + } + return nil +} diff --git a/internal/wails/cli_test.go b/internal/wails/cli_test.go new file mode 100644 index 0000000..9ae13eb --- /dev/null +++ b/internal/wails/cli_test.go @@ -0,0 +1,83 @@ +package wails_test + +import ( + "context" + "errors" + "strings" + "testing" + + "github.com/leonmika/wails-release/internal/runner" + "github.com/leonmika/wails-release/internal/wails" +) + +func TestParseInstalledVersion(t *testing.T) { + cases := map[string]string{ + "Wails CLI v2.11.0": "v2.11.0", + "Wails CLI v2.10.1\n": "v2.10.1", + "Wails v2.9.0": "v2.9.0", + } + for in, want := range cases { + got, err := wails.ParseCLIVersion([]byte(in)) + if err != nil { + t.Fatalf("unexpected error parsing %q: %v", in, err) + } + if got != want { + t.Fatalf("input %q: got %q want %q", in, got, want) + } + } +} + +func TestParseInstalledVersion_Unparseable(t *testing.T) { + _, err := wails.ParseCLIVersion([]byte("nope")) + if err == nil { + t.Fatal("expected error") + } +} + +func TestEnsureCLI_AlreadyInstalledMatchingSkipsInstall(t *testing.T) { + f := &runner.Fake{} + f.On("wails", []string{"-v"}).Return([]byte("Wails CLI v2.11.0"), nil) + + err := wails.EnsureCLI(context.Background(), f, "v2.11.0") + if err != nil { + t.Fatalf("unexpected: %v", err) + } + + for _, c := range f.Calls { + if c.Name == "go" { + t.Fatalf("expected no `go install`, got call: %+v", c) + } + } +} + +func TestEnsureCLI_MismatchTriggersInstall(t *testing.T) { + f := &runner.Fake{} + f.On("wails", []string{"-v"}).Return([]byte("Wails CLI v2.10.0"), nil) + f.On("go", []string{"install", "github.com/wailsapp/wails/v2/cmd/wails@v2.11.0"}).Return(nil, nil) + + err := wails.EnsureCLI(context.Background(), f, "v2.11.0") + if err != nil { + t.Fatalf("unexpected: %v", err) + } + + saw := false + for _, c := range f.Calls { + if c.Name == "go" && c.Args[0] == "install" && strings.Contains(c.Args[1], "@v2.11.0") { + saw = true + } + } + if !saw { + t.Fatal("expected go install call, did not see one") + } +} + +func TestEnsureCLI_NotInstalledTriggersInstall(t *testing.T) { + f := &runner.Fake{} + f.On("wails", []string{"-v"}).Return(nil, errors.New("exec: not found")) + f.On("go", []string{"install", "github.com/wailsapp/wails/v2/cmd/wails@v2.11.0"}).Return(nil, nil) + + err := wails.EnsureCLI(context.Background(), f, "v2.11.0") + if err != nil { + t.Fatalf("unexpected: %v", err) + } +}