From 2aea5e2daeeb51351a2bd06863909277435ba8e0 Mon Sep 17 00:00:00 2001 From: Ronen Lapushner Date: Tue, 21 Jan 2025 21:15:24 +0200 Subject: [PATCH 01/37] Added initial Windows 11-supporting branding --- v2/internal/system/operatingsystem/os.go | 7 ++-- .../system/operatingsystem/os_windows.go | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/v2/internal/system/operatingsystem/os.go b/v2/internal/system/operatingsystem/os.go index 39f1de8e0..028a97b2e 100644 --- a/v2/internal/system/operatingsystem/os.go +++ b/v2/internal/system/operatingsystem/os.go @@ -2,9 +2,10 @@ package operatingsystem // OS contains information about the operating system type OS struct { - ID string - Name string - Version string + ID string + Name string + Version string + Branding string } // Info retrieves information about the current platform diff --git a/v2/internal/system/operatingsystem/os_windows.go b/v2/internal/system/operatingsystem/os_windows.go index 38ea43a12..a9aa05a92 100644 --- a/v2/internal/system/operatingsystem/os_windows.go +++ b/v2/internal/system/operatingsystem/os_windows.go @@ -4,10 +4,44 @@ package operatingsystem import ( "fmt" + "strings" + "syscall" + "unsafe" + "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" ) +func stripNulls(str string) string { + // Split the string into substrings at each null character + substrings := strings.Split(str, "\x00") + + // Join the substrings back into a single string + strippedStr := strings.Join(substrings, "") + + return strippedStr +} + +func mustStringToUTF16Ptr(input string) *uint16 { + input = stripNulls(input) + result, err := syscall.UTF16PtrFromString(input) + if err != nil { + panic(err) + } + return result +} + +func getBranding() string { + var modBranding = syscall.NewLazyDLL("winbrand.dll") + var brandingFormatString = modBranding.NewProc("BrandingFormatString") + + windowsLong := mustStringToUTF16Ptr("%WINDOWS_LONG%\x00") + ret, _, _ := brandingFormatString.Call( + uintptr(unsafe.Pointer(windowsLong)), + ) + return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(ret))) +} + func platformInfo() (*OS, error) { // Default value var result OS @@ -27,6 +61,7 @@ func platformInfo() (*OS, error) { result.Name = productName result.Version = fmt.Sprintf("%s (Build: %s)", releaseId, currentBuild) result.ID = displayVersion + result.Branding = getBranding() return &result, key.Close() } From f82c971cb0016b4c9efdd678aac10fb105e6382e Mon Sep 17 00:00:00 2001 From: Ronen Lapushner Date: Tue, 21 Jan 2025 21:18:10 +0200 Subject: [PATCH 02/37] Added "Branding" to doctor --- v2/cmd/wails/doctor.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/cmd/wails/doctor.go b/v2/cmd/wails/doctor.go index 5306cab17..eac5048b3 100644 --- a/v2/cmd/wails/doctor.go +++ b/v2/cmd/wails/doctor.go @@ -2,12 +2,13 @@ package main import ( "fmt" - "github.com/wailsapp/wails/v2/internal/shell" "runtime" "runtime/debug" "strconv" "strings" + "github.com/wailsapp/wails/v2/internal/shell" + "github.com/pterm/pterm" "github.com/jaypipes/ghw" @@ -78,6 +79,7 @@ func diagnoseEnvironment(f *flags.Doctor) error { {pterm.Bold.Sprint("OS"), info.OS.Name}, {pterm.Bold.Sprint("Version"), info.OS.Version}, {pterm.Bold.Sprint("ID"), info.OS.ID}, + {pterm.Bold.Sprint("Branding"), info.OS.Branding}, {pterm.Bold.Sprint("Go Version"), runtime.Version()}, {pterm.Bold.Sprint("Platform"), runtime.GOOS}, {pterm.Bold.Sprint("Architecture"), runtime.GOARCH}, From 74fa41330e1ee4c9cd5458a5e0db7344142b4d12 Mon Sep 17 00:00:00 2001 From: Ronen Lapushner Date: Thu, 23 Jan 2025 14:15:53 +0200 Subject: [PATCH 03/37] Added to chaneglog --- website/src/pages/changelog.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index ee9f029f3..1fc21a5ff 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added option to set window class name on Windows. Added in [PR](https://github.com/wailsapp/wails/pull/3828) by @APshenkin +- Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) ### Fixed - Fixed cross compilation failed with CGO [PR](https://github.com/wailsapp/wails/pull/3795) by [@fcying](https://github.com/fcying) From 7d66f3c26a2f292e80e99cfd7ad56b6211b28eb4 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sun, 2 Mar 2025 10:50:09 +1100 Subject: [PATCH 04/37] Update changelog --- website/src/pages/changelog.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 9d87de5a9..05128a6f3 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated recommendation for Svelte router in [#4085](https://github.com/wailsapp/wails/pull/4085) by [@benmccann](https://github.com/benmccann) +### Added +- Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) + ## v2.10.1 - 2025-02-24 @@ -28,7 +31,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added option to set window class name on Windows. Added in [PR](https://github.com/wailsapp/wails/pull/3828) by @APshenkin -- Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) ### Fixed - Fixed dev mode logging bug by @attperac in [#3972](https://wailsapp/wails/pull/3972) From fa4bf38b12c9c3f9ec88ef1aea9af584c45d649a Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Mon, 3 Mar 2025 21:32:09 +1100 Subject: [PATCH 05/37] Try warpbuild --- .github/workflows/build-and-test-v3.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test-v3.yml b/.github/workflows/build-and-test-v3.yml index 61cb9dde9..e77c6190a 100644 --- a/.github/workflows/build-and-test-v3.yml +++ b/.github/workflows/build-and-test-v3.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-latest, macos-latest, ubuntu-latest] + os: [warp-windows-latest-x64-2x, warp-macos-15-arm64-6x, warp-ubuntu-latest-x64-2x] go-version: [1.24] steps: From 114c15197b20c7da038fee0d001911ae6a9c9857 Mon Sep 17 00:00:00 2001 From: "dingda.li" Date: Tue, 4 Mar 2025 14:39:06 +0800 Subject: [PATCH 06/37] fix(windows): correct window restoration behavior after minimization - Replace SW_RESTORE with SW_SHOW flag - Resolves #4109 --- v2/internal/frontend/desktop/windows/winc/form.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/internal/frontend/desktop/windows/winc/form.go b/v2/internal/frontend/desktop/windows/winc/form.go index 8a42d63f3..c9acf7278 100644 --- a/v2/internal/frontend/desktop/windows/winc/form.go +++ b/v2/internal/frontend/desktop/windows/winc/form.go @@ -145,7 +145,7 @@ func (fm *Form) Restore() { SC_RESTORE, 0, ) - w32.ShowWindow(fm.hwnd, w32.SW_RESTORE) + w32.ShowWindow(fm.hwnd, w32.SW_SHOW) } // Public methods From a04a31be8c0b13a656adb8d31cb994841a55e88d Mon Sep 17 00:00:00 2001 From: leaanthony <1943904+leaanthony@users.noreply.github.com> Date: Wed, 5 Mar 2025 00:16:53 +0000 Subject: [PATCH 07/37] chore: update sponsors.svg --- website/static/img/sponsors.svg | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/website/static/img/sponsors.svg b/website/static/img/sponsors.svg index 71215912a..92c50b150 100644 --- a/website/static/img/sponsors.svg +++ b/website/static/img/sponsors.svg @@ -156,50 +156,54 @@ text { Helpers - + - + - + - + - + - + - + - + - + - + - + - + + + + + From 6381e520b9137a8876e2217e63e653ec20a2db31 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 8 Mar 2025 10:53:18 +1100 Subject: [PATCH 08/37] Pipeline update --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 52586661c..5dbf86179 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -11,7 +11,7 @@ on: jobs: check_docs: name: Check Docs - if: ${{github.repository == 'wailsapp/wails' && contains(github.head_ref,'feature/')}} + if: ${{github.repository == 'wailsapp/wails' && github.base_ref == 'master'}} runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From aee6d997023f3efaa371dd5e3dc315de88d21530 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 8 Mar 2025 11:34:08 +1100 Subject: [PATCH 09/37] Remove Warp agents --- .github/workflows/build-and-test-v3.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test-v3.yml b/.github/workflows/build-and-test-v3.yml index e77c6190a..61cb9dde9 100644 --- a/.github/workflows/build-and-test-v3.yml +++ b/.github/workflows/build-and-test-v3.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - os: [warp-windows-latest-x64-2x, warp-macos-15-arm64-6x, warp-ubuntu-latest-x64-2x] + os: [windows-latest, macos-latest, ubuntu-latest] go-version: [1.24] steps: From 267e4ec9faa55554bdee848dc7de9515bf5a9ea9 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 8 Mar 2025 11:43:22 +1100 Subject: [PATCH 10/37] PR workflow change --- .github/workflows/{pr.yml => pr-v2.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{pr.yml => pr-v2.yml} (95%) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr-v2.yml similarity index 95% rename from .github/workflows/pr.yml rename to .github/workflows/pr-v2.yml index 5dbf86179..8adc29a56 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr-v2.yml @@ -55,7 +55,7 @@ jobs: test_go: name: Run Go Tests runs-on: ${{ matrix.os }} - if: github.event.review.state == 'approved' + if: github.event.review.state == 'approved' && github.repository == 'wailsapp/wails' && github.base_ref == 'master' strategy: matrix: os: [ubuntu-22.04, windows-latest, macos-latest, ubuntu-24.04] From a78e8c95ee3bc498f42c00de85373efb75432560 Mon Sep 17 00:00:00 2001 From: leaanthony <1943904+leaanthony@users.noreply.github.com> Date: Sun, 9 Mar 2025 00:15:18 +0000 Subject: [PATCH 11/37] chore: update sponsors.svg --- website/static/img/sponsors.svg | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/website/static/img/sponsors.svg b/website/static/img/sponsors.svg index 92c50b150..597fbb272 100644 --- a/website/static/img/sponsors.svg +++ b/website/static/img/sponsors.svg @@ -1,5 +1,5 @@ - + From 6b288be2fa3ecc61d82096c3f09a35e32820000d Mon Sep 17 00:00:00 2001 From: leaanthony <1943904+leaanthony@users.noreply.github.com> Date: Tue, 11 Mar 2025 00:16:55 +0000 Subject: [PATCH 12/37] chore: update sponsors.svg --- website/static/img/sponsors.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/static/img/sponsors.svg b/website/static/img/sponsors.svg index 597fbb272..107d43ba4 100644 --- a/website/static/img/sponsors.svg +++ b/website/static/img/sponsors.svg @@ -95,7 +95,7 @@ text { vaaski - + Sander From 6a3ba3d61373346b2cbed83c23ae816d1db04a22 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 12 Mar 2025 08:32:26 +1100 Subject: [PATCH 13/37] Update Results stage in pipeline (cherry picked from commit fe6635c1ebc1638ba958095a37a46f53001d8a40) --- .github/workflows/build-and-test-v3.yml | 33 +++++++++++++++---------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-test-v3.yml b/.github/workflows/build-and-test-v3.yml index 61cb9dde9..6ae09eab8 100644 --- a/.github/workflows/build-and-test-v3.yml +++ b/.github/workflows/build-and-test-v3.yml @@ -173,16 +173,23 @@ jobs: cd ${{ matrix.template }} wails3 build - results: - if: ${{ always() }} - runs-on: ubuntu-latest - name: v3 Build Results - needs: [test_go, test_js, test_templates] - steps: - - run: | - result="${{ needs.build.result }}" - if [[ $result == "success" || $result == "skipped" ]]; then - exit 0 - else - exit 1 - fi \ No newline at end of file +results: + if: ${{ always() }} + runs-on: ubuntu-latest + name: v3 Build Results + needs: [test_go, test_js, test_templates] + steps: + - run: | + go_result="${{ needs.test_go.result }}" + js_result="${{ needs.test_js.result }}" + templates_result="${{ needs.test_templates.result }}" + + if [[ $go_result == "success" || $go_result == "skipped" ]] && \ + [[ $js_result == "success" || $js_result == "skipped" ]] && \ + [[ $templates_result == "success" || $templates_result == "skipped" ]]; then + echo "All required jobs succeeded or were skipped" + exit 0 + else + echo "One or more required jobs failed" + exit 1 + fi \ No newline at end of file From 5df9ac1a5300cfeebe2ae54baa151be06af2ea62 Mon Sep 17 00:00:00 2001 From: "dingda.li" Date: Wed, 12 Mar 2025 15:43:20 +0800 Subject: [PATCH 14/37] docs: update changelog for window restoration fix --- website/src/pages/changelog.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 05128a6f3..0dcf1d089 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) +### Fixed +- Fixed window restoration behavior after minimization by @superDingda in [#4109](https://github.com/wailsapp/wails/issues/4109) ## v2.10.1 - 2025-02-24 From e9100152e7b43aaadf36811f27aacbd5a367d920 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Thu, 13 Mar 2025 00:06:07 +0000 Subject: [PATCH 15/37] feat: add option to disable panic recovery in message processing --- v2/internal/app/app_dev.go | 2 +- v2/internal/app/app_production.go | 2 +- v2/internal/frontend/dispatcher/dispatcher.go | 52 ++++++++++--------- v2/pkg/options/options.go | 3 ++ website/docs/reference/options.mdx | 7 +++ 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/v2/internal/app/app_dev.go b/v2/internal/app/app_dev.go index 89265c9b9..347daac05 100644 --- a/v2/internal/app/app_dev.go +++ b/v2/internal/app/app_dev.go @@ -212,7 +212,7 @@ func CreateApp(appoptions *options.App) (*App, error) { eventHandler := runtime.NewEvents(myLogger) ctx = context.WithValue(ctx, "events", eventHandler) - messageDispatcher := dispatcher.NewDispatcher(ctx, myLogger, appBindings, eventHandler, appoptions.ErrorFormatter) + messageDispatcher := dispatcher.NewDispatcher(ctx, myLogger, appBindings, eventHandler, appoptions.ErrorFormatter, appoptions.DisablePanicRecovery) // Create the frontends and register to event handler desktopFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher) diff --git a/v2/internal/app/app_production.go b/v2/internal/app/app_production.go index 4c217b17c..9eb0e5a66 100644 --- a/v2/internal/app/app_production.go +++ b/v2/internal/app/app_production.go @@ -82,7 +82,7 @@ func CreateApp(appoptions *options.App) (*App, error) { ctx = context.WithValue(ctx, "buildtype", "production") } - messageDispatcher := dispatcher.NewDispatcher(ctx, myLogger, appBindings, eventHandler, appoptions.ErrorFormatter) + messageDispatcher := dispatcher.NewDispatcher(ctx, myLogger, appBindings, eventHandler, appoptions.ErrorFormatter, appoptions.DisablePanicRecovery) appFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher) eventHandler.AddFrontend(appFrontend) diff --git a/v2/internal/frontend/dispatcher/dispatcher.go b/v2/internal/frontend/dispatcher/dispatcher.go index 60a99f467..24a43cfef 100644 --- a/v2/internal/frontend/dispatcher/dispatcher.go +++ b/v2/internal/frontend/dispatcher/dispatcher.go @@ -11,38 +11,42 @@ import ( ) type Dispatcher struct { - log *logger.Logger - bindings *binding.Bindings - events frontend.Events - bindingsDB *binding.DB - ctx context.Context - errfmt options.ErrorFormatter + log *logger.Logger + bindings *binding.Bindings + events frontend.Events + bindingsDB *binding.DB + ctx context.Context + errfmt options.ErrorFormatter + disablePanicRecovery bool } -func NewDispatcher(ctx context.Context, log *logger.Logger, bindings *binding.Bindings, events frontend.Events, errfmt options.ErrorFormatter) *Dispatcher { +func NewDispatcher(ctx context.Context, log *logger.Logger, bindings *binding.Bindings, events frontend.Events, errfmt options.ErrorFormatter, disablePanicRecovery bool) *Dispatcher { return &Dispatcher{ - log: log, - bindings: bindings, - events: events, - bindingsDB: bindings.DB(), - ctx: ctx, - errfmt: errfmt, + log: log, + bindings: bindings, + events: events, + bindingsDB: bindings.DB(), + ctx: ctx, + errfmt: errfmt, + disablePanicRecovery: disablePanicRecovery, } } func (d *Dispatcher) ProcessMessage(message string, sender frontend.Frontend) (_ string, err error) { - defer func() { - if e := recover(); e != nil { - if errPanic, ok := e.(error); ok { - err = errPanic - } else { - err = fmt.Errorf("%v", e) + if !d.disablePanicRecovery { + defer func() { + if e := recover(); e != nil { + if errPanic, ok := e.(error); ok { + err = errPanic + } else { + err = fmt.Errorf("%v", e) + } } - } - if err != nil { - d.log.Error("process message error: %s -> %s", message, err) - } - }() + if err != nil { + d.log.Error("process message error: %s -> %s", message, err) + } + }() + } if message == "" { return "", errors.New("No message to process") diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go index 282a25691..082ac48cd 100644 --- a/v2/pkg/options/options.go +++ b/v2/pkg/options/options.go @@ -98,6 +98,9 @@ type App struct { // DragAndDrop options for drag and drop behavior DragAndDrop *DragAndDrop + + // DisablePanicRecovery disables the panic recovery system in messages processing + DisablePanicRecovery bool } type ErrorFormatter func(error) any diff --git a/website/docs/reference/options.mdx b/website/docs/reference/options.mdx index efcc67961..9d999f421 100644 --- a/website/docs/reference/options.mdx +++ b/website/docs/reference/options.mdx @@ -487,6 +487,13 @@ services of Apple and Microsoft. Name: EnableFraudulentWebsiteDetection
Type: `bool` +### DisablePanicRecovery + +DisablePanicRecovery disables the automatic recovery from panics in message processing. By default, Wails will recover from panics in message processing and log the error. If you want to handle panics yourself, set this to `true`. + +Name: DisablePanicRecovery
+Type: `bool` + ### Bind A slice of struct instances defining methods that need to be bound to the frontend. From 2478926274458fa6e4e789084dfaa70afdb0e7f3 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Thu, 13 Mar 2025 00:17:19 +0000 Subject: [PATCH 16/37] add changelog --- website/src/pages/changelog.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 05128a6f3..203bcb362 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) - +- Added `DisablePanicRecovery` option to allow handle panics manually [#https://github.com/wailsapp/wails/pull/4136](https://github.com/wailsapp/wails/pull/4136) by [@APshenkin](https://github.com/APshenkin) ## v2.10.1 - 2025-02-24 From ef73a75c6848f2813f3a26bb798ea56284aac961 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Thu, 13 Mar 2025 00:18:09 +0000 Subject: [PATCH 17/37] add changelog --- website/src/pages/changelog.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 203bcb362..e65617fde 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) -- Added `DisablePanicRecovery` option to allow handle panics manually [#https://github.com/wailsapp/wails/pull/4136](https://github.com/wailsapp/wails/pull/4136) by [@APshenkin](https://github.com/APshenkin) +- Added `DisablePanicRecovery` option to allow handle panics manually [#4136](https://github.com/wailsapp/wails/pull/4136) by [@APshenkin](https://github.com/APshenkin) ## v2.10.1 - 2025-02-24 From ea2bfa9bcd3624a15ba75ec500fec9ddf5f2716e Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sun, 16 Mar 2025 11:34:29 +1100 Subject: [PATCH 18/37] Update verify steps in pipeline --- .github/workflows/pr-v2.yml | 2 +- .github/workflows/upload-source-documents.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-v2.yml b/.github/workflows/pr-v2.yml index 8adc29a56..572260b8b 100644 --- a/.github/workflows/pr-v2.yml +++ b/.github/workflows/pr-v2.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v3 - name: Verify Changed files - uses: tj-actions/verify-changed-files@v17 + uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1 id: verify-changed-files with: files: | diff --git a/.github/workflows/upload-source-documents.yml b/.github/workflows/upload-source-documents.yml index df15246fc..69d6c3e48 100644 --- a/.github/workflows/upload-source-documents.yml +++ b/.github/workflows/upload-source-documents.yml @@ -15,7 +15,7 @@ jobs: - name: Verify Changed files id: changed-files - uses: tj-actions/changed-files@v41 + uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1 with: files: | website/**/*.mdx From c2db2e550f8c8975962e47e5f9dfd85046991c3a Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sun, 16 Mar 2025 12:36:59 +1100 Subject: [PATCH 19/37] Update semgrep action --- .github/workflows/semgrep.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 453e4cb85..7533a3a04 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -5,6 +5,7 @@ on: branches: - main - master + - v3-alpha paths: - .github/workflows/semgrep.yml schedule: From 538ba8daac8e76377e2d67f7a198b9bce6c89fb2 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sun, 16 Mar 2025 13:32:03 +1100 Subject: [PATCH 20/37] Blog Post --- .../2025-03-16-security-incident-response.mdx | 89 ++++++++++++++++++ website/static/img/blog/shield.png | Bin 0 -> 33103 bytes 2 files changed, 89 insertions(+) create mode 100644 website/blog/2025-03-16-security-incident-response.mdx create mode 100644 website/static/img/blog/shield.png diff --git a/website/blog/2025-03-16-security-incident-response.mdx b/website/blog/2025-03-16-security-incident-response.mdx new file mode 100644 index 000000000..e9903c570 --- /dev/null +++ b/website/blog/2025-03-16-security-incident-response.mdx @@ -0,0 +1,89 @@ +--- +slug: security-incident-response-march-2025 +title: Proactive Security Response - GitHub Actions Supply Chain Attack +authors: [leaanthony] +tags: [wails, security] +--- + +
+ Security Shield +
+
+ +:::note TL;DR +**Good news! Wails was NOT affected by this security incident.** Our thorough investigation confirmed that no secrets were leaked, and the Wails codebase and releases remain completely secure. We've already taken proactive measures to further strengthen our security posture. +::: + +## Introduction + +On 15th March 2025 (AEST), the Wails team was alerted to a security incident involving the `tj-actions/changed-files` GitHub Action. This widely-used action (with over 23,000 repositories depending on it) was compromised in a supply chain attack. While this action was used in some of our CI/CD workflows, we're pleased to confirm that Wails remained fully protected throughout. + +This post shares the details of the incident, our response, and the additional safeguards we've implemented to ensure the continued security of the Wails project. + +## Incident Details + +The security company StepSecurity [reported](https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised) that the `tj-actions/changed-files` GitHub Action was compromised beginning around 9:00 AM March 14th, 2025 Pacific Time (4:00 PM UTC). + +In this attack, malicious code was injected into the action that attempted to dump CI/CD secrets from GitHub Actions runner processes into public logs. The attackers modified the action's code and retroactively updated multiple version tags to reference the malicious commit. + +## Our Proactive Assessment + +Upon learning this, we immediately launched a comprehensive assessment of our systems: + +1. We identified the following Wails workflows that were using the action: + - For Wails v2: `pr-v2.yml` and `upload-source-documents.yml` + - For Wails v3: `pr-v3.yml`, `publish-npm.yml`, and `upload-source-documents.yml` + +2. Our security team conducted a thorough review of all workflow logs for the affected actions during the time period of the compromise. + +3. We're happy to confirm that **no secrets were leaked** in any of our workflow logs, and the Wails codebase remained completely secure. + +## Action Taken + +We took immediate steps to address this situation: + +1. We swiftly replaced all instances of the affected `tj-actions/changed-files` action with the secure alternative `step-security/changed-files` provided by StepSecurity. + +2. As an extra precautionary measure, we temporarily removed all secrets from our GitHub Actions workflows. + +## What This Means for You + +We want to reassure our community that: + +1. The Wails codebase was never compromised in any way. +2. No malicious code was introduced into any Wails releases. +3. This situation only potentially affected our CI/CD pipeline, not the actual Wails source code or releases. +4. No sensitive information or secrets were exposed during this time. + +**In short: All Wails releases remain secure and trustworthy, and no action is required on your part.** + +## Strengthening Our Security Posture + +To minimise exposure to similar potential incidents in the future, we're enhancing our security practices by: + +1. Implementing stricter version pinning for all third-party actions used in our workflows, preferably pinning to specific commit hashes rather than version tags. + +2. Establishing a regular security review process for our CI/CD pipelines and dependencies. + +3. Exploring the use of additional security tools like StepSecurity's Harden-Runner to provide enhanced protection for our GitHub Actions workflows. + +4. Developing a more comprehensive security incident response plan to ensure we can respond quickly and effectively to any future security concerns. + +It's worth noting that the Wails project already employs several security tools as part of our development process: + +- **Semgrep**: We use Semgrep for static code analysis to identify potential security vulnerabilities and code quality issues. +- **Snyk**: We employ Snyk to continuously monitor our dependencies for known vulnerabilities and receive alerts when security patches are needed. + +These existing security measures, combined with our enhanced preventative steps, demonstrate our ongoing commitment to maintaining the security and integrity of the Wails project. + +## Moving Forward + +The security of the Wails project and the trust of our community are our highest priorities. We remain committed to transparency and will continue to promptly address any security concerns that arise. + +We would like to thank StepSecurity for their quick response in identifying this issue and providing a secure alternative action. + +If you have any questions or concerns about this, please don't hesitate to reach out to us on [GitHub](https://github.com/wailsapp/wails) or [Discord](https://discord.gg/JDdSxwjhGf). We're always here to help. diff --git a/website/static/img/blog/shield.png b/website/static/img/blog/shield.png new file mode 100644 index 0000000000000000000000000000000000000000..7b715f8f4f71093881422ee80781ebd78781ec01 GIT binary patch literal 33103 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 literal 0 HcmV?d00001 From 4541cf0b3a5a4980d4fe18daae431934c707fb1f Mon Sep 17 00:00:00 2001 From: josc146 Date: Sun, 16 Mar 2025 18:26:26 +0800 Subject: [PATCH 21/37] add -skipembedcreate cli option --- v2/cmd/wails/build.go | 1 + v2/cmd/wails/flags/buildcommon.go | 19 ++++++++++--------- v2/cmd/wails/flags/dev.go | 31 ++++++++++++++++--------------- v2/pkg/commands/build/build.go | 7 +++++-- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/v2/cmd/wails/build.go b/v2/cmd/wails/build.go index 7364df8ba..05f235078 100644 --- a/v2/cmd/wails/build.go +++ b/v2/cmd/wails/build.go @@ -85,6 +85,7 @@ func buildApplication(f *flags.Build) error { GarbleArgs: f.GarbleArgs, SkipBindings: f.SkipBindings, ProjectData: projectOptions, + SkipEmbedCreate: f.SkipEmbedCreate, } tableData := pterm.TableData{ diff --git a/v2/cmd/wails/flags/buildcommon.go b/v2/cmd/wails/flags/buildcommon.go index 4bbc4c62c..a22f7a502 100644 --- a/v2/cmd/wails/flags/buildcommon.go +++ b/v2/cmd/wails/flags/buildcommon.go @@ -1,15 +1,16 @@ package flags type BuildCommon struct { - LdFlags string `description:"Additional ldflags to pass to the compiler"` - Compiler string `description:"Use a different go compiler to build, eg go1.15beta1"` - SkipBindings bool `description:"Skips generation of bindings"` - RaceDetector bool `name:"race" description:"Build with Go's race detector"` - SkipFrontend bool `name:"s" description:"Skips building the frontend"` - Verbosity int `name:"v" description:"Verbosity level (0 = quiet, 1 = normal, 2 = verbose)"` - Tags string `description:"Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"` - NoSyncGoMod bool `description:"Don't sync go.mod"` - SkipModTidy bool `name:"m" description:"Skip mod tidy before compile"` + LdFlags string `description:"Additional ldflags to pass to the compiler"` + Compiler string `description:"Use a different go compiler to build, eg go1.15beta1"` + SkipBindings bool `description:"Skips generation of bindings"` + RaceDetector bool `name:"race" description:"Build with Go's race detector"` + SkipFrontend bool `name:"s" description:"Skips building the frontend"` + Verbosity int `name:"v" description:"Verbosity level (0 = quiet, 1 = normal, 2 = verbose)"` + Tags string `description:"Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"` + NoSyncGoMod bool `description:"Don't sync go.mod"` + SkipModTidy bool `name:"m" description:"Skip mod tidy before compile"` + SkipEmbedCreate bool `description:"Skips creation of embed files"` } func (c BuildCommon) Default() BuildCommon { diff --git a/v2/cmd/wails/flags/dev.go b/v2/cmd/wails/flags/dev.go index 501450a98..b2ae1c04f 100644 --- a/v2/cmd/wails/flags/dev.go +++ b/v2/cmd/wails/flags/dev.go @@ -117,21 +117,22 @@ func (d *Dev) loadAndMergeProjectConfig() error { // GenerateBuildOptions creates a build.Options using the flags func (d *Dev) GenerateBuildOptions() *build.Options { result := &build.Options{ - OutputType: "dev", - Mode: build.Dev, - Devtools: true, - Arch: runtime.GOARCH, - Pack: true, - Platform: runtime.GOOS, - LDFlags: d.LdFlags, - Compiler: d.Compiler, - ForceBuild: d.ForceBuild, - IgnoreFrontend: d.SkipFrontend, - SkipBindings: d.SkipBindings, - Verbosity: d.Verbosity, - WailsJSDir: d.WailsJSDir, - RaceDetector: d.RaceDetector, - ProjectData: d.projectConfig, + OutputType: "dev", + Mode: build.Dev, + Devtools: true, + Arch: runtime.GOARCH, + Pack: true, + Platform: runtime.GOOS, + LDFlags: d.LdFlags, + Compiler: d.Compiler, + ForceBuild: d.ForceBuild, + IgnoreFrontend: d.SkipFrontend, + SkipBindings: d.SkipBindings, + Verbosity: d.Verbosity, + WailsJSDir: d.WailsJSDir, + RaceDetector: d.RaceDetector, + ProjectData: d.projectConfig, + SkipEmbedCreate: d.SkipEmbedCreate, } return result diff --git a/v2/pkg/commands/build/build.go b/v2/pkg/commands/build/build.go index f247f2db1..ca128ae9c 100644 --- a/v2/pkg/commands/build/build.go +++ b/v2/pkg/commands/build/build.go @@ -69,6 +69,7 @@ type Options struct { Obfuscated bool // Indicates that bound methods should be obfuscated GarbleArgs string // The arguments for Garble SkipBindings bool // Skip binding generation + SkipEmbedCreate bool // Skip embed creation when in development mode } // Build the project! @@ -120,8 +121,10 @@ func Build(options *Options) (string, error) { } // Create embed directories if they don't exist - if err := CreateEmbedDirectories(cwd, options); err != nil { - return "", err + if !options.SkipEmbedCreate { + if err := CreateEmbedDirectories(cwd, options); err != nil { + return "", err + } } // Generate bindings From 915efa06bb2023ed1ca10a16e6b3a6804d9f7fd8 Mon Sep 17 00:00:00 2001 From: josc146 Date: Sun, 16 Mar 2025 18:35:56 +0800 Subject: [PATCH 22/37] fix -m build flag for dev command when recompile --- v2/cmd/wails/flags/dev.go | 1 + 1 file changed, 1 insertion(+) diff --git a/v2/cmd/wails/flags/dev.go b/v2/cmd/wails/flags/dev.go index 501450a98..a15d9ce53 100644 --- a/v2/cmd/wails/flags/dev.go +++ b/v2/cmd/wails/flags/dev.go @@ -128,6 +128,7 @@ func (d *Dev) GenerateBuildOptions() *build.Options { ForceBuild: d.ForceBuild, IgnoreFrontend: d.SkipFrontend, SkipBindings: d.SkipBindings, + SkipModTidy: d.SkipModTidy, Verbosity: d.Verbosity, WailsJSDir: d.WailsJSDir, RaceDetector: d.RaceDetector, From ecf1c41db49da05e0db3cbac8341710408618bee Mon Sep 17 00:00:00 2001 From: josc146 Date: Sun, 16 Mar 2025 19:32:31 +0800 Subject: [PATCH 23/37] improve comment --- v2/pkg/commands/build/build.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/pkg/commands/build/build.go b/v2/pkg/commands/build/build.go index ca128ae9c..b4e83dd69 100644 --- a/v2/pkg/commands/build/build.go +++ b/v2/pkg/commands/build/build.go @@ -69,7 +69,7 @@ type Options struct { Obfuscated bool // Indicates that bound methods should be obfuscated GarbleArgs string // The arguments for Garble SkipBindings bool // Skip binding generation - SkipEmbedCreate bool // Skip embed creation when in development mode + SkipEmbedCreate bool // Skip creation of embed files } // Build the project! From 61876448f77a7766873602a18a6e524387777bf7 Mon Sep 17 00:00:00 2001 From: josc146 Date: Wed, 19 Mar 2025 14:55:47 +0800 Subject: [PATCH 24/37] Update changelog --- website/src/pages/changelog.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 05128a6f3..e7b6bdad4 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) +### Fixed +- Fixed -m build flag for dev command not working when recompiling in [#4141](https://github.com/wailsapp/wails/pull/4141) by @josStorer ## v2.10.1 - 2025-02-24 From 6bf875f2a25cd6956929e3c044569e7433732722 Mon Sep 17 00:00:00 2001 From: josc146 Date: Wed, 19 Mar 2025 15:04:21 +0800 Subject: [PATCH 25/37] Update changelog --- website/src/pages/changelog.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 05128a6f3..754b4a03e 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) +- Added `-skipembedcreate` flag to build and dev command to improve compile and recompile speed [#4143](https://github.com/wailsapp/wails/pull/4143) by @josStorer ## v2.10.1 - 2025-02-24 From ae5e3f47a73c8b8618d58eb4cc66acd0a69269cd Mon Sep 17 00:00:00 2001 From: josc146 Date: Wed, 19 Mar 2025 15:16:27 +0800 Subject: [PATCH 26/37] update cli doc for -skipembedcreate --- website/docs/reference/cli.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/reference/cli.mdx b/website/docs/reference/cli.mdx index dc816a56b..84aba8fd8 100644 --- a/website/docs/reference/cli.mdx +++ b/website/docs/reference/cli.mdx @@ -73,6 +73,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for | -race | Build with Go's race detector | | | -s | Skip building the frontend | | | -skipbindings | Skip bindings generation | | +| -skipembedcreate | Skip automatic creation of non-existent embed directories and gitkeep files | | | -tags "extra tags" | Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated | | | -trimpath | Remove all file system paths from the resulting executable. | | | -u | Updates your project's `go.mod` to use the same version of Wails as the CLI | | @@ -203,6 +204,7 @@ Your system is ready for Wails development! | -s | Skip building the frontend | false | | -save | Saves the given `assetdir`, `reloaddirs`, `wailsjsdir`, `debounce`, `devserver` and `frontenddevserverurl` flags in `wails.json` to become the defaults for subsequent invocations. | | | -skipbindings | Skip bindings generation | | +| -skipembedcreate | Skip automatic creation of non-existent embed directories and gitkeep files | | | -tags "extra tags" | Build tags to pass to compiler (quoted and space separated) | | | -v | Verbosity level (0 - silent, 1 - standard, 2 - verbose) | 1 | | -wailsjsdir | The directory to generate the generated Wails JS modules | Value in `wails.json` | From 59d8650a0fd66e8d27a17523df5bbed1acfd4847 Mon Sep 17 00:00:00 2001 From: josc146 Date: Thu, 20 Mar 2025 16:33:04 +0800 Subject: [PATCH 27/37] Update dev.go --- v2/cmd/wails/flags/dev.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/cmd/wails/flags/dev.go b/v2/cmd/wails/flags/dev.go index 73ed6e680..7673cad05 100644 --- a/v2/cmd/wails/flags/dev.go +++ b/v2/cmd/wails/flags/dev.go @@ -128,7 +128,7 @@ func (d *Dev) GenerateBuildOptions() *build.Options { ForceBuild: d.ForceBuild, IgnoreFrontend: d.SkipFrontend, SkipBindings: d.SkipBindings, - SkipModTidy: d.SkipModTidy, + SkipModTidy: d.SkipModTidy, Verbosity: d.Verbosity, WailsJSDir: d.WailsJSDir, RaceDetector: d.RaceDetector, From 9c7dc05ceae4defbf5caf63af62827efb350d830 Mon Sep 17 00:00:00 2001 From: "B.T" <52111440+brianetaveras@users.noreply.github.com> Date: Tue, 25 Mar 2025 08:10:44 -0400 Subject: [PATCH 28/37] Update options docs to clarify linux webview gpu defaults --- website/versioned_docs/version-v2.10/reference/options.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/versioned_docs/version-v2.10/reference/options.mdx b/website/versioned_docs/version-v2.10/reference/options.mdx index efcc67961..40299e300 100644 --- a/website/versioned_docs/version-v2.10/reference/options.mdx +++ b/website/versioned_docs/version-v2.10/reference/options.mdx @@ -126,7 +126,7 @@ func main() { Linux: &linux.Options{ Icon: icon, WindowIsTranslucent: false, - WebviewGpuPolicy: linux.WebviewGpuPolicyAlways, + WebviewGpuPolicy: linux.WebviewGpuPolicyNever, ProgramName: "wails" }, Debug: options.Debug{ @@ -1066,7 +1066,8 @@ This option is used for determining the webview's hardware acceleration policy. Name: WebviewGpuPolicy
Type: [`options.WebviewGpuPolicy`](#webviewgpupolicy-type)
-Default: `WebviewGpuPolicyAlways` +Default (Windows, macOS): `WebviewGpuPolicyAlways`
+Default (Linux): `WebviewGpuPolicyNever`
##### WebviewGpuPolicy type From 2acf32cd36bd59f84878785ba2ffe2b81a3c0af6 Mon Sep 17 00:00:00 2001 From: Louis Date: Thu, 27 Mar 2025 12:49:10 +0800 Subject: [PATCH 29/37] doc/howdoesitwork: fix indentation --- website/versioned_docs/version-v2.10/howdoesitwork.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/versioned_docs/version-v2.10/howdoesitwork.mdx b/website/versioned_docs/version-v2.10/howdoesitwork.mdx index 48243f4eb..69134ac24 100644 --- a/website/versioned_docs/version-v2.10/howdoesitwork.mdx +++ b/website/versioned_docs/version-v2.10/howdoesitwork.mdx @@ -33,7 +33,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" - "github.com/wailsapp/wails/v2/pkg/options/assetserver" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -150,7 +150,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" - "github.com/wailsapp/wails/v2/pkg/options/assetserver" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist From 9763cd3465e9fb328af10d0701603fc6b705a1bf Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 31 Mar 2025 19:56:45 -0400 Subject: [PATCH 30/37] Added updates to the changelog --- website/src/pages/changelog.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 754b4a03e..f099673dc 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated recommendation for Svelte router in [#4085](https://github.com/wailsapp/wails/pull/4085) by [@benmccann](https://github.com/benmccann) +- Updated documentation to clarify `WebviewGpuPolicy` default behavior on Linux in [#4162](https://github.com/wailsapp/wails/pull/4162) by [@brianetaveras](https://github.com/brianetaveras) ### Added - Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) From e7643b860564d00d1b0f244a344cfd4f47976a6b Mon Sep 17 00:00:00 2001 From: brian Date: Mon, 31 Mar 2025 20:08:45 -0400 Subject: [PATCH 31/37] Added more information to the documentation --- website/versioned_docs/version-v2.10/reference/options.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/versioned_docs/version-v2.10/reference/options.mdx b/website/versioned_docs/version-v2.10/reference/options.mdx index 40299e300..ed7ccda01 100644 --- a/website/versioned_docs/version-v2.10/reference/options.mdx +++ b/website/versioned_docs/version-v2.10/reference/options.mdx @@ -1067,7 +1067,9 @@ This option is used for determining the webview's hardware acceleration policy. Name: WebviewGpuPolicy
Type: [`options.WebviewGpuPolicy`](#webviewgpupolicy-type)
Default (Windows, macOS): `WebviewGpuPolicyAlways`
-Default (Linux): `WebviewGpuPolicyNever`
+Default (Linux): Due to [#2977](https://github.com/wailsapp/wails/issues/2977,), if `options.Linux` is nil + in the call to `wails.Run()`, `WebviewGpuPolicy` is set by default to `WebviewGpuPolicyNever`. You can override this behavior by passing a non-nil `Options` and set `WebviewGpuPolicy` as needed. + ##### WebviewGpuPolicy type From 92a25c7dfdb5a3972a92ee344a29f701fb3d7c80 Mon Sep 17 00:00:00 2001 From: "dingda.li" Date: Tue, 1 Apr 2025 15:19:27 +0800 Subject: [PATCH 32/37] fix: suppress excessive console logs after updating to v2.10.1 --- v2/internal/app/app_dev.go | 29 +++++++++++++++++++---------- website/src/pages/changelog.mdx | 2 ++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/v2/internal/app/app_dev.go b/v2/internal/app/app_dev.go index 89265c9b9..4e668bc9a 100644 --- a/v2/internal/app/app_dev.go +++ b/v2/internal/app/app_dev.go @@ -46,7 +46,12 @@ func CreateApp(appoptions *options.App) (*App, error) { ctx = context.WithValue(ctx, "debug", true) ctx = context.WithValue(ctx, "devtoolsEnabled", true) - // Set up logger + // Set up logger if the appoptions.LogLevel is an invalid value, set it to the default log level + appoptions.LogLevel, err = pkglogger.StringToLogLevel(appoptions.LogLevel.String()) + if err != nil { + return nil, err + } + myLogger := logger.New(appoptions.Logger) myLogger.SetLogLevel(appoptions.LogLevel) @@ -91,15 +96,8 @@ func CreateApp(appoptions *options.App) (*App, error) { if frontendDevServerURLFlag != nil { frontendDevServerURL = *frontendDevServerURLFlag } - // Only override LogLevel if the flag was explicitly set - if loglevelFlag != nil && devFlags.Lookup("loglevel").Value.String() != appoptions.LogLevel.String() { - loggerLevel, err := pkglogger.StringToLogLevel(*loglevelFlag) - if err != nil { - return nil, err - } - if loggerLevel != appoptions.LogLevel { - myLogger.SetLogLevel(loggerLevel) - } + if loglevelFlag != nil { + loglevel = *loglevelFlag } } @@ -176,6 +174,17 @@ func CreateApp(appoptions *options.App) (*App, error) { ctx = context.WithValue(ctx, "devserver", devServer) } + if loglevel != "" { + level, err := pkglogger.StringToLogLevel(loglevel) + if err != nil { + return nil, err + } + // Only set the log level if it's different from the appoptions.LogLevel + if level != appoptions.LogLevel { + myLogger.SetLogLevel(level) + } + } + // Attach logger to context ctx = context.WithValue(ctx, "logger", myLogger) ctx = context.WithValue(ctx, "buildtype", "dev") diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 754b4a03e..01bc757ae 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added "Branding" section to `wails doctor` to correctly identify Windows 11 [#3891](https://github.com/wailsapp/wails/pull/3891) by [@ronen25](https://github.com/ronen25) - Added `-skipembedcreate` flag to build and dev command to improve compile and recompile speed [#4143](https://github.com/wailsapp/wails/pull/4143) by @josStorer +### Fixed +- Fixed excessive console logging after updating to v2.10.1 by @superDingda in [#4111](https://github.com/wailsapp/wails/issues/4111) ## v2.10.1 - 2025-02-24 From 6ac0bcdf7f18d9420435e9936a98cc8ce0467b86 Mon Sep 17 00:00:00 2001 From: darkb0ts Date: Mon, 7 Apr 2025 12:26:06 +0530 Subject: [PATCH 33/37] new template for wails 2025 react-ts and typescript --- website/docs/community/templates.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/community/templates.mdx b/website/docs/community/templates.mdx index fdd2778c4..088b64a68 100644 --- a/website/docs/community/templates.mdx +++ b/website/docs/community/templates.mdx @@ -41,6 +41,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for - [wails-template-nextjs](https://github.com/LGiki/wails-template-nextjs) - A template using Next.js and TypeScript - [wails-template-nextjs-app-router](https://github.com/thisisvk-in/wails-template-nextjs-app-router) - A template using Next.js and TypeScript with App router - [wails-vite-react-ts-tailwind-template](https://github.com/hotafrika/wails-vite-react-ts-tailwind-template) - A template for React + TypeScript + Vite + TailwindCSS +- [Wails-vite-ts-tailwindcss-shadcn-template-2025](https://github.com/darkb0ts/Wails-vite-ts-tailwindcss-shadcn-template-2025) - A template for React + TypeScript + Vite - [wails-vite-react-ts-tailwind-shadcnui-template](https://github.com/Mahcks/wails-vite-react-tailwind-shadcnui-ts) - A template with Vite, React, TypeScript, TailwindCSS, and shadcn/ui - [wails-nextjs-tailwind-template](https://github.com/kairo913/wails-nextjs-tailwind-template) - A template using Next.js and Typescript with TailwindCSS From bc7b816c37bfb59cd541db57dde34c93e22dbbff Mon Sep 17 00:00:00 2001 From: joshuapare Date: Thu, 10 Apr 2025 08:35:49 -0500 Subject: [PATCH 34/37] postfix [] in instance is element accessor, not array type declaration --- .../binding_test/binding_deepelements_test.go | 24 +++++++++---------- v2/internal/typescriptify/typescriptify.go | 12 +++++----- website/src/pages/changelog.mdx | 1 + 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/v2/internal/binding/binding_test/binding_deepelements_test.go b/v2/internal/binding/binding_test/binding_deepelements_test.go index 488c58f6d..034687474 100644 --- a/v2/internal/binding/binding_test/binding_deepelements_test.go +++ b/v2/internal/binding/binding_test/binding_deepelements_test.go @@ -63,18 +63,18 @@ export namespace binding_test { DoubleFour: number[][]; Triple: number[][][]; SingleMap: Record; - SliceMap: Record; - DoubleSliceMap: Record; - ArrayMap: Record; - DoubleArrayMap1: Record; - DoubleArrayMap2: Record; - DoubleArrayMap3: Record; + SliceMap: Record>; + DoubleSliceMap: Record>>; + ArrayMap: Record>; + DoubleArrayMap1: Record>>; + DoubleArrayMap2: Record>>; + DoubleArrayMap3: Record>>; OneStructs: DeepMessage[]; TwoStructs: DeepMessage[][]; ThreeStructs: DeepMessage[][][]; - MapStructs: Record; - MapTwoStructs: Record; - MapThreeStructs: Record; + MapStructs: Record>; + MapTwoStructs: Record>>; + MapThreeStructs: Record>>>; static createFrom(source: any = {}) { return new DeepElements(source); @@ -97,9 +97,9 @@ export namespace binding_test { this.OneStructs = this.convertValues(source["OneStructs"], DeepMessage); this.TwoStructs = this.convertValues(source["TwoStructs"], DeepMessage); this.ThreeStructs = this.convertValues(source["ThreeStructs"], DeepMessage); - this.MapStructs = this.convertValues(source["MapStructs"], DeepMessage[], true); - this.MapTwoStructs = this.convertValues(source["MapTwoStructs"], DeepMessage[][], true); - this.MapThreeStructs = this.convertValues(source["MapThreeStructs"], DeepMessage[][][], true); + this.MapStructs = this.convertValues(source["MapStructs"], Array, true); + this.MapTwoStructs = this.convertValues(source["MapTwoStructs"], Array>, true); + this.MapThreeStructs = this.convertValues(source["MapThreeStructs"], Array>>, true); } convertValues(a: any, classs: any, asMap: boolean = false): any { diff --git a/v2/internal/typescriptify/typescriptify.go b/v2/internal/typescriptify/typescriptify.go index 95376b2f4..216fba820 100644 --- a/v2/internal/typescriptify/typescriptify.go +++ b/v2/internal/typescriptify/typescriptify.go @@ -40,9 +40,7 @@ const ( jsVariableNameRegex = `^([A-Z]|[a-z]|\$|_)([A-Z]|[a-z]|[0-9]|\$|_)*$` ) -var ( - jsVariableUnsafeChars = regexp.MustCompile(`[^A-Za-z0-9_]`) -) +var jsVariableUnsafeChars = regexp.MustCompile(`[^A-Za-z0-9_]`) func nameTypeOf(typeOf reflect.Type) string { tname := typeOf.Name() @@ -277,6 +275,7 @@ func (t *typeScriptClassBuilder) AddMapField(fieldName string, field reflect.Str valueType := field.Type.Elem() valueTypeName := nameTypeOf(valueType) valueTypeSuffix := "" + valueTypePrefix := "" if valueType.Kind() == reflect.Ptr { valueType = valueType.Elem() valueTypeName = nameTypeOf(valueType) @@ -289,7 +288,8 @@ func (t *typeScriptClassBuilder) AddMapField(fieldName string, field reflect.Str } valueType = valueType.Elem() valueTypeName = nameTypeOf(valueType) - valueTypeSuffix = strings.Repeat("[]", arrayDepth) + valueTypeSuffix = strings.Repeat(">", arrayDepth) + valueTypePrefix = strings.Repeat("Array<", arrayDepth) } if valueType.Kind() == reflect.Ptr { valueType = valueType.Elem() @@ -325,10 +325,10 @@ func (t *typeScriptClassBuilder) AddMapField(fieldName string, field reflect.Str fieldName = fmt.Sprintf(`"%s"?`, strippedFieldName) } } - t.fields = append(t.fields, fmt.Sprintf("%s%s: Record<%s, %s>;", t.indent, fieldName, keyTypeStr, valueTypeName+valueTypeSuffix)) + t.fields = append(t.fields, fmt.Sprintf("%s%s: Record<%s, %s>;", t.indent, fieldName, keyTypeStr, valueTypePrefix+valueTypeName+valueTypeSuffix)) if valueType.Kind() == reflect.Struct { t.constructorBody = append(t.constructorBody, fmt.Sprintf("%s%sthis%s = this.convertValues(source[\"%s\"], %s, true);", - t.indent, t.indent, dotField, strippedFieldName, t.prefix+valueTypeName+valueTypeSuffix+t.suffix)) + t.indent, t.indent, dotField, strippedFieldName, t.prefix+valueTypePrefix+valueTypeName+valueTypeSuffix+t.suffix)) } else { t.constructorBody = append(t.constructorBody, fmt.Sprintf("%s%sthis%s = source[\"%s\"];", t.indent, t.indent, dotField, strippedFieldName)) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 68288cfff..fc1a5ff6b 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `-skipembedcreate` flag to build and dev command to improve compile and recompile speed [#4143](https://github.com/wailsapp/wails/pull/4143) by @josStorer ### Fixed +- Fixed typescript generation of maps with key of array of structs by @joshuapare in [#4209](https://github.com/wailsapp/wails/pull/4209) - Fixed -m build flag for dev command not working when recompiling in [#4141](https://github.com/wailsapp/wails/pull/4141) by @josStorer - Fixed window restoration behavior after minimization by @superDingda in [#4109](https://github.com/wailsapp/wails/issues/4109) - Fixed excessive console logging after updating to v2.10.1 by @superDingda in [#4111](https://github.com/wailsapp/wails/issues/4111) From f8998da78d769c066960ff4cb1d0192d7637f604 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 12 Apr 2025 13:21:37 +1000 Subject: [PATCH 35/37] Fix for webview2 detection --- v2/pkg/buildassets/build/windows/installer/wails_tools.nsh | 2 +- website/src/pages/changelog.mdx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/v2/pkg/buildassets/build/windows/installer/wails_tools.nsh b/v2/pkg/buildassets/build/windows/installer/wails_tools.nsh index f9c0f8852..2f6d32195 100644 --- a/v2/pkg/buildassets/build/windows/installer/wails_tools.nsh +++ b/v2/pkg/buildassets/build/windows/installer/wails_tools.nsh @@ -158,7 +158,7 @@ RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}" ${If} ${REQUEST_EXECUTION_LEVEL} == "user" # If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed - ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" ${If} $0 != "" Goto ok ${EndIf} diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 68288cfff..e34a51eff 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed -m build flag for dev command not working when recompiling in [#4141](https://github.com/wailsapp/wails/pull/4141) by @josStorer - Fixed window restoration behavior after minimization by @superDingda in [#4109](https://github.com/wailsapp/wails/issues/4109) - Fixed excessive console logging after updating to v2.10.1 by @superDingda in [#4111](https://github.com/wailsapp/wails/issues/4111) +- Fixed incorrect Webview2 reg key in HKCU path by @leaanthony. ## v2.10.1 - 2025-02-24 From b12dcb054bfd689d448fa701f120107b7f8dc597 Mon Sep 17 00:00:00 2001 From: "dingda.li" Date: Tue, 15 Apr 2025 14:13:56 +0800 Subject: [PATCH 36/37] fix: prevent truncation of long WebSocket messages in dev mode --- v2/go.mod | 1 + v2/go.sum | 2 + v2/internal/frontend/devserver/devserver.go | 85 +++++++++++++-------- website/src/pages/changelog.mdx | 1 + 4 files changed, 56 insertions(+), 33 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index 036dc4812..7bf6de3cd 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -15,6 +15,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 github.com/jackmordaunt/icns v1.0.0 github.com/jaypipes/ghw v0.13.0 github.com/labstack/echo/v4 v4.13.3 diff --git a/v2/go.sum b/v2/go.sum index ccf53eb07..921ea0477 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -107,6 +107,8 @@ github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU= diff --git a/v2/internal/frontend/devserver/devserver.go b/v2/internal/frontend/devserver/devserver.go index 3d623ed6d..1aa88059a 100644 --- a/v2/internal/frontend/devserver/devserver.go +++ b/v2/internal/frontend/devserver/devserver.go @@ -20,17 +20,23 @@ import ( "github.com/wailsapp/wails/v2/internal/frontend/runtime" + "github.com/gorilla/websocket" "github.com/labstack/echo/v4" "github.com/wailsapp/wails/v2/internal/binding" "github.com/wailsapp/wails/v2/internal/frontend" "github.com/wailsapp/wails/v2/internal/logger" "github.com/wailsapp/wails/v2/internal/menumanager" "github.com/wailsapp/wails/v2/pkg/options" - "golang.org/x/net/websocket" ) type Screen = frontend.Screen +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { return true }, +} + type DevWebServer struct { server *echo.Echo ctx context.Context @@ -155,51 +161,64 @@ func (d *DevWebServer) handleReloadApp(c echo.Context) error { } func (d *DevWebServer) handleIPCWebSocket(c echo.Context) error { - websocket.Handler(func(c *websocket.Conn) { - d.LogDebug(fmt.Sprintf("Websocket client %p connected", c)) + conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil) + if err != nil { + d.logger.Error("WebSocket upgrade failed %v", err) + return err + } + d.LogDebug(fmt.Sprintf("WebSocket client %p connected", conn)) + + d.socketMutex.Lock() + d.websocketClients[conn] = &sync.Mutex{} + locker := d.websocketClients[conn] + d.socketMutex.Unlock() + + var wg sync.WaitGroup + + defer func() { + wg.Wait() d.socketMutex.Lock() - d.websocketClients[c] = &sync.Mutex{} - locker := d.websocketClients[c] + delete(d.websocketClients, conn) d.socketMutex.Unlock() + d.LogDebug(fmt.Sprintf("WebSocket client %p disconnected", conn)) + conn.Close() + }() - defer func() { - d.socketMutex.Lock() - delete(d.websocketClients, c) - d.socketMutex.Unlock() - d.LogDebug(fmt.Sprintf("Websocket client %p disconnected", c)) - }() + for { + _, msgBytes, err := conn.ReadMessage() + if err != nil { + break + } - var msg string - defer c.Close() - for { - if err := websocket.Message.Receive(c, &msg); err != nil { - break - } - // We do not support drag in browsers - if msg == "drag" { - continue + msg := string(msgBytes) + wg.Add(1) + + go func(m string) { + defer wg.Done() + + if m == "drag" { + return } - // Notify the other browsers of "EventEmit" - if len(msg) > 2 && strings.HasPrefix(string(msg), "EE") { - d.notifyExcludingSender([]byte(msg), c) + if len(m) > 2 && strings.HasPrefix(m, "EE") { + d.notifyExcludingSender([]byte(m), conn) } - // Send the message to dispatch to the frontend - result, err := d.dispatcher.ProcessMessage(string(msg), d) + result, err := d.dispatcher.ProcessMessage(m, d) if err != nil { d.logger.Error(err.Error()) } + if result != "" { locker.Lock() - if err = websocket.Message.Send(c, result); err != nil { - locker.Unlock() - break + defer locker.Unlock() + if err := conn.WriteMessage(websocket.TextMessage, []byte(result)); err != nil { + d.logger.Error("Websocket write message failed %v", err) } - locker.Unlock() } - } - }).ServeHTTP(c.Response(), c.Request()) + }(msg) + } + return nil } @@ -222,7 +241,7 @@ func (d *DevWebServer) broadcast(message string) { return } locker.Lock() - err := websocket.Message.Send(client, message) + err := client.WriteMessage(websocket.TextMessage, []byte(message)) if err != nil { locker.Unlock() d.logger.Error(err.Error()) @@ -256,7 +275,7 @@ func (d *DevWebServer) broadcastExcludingSender(message string, sender *websocke return } locker.Lock() - err := websocket.Message.Send(client, message) + err := client.WriteMessage(websocket.CloseMessage, []byte(message)) if err != nil { locker.Unlock() d.logger.Error(err.Error()) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index e34a51eff..780fd1474 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed window restoration behavior after minimization by @superDingda in [#4109](https://github.com/wailsapp/wails/issues/4109) - Fixed excessive console logging after updating to v2.10.1 by @superDingda in [#4111](https://github.com/wailsapp/wails/issues/4111) - Fixed incorrect Webview2 reg key in HKCU path by @leaanthony. +- Fixed long WebSocket messages being truncated in `wails dev` by @superDingda in [#4211](https://github.com/wailsapp/wails/issues/4211) ## v2.10.1 - 2025-02-24 From fc1cf90574687dd5e8c51133d858affef53aa881 Mon Sep 17 00:00:00 2001 From: "dingda.li" Date: Tue, 15 Apr 2025 15:42:10 +0800 Subject: [PATCH 37/37] fix: correct message type to TextMessage --- v2/internal/frontend/devserver/devserver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/internal/frontend/devserver/devserver.go b/v2/internal/frontend/devserver/devserver.go index 1aa88059a..8a130890d 100644 --- a/v2/internal/frontend/devserver/devserver.go +++ b/v2/internal/frontend/devserver/devserver.go @@ -275,7 +275,7 @@ func (d *DevWebServer) broadcastExcludingSender(message string, sender *websocke return } locker.Lock() - err := client.WriteMessage(websocket.CloseMessage, []byte(message)) + err := client.WriteMessage(websocket.TextMessage, []byte(message)) if err != nil { locker.Unlock() d.logger.Error(err.Error())