diff --git a/v2/cmd/wails/internal/commands/build/build.go b/v2/cmd/wails/internal/commands/build/build.go index 7d695361..8662268e 100644 --- a/v2/cmd/wails/internal/commands/build/build.go +++ b/v2/cmd/wails/internal/commands/build/build.go @@ -69,6 +69,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) { cleanBuildDirectory := false command.BoolFlag("clean", "Clean the build directory before building", &cleanBuildDirectory) + webview2 := "download" + command.StringFlag("webview2", "WebView2 installer strategy: download,embed,browser,error.", &webview2) + command.Action(func() error { quiet := verbosity == 0 @@ -122,6 +125,25 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) { } } + // Webview2 installer strategy (download by default) + wv2rtstrategy := "" + webview2 = strings.ToLower(webview2) + if webview2 != "" { + validWV2Runtime := slicer.String([]string{"download", "embed", "browser", "error"}) + if !validWV2Runtime.Contains(webview2) { + return fmt.Errorf("invalid option for flag 'webview2': %s", webview2) + } + // These are the build tags associated with the strategies + switch webview2 { + case "embed": + wv2rtstrategy = "wv2runtime.embed" + case "error": + wv2rtstrategy = "wv2runtime.error" + case "browser": + wv2rtstrategy = "wv2runtime.browser" + } + } + // Create BuildOptions buildOptions := &build.Options{ Logger: logger, @@ -137,6 +159,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) { Compress: compress, CompressFlags: compressFlags, UserTags: userTags, + WebView2Strategy: wv2rtstrategy, } // Calculate platform and arch diff --git a/v2/go.mod b/v2/go.mod index a9234361..53b14c63 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -14,6 +14,7 @@ require ( github.com/leaanthony/go-ansi-parser v1.0.1 github.com/leaanthony/gosod v1.0.1 github.com/leaanthony/slicer v1.5.0 + github.com/leaanthony/webview2runtime v1.1.0 github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0 github.com/matryer/is v1.4.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect @@ -25,7 +26,7 @@ require ( github.com/wzshiming/ctc v1.2.3 github.com/xyproto/xpm v1.2.1 golang.org/x/net v0.0.0-20200822124328-c89045814202 - golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c + golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 nhooyr.io/websocket v1.8.6 ) diff --git a/v2/go.sum b/v2/go.sum index 1cc1a364..47464eff 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -50,6 +50,8 @@ github.com/leaanthony/gosod v1.0.1 h1:F+4c3DmEBfigi7oAswCV2RpQ+k4DcNbhuCZUGdBHac github.com/leaanthony/gosod v1.0.1/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU= github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/leaanthony/webview2runtime v1.1.0 h1:N0pv55ift8XtqozIp4PNOtRCJ/Qdd/qzx80lUpalS4c= +github.com/leaanthony/webview2runtime v1.1.0/go.mod h1:hH9GnWCve3DYzNaPOcPbhHQ7fodXR1QJNsnwixid4Tk= github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0 h1:FPGYnfxuuxqCZhrGq8nKjthEcYHgHmFbyY953Xv9cNI= github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= @@ -110,8 +112,8 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c= -golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ= +golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/v2/internal/app/desktop.go b/v2/internal/app/desktop.go index 45674939..fbcf33b5 100644 --- a/v2/internal/app/desktop.go +++ b/v2/internal/app/desktop.go @@ -103,8 +103,17 @@ func CreateApp(appoptions *options.App) (*App, error) { // Initialise the app err := result.Init() + if err != nil { + return nil, err + } - return result, err + // Preflight Checks + err = result.PreflightChecks(appoptions) + if err != nil { + return nil, err + } + + return result, nil } diff --git a/v2/internal/app/preflight_default.go b/v2/internal/app/preflight_default.go new file mode 100644 index 00000000..5508a774 --- /dev/null +++ b/v2/internal/app/preflight_default.go @@ -0,0 +1,9 @@ +//+build !windows + +package app + +import "github.com/wailsapp/wails/v2/pkg/options" + +func (a *App) PreflightChecks(options *options.App) error { + return nil +} diff --git a/v2/internal/app/preflight_windows.go b/v2/internal/app/preflight_windows.go new file mode 100644 index 00000000..4845dfc5 --- /dev/null +++ b/v2/internal/app/preflight_windows.go @@ -0,0 +1,19 @@ +//+build windows + +package app + +import ( + "github.com/wailsapp/wails/v2/internal/ffenestri/windows/wv2runtime" + "github.com/wailsapp/wails/v2/pkg/options" +) + +func (a *App) PreflightChecks(options *options.App) error { + + // Process the webview2 runtime situation. We can pass a strategy in via the `webview2` flag for `wails build`. + // This will determine how wv2runtime.Process will handle a lack of valid runtime. + err := wv2runtime.Process() + if err != nil { + return err + } + return nil +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/browser.go b/v2/internal/ffenestri/windows/wv2runtime/browser.go new file mode 100644 index 00000000..6166645a --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/browser.go @@ -0,0 +1,23 @@ +// +build wv2runtime.browser + +package wv2runtime + +import ( + "fmt" + "github.com/leaanthony/webview2runtime" +) + +func doInstallationStrategy(installStatus installationStatus) error { + confirmed, err := webview2runtime.Confirm("This application requires the WebView2 runtime. Press OK to open the download page. Minimum version required: "+minimumRuntimeVersion, "Missing Requirements") + if err != nil { + return err + } + if confirmed { + err = webview2runtime.OpenInstallerDownloadWebpage() + if err != nil { + return err + } + } + + return fmt.Errorf("webview2 runtime not installed") +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/download.go b/v2/internal/ffenestri/windows/wv2runtime/download.go new file mode 100644 index 00000000..cfb2aa19 --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/download.go @@ -0,0 +1,35 @@ +// +build !wv2runtime.error +// +build !wv2runtime.browser +// +build !wv2runtime.embed + +package wv2runtime + +import ( + "fmt" + "github.com/leaanthony/webview2runtime" +) + +func doInstallationStrategy(installStatus installationStatus) error { + message := "The WebView2 runtime is required. " + if installStatus == needsUpdating { + message = "The Webview2 runtime needs updating. " + } + message += "Press Ok to download and install. Note: The installer will download silently so please wait." + confirmed, err := webview2runtime.Confirm(message, "Missing Requirements") + if err != nil { + return err + } + if !confirmed { + return fmt.Errorf("webview2 runtime not installed") + } + installedCorrectly, err := webview2runtime.InstallUsingBootstrapper() + if err != nil { + _ = webview2runtime.Error(err.Error(), "Error") + return err + } + if !installedCorrectly { + err = webview2runtime.Error("The runtime failed to install correctly. Please try again.", "Error") + return err + } + return nil +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/embed.go b/v2/internal/ffenestri/windows/wv2runtime/embed.go new file mode 100644 index 00000000..99a472d4 --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/embed.go @@ -0,0 +1,33 @@ +// +build wv2runtime.embed + +package wv2runtime + +import ( + "fmt" + "github.com/leaanthony/webview2runtime" +) + +func doInstallationStrategy(installStatus installationStatus) error { + message := "The WebView2 runtime is required. " + if installStatus == needsUpdating { + message = "The Webview2 runtime needs updating. " + } + message += "Press Ok to install." + confirmed, err := webview2runtime.Confirm(message, "Missing Requirements") + if err != nil { + return err + } + if !confirmed { + return fmt.Errorf("webview2 runtime not installed") + } + installedCorrectly, err := webview2runtime.InstallUsingEmbeddedBootstrapper() + if err != nil { + _ = webview2runtime.Error(err.Error(), "Error") + return err + } + if !installedCorrectly { + err = webview2runtime.Error("The runtime failed to install correctly. Please try again.", "Error") + return err + } + return nil +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/error.go b/v2/internal/ffenestri/windows/wv2runtime/error.go new file mode 100644 index 00000000..f03d8c81 --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/error.go @@ -0,0 +1,13 @@ +// +build wv2runtime.error + +package wv2runtime + +import ( + "fmt" + "github.com/leaanthony/webview2runtime" +) + +func doInstallationStrategy(installStatus installationStatus) error { + _ = webview2runtime.Error("The WebView2 runtime is required to run this application. Please contact your system administrator.", "Error") + return fmt.Errorf("webview2 runtime not installed") +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go b/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go new file mode 100644 index 00000000..205c53fa --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go @@ -0,0 +1,34 @@ +package wv2runtime + +import ( + "github.com/leaanthony/webview2runtime" +) + +const minimumRuntimeVersion string = "91.0.864.48" + +type installationStatus int + +const ( + needsInstalling installationStatus = iota + needsUpdating + installed +) + +func Process() error { + installStatus := needsInstalling + installedVersion := webview2runtime.GetInstalledVersion() + if installedVersion != nil { + installStatus = installed + updateRequired, err := installedVersion.IsOlderThan(minimumRuntimeVersion) + if err != nil { + _ = webview2runtime.Error(err.Error(), "Error") + return err + } + // Installed and does not require updating + if !updateRequired { + return nil + } + installStatus = needsUpdating + } + return doInstallationStrategy(installStatus) +} diff --git a/v2/pkg/commands/build/base.go b/v2/pkg/commands/build/base.go index bb3e48c8..0df492b2 100644 --- a/v2/pkg/commands/build/base.go +++ b/v2/pkg/commands/build/base.go @@ -213,6 +213,12 @@ func (b *BaseBuilder) CompileProject(options *Options) error { var tags slicer.StringSlicer tags.Add(options.OutputType) tags.AddSlice(options.UserTags) + + // Add webview2 strategy if we have it + if options.WebView2Strategy != "" { + tags.Add(options.WebView2Strategy) + } + if options.Mode == Debug { tags.Add("debug") } diff --git a/v2/pkg/commands/build/build.go b/v2/pkg/commands/build/build.go index 3eee7ee1..2dc3cc73 100644 --- a/v2/pkg/commands/build/build.go +++ b/v2/pkg/commands/build/build.go @@ -47,6 +47,7 @@ type Options struct { Compress bool // Compress the final binary CompressFlags string // Flags to pass to UPX AppleIdentity string + WebView2Strategy string // WebView2 installer strategy } // Build the project!