Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v3] Late service registration and error handling overhaul #4066

Merged
merged 30 commits into from
Feb 19, 2025

Conversation

fbbdev
Copy link
Collaborator

@fbbdev fbbdev commented Feb 13, 2025

Description

This PR provides three QOL improvements for the runtime side of the binding/service system and fixes a major bug:

  • it adds a method App.RegisterService for registering services after app creation (but before App.Run);
  • it adds rich error handling for binding calls inspired by the v2 approach, with json marshaling as default behaviour and global + per-service MarshalError functions for custom handling (see the updated docs);
  • it standardises and documents the order in which services are started and shut down (registration order and reverse registration order, respectively);
  • it fixes a macOS bug whereby service shutdown methods would not be called on quit (due to the fact that on macOS the app event loop never returns control back to App.Run).

Additionally, it adds a test harness for end-to-end tests of the application object and uses it to test service startup/shutdown sequences and error handling. Note that the go test framework spawns one process per package, hence – against go conventions – I had to split tests over many packages because at most one application run per process is supported.

🚨 Breaking changes 🚨

There are a few minor breaking changes necessitated by the restructuring described above (should not affect users in practice):

  • ServiceStartup hooks are now invoked when App.Run is called, not in application.New
  • ServiceStartup errors are now returned from App.Run instead of terminating the process
  • Binding and dialog calls from JS now reject with rich error objects instead of simple strings

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Ran the test suite including new and updated tests, verified that everything is passing; tested examples manually.

  • Windows
  • macOS
  • Linux

Test Configuration

 Wails (v3.0.0-dev)  Wails Doctor

# System

┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
| Name          | MacOS                                                                                                          |
| Version       | 12.6.6                                                                                                         |
| ID            | 21G646                                                                                                         |
| Branding      | Monterey                                                                                                       |
| Platform      | darwin                                                                                                         |
| Architecture  | amd64                                                                                                          |
| Apple Silicon | unknown                                                                                                        |
| CPU           | Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz                                                                       |
| CPU           | Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz                                                                       |
| GPU           |  cores, Metal Family: Supported, Metal GPUFamily macOS 2                                                       |
|       Metal Family: Supported, Metal GPUFamily macOS 2                                                                         |
| Memory        | 32 GB                                                                                                          |
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

# Build Environment

┌─────────────────────────────────────────────────────────┐
| Wails CLI    | v3.0.0-dev                               |
| Go Version   | go1.23.5                                 |
| Revision     | 7f6b911e15fccadd140077b809d244a9878bf2c6 |
| Modified     | true                                     |
| -buildmode   | exe                                      |
| -compiler    | gc                                       |
| CGO_CFLAGS   |                                          |
| CGO_CPPFLAGS |                                          |
| CGO_CXXFLAGS |                                          |
| CGO_ENABLED  | 1                                        |
| CGO_LDFLAGS  |                                          |
| GOAMD64      | v1                                       |
| GOARCH       | amd64                                    |
| GOOS         | darwin                                   |
| vcs          | git                                      |
| vcs.modified | true                                     |
| vcs.revision | 7f6b911e15fccadd140077b809d244a9878bf2c6 |
| vcs.time     | 2025-02-12T08:35:30Z                     |
└─────────────────────────────────────────────────────────┘

# Dependencies

┌───────────────────────────┐
| npm             | 10.8.2  |
| *NSIS           | v3.10   |
| Xcode cli tools | 2395    |
└─ * - Optional Dependency ─┘

# Checking for issues

 SUCCESS  No issues found

# Diagnosis

 SUCCESS  Your system is ready for Wails development!

Checklist:

  • I have updated docs/src/content/docs/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features

    • Introduced a post-shutdown hook to allow custom code execution after shutdown.
    • Added a new error type for improved fatal error detection, along with a streamlined service registration approach.
  • Improvements

    • Upgraded error handling in JavaScript bindings and dialogs so that errors now return standardized error objects.
    • Enhanced the reliability of service startup and shutdown order for better lifecycle management.
    • Improved error handling and logging consistency across various components.
    • Updated error handling in multiple methods to use structured error messages and improved formatting.
    • Standardized documentation for service lifecycle management and error handling.
  • Documentation

    • Expanded and clarified content on error handling and service lifecycle procedures.
  • Tests

    • Added a test harness to validate application startup and shutdown sequences.
    • Introduced comprehensive tests for service startup and shutdown behaviors, including error handling scenarios.

Copy link
Contributor

coderabbitai bot commented Feb 13, 2025

Walkthrough

This pull request introduces extensive improvements across documentation, runtime, and core application logic. The updates include enhanced error handling with new types and hooks (such as the PostShutdown hook and FatalError), modifications to service startup/shutdown processes with locking improvements, and a switch from map- to slice-based storage for service handlers in the AssetServer. Additionally, the runtime calling interface and TypeScript definitions have been refined, and comprehensive unit tests for service lifecycle and error scenarios have been added.

Changes

Files Change Summary
docs/src/content/docs/changelog.mdx, docs/src/content/docs/learn/{bindings,services}.mdx Updated documentation to include new hooks, improved error handling, and clarified service lifecycle management.
v3/internal/assetserver/assetserver.go, v3/internal/assetserver/bundledassets/runtime.js Refactored AssetServer to use a slice for managing service handlers; runtime JS refactored with variable renames and async/await improvements.
v3/internal/runtime/desktop/@wailsio/runtime/{calls.js,runtime.js,types/*} Enhanced runtime call error handling with a new Call function, introduced RuntimeError class, and updated TypeScript signatures.
v3/internal/{generator/collect/service.go,hash/fnv.go}, v3/internal/runtime/Taskfile.yaml Simplified method ID generation, updated the Fnv function to remove error returns, and standardized task directory settings.
v3/pkg/application/{application.go,application_options.go,bindings.go,errors.go,services.go,webview_window.go,window.go,messageprocessor_*.go} Overhauled core application logic: added service lifecycle locks, PostShutdown hook, MarshalError field, FatalError type, and improved error handling in bindings and message processors.
v3/pkg/application/internal/tests/{services/*,utils.go} Added comprehensive unit tests covering service startup, shutdown, and error handling alongside new test utils for application lifecycle management.

Sequence Diagram(s)

sequenceDiagram
  participant App as Application
  participant Svc as Service
  participant Lock as ShutdownLock
  App->>Svc: Invoke ServiceStartup()
  alt Successful startup
    Svc-->>App: Return success
    App->>Svc: Continue running
  else Startup error
    Svc-->>App: Return error
    App->>Lock: Acquire shutdown lock
    App->>Svc: Invoke ServiceShutdown() in reverse order
  end
Loading
sequenceDiagram
  participant JS as JavaScript Client
  participant RT as Runtime
  participant EH as ErrorHandler
  JS->>RT: Call(options)
  alt Valid JSON response
    RT-->>JS: Return parsed data
  else Error occurs
    RT->>EH: Process error (parse & wrap error)
    EH-->>JS: Reject promise with error object
  end
Loading

Possibly related PRs

  • [v3 alpha] docs: add note that internal service methods are not included in generated bindings #3982: The changes in the main PR, which include the addition of service lifecycle methods and error handling improvements, are related to the retrieved PR that documents the exclusion of internal service methods from generated bindings, as both pertain to the service management and lifecycle in the Wails framework.
  • recover from process message instead crash the whole app #4016: The changes in the main PR, particularly the enhancements to error handling and the introduction of new methods like MarshalError, are related to the modifications in the retrieved PR, which also focuses on improving error handling in the ProcessMessage method of the Dispatcher struct. Both PRs aim to enhance the application's stability and error management.

Suggested reviewers

  • leaanthony

Poem

I'm a rabbit with code in my soul,
Hopping through changes to make systems whole.
With hooks and errors tidied in a rhyme,
Services start and shutdown in perfect time.
I munch on carrots of clean, crisp code,
Celebrating improvements down the digital road!

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 golangci-lint (1.62.2)

Error: can't load config: the Go language version (go1.23) used to build golangci-lint is lower than the targeted Go version (1.24.0)
Failed executing command with error: can't load config: the Go language version (go1.23) used to build golangci-lint is lower than the targeted Go version (1.24.0)

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (33)
v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go (4)

29-31: Add documentation to clarify test scenarios.

The test setup would benefit from comments explaining:

  1. The purpose of each service configuration
  2. The expected behavior of services with StartupErr and ShutdownErr flags

Add documentation above the test:

+// TestServiceStartupShutdownError validates the following scenarios:
+// - Services 1-3: Initial registration with Service2 configured to fail on shutdown
+// - Services 4-5: Concurrent registration with both startup and shutdown errors
+// - Service 6: Sequential registration with both startup and shutdown errors
 func TestServiceStartupShutdownError(t *testing.T) {

Also applies to: 32-39


44-68: Extract error handler logic for better maintainability.

The error handler logic is complex and would be more maintainable if extracted into a separate function.

Consider refactoring to:

+func newErrorHandler(t *testing.T, app *application.App, expectedErrors []int, errCount *atomic.Int64) func(error) {
+    return func(err error) {
+        var mock *svctest.Error
+        if !errors.As(err, &mock) {
+            app.Logger.Error(err.Error())
+            return
+        }
+
+        i := int(errCount.Add(1) - 1)
+        if i < len(expectedErrors) && mock.Id == expectedErrors[i] {
+            return
+        }
+
+        cut := min(i, len(expectedErrors))
+        if slices.Contains(expectedErrors[:cut], mock.Id) {
+            t.Errorf("Late or duplicate shutdown error for service #%d", mock.Id)
+        } else if slices.Contains(expectedErrors[cut:], mock.Id) {
+            t.Errorf("Early shutdown error for service #%d", mock.Id)
+        } else {
+            t.Errorf("Unexpected shutdown error for service #%d", mock.Id)
+        }
+    }
+}

 var app *application.App
 app = apptest.New(application.Options{
     Services: services[:3],
-    ErrorHandler: func(err error) {
-        // ... current error handler logic ...
-    },
+    ErrorHandler: newErrorHandler(t, app, expectedShutdownErrors, &errCount),
 })

108-110: Use named constants for magic numbers.

The test uses magic numbers for expected counts which could be clearer with named constants.

Consider using named constants:

+const (
+    expectedStartupCount  = 4
+    expectedShutdownCount = 3
+)

-    if count := seq.Load(); count != 4+3 {
-        t.Errorf("Wrong startup+shutdown call count: wanted %d+%d, got %d", 4, 3, count)
+    if count := seq.Load(); count != expectedStartupCount+expectedShutdownCount {
+        t.Errorf("Wrong startup+shutdown call count: wanted %d+%d, got %d",
+            expectedStartupCount, expectedShutdownCount, count)

120-140: Improve type assertion safety and error messages.

The validation function could benefit from safer type assertions and more descriptive error messages.

Consider the following improvements:

 func validate(t *testing.T, svc application.Service, startup bool, shutdown bool) {
-    id := svc.Instance().(interface{ Id() int }).Id()
-    startupSeq := svc.Instance().(interface{ StartupSeq() int64 }).StartupSeq()
-    shutdownSeq := svc.Instance().(interface{ ShutdownSeq() int64 }).ShutdownSeq()
+    instance := svc.Instance()
+    
+    idGetter, ok := instance.(interface{ Id() int })
+    if !ok {
+        t.Fatalf("Service instance does not implement Id() method")
+    }
+    id := idGetter.Id()
+    
+    startupGetter, ok := instance.(interface{ StartupSeq() int64 })
+    if !ok {
+        t.Fatalf("Service #%d does not implement StartupSeq() method", id)
+    }
+    startupSeq := startupGetter.StartupSeq()
+    
+    shutdownGetter, ok := instance.(interface{ ShutdownSeq() int64 })
+    if !ok {
+        t.Fatalf("Service #%d does not implement ShutdownSeq() method", id)
+    }
+    shutdownSeq := shutdownGetter.ShutdownSeq()

     if startup != (startupSeq != 0) {
         if startupSeq == 0 {
-            t.Errorf("Service #%d did not start up", id)
+            t.Errorf("Service #%d failed to start up as expected", id)
         } else {
-            t.Errorf("Unexpected startup for service #%d at seq=%d", id, startupSeq)
+            t.Errorf("Service #%d unexpectedly started up with sequence number %d", id, startupSeq)
         }
     }

     if shutdown != (shutdownSeq != 0) {
         if shutdownSeq == 0 {
-            t.Errorf("Service #%d did not shut down", id)
+            t.Errorf("Service #%d failed to shut down as expected", id)
         } else {
-            t.Errorf("Unexpected shutdown for service #%d at seq=%d", id, shutdownSeq)
+            t.Errorf("Service #%d unexpectedly shut down with sequence number %d", id, shutdownSeq)
         }
     }
 }
v3/pkg/application/errors.go (1)

42-55: Potential side-effects from os.Exit(1).

While it is normal to exit on fatal errors, keep in mind that calling os.Exit(1) makes it impossible for upstream code or tests to handle or recover from these errors.

Consider refactoring to allow for graceful shutdown or returning the error up the call stack in testing contexts. For example:

 func Fatal(message string, args ...any) {
     err := &FatalError{
         err:      fmt.Errorf(message, args...),
         internal: true,
     }

-    if globalApplication != nil {
-        globalApplication.handleError(err)
-    } else {
-        fmt.Println(err)
-    }
-    os.Exit(1)
+    handleOrLogError(err)
+    // Possibly re-throw or return an error here for testability.
 }
v3/internal/runtime/desktop/@wailsio/runtime/types/calls.d.ts (5)

2-13: Improved documentation for Call.

Clarifying the error types (ReferenceError, TypeError, RuntimeError, etc.) is helpful for clients using this API. Returning Promise<any> is a flexible choice, although using more specific return types can provide stronger type safety.

Consider introducing a generic type parameter (e.g., Promise<T>) where possible to reduce the reliance on any.


15-21: ByName function references Call details.

The documentation and new return type of Promise<any> similarly benefit from more explicit type definitions to avoid overuse of any. Overall, the updates align with the new structured error handling approach.


33-39: Definition of CallOptions (initial doc block).

Setting explicit type properties for call descriptor objects clarifies usage. The doc block is well-formed, though repeated references to any[] hamper type safety.


41-52: RuntimeError class might benefit from naming enforcement.

While extending Error, consider explicitly setting this.name = "RuntimeError" to ensure consistent naming in stack traces across various environments.

 constructor(message: string, ...args: any[]) {
     super(message, ...args);
+    this.name = "RuntimeError";
 }

56-69: Final CallOptions type definition.

Defining CallOptions with optional methodID or methodName fields covers multiple invocation strategies. Again, the reliance on any[] is flexible but reduces strictness. Consider further type refinement in the future.

v3/internal/assetserver/assetserver.go (2)

27-30: services []service addition to AssetServer.

Switching from a map to a slice simplifies ordering and iteration, although large numbers of services could affect performance with linear searches.

Consider storing services in a trie or a map for faster lookups if the number of routes is expected to grow.


118-122: Linear route matching may cause scaling issues.

Scanning services in a loop is clear but could degrade performance with many registered services.

A more sophisticated data structure (like a prefix tree) can prevent potential slowdowns on large, heavily routed apps.

v3/pkg/application/messageprocessor_call.go (4)

18-30: Ensure fallback from JSON marshalling is clear.

If JSON marshalling fails, the logic correctly calls window.CallError without JSON. However, consider including the marshaling error details in the fallback call as well, so the caller knows why the data could not be serialized.


45-45: Clarify missing call-id error cause.

The current message is generic; consider indicating 'call-id' was nil or empty. This helps pinpoint the exact issue in logs.


87-93: Caution around cancelRequired scope.

The deferred check is a reliable pattern, but consider clarifying the comment to emphasize that it prevents resource leaks if the goroutine fails before scheduling.


114-119: Improved logging details.

Logging the method reference is helpful. To minimize sensitive data logs, consider sanitizing or omitting potentially private arguments from production logs.

v3/internal/runtime/desktop/@wailsio/runtime/src/calls.js (1)

128-128: Documentation for CallOptions.

The new doc lines help clarify usage. However, ensure references to package.struct.method or ID remain consistent across the codebase if further changes occur.

Also applies to: 130-133

v3/pkg/application/bindings.go (2)

69-78: Added FQN and marshalError to BoundMethod.

This aids debugging and custom error handling. If FQN might reveal internal structure, consider sanitizing it in logs or leaving that to a debug-only feature.


352-377: Primary/secondary error marshaler logic.

Using a primary custom marshaler then a fallback defaultMarshalError allows high extensibility. Consider logging an internal warning if the primary returns nil often, to help identify incomplete custom marshaling implementations.

v3/pkg/application/application.go (1)

365-379: RegisterService adds late registration logic
Panicking when services are registered post-start is intentional. It enforces a strict lifecycle contract but eliminates more graceful runtime injection. If this is desirable behavior, it looks fine. Otherwise, consider returning an error instead of panicking for a gentler fail.

v3/internal/runtime/desktop/@wailsio/runtime/types/runtime.d.ts (1)

12-13: Consider defining explicit function type instead of using 'Function'.

While the parameter type change from object to number is good for type safety, the return type Function is too generic and could lead to type-related bugs.

Consider defining an explicit function type that describes the expected signature:

-export function newRuntimeCallerWithID(object: number, windowName: string): Function;
+type RuntimeCaller = (method: string, ...args: any[]) => Promise<any>;
+export function newRuntimeCallerWithID(object: number, windowName: string): RuntimeCaller;

Also applies to: 16-16

v3/pkg/application/internal/tests/utils.go (2)

30-31: Consider adding timeouts to prevent indefinite blocking.

The code uses select{} to block forever in multiple places. While this might be intentional, it could make tests harder to debug if they hang.

Consider adding timeouts to prevent tests from hanging indefinitely:

-select {} // Block forever
+select {
+case <-time.After(30 * time.Second):
+    panic("Test timed out after 30 seconds")
+}

Also applies to: 44-45, 69-69


50-61: Consider adding context for test cancellation.

The Run function could benefit from context support to allow graceful test cancellation.

Consider adding context support:

-func Run(t *testing.T, app *application.App) error {
+func Run(ctx context.Context, t *testing.T, app *application.App) error {
 	appChan <- app
 	select {
 	case err := <-errChan:
 		return err
 	case fatal := <-endChan:
 		if fatal != nil {
 			t.Fatal(fatal)
 		}
 		return fatal
+	case <-ctx.Done():
+		return ctx.Err()
 	}
 }
v3/pkg/application/internal/tests/services/shutdown/shutdown_test.go (1)

70-75: Document the expected shutdown sequence.

Consider adding a comment explaining the expected shutdown sequence (5 -> 4 -> 2,3 -> 1 -> 0) to improve test maintainability.

+// Expected shutdown sequence:
+// Service6 (id:5) -> Service5 (id:4) -> Service3,4 (id:2,3) -> Service2 (id:1) -> Service1 (id:0)
 validate(t, services[0], 5)
 validate(t, services[1], 4)
 validate(t, services[2], 2, 3)
v3/pkg/application/internal/tests/services/startup/startup_test.go (1)

80-85: Document the expected startup sequence.

Consider adding a comment explaining the expected startup sequence (0 -> 1 -> 2 -> 3 -> 4,5) to improve test maintainability.

+// Expected startup sequence:
+// Service1 (id:0) -> Service2 (id:1) -> Service3 (id:2) -> Service4 (id:3) -> Service5,6 (id:4,5)
 validate(t, services[0], 0)
 validate(t, services[1], 1)
 validate(t, services[2], 2)
v3/pkg/application/internal/tests/services/startupshutdown/startupshutdown_test.go (1)

27-81: Add test documentation and enhance concurrent registration validation.

The test effectively validates service lifecycle, but would benefit from:

  1. Documentation explaining the test scenarios and expected behavior
  2. Explicit validation that concurrent registration succeeded

Add documentation and enhance validation:

 func TestServiceStartupShutdown(t *testing.T) {
+    // TestServiceStartupShutdown validates:
+    // - Sequential registration of services 1-3
+    // - Concurrent registration of services 4-5
+    // - Post-concurrent registration of service 6
+    // - Proper startup/shutdown sequence
+    // - No duplicate startup/shutdown calls
     var seq atomic.Int64
 
     services := []application.Service{
         svctest.Configure(&Service1{}, svctest.Config{Id: 0, T: t, Seq: &seq}),
         svctest.Configure(&Service2{}, svctest.Config{Id: 1, T: t, Seq: &seq}),
         svctest.Configure(&Service3{}, svctest.Config{Id: 2, T: t, Seq: &seq}),
         svctest.Configure(&Service4{}, svctest.Config{Id: 3, T: t, Seq: &seq}),
         svctest.Configure(&Service5{}, svctest.Config{Id: 4, T: t, Seq: &seq}),
         svctest.Configure(&Service6{}, svctest.Config{Id: 5, T: t, Seq: &seq}),
     }
 
     app := apptest.New(application.Options{
         Services: services[:3],
     })
 
     var wg sync.WaitGroup
     wg.Add(2)
+    registrationErrors := make(chan error, 2)
     go func() {
-        app.RegisterService(services[3])
+        if err := app.RegisterService(services[3]); err != nil {
+            registrationErrors <- fmt.Errorf("failed to register service 4: %w", err)
+        }
         wg.Done()
     }()
     go func() {
-        app.RegisterService(services[4])
+        if err := app.RegisterService(services[4]); err != nil {
+            registrationErrors <- fmt.Errorf("failed to register service 5: %w", err)
+        }
         wg.Done()
     }()
     wg.Wait()
+    close(registrationErrors)
+    for err := range registrationErrors {
+        t.Error(err)
+    }
v3/pkg/application/internal/tests/services/startuperror/startuperror_test.go (1)

72-76: Improve error message clarity and validation.

The error messages could be more descriptive to aid debugging.

Enhance error messages:

     if mock == nil {
-        t.Fatal("Wanted startup error for service #3 or #4, got none")
+        t.Fatal("Expected startup error from either service #3 or #4, but no error was returned. This indicates a failure in error propagation during service startup.")
     } else if mock.Id != 3 && mock.Id != 4 {
-        t.Errorf("Wanted startup error for service #3 or #4, got #%d", mock.Id)
+        t.Errorf("Expected startup error from either service #3 or #4, but got error from service #%d. This indicates incorrect error handling or unexpected service startup order.", mock.Id)
     }
v3/pkg/application/internal/tests/services/shutdownerror/shutdownerror_test.go (1)

47-67: Refactor error handling for better maintainability.

The error handling logic is complex and could be simplified by extracting the validation into a separate function.

Extract error validation:

+    func validateShutdownError(t *testing.T, mock *svctest.Error, i int, expectedErrors []int) {
+        if i >= len(expectedErrors) {
+            t.Errorf("Unexpected shutdown error for service #%d: no more errors expected", mock.Id)
+            return
+        }
+        
+        if mock.Id == expectedErrors[i] {
+            return
+        }
+        
+        cut := min(i, len(expectedErrors))
+        switch {
+        case slices.Contains(expectedErrors[:cut], mock.Id):
+            t.Errorf("Late or duplicate shutdown error for service #%d", mock.Id)
+        case slices.Contains(expectedErrors[cut:], mock.Id):
+            t.Errorf("Early shutdown error for service #%d", mock.Id)
+        default:
+            t.Errorf("Unexpected shutdown error for service #%d", mock.Id)
+        }
+    }
+
     ErrorHandler: func(err error) {
         var mock *svctest.Error
         if !errors.As(err, &mock) {
             app.Logger.Error(err.Error())
             return
         }
 
         i := int(errCount.Add(1) - 1)
-        if i < len(expectedShutdownErrors) && mock.Id == expectedShutdownErrors[i] {
-            return
-        }
-
-        cut := min(i, len(expectedShutdownErrors))
-        if slices.Contains(expectedShutdownErrors[:cut], mock.Id) {
-            t.Errorf("Late or duplicate shutdown error for service #%d", mock.Id)
-        } else if slices.Contains(expectedShutdownErrors[cut:], mock.Id) {
-            t.Errorf("Early shutdown error for service #%d", mock.Id)
-        } else {
-            t.Errorf("Unexpected shutdown error for service #%d", mock.Id)
-        }
+        validateShutdownError(t, mock, i, expectedShutdownErrors)
     },
v3/pkg/application/internal/tests/services/common.go (2)

22-28: Improve generic constraints documentation.

The generic constraints could be more clearly documented to explain their purpose.

Add documentation:

+// Configure is a generic function that configures a service with the given Config.
+// Type parameters:
+// - T: The concrete service type
+// - P: A pointer to T that implements Configure(Config)
 func Configure[T any, P interface {
     *T
     Configure(Config)
 }](srv P, c Config) application.Service {
     srv.Configure(c)
     return application.NewServiceWithOptions(srv, c.Options)
 }

149-168: Enhance error messages in ServiceShutdown.

The error messages in ServiceShutdown could be more descriptive to aid debugging.

Improve error messages:

 func (s *StartupShutdowner) ServiceShutdown() error {
     if s.shutdown != 0 {
-        s.T.Errorf("Double shutdown for service #%d: first at seq=%d, then at seq=%d", s.Id(), s.shutdown, s.Seq.Load())
+        s.T.Errorf("Service #%d received multiple shutdown calls: initial shutdown at sequence %d, duplicate shutdown attempted at sequence %d", s.Id(), s.shutdown, s.Seq.Load())
         return nil
     }
 
     s.shutdown = s.Seq.Add(1)
 
     select {
     case <-s.ctx.Done():
     default:
-        s.T.Errorf("Service #%d shut down before context cancellation", s.Id())
+        s.T.Errorf("Service #%d shutdown was triggered before context cancellation. This indicates a potential race condition or premature shutdown.", s.Id())
     }
 
     if s.ShutdownErr {
         return &Error{Id: s.Id()}
     } else {
         return nil
     }
 }
v3/internal/assetserver/bundledassets/runtime.js (3)

24-28: Consider using a more secure random generator for unique IDs.

Currently, the D function utilizes Math.random() and a fixed character set to generate IDs. While this approach is typically sufficient for UI-centric tracking, it’s not cryptographically secure. If these IDs are ever used for security-related tasks, consider using a more robust generator (e.g., crypto.getRandomValues() in the browser) or verifying that the risk of collision remains acceptable for your use case.


97-102: Ensure JSON parse safety when resolving dialog results.

Functions like Be(e,n,i) parse user-facing data using JSON.parse(n) if i is truthy. If the content doesn't match a valid JSON object, the resulting error is caught immediately, but the user experience might be abruptly affected. Consider logging or more gracefully handling parse failures, e.g., returning partial data or clarifying the nature of the invalid response.


198-209: Context menu logic might conflict with editable elements.

Within wi(e), you override the default context menu unless the target is editable or certain selection conditions pass. For some rich text fields or embedded frameworks, forcibly hiding the context menu can degrade user experience. Confirm these conditions align with your product requirements and consider letting certain user actions pass through to the native menu if needed.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c85b10 and 97e8a81.

⛔ Files ignored due to path filters (1)
  • v3/internal/runtime/desktop/@wailsio/runtime/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (43)
  • docs/src/content/docs/changelog.mdx (3 hunks)
  • docs/src/content/docs/learn/bindings.mdx (1 hunks)
  • docs/src/content/docs/learn/services.mdx (4 hunks)
  • v3/internal/assetserver/assetserver.go (3 hunks)
  • v3/internal/assetserver/bundledassets/runtime.js (1 hunks)
  • v3/internal/generator/collect/service.go (2 hunks)
  • v3/internal/hash/fnv.go (1 hunks)
  • v3/internal/runtime/Taskfile.yaml (1 hunks)
  • v3/internal/runtime/desktop/@wailsio/runtime/src/calls.js (3 hunks)
  • v3/internal/runtime/desktop/@wailsio/runtime/src/dialogs.js (2 hunks)
  • v3/internal/runtime/desktop/@wailsio/runtime/src/runtime.js (3 hunks)
  • v3/internal/runtime/desktop/@wailsio/runtime/types/calls.d.ts (1 hunks)
  • v3/internal/runtime/desktop/@wailsio/runtime/types/runtime.d.ts (1 hunks)
  • v3/pkg/application/application.go (15 hunks)
  • v3/pkg/application/application_dev.go (1 hunks)
  • v3/pkg/application/application_options.go (2 hunks)
  • v3/pkg/application/bindings.go (8 hunks)
  • v3/pkg/application/bindings_test.go (2 hunks)
  • v3/pkg/application/errors.go (1 hunks)
  • v3/pkg/application/internal/tests/services/common.go (1 hunks)
  • v3/pkg/application/internal/tests/services/shutdown/shutdown_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/shutdownerror/shutdownerror_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/startup/startup_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/startuperror/startuperror_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/startupshutdown/startupshutdown_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go (1 hunks)
  • v3/pkg/application/internal/tests/utils.go (1 hunks)
  • v3/pkg/application/messageprocessor.go (8 hunks)
  • v3/pkg/application/messageprocessor_application.go (2 hunks)
  • v3/pkg/application/messageprocessor_browser.go (2 hunks)
  • v3/pkg/application/messageprocessor_call.go (3 hunks)
  • v3/pkg/application/messageprocessor_clipboard.go (2 hunks)
  • v3/pkg/application/messageprocessor_contextmenu.go (2 hunks)
  • v3/pkg/application/messageprocessor_dialog.go (6 hunks)
  • v3/pkg/application/messageprocessor_events.go (2 hunks)
  • v3/pkg/application/messageprocessor_params.go (4 hunks)
  • v3/pkg/application/messageprocessor_screens.go (2 hunks)
  • v3/pkg/application/messageprocessor_system.go (2 hunks)
  • v3/pkg/application/messageprocessor_window.go (6 hunks)
  • v3/pkg/application/services.go (3 hunks)
  • v3/pkg/application/webview_window.go (2 hunks)
  • v3/pkg/application/webview_window_darwin.go (1 hunks)
  • v3/pkg/application/window.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • v3/pkg/application/application_dev.go
🧰 Additional context used
🪛 LanguageTool
docs/src/content/docs/learn/bindings.mdx

[typographical] ~477-~477: The word “hence” is an adverb that can’t be used like a conjunction, and therefore needs to be separated from the sentence.
Context: ...Call.RuntimeError` class from the wails runtime, hence you can also test its type like this: ...

(THUS_SENTENCE)


[style] ~578-~578: As a shorter alternative for ‘able to’, consider using “can not”.
Context: ...ctions are not allowed to fail. If they are not able to process a given error and return valid ...

(BE_ABLE_TO)

docs/src/content/docs/learn/services.mdx

[uncategorized] ~123-~123: Do not mix variants of the same word (‘initialise’ and ‘initialize’) within a single text.
Context: ...mation about the service. Services are initialised in the exact order of registration: fir...

(EN_WORD_COHERENCY)


[uncategorized] ~123-~123: Possible missing comma found.
Context: ...sed in the exact order of registration: first those listed in the Services field of...

(AI_HYDRA_LEO_MISSING_COMMA)

🪛 Biome (1.9.4)
v3/internal/runtime/desktop/@wailsio/runtime/types/runtime.d.ts

[error] 16-16: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

v3/internal/assetserver/bundledassets/runtime.js

[error] 1-1: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run Go Tests (ubuntu-latest, 1.23)
  • GitHub Check: Run Go Tests (macos-latest, 1.23)
  • GitHub Check: Run Go Tests (windows-latest, 1.23)
🔇 Additional comments (112)
v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go (3)

1-14: LGTM!

The package structure and imports are well-organized and appropriate for the test functionality.


16-18: LGTM!

The TestMain function correctly initializes the test environment using the application test harness.


20-27: LGTM!

The service type definitions are clean and follow a consistent pattern, providing a good foundation for testing different scenarios.

v3/pkg/application/errors.go (5)

6-6: Addition of "strings" import.

This import is required for using the strings.Builder in the Error() method. Everything looks good and no immediate issues are evident.


9-16: Clear documentation for FatalError.

The struct's purpose and accompanying comments are clear, ensuring that developers understand the function of FatalError including how to retrieve the underlying error. This is helpful for diagnostic and reporting needs.


18-21: Internal() method logic is straightforward.

Returning the internal field directly is concise and aligns with typical error handling patterns. No changes needed.


23-27: Unwrap method confirms best practices.

Allowing error inspection with errors.As is a standard Go practice for wrapped errors. This is nicely implemented.


29-40: Robust Error() method.

Using strings.Builder for message composition is efficient, and the contextual details in the formatted output help with debugging. The fallback instructions for reporting issues are a good addition for diagnosing internal errors.

v3/internal/runtime/desktop/@wailsio/runtime/types/calls.d.ts (1)

24-31: ByID function documentation improvements.

Mirroring the same approach as ByName helps maintain consistency. Changing the return type to Promise<any> is consistent with the rest of the API.

v3/internal/assetserver/assetserver.go (1)

22-25: New service struct design.

Storing route and handler allows for an extensible approach. Ensure all service routes are validated or sanitized before they are stored.

v3/pkg/application/messageprocessor_call.go (16)

6-6: Import usage confirmed.

No issues noted with adding "errors" to the import list. This change is necessary for errors.As usage.


19-29: Check for nil pointer dereference on callID.

If callID can ever be nil, then *callID will crash. Consider validating callID before dereferencing to avoid potential runtime panics.


32-34: Simplified callback signature looks good.

Removing isJSON cleans up the function. The updated approach aligns with the new JSON-checking logic in the error and result handlers.


39-39: Consistent error message.

Using "Invalid binding call:" for uniformity is a good improvement for maintainability and debugging.


49-54: Concurrency approach for cancel function retrieval.

Locking the map before getting cancel is correct. Releasing the lock immediately and then checking cancel is a good pattern to avoid prolonged lock usage.

Also applies to: 58-58


66-66: Consistent usage of m.httpError.

The uniform usage of m.httpError strengthens the consistency and readability of error reporting.


72-72: Graceful handling of missing call-id.

Maintaining the same "Invalid binding call" pattern remains consistent with the rest of the error logic.


81-81: Error consistency for parsing call options.

Again, standardizing the error message is beneficial for tracing issues.


95-105: Ambiguous callID check is correct.

Locking the map and ensuring no duplicate callID is a safe concurrency approach.


112-112: HTTP response sent before async call.

Returning 200 (OK) early, then reporting subsequent errors through callbacks is a solid approach for asynchronous calls.


130-150: Split name vs. ID resolution logic.

The fallback logic for unresolved methods is straightforward and uses CallError consistently. Good job returning gracefully before continuing.


152-159: Deferred logging of completed call.

Deferring an info log with final arguments and results helps trace method invocations. Watch out for potential sensitive data in logs.


161-168: Structured error handling with CallError.

Differentiating ReferenceError/TypeError from RuntimeError fosters clearer error reporting to consumers.


185-185: Use of specialized callback to return success.

The callCallback approach is aligned with the updated no-isJSON signature.


187-187: Explicitly disabling cancel.

Setting cancelRequired = false ensures the deferred cancel will not trigger unnecessarily.


190-190: Catch-all for unknown method codes.

Throwing an HTTP error with the unrecognized method is helpful for debugging.

v3/internal/runtime/desktop/@wailsio/runtime/src/calls.js (5)

51-62: Robust JSON parsing in resultHandler.

Gracefully handling empty results, non-JSON results, and JSON parse errors improves reliability, ensuring the promise is resolved or rejected consistently.


137-151: RuntimeError custom class.

Defining a specialized RuntimeError helps distinguish between different error categories in the application. Good practice extending the Error class.


154-164: Centralized Call(options) logic.

Consolidating the binding call logic is a welcomed change for maintainability. The use of a promise plus an optional .cancel() is a neat approach.


195-195: Refactored ByName usage.

Delegating to Call(...) eliminates duplicate code paths and keeps all error handling in one place.

Also applies to: 198-199


210-210: Refactored ByID usage.

Like ByName, the refactor ensures consistent call cancellation and error handling logic.

Also applies to: 213-214

v3/pkg/application/bindings.go (10)

23-30: Enum-like ErrorKind definitions.

Declaring ReferenceError, TypeError, and RuntimeError clarifies error categorization across bound methods.


31-35: CallError struct and .Error() method.

Providing a structured error type is beneficial. Exposing a JSON field for Kind, Message, and optional Cause is a clean design.

Also applies to: 37-39


82-82: Bindings struct augmentation for error marshaling.

Keeping the error marshaling function at the root of Bindings is a straightforward way to unify custom and default marshaling.


88-90: Wrapping primary and fallback error marshaler.

The wrapErrorMarshaler usage ensures the fallback is invoked only if the primary yields nil. This is a robust approach.


97-105: Add(service) concurrency and collisions checked.

Detecting name collisions and hash collisions is crucial. Good job returning an explicit error for collisions.


137-137: Simplified retrieval in Get(...).

Directly returning b.boundMethods[options.MethodName] for clarity is consistent with the new naming approach.


196-205: Method reflection filtering.

Excluding known internal service methods and capturing method metadata with FQN is a proper technique. The fallback for generics is also well-handled.

Also applies to: 208-215


250-251: BoundMethod.String() implementation.

Returning the FQN is helpful for logging or debugging. Straightforward and consistent with BoundMethod usage.


255-261: Documentation clarifies multi-return error logic.

No feedback needed; these doc lines successfully detail the multi-error approach for bound methods.


262-262: Enhanced error capture within Call(...).

  1. Checking argument counts, unmarshal errors, and appended runtime errors is thorough.
  2. The approach of combining multiple returned error values into a single CallError with a Cause struct is a robust design.
  3. The fallback to collecting non-error outputs into a slice is correct for multi-valued returns.

Also applies to: 270-274, 291-295, 313-324, 327-347

v3/pkg/application/application.go (13)

15-15: Confirm Go version compatibility for slices package usage
This import relies on Go 1.21+ for the built-in slices package. Ensure that the CLI or build environment is set to a compatible version of Go.


140-141: Adopting slices.Clone for service slices
Cloning the services slice is a neat approach to prevent unintended shared references. This also requires Go 1.21+. Looks good.


293-294: Lifecycle booleans
Defining starting and running as separate booleans is a clear way to represent the application’s lifecycle states. Using the runLock ensures thread safety.


329-331: Introducing new synchronization fields
performingShutdown, shutdownLock, and serviceShutdownLock enhance clarity around shutdown concurrency. Ensure consistent use of these locks when referencing lifecycle fields.


356-356: No functional change detected
This appears to be a whitespace or formatting update.


579-587: Preventing multiple or duplicate Run calls
Locking and checking starting || running effectively prevents misuse. Error return for a repeated Run invocation is a sensible safeguard.


600-605: Defers for shutdown
Deferring shutdownServices() followed by cancel() ensures context cancellation happens first (LIFO), aligning with the comment. This ordering looks correct.


607-615: Service startup loop
Sequentially starting services and updating a.options.Services ensures partial progress is tracked. If a startup fails midway, only successfully started services remain in the slice for eventual shutdown. Nicely done.


687-710: startupService logic
Binding service methods and attaching HTTP routes in one place is clean. Consider checking for route conflicts if multiple services share the same path.


715-735: shutdownServices method
This reverse-order shutdown is consistent and ensures the most recently started service closes first. Good for proper teardown.


853-863: Deferred shutdown registration
Lock usage here to append or call the shutdown function ensures no race conditions if shutdown has started. This approach appears robust.


874-885: Lock usage in cleanup
Locking on entry and early unlock upon detecting performingShutdown is a straightforward way to prevent overlapping cleanups.


889-914: Final resource cleanup
The multi-step shutdownServices() call and closing windows, trays, plus single-instance cleanup look well-orchestrated. Good synergy with postQuit() and optional PostShutdown.

v3/internal/hash/fnv.go (1)

5-9: Removed error return is appropriate
Since standard hash implementations never return errors, removing the error return simplifies the function signature without losing functionality.

v3/pkg/application/messageprocessor_system.go (3)

4-4: Importing fmt
This import is needed for structured error creation. No issues noted.


25-26: Returning after sending an unknown method error
Immediate return after the error response stops further processing, preventing unintended behavior. This is a good pattern.


29-29: Log message adjusted
Minor log message style change (capitalization). No other concerns.

v3/pkg/application/messageprocessor_application.go (2)

32-33: LGTM! Enhanced error handling with early return.

The error handling has been improved by using fmt.Errorf for better error messages and adding an early return to prevent further execution after an error.


36-36: ⚠️ Potential issue

Fix incorrect method name in logging.

The logging statement uses applicationMethodNames[method] but the prefix is incorrectly using "Browser." instead of "Application.".

-	m.Info("Runtime call:", "method", "Application."+applicationMethodNames[method])
+	m.Info("Runtime call:", "method", "Application."+applicationMethodNames[method])

Likely an incorrect or invalid review comment.

v3/pkg/application/messageprocessor_browser.go (2)

4-9: LGTM! Improved import organization.

The imports are now properly organized with standard library imports first, followed by third-party imports.


22-22: LGTM! Enhanced error handling with consistent messaging.

The error handling has been improved with:

  • Better error wrapping using fmt.Errorf
  • Consistent "Invalid browser call:" prefix
  • Clear error messages for specific failure cases

Also applies to: 30-30, 36-36, 43-43

v3/pkg/application/messageprocessor_events.go (2)

21-30: LGTM! Enhanced error handling with better validation.

The error handling has been improved with:

  • Better error wrapping using fmt.Errorf
  • Consistent "Invalid events call:" prefix
  • Clear validation messages for missing event name

36-36: LGTM! Comprehensive logging of event details.

The logging now includes all relevant event information: method, name, sender, data, and cancellation status.

v3/pkg/application/messageprocessor_clipboard.go (1)

26-36: LGTM! Improved variable scoping and pointer handling.

The changes improve the code by:

  • Moving text declaration outside switch for better scope
  • Using pointer handling for safer text extraction
  • Adding proper nil checks before dereferencing
v3/pkg/application/messageprocessor_contextmenu.go (3)

38-38: LGTM! Enhanced error message formatting.

The error message now provides better context by wrapping the original error with additional details.


44-45: LGTM! Improved logging with detailed context.

The logging now includes comprehensive information about the context menu action, making debugging easier.


47-48: LGTM! Better error handling for unknown methods.

The error message for unknown methods is now more descriptive and consistent with the overall error handling improvements.

v3/pkg/application/messageprocessor_screens.go (3)

25-25: LGTM! Operation-specific error messages.

The error messages now clearly indicate which specific operation failed (GetScreens, GetPrimary, Window.GetScreen), improving error traceability.

Also applies to: 32-32, 39-39


44-45: LGTM! Consistent error handling for unknown methods.

The error handling for unknown methods now follows the same pattern as other message processors.


48-48: LGTM! Standardized logging format.

The logging format is now consistent with other message processors.

v3/pkg/application/internal/tests/utils.go (1)

12-14: Consider buffered channels with appropriate capacity.

The channels are buffered with size 1, which might be insufficient if multiple errors or applications need to be handled simultaneously.

Please verify if the buffer size of 1 is sufficient for your use case, especially in concurrent test scenarios.

v3/pkg/application/window.go (1)

8-8: LGTM! Improved error handling consistency.

The addition of the isJSON parameter to CallError aligns it with DialogResponse, providing consistent error handling across callback methods.

v3/pkg/application/internal/tests/services/shutdown/shutdown_test.go (1)

27-76: LGTM! Comprehensive shutdown sequence test.

The test effectively validates:

  • Concurrent service registration
  • Proper shutdown sequence
  • Atomic counter for thread-safe sequence tracking
v3/internal/runtime/desktop/@wailsio/runtime/src/runtime.js (1)

63-92: LGTM! Improved error handling and modern async patterns.

The refactoring:

  • Uses async/await for better readability
  • Simplifies error handling
  • Streamlines response processing
v3/pkg/application/internal/tests/services/startup/startup_test.go (1)

27-86: LGTM! Comprehensive startup sequence test.

The test effectively validates:

  • Service startup sequence
  • Concurrent registration
  • Various service configurations
v3/pkg/application/messageprocessor_params.go (2)

144-145: LGTM! Enhanced type safety in convertNumber.

The addition of the default case improves robustness by explicitly handling unmatched types.


175-177: LGTM! Improved type assertion safety.

The changes in Float64 and Bool methods now use safe type assertions with ok checks, preventing potential panics from failed type assertions.

Also applies to: 187-189

v3/pkg/application/bindings_test.go (4)

47-47: LGTM! Improved code clarity.

Using underscore for unused parameter name clearly indicates intent.


55-59: LGTM! Enhanced function readability.

Using named return parameter improves code readability and documentation.


67-67: LGTM! Simplified error handling in tests.

Converting error type to string type in test cases makes error comparison more straightforward and maintainable.

Also applies to: 74-74, 81-81, 82-82, 88-88, 102-102, 109-109, 116-116, 123-123, 144-144, 151-151


159-161: LGTM! Improved bindings initialization.

Using NewBindings with nil parameters and separate Add call provides better separation of concerns.

v3/pkg/application/messageprocessor_dialog.go (2)

30-31: LGTM! Simplified error logging.

The error logging is now more concise and structured.


41-41: LGTM! Improved error handling consistency.

The changes consistently use fmt.Errorf for error wrapping and provide better context in error messages.

Also applies to: 47-47, 58-58, 114-114, 119-119, 128-128, 142-142, 155-155, 165-166

v3/pkg/application/services.go (4)

31-39: LGTM! Added per-service error marshaling.

The new MarshalError field allows customization of error marshaling at the service level, with a clear fallback mechanism to the global handler.


85-94: LGTM! Improved service startup documentation.

The documentation now clearly specifies the order of service startup notifications and error handling behavior.


105-117: LGTM! Enhanced service shutdown documentation.

The documentation now clearly specifies the reverse order of shutdown notifications and error handling behavior.


122-134: LGTM! Improved service name resolution.

The getServiceName function now follows a clear precedence order:

  1. ServiceOptions.Name
  2. ServiceName interface
  3. Type name
v3/pkg/application/messageprocessor.go (3)

45-52: LGTM! Improved error handling with structured logging.

The changes enhance error handling by:

  1. Using structured logging with key-value pairs for better error context
  2. Using http.StatusUnprocessableEntity (422) which is more appropriate for indicating processing errors

65-66: LGTM! Enhanced error logging with structured format.

The error logging has been improved to use structured format with key-value pairs, making it easier to parse and analyze logs.

Also applies to: 70-71


154-155: LGTM! Consistent structured logging format.

The error logging format has been updated to maintain consistency with the structured logging pattern used throughout the file.

Also applies to: 160-161

v3/internal/runtime/desktop/@wailsio/runtime/src/dialogs.js (2)

138-138: LGTM! Improved race condition handling.

Moving the deletion of the dialog response before promise resolution prevents potential race conditions.


159-159: LGTM! Enhanced error handling with Error objects.

Using Error objects instead of strings for rejections is a better practice as it:

  1. Provides stack traces for better debugging
  2. Aligns with JavaScript error handling patterns
v3/pkg/application/application_options.go (3)

34-42: LGTM! Added custom error marshaling support.

The MarshalError function provides a flexible way to customize error marshaling for service methods, with clear documentation on its behavior and requirements.


48-48: LGTM! Fixed typo in comment.

Fixed grammatical error in the Logger field comment.


76-82: LGTM! Added post-shutdown hook.

The PostShutdown function provides a useful hook for executing custom code after shutdown, particularly beneficial for testing and logging on platforms where Run() doesn't return.

v3/internal/generator/collect/service.go (2)

223-223: LGTM! Simplified ID calculation.

Removed unused error return from hash.Fnv call, making the code more concise.


235-235: LGTM! Improved method ID handling.

Added methodIdFound boolean to prevent overwriting a custom method ID if it was already set through a directive.

Also applies to: 268-268

v3/pkg/application/messageprocessor_window.go (2)

114-114: Improved error message formatting.

The error message formatting has been enhanced to provide better context and structure using fmt.Errorf for error wrapping.


149-150: Consistent error message formatting across window operations.

The error messages have been standardized to follow the format "Invalid window call:" followed by a specific error description. This improves error clarity and maintainability.

Also applies to: 201-202, 206-207, 214-215, 222-223, 227-228, 232-233, 237-238, 250-251, 258-259, 263-264, 271-272, 276-277, 284-285, 289-290, 305-306, 310-311, 318-319, 326-327, 374-375

v3/pkg/application/webview_window.go (2)

303-307: Enhanced error handling with JSON format support.

The CallError method now includes an isJSON parameter to properly handle JSON-formatted error results.


321-325: Consistent JSON format handling in dialog responses.

The DialogResponse method now includes an isJSON parameter to maintain consistency with the error handling improvements.

v3/pkg/application/webview_window_darwin.go (1)

1041-1047: Improved thread safety during shutdown.

The changes enhance thread safety by:

  1. Properly locking access to the shutdown state
  2. Using a local variable to store the state
  3. Early return if shutdown is in progress
v3/internal/runtime/Taskfile.yaml (1)

15-15: Standardized task execution directories.

The changes ensure consistent task execution by specifying the dir attribute for both test and update tasks, aligning them with the existing install-deps task configuration.

Also applies to: 20-21

docs/src/content/docs/learn/services.mdx (2)

42-46: Well-documented service registration process!

The documentation clearly explains both the initial service registration process and the new late binding registration feature. The examples effectively demonstrate the usage patterns.

Also applies to: 73-91


120-126: Clear documentation of service lifecycle!

The documentation effectively explains:

  • Service initialization order
  • Context lifecycle management
  • Shutdown sequence

Also applies to: 136-138

🧰 Tools
🪛 LanguageTool

[uncategorized] ~123-~123: Do not mix variants of the same word (‘initialise’ and ‘initialize’) within a single text.
Context: ...mation about the service. Services are initialised in the exact order of registration: fir...

(EN_WORD_COHERENCY)


[uncategorized] ~123-~123: Possible missing comma found.
Context: ...sed in the exact order of registration: first those listed in the Services field of...

(AI_HYDRA_LEO_MISSING_COMMA)

docs/src/content/docs/learn/bindings.mdx (1)

452-596: Excellent error handling documentation!

The new section comprehensively covers:

  • Error marshaling and customization
  • Promise rejection scenarios
  • Practical examples of error handling
  • Type error handling and troubleshooting
🧰 Tools
🪛 LanguageTool

[typographical] ~477-~477: The word “hence” is an adverb that can’t be used like a conjunction, and therefore needs to be separated from the sentence.
Context: ...Call.RuntimeError` class from the wails runtime, hence you can also test its type like this: ...

(THUS_SENTENCE)


[style] ~578-~578: As a shorter alternative for ‘able to’, consider using “can not”.
Context: ...ctions are not allowed to fail. If they are not able to process a given error and return valid ...

(BE_ABLE_TO)

docs/src/content/docs/changelog.mdx (1)

55-60: Well-structured changelog entries!

The entries effectively document:

  • New features (PostShutdown, FatalError, etc.)
  • Critical fixes for error handling and service shutdown
  • Breaking changes in service startup behavior

Also applies to: 86-87, 101-103

v3/internal/assetserver/bundledassets/runtime.js (4)

33-53: Validate handling of fetch errors and non-JSON responses.

Inside Ae(e,n,i,o), the code checks if the Content-Type header contains "application/json" before attempting to parse. This is good defensive coding, but consider explicitly handling edge cases:

  • Non-JSON but valid textual content,
  • Responses missing Content-Type or incorrectly labeled.
    Also, ensure that upstream callers can gracefully handle any new or more specific error conditions you might raise here.

166-210: Verify Window method coverage for all platforms.

The N class provides a comprehensive set of window-manipulation methods (e.g., Maximise(), Minimise(), SetPosition(), etc.). Ensure each method is fully tested and behaves consistently across Windows, macOS, and Linux. Differences in windowing behavior on each platform can cause unexpected results, especially with size constraints or fullscreen toggles.


343-364: Confirm drag behavior for nested draggable elements.

mi(e) and the related event handlers track a single boolean (S) to manage drag state. In nested or complex layouts, multiple elements could be marked with --wails-draggable. Ensure that capturing and stopping the drag event for one element won’t incorrectly propagate to others. You may want to handle multi-element drag more granularly if your UI evolves to have nested draggable regions.


198-198: Switch statement declaration false-positive from static analysis.

The static analysis hint suggests wrapping a declaration in a block to prevent access by other switch clauses. However, in wi(e), there are no new variable declarations within the switch. Therefore, this is likely a false positive. No immediate action is required unless you plan to introduce scoping for variable declarations in the switch body.

Comment on lines +132 to 134
func (a *AssetServer) AttachServiceHandler(route string, handler http.Handler) {
a.services = append(a.services, service{route, handler})
}
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

AttachServiceHandler usage.

This method is straightforward and aligns with the shift to slice-based services. Just ensure route collisions or duplicates won't cause unintended behavior.

Check for any unit tests verifying route collision scenarios:


🏁 Script executed:

#!/bin/bash
# Searching for tests referencing AttachServiceHandler usage or route collisions
rg "AttachServiceHandler" -A 5

Length of output: 1353


Action Required: Add Unit Tests for Route Collision Scenarios

The implementation of AttachServiceHandler is straightforward and correctly aligns with the shift to slice-based storage. However, no unit tests were found addressing potential route collisions or duplicate route registrations, which may lead to unintended behavior when duplicate routes are appended.

  • File Affected: v3/internal/assetserver/assetserver.go (lines 132–134)
  • Suggestion: Please add unit tests that simulate duplicate route registrations to ensure that route collisions are either handled gracefully or prevented.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old behaviour was to have a map with routes as keys and do a linear scan of the map. This was worse than a slice for two reasons: first, route collisions would result in silent substitutions, and second the iteration order was non-deterministic.

The slice approach is not the best possible, but enforces a clear rule: priority goes to the first in registration order.

We might want to implement an actual trie of path segments here and warn about duplicates @leaanthony
A much quicker and easier solution might be a http.ServeMux, but we'll have to work around its redirect behaviour or at least emit errors in case of redirects for the moment being.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with this for now 👍

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@fbbdev
Copy link
Collaborator Author

fbbdev commented Feb 13, 2025

It seems the new tests are failing randomly on ubuntu with a message about not being able to open a display (some of them pass though). Will have to spin up a vm and check tomorrow.

@fbbdev
Copy link
Collaborator Author

fbbdev commented Feb 13, 2025

Perhaps adding this (or something similar) to the pipelines

https://github.com/marketplace/actions/setup-xvfb

might be enough to solve the issue?

@fbbdev fbbdev changed the title [v3] Late binding registration and error handling overhaul [v3] Late service registration and error handling overhaul Feb 13, 2025
leaanthony
leaanthony previously approved these changes Feb 16, 2025
Comment on lines +132 to 134
func (a *AssetServer) AttachServiceHandler(route string, handler http.Handler) {
a.services = append(a.services, service{route, handler})
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with this for now 👍

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
v3/pkg/application/single_instance_linux.go (1)

55-64: ⚠️ Potential issue

Fix potential race condition in error handling.

The err variable is captured by the closure in setup.Do but modified outside the closure, which could lead to a race condition.

Apply this fix:

 setup.Do(func() {
     f := dbusHandler(func(message string) {
         secondInstanceBuffer <- message
     })

-    err = conn.Export(f, dbus.ObjectPath(l.dbusPath), l.dbusName)
+    var exportErr error
+    exportErr = conn.Export(f, dbus.ObjectPath(l.dbusPath), l.dbusName)
+    if exportErr != nil {
+        err = exportErr
+    }
 })
🧹 Nitpick comments (5)
v3/pkg/application/popupmenu_windows.go (1)

135-141: Consider more graceful error handling for menu operations.

Currently, menu-related errors trigger globalApplication.fatal, which terminates the application. Consider returning errors instead, allowing the caller to decide how to handle menu failures. This would align better with the PR's objective of improved error handling.

Example approach:

-			globalApplication.fatal("error adding menu item '%s'", menuText)
+			return fmt.Errorf("error adding menu item '%s'", menuText)
-				globalApplication.fatal("error setting menu icons: %w", err)
+				return fmt.Errorf("error setting menu icons: %w", err)

This would require updating the buildMenu function signature to:

func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) error

And handling the error in the caller (Update method).

v3/pkg/application/single_instance_windows.go (1)

81-115: Add documentation for the createEventTargetWindow function.

This function creates a hidden message-only window for handling single instance notifications. Consider adding documentation to explain:

  • The purpose of the window
  • The significance of the window class registration
  • The meaning of the window style flags
v3/pkg/application/application.go (3)

365-382: Add nil check for service parameter.

While the implementation is good with proper locking and timing validation, consider adding a nil check for the service parameter to prevent potential panics.

 func (a *App) RegisterService(service Service) {
+    if service.instance == nil {
+        a.error("cannot register nil service")
+        return
+    }
     a.runLock.Lock()
     defer a.runLock.Unlock()

608-618: Add debug logs for service startup progress.

Consider adding debug logs to track service startup progress, which would help with troubleshooting in production environments.

 services := a.options.Services
 a.options.Services = nil
+a.debug("Starting %d services...", len(services))
 for i, service := range services {
+    a.debug("Starting service %d/%d: %s", i+1, len(services), getServiceName(service))
     if err := a.startupService(service); err != nil {
         return fmt.Errorf("error starting service '%s': %w", getServiceName(service), err)
     }
     // Schedule started services for shutdown.
     a.options.Services = services[:i+1]
 }
+a.debug("All services started successfully")

718-738: Consider aggregating shutdown errors.

The current implementation logs shutdown errors individually. Consider collecting all errors into a single error type to provide a comprehensive view of shutdown failures.

 func (a *App) shutdownServices() {
     a.serviceShutdownLock.Lock()
     defer a.serviceShutdownLock.Unlock()
 
     a.cancel()
 
+    var shutdownErrors []error
     for len(a.options.Services) > 0 {
         last := len(a.options.Services) - 1
         service := a.options.Services[last]
         a.options.Services = a.options.Services[:last]
 
         if s, ok := service.instance.(ServiceShutdown); ok {
             a.debug("Shutting down service:", "name", getServiceName(service))
             if err := s.ServiceShutdown(); err != nil {
-                a.error("Error shutting down service '%s': %w", getServiceName(service), err)
+                shutdownErrors = append(shutdownErrors, fmt.Errorf("error shutting down service '%s': %w", getServiceName(service), err))
             }
         }
     }
+    if len(shutdownErrors) > 0 {
+        for _, err := range shutdownErrors {
+            a.error(err.Error())
+        }
+    }
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4702a2b and 112dcc8.

📒 Files selected for processing (26)
  • .github/workflows/build-and-test-v3.yml (1 hunks)
  • docs/src/content/docs/changelog.mdx (3 hunks)
  • v3/pkg/application/application.go (17 hunks)
  • v3/pkg/application/application_darwin.go (1 hunks)
  • v3/pkg/application/application_linux.go (1 hunks)
  • v3/pkg/application/application_windows.go (4 hunks)
  • v3/pkg/application/dialogs_windows.go (3 hunks)
  • v3/pkg/application/linux_cgo.go (1 hunks)
  • v3/pkg/application/menuitem.go (2 hunks)
  • v3/pkg/application/menuitem_windows.go (2 hunks)
  • v3/pkg/application/panic_handler.go (2 hunks)
  • v3/pkg/application/popupmenu_windows.go (1 hunks)
  • v3/pkg/application/screen_windows.go (2 hunks)
  • v3/pkg/application/screenmanager.go (3 hunks)
  • v3/pkg/application/single_instance_linux.go (3 hunks)
  • v3/pkg/application/single_instance_windows.go (2 hunks)
  • v3/pkg/application/systemtray.go (2 hunks)
  • v3/pkg/application/systemtray_darwin.go (2 hunks)
  • v3/pkg/application/systemtray_linux.go (12 hunks)
  • v3/pkg/application/systemtray_windows.go (3 hunks)
  • v3/pkg/application/webview_window.go (7 hunks)
  • v3/pkg/application/webview_window_darwin.go (3 hunks)
  • v3/pkg/application/webview_window_linux.go (2 hunks)
  • v3/pkg/application/webview_window_windows.go (22 hunks)
  • v3/pkg/application/webview_window_windows_devtools.go (1 hunks)
  • v3/pkg/application/webview_window_windows_production.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • v3/pkg/application/linux_cgo.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • v3/pkg/application/webview_window_darwin.go
  • v3/pkg/application/webview_window.go
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run Go Tests (ubuntu-latest, 1.23)
  • GitHub Check: Run Go Tests (macos-latest, 1.23)
  • GitHub Check: Run Go Tests (windows-latest, 1.23)
🔇 Additional comments (42)
v3/pkg/application/popupmenu_windows.go (2)

135-135: LGTM! Improved error message formatting.

The error message is now more concise and readable with the menu text clearly delineated using single quotes.


140-140: LGTM! Enhanced error handling with error wrapping.

The use of %w verb enables error wrapping, preserving the error chain and making it easier to trace the root cause of menu icon setting failures.

v3/pkg/application/systemtray_linux.go (2)

11-13: LGTM! Import changes are appropriate.

The addition of the os package is necessary for getting the process ID to create a unique DBus name.


182-183: LGTM! Error handling improvements are well-implemented.

The changes consistently improve error handling across the file by:

  1. Using %w for error wrapping (instead of %v)
  2. Maintaining consistent error message prefixes
  3. Providing descriptive error messages

These improvements align with the PR's objective of overhauling error handling.

Also applies to: 191-192, 274-275, 279-280, 284-285, 291-292, 296-297, 301-302, 319-320, 333-334, 348-349, 392-393, 406-407, 449-450, 461-462, 595-596

v3/pkg/application/webview_window_windows_production.go (4)

1-3: LGTM! Build constraints are appropriate.

The build constraints correctly limit this implementation to Windows production builds without devtools, which aligns with the security best practice of disabling developer tools in production.


5-5: LGTM! Import is appropriate.

The import of the WebView2 edge package is necessary for Windows-specific WebView functionality.


7-7: LGTM! Empty implementation is appropriate.

The empty implementation of openDevTools is correct for production builds where developer tools should be disabled.


9-14:

✅ Verification successful

LGTM! Error handling improvement aligns with PR objectives.

The change from fatal(err.Error()) to handleFatalError(err) is part of the broader error handling overhaul, preserving error context for better diagnostics while maintaining the fatal nature of the error.

Let's verify this is part of a consistent pattern across the codebase:


🏁 Script executed:

#!/bin/bash
# Description: Verify the consistent use of handleFatalError across the codebase
# Expect: Multiple occurrences showing a pattern of migration from fatal to handleFatalError

# Search for both old and new error handling patterns
echo "=== Old pattern (fatal) ==="
rg "fatal\(err\.Error\(\)\)" ./v3/pkg/application/

echo -e "\n=== New pattern (handleFatalError) ==="
rg "handleFatalError\(err\)" ./v3/pkg/application/

Length of output: 2721


Verification complete: consistent error handling implementation

The change from using fatal(err.Error()) to handleFatalError(err) has been verified as a consistent update across the codebase. The shell script confirms that the legacy pattern was fully replaced and that the new error handling approach is applied in multiple files (e.g., in the Windows, Linux, and Darwin specific implementations). This update preserves error context and aligns well with the overall error handling overhaul.

  • Verified that there are no occurrences of fatal(err.Error()) in the relevant directory.
  • Confirmed consistent usage of handleFatalError(err) across different modules.

LGTM!

v3/pkg/application/webview_window_windows_devtools.go (1)

14-14:

✅ Verification successful

LGTM! Improved error handling.

The change to use handleFatalError aligns with the PR's error handling improvements, providing better error context and management.

Let's verify consistent error handling across other platform-specific implementations:


🏁 Script executed:

#!/bin/bash
# Search for platform-specific devtools implementations
rg -g "webview_window_*_devtools.go" "fatal|handleFatalError" --type go

Length of output: 169


LGTM! The updated call to handleFatalError clearly improves error handling in the Windows-specific devtools implementation. Our verification confirms that this change is the sole occurrence (as expected for Windows), and there aren’t any discrepancies in error handling across platform-specific files. Please ensure that similar improvements are applied if new platform implementations are added in the future.

v3/pkg/application/panic_handler.go (2)

76-79: LGTM! More idiomatic error handling.

The simplified error type assertion is cleaner and more idiomatic Go code.


103-103: LGTM! Proper error wrapping.

Using %w verb ensures proper error wrapping, maintaining the error chain for better debugging.

v3/pkg/application/single_instance_linux.go (1)

5-13: LGTM! Cleaner imports.

Simplified imports by using the more appropriate errors package for error creation.

v3/pkg/application/screen_windows.go (1)

83-83: LGTM! Simplified error creation.

Using errors.New for static error messages is more idiomatic than fmt.Errorf.

v3/pkg/application/single_instance_windows.go (2)

36-37: LGTM! Improved error handling.

The change from fmt.Errorf to errors.New is appropriate for static error messages and aligns with the PR's error handling improvements.


72-74: LGTM! Consistent error handling.

The use of errors.New maintains consistency with the PR's error handling improvements.

v3/pkg/application/menuitem_windows.go (1)

124-126: LGTM! Enhanced error wrapping.

The use of %w verb for error formatting enables proper error wrapping, improving error tracing capabilities.

v3/pkg/application/dialogs_windows.go (2)

44-46: LGTM! Improved fatal error handling.

The change to handleFatalError aligns with the PR's error handling improvements, likely providing better error context and handling.


244-246: LGTM! Enhanced error wrapping.

The use of %w verb for error formatting enables proper error wrapping, improving error tracing capabilities.

v3/pkg/application/application_linux.go (1)

210-213: LGTM! Enhanced error wrapping.

The use of %w verb for error formatting enables proper error wrapping, improving error tracing capabilities.

v3/pkg/application/systemtray_darwin.go (2)

33-33: LGTM! Import of errors package.

The addition of the errors package aligns with the PR's objective to standardize error handling across the codebase.


128-128: LGTM! Simplified error creation.

The change from fmt.Errorf to errors.New is appropriate here as the error message is a static string that doesn't require formatting.

v3/pkg/application/systemtray.go (2)

4-4: LGTM! Import of errors package.

The addition of the errors package aligns with the PR's objective to standardize error handling across the codebase.


126-126: LGTM! Simplified error creation.

The change from fmt.Errorf to errors.New is appropriate here as the error message is a static string that doesn't require formatting.

v3/pkg/application/menuitem.go (2)

227-227: LGTM! Improved error message formatting.

Using %v for the role value provides a clear and readable error message.


278-278: LGTM! Enhanced error wrapping.

Using %w for error wrapping allows the original error to be properly propagated and unwrapped by error handling code.

v3/pkg/application/systemtray_windows.go (3)

6-12: LGTM! Import organization and addition of errors package.

The imports are well-organized, and the addition of the errors package aligns with the PR's objective to standardize error handling across the codebase.


124-124: LGTM! Simplified error creation.

The change from fmt.Errorf to errors.New is appropriate here as the error message is a static string that doesn't require formatting.


190-190: LGTM! Enhanced error wrapping.

Using %w for error wrapping allows the original error to be properly propagated and unwrapped by error handling code.

v3/pkg/application/application.go (2)

107-118: LGTM! Error wrapping improvements.

The use of fmt.Errorf with %w verb for error wrapping is a good practice as it preserves the error chain and provides better context for debugging.


140-142: LGTM! Improved service initialization.

Good improvements:

  1. Using slices.Clone prevents external modifications to the services slice
  2. Adding error marshaling support through MarshalError enhances error handling
v3/pkg/application/webview_window_windows.go (1)

186-196: LGTM! Simplified error creation.

Good change to use errors.New for static error messages instead of fmt.Errorf. This is more efficient and clearer for messages that don't need formatting.

.github/workflows/build-and-test-v3.yml (1)

71-74: LGTM! Improved test reliability with virtual display.

Good addition of setup-xvfb action to provide a virtual display for tests on non-macOS environments. This should resolve the reported Ubuntu test failures.

docs/src/content/docs/changelog.mdx (1)

57-62: LGTM! Comprehensive changelog entries.

The changelog entries are well-written, providing clear descriptions of all major changes and proper attribution to contributors.

v3/pkg/application/application_darwin.go (1)

365-365: LGTM! Improved error wrapping.

The change from err.Error() to %w verb enhances error handling by preserving the original error context, which enables proper error unwrapping and inspection using errors.Is/As.

v3/pkg/application/application_windows.go (4)

6-6: LGTM! Added errors package import.

The import aligns with the move towards using errors.New for error creation.


218-218: LGTM! Improved error handling.

Using handleError provides better error handling structure and aligns with the PR's error handling improvements.


321-321: LGTM! Simplified error creation.

Using errors.New instead of fmt.Errorf is appropriate here as no formatting is needed.


328-328: LGTM! Enhanced error handling structure.

The changes properly distinguish between regular and fatal errors using handleError and handleFatalError respectively.

Also applies to: 340-340

v3/pkg/application/webview_window_linux.go (2)

327-327: LGTM! Enhanced error handling.

Using handleFatalError provides better error handling structure and aligns with the PR's error handling improvements.


383-383: LGTM! Improved error wrapping.

The change to use %w verb enhances error handling by preserving the original error context.

v3/pkg/application/screenmanager.go (2)

4-4: LGTM! Added errors package import.

The import aligns with the move towards using errors.New for error creation.


366-366: LGTM! Simplified error creation.

Using errors.New instead of fmt.Errorf is appropriate here as no formatting is needed. This change:

  • Simplifies the code
  • Improves readability
  • Reduces unnecessary string formatting

Also applies to: 400-400, 402-402

@fbbdev
Copy link
Collaborator Author

fbbdev commented Feb 16, 2025

Updated with

  • virtual display for ubuntu testing pipelines
  • fixed error formatting everywhere

⚠️ COMMIT 33d0004 CHANGES A GITHUB ACTIONS WORKFLOW, INSPECT CAREFULLY ⚠️

Pipeline run in my repo: https://github.com/fbbdev/wails/actions/runs/13355679249

@fbbdev
Copy link
Collaborator Author

fbbdev commented Feb 16, 2025

Formatting errors completely fixed.

Ubuntu tests are randomly timing out waiting for some desktop service 😩

Looking for a solution.

@fbbdev
Copy link
Collaborator Author

fbbdev commented Feb 17, 2025

All lights green at last 🥳
https://github.com/fbbdev/wails/actions/runs/13371757132

This is ready to merge.

Had to fix wails3 init as well, a change in Go 1.24 broke dev mode detection hence template tests were not passing:

The go build command now sets the main module’s version in the compiled binary based on the version control system tag and/or commit. A +dirty suffix will be appended if there are uncommitted changes.

https://go.dev/doc/go1.24#go-command
Dev mode detection previously relied on the main module's version being (devel).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
v3/internal/commands/update_cli.go (1)

166-171: ⚠️ Potential issue

Fix redundant command execution in error handling.

There's a critical issue in the error handling:

  1. CombinedOutput() already executes the command
  2. The subsequent cmd.Run() is redundant and will fail
  3. This leads to misleading error messages

Apply this fix:

-	sout, serr := cmd.CombinedOutput()
-	if err := cmd.Run(); err != nil {
+	output, err := cmd.CombinedOutput()
+	if err != nil {
 		pterm.Println("Failed.")
-		pterm.Error.Println(string(sout) + "\n" + serr.Error())
+		pterm.Error.Println(string(output))
 		return err
 	}
🧹 Nitpick comments (16)
v3/pkg/application/internal/tests/services/startupshutdown/startupshutdown_test.go (3)

18-25: Add documentation for service types.

Consider adding documentation to explain why there are 6 distinct service types and how they differ in their test scenarios. This would help other developers understand the test coverage better.


27-81: Consider using subtests for better organization.

The test covers multiple scenarios (immediate registration, concurrent registration, and post-wait registration). Consider breaking these into subtests using t.Run() for better organization and clearer failure reporting.

Example refactor:

-func TestServiceStartupShutdown(t *testing.T) {
+func TestServiceStartupShutdown(t *testing.T) {
+    t.Run("ImmediateRegistration", func(t *testing.T) {
+        // Test services[0:3]
+    })
+    
+    t.Run("ConcurrentRegistration", func(t *testing.T) {
+        // Test services[3:5]
+    })
+    
+    t.Run("PostWaitRegistration", func(t *testing.T) {
+        // Test services[5]
+    })
+}

83-102: Add safety checks and documentation for sequence validation.

The validation logic could be improved in two ways:

  1. Add safety checks for type assertions
  2. Document the sequence number logic

Example improvement:

 func validate(t *testing.T, svc application.Service, bound int64) {
+    // Validate interface implementation
+    svcInstance, ok := svc.Instance().(interface {
+        Id() int
+        StartupSeq() int64
+        ShutdownSeq() int64
+    })
+    if !ok {
+        t.Fatalf("Service instance does not implement required methods")
+        return
+    }
+
+    // Get service details
+    id := svcInstance.Id()
+    startup := svcInstance.StartupSeq()
+    shutdown := svcInstance.ShutdownSeq()

Also, consider adding a comment explaining the sequence number logic:

// Sequence numbers follow these rules:
// 1. Startup sequences increment from 1 to N
// 2. Shutdown sequences are calculated as (bound - startup)
// This ensures shutdown occurs in reverse order of startup
v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go (9)

20-27: Add documentation for service types.

Consider adding documentation to explain the purpose of each service type and how they differ in their test scenarios.

 type (
+    // Service1 represents a service that successfully starts up and shuts down
     Service1 struct{ svctest.StartupShutdowner }
+    // Service2 represents a service that fails during shutdown
     Service2 struct{ svctest.StartupShutdowner }
+    // Service3 represents a service that successfully starts up and shuts down
     Service3 struct{ svctest.StartupShutdowner }
+    // Service4 represents a service that fails during both startup and shutdown
     Service4 struct{ svctest.StartupShutdowner }
+    // Service5 represents a service that fails during both startup and shutdown
     Service5 struct{ svctest.StartupShutdowner }
+    // Service6 represents a service that fails during both startup and shutdown
     Service6 struct{ svctest.StartupShutdowner }
 )

29-43: Document test scenarios and expected behaviors.

Consider adding a test description that explains:

  1. The purpose of the sequence counter
  2. The different service configurations and their expected behaviors
  3. The expected shutdown error count
+// TestServiceStartupShutdownError validates service lifecycle management by:
+// - Testing successful startup and shutdown (Service1, Service3)
+// - Testing shutdown errors (Service2)
+// - Testing startup and shutdown errors (Service4, Service5, Service6)
+// - Validating concurrent service registration
+// - Ensuring proper error propagation and handling
 func TestServiceStartupShutdownError(t *testing.T) {

44-68: Enhance error messages for better debugging.

The error messages could be more descriptive to aid in debugging test failures.

-            t.Errorf("Late or duplicate shutdown error for service #%d", mock.Id)
+            t.Errorf("Service #%d shutdown error occurred after expected sequence (expected at position %d, got %d)", mock.Id, cut, i)
-            t.Errorf("Early shutdown error for service #%d", mock.Id)
+            t.Errorf("Service #%d shutdown error occurred before expected sequence (expected after position %d, got %d)", mock.Id, cut, i)
-            t.Errorf("Unexpected shutdown error for service #%d", mock.Id)
+            t.Errorf("Service #%d generated unexpected shutdown error (not in expected list: %v)", mock.Id, expectedShutdownErrors)

70-82: Consider additional concurrent registration test cases.

The current test covers basic concurrent registration. Consider adding test cases for:

  1. Race conditions during registration
  2. Multiple concurrent registrations (>2)
  3. Registration during startup/shutdown

84-87: Clarify application start error message.

The current error message doesn't explain why application start is unexpected.

-        t.Errorf("Application started")
+        t.Errorf("Application unexpectedly started despite service startup errors")

89-106: Enhance error validation messages.

The error messages could be more descriptive to aid in debugging.

-        t.Fatal("Wanted error for service #3 or #4, got none")
+        t.Fatal("Expected startup error from either Service4 (#3) or Service5 (#4), but no error was returned")
-        t.Errorf("Wanted error for service #3 or #4, got #%d", mock.Id)
+        t.Errorf("Expected startup error from either Service4 (#3) or Service5 (#4), but got error from service #%d", mock.Id)
-        t.Errorf("Wrong shutdown error count: wanted %d, got %d", len(expectedShutdownErrors), ec)
+        t.Errorf("Incorrect number of shutdown errors: expected %d (from service #%v), got %d", len(expectedShutdownErrors), expectedShutdownErrors, ec)

108-110: Clarify sequence validation.

The magic numbers (4+3) should be explained or calculated from the test configuration.

-    if count := seq.Load(); count != 4+3 {
-        t.Errorf("Wrong startup+shutdown call count: wanted %d+%d, got %d", 4, 3, count)
+    expectedStartups := 4  // Services 1-4 attempt startup
+    expectedShutdowns := 3 // Services 1-3 complete shutdown
+    if count := seq.Load(); count != expectedStartups+expectedShutdowns {
+        t.Errorf("Incorrect startup+shutdown call count: expected %d startups + %d shutdowns = %d, got %d",
+            expectedStartups, expectedShutdowns, expectedStartups+expectedShutdowns, count)

112-118: Document expected service states.

Add comments to explain the expected state of each service.

+    // Validate services that should complete both startup and shutdown
     validate(t, services[0], true, true)   // Service1: successful startup and shutdown
     validate(t, services[1], true, true)   // Service2: successful startup and shutdown
     validate(t, services[2], true, true)   // Service3: successful startup and shutdown
+    // Validate services that should fail during startup
     validate(t, services[3], mock.Id == 3, false)  // Service4: startup may fail
     validate(t, services[4], mock.Id == 4, false)  // Service5: startup may fail
     validate(t, services[5], false, false)         // Service6: should not start

120-140: Improve type safety in validation function.

Consider defining an interface for the service instance to avoid type assertions.

+// ServiceValidator interface defines the methods required for service validation
+type ServiceValidator interface {
+    Id() int
+    StartupSeq() int64
+    ShutdownSeq() int64
+}

 func validate(t *testing.T, svc application.Service, startup bool, shutdown bool) {
-    id := svc.Instance().(interface{ Id() int }).Id()
-    startupSeq := svc.Instance().(interface{ StartupSeq() int64 }).StartupSeq()
-    shutdownSeq := svc.Instance().(interface{ ShutdownSeq() int64 }).ShutdownSeq()
+    validator, ok := svc.Instance().(ServiceValidator)
+    if !ok {
+        t.Fatalf("Service instance does not implement ServiceValidator interface")
+    }
+    id := validator.Id()
+    startupSeq := validator.StartupSeq()
+    shutdownSeq := validator.ShutdownSeq()
v3/pkg/application/internal/tests/services/startup/startup_test.go (1)

27-86: Consider enhancing test readability and coverage.

The test effectively validates both sequential and concurrent service registration. However, consider these improvements:

  1. Convert to table-driven tests for different service configurations.
  2. Add comments explaining the expected startup order and why certain sequence numbers are expected.
  3. Consider testing edge cases like registration after app start.

Here's an example of how to structure the table-driven tests:

func TestServiceStartup(t *testing.T) {
    tests := []struct {
        name            string
        initialServices []int      // indices of services to register at creation
        concurrentRegs  [][]int    // groups of indices to register concurrently
        finalRegs       []int      // indices of services to register sequentially
        expectedOrder   []int      // expected service startup order
    }{
        {
            name:            "current test case",
            initialServices: []int{0, 1, 2},
            concurrentRegs:  [][]int{{3, 4}},
            finalRegs:       []int{5},
            expectedOrder:   []int{0, 1, 2, 3, 4, 5},
        },
        // Add more test cases here
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // Test implementation
        })
    }
}
v3/pkg/application/internal/tests/utils.go (3)

12-14: Add documentation for global channels.

Please add comments explaining the purpose of each channel to improve code maintainability:

+// appChan is used to pass the application instance from test to main goroutine
 var appChan chan *application.App = make(chan *application.App, 1)
+// errChan is used to communicate non-fatal errors during application execution
 var errChan chan error = make(chan error, 1)
+// endChan is used to signal application termination with optional fatal error
 var endChan chan error = make(chan error, 1)

67-74: Improve error handling and cleanup in Main function.

The function could benefit from better error handling and cleanup:

  1. Consider logging errors from app.Run().
  2. The infinite select{} block could make cleanup difficult.
 func Main(m *testing.M) {
+	var exitCode int
 	go func() {
-		os.Exit(m.Run())
+		exitCode = m.Run()
+		// Signal test completion
+		close(appChan)
 	}()
 
-	errChan <- (<-appChan).Run()
-	select {} // Block forever
+	app := <-appChan
+	if app == nil {
+		// Tests completed before app was created
+		os.Exit(exitCode)
+	}
+
+	if err := app.Run(); err != nil {
+		// Log error and ensure non-zero exit
+		fmt.Fprintf(os.Stderr, "Application error: %v\n", err)
+		if exitCode == 0 {
+			exitCode = 1
+		}
+	}
+	os.Exit(exitCode)
 }

18-52:

❓ Verification inconclusive

Document the function and consider alternatives to infinite blocks.

The function needs documentation and has potential issues with infinite blocks:

  1. Add function documentation to explain its purpose and behavior.
  2. The infinite select{} blocks at lines 34 and 48 could make cleanup difficult if the application needs to be forcefully terminated.
+// New creates a new application instance or returns an existing one.
+// It wraps the provided options to handle fatal errors and shutdown events
+// in a test-friendly way.
 func New(t *testing.T, options application.Options) *application.App {

Consider using a more graceful approach to blocking:

-			select {} // Block forever
+			// Use a channel to block until test completion
+			<-t.Context().Done()

Let's verify if the test context is available in the current version:


🏁 Script executed:

#!/bin/bash
# Check if testing.T has Context() method in the current Go version
go doc testing.T | grep -A 2 "Context"

Length of output: 38


Documentation and Infinite Block Handling – Manual Verification Needed

Please add documentation explaining the purpose and behavior of this function and consider a more graceful approach to infinite blocking. For example:

  • Add a comment header explaining that the function retrieves or creates an application instance and customizes error handling and shutdown behavior.
  • Replace the infinite select {} blocks with a mechanism that can gracefully terminate. One suggestion is using a channel to block until test completion (e.g., <-t.Context().Done()). However, note that our check for testing.T.Context() returned no output, so its availability in your environment is unclear. Please manually verify that testing.T supports the Context() method in your current Go version before adopting this change.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5f90d0 and 4a23b94.

⛔ Files ignored due to path filters (2)
  • v3/examples/dev/go.sum is excluded by !**/*.sum
  • v3/examples/file-association/go.sum is excluded by !**/*.sum
📒 Files selected for processing (14)
  • .github/workflows/build-and-test-v3.yml (5 hunks)
  • v3/examples/dev/go.mod (2 hunks)
  • v3/examples/file-association/go.mod (2 hunks)
  • v3/internal/buildinfo/buildinfo.go (2 hunks)
  • v3/internal/commands/update_cli.go (2 hunks)
  • v3/internal/templates/_common/go.mod.tmpl (1 hunks)
  • v3/internal/templates/_common/go.sum.tmpl (1 hunks)
  • v3/pkg/application/internal/tests/services/shutdown/shutdown_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/shutdownerror/shutdownerror_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/startup/startup_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/startuperror/startuperror_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/startupshutdown/startupshutdown_test.go (1 hunks)
  • v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go (1 hunks)
  • v3/pkg/application/internal/tests/utils.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • .github/workflows/build-and-test-v3.yml
  • v3/pkg/application/internal/tests/services/shutdown/shutdown_test.go
  • v3/pkg/application/internal/tests/services/shutdownerror/shutdownerror_test.go
  • v3/pkg/application/internal/tests/services/startuperror/startuperror_test.go
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run Go Tests (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests (macos-latest, 1.24)
  • GitHub Check: Run Go Tests (windows-latest, 1.24)
🔇 Additional comments (51)
v3/pkg/application/internal/tests/services/startupshutdown/startupshutdown_test.go (3)

1-12: LGTM! Well-organized imports.

The imports are clean and appropriate for the test requirements, including necessary synchronization primitives and test utilities.


14-16: LGTM! Proper test initialization.

The TestMain function correctly uses the test harness (apptest.Main) for initialization, which aligns with the PR's goal of introducing end-to-end testing capabilities.


65-68: LGTM! Proper error handling.

The error handling is appropriate, using t.Fatal for critical errors that should stop test execution.

v3/examples/dev/go.mod (12)

3-3: Update Go Version to 1.24.0
This update bumps the Go version to 1.24.0, ensuring that the project can leverage the latest language features and improvements. Please verify that all parts of the codebase and build scripts are compatible with this update.


10-10: Upgrade github.com/ebitengine/purego to v0.8.2
Ensure that the updated version does not introduce any API changes or incompatibilities. It is advisable to review the release notes for potential breaking changes.


14-14: Upgrade github.com/go-git/go-git/v5 to v5.13.2
This minor version bump should provide bug fixes and improvements. Please verify that there are no adverse effects on the parts of the system that depend on this library.


25-25: Upgrade github.com/leaanthony/u to v1.1.1
This small dependency update likely includes minor fixes or enhancements. Confirm that the integration remains seamless.


26-26: Upgrade github.com/lmittmann/tint to v1.0.7
Check that no breaking changes were introduced in this update, ensuring that any usage of tint in the project continues to function as expected.


27-27: Upgrade github.com/mattn/go-colorable to v0.1.14
This update appears straightforward. It’s recommended to quickly review the changelog so that any subtle changes or deprecations are identified.


32-32: Upgrade github.com/pkg/browser to v0.0.0-20240102092130-5ac0b6a4141c
This dependency update includes a significant version change. Please ensure that its API and behavior remain compatible with how it’s used elsewhere in the project.


35-35: Upgrade github.com/samber/lo to v1.49.1
The version bump should introduce improvements and fixes. Confirm that no breaking changes affect the parts of the code that rely on this library.


41-41: Upgrade golang.org/x/crypto to v0.33.0
It is important to verify that cryptographic functionalities remain secure and that any changes in the library do not affect the security posture of the application.


42-42: Upgrade golang.org/x/exp to v0.0.0-20250210185358-939b2ce775ac
Given that this is an experimental package, ensure that the new features or modifications do not inadvertently impact the stability of the application.


43-43: Upgrade golang.org/x/net to v0.35.0
This update likely includes performance and stability improvements for networking. Verify that network-related functions continue to work as expected.


44-44: Upgrade golang.org/x/sys to v0.30.0
Review the release notes to ensure that system-level operations or platform-specific functionalities are not negatively impacted by this change.

v3/pkg/application/internal/tests/services/startupshutdownerror/startupshutdownerror_test.go (1)

1-18: LGTM! Well-organized test setup.

The imports are properly organized, and the test initialization is correctly handled through TestMain.

v3/pkg/application/internal/tests/services/startup/startup_test.go (2)

1-16: LGTM! Well-structured test setup.

The imports are well-organized, and the test setup follows Go testing best practices.


18-25: LGTM! Clean type definitions.

Good use of a type block for related service types, maintaining consistency and readability.

v3/internal/buildinfo/buildinfo.go (2)

6-8: LGTM!

The import changes follow standard Go conventions with stdlib imports ("slices") before third-party imports.


34-36:

❓ Verification inconclusive

Verify and document the development mode detection change.

The logic for detecting development mode has been significantly altered from checking BuildInfo.Main.Version == "(devel)" to checking for the presence of git VCS information. While this might be more reliable, please:

  1. Verify that this change doesn't break existing development environments
  2. Document the rationale for this change
  3. Consider adding a comment explaining why git presence indicates development mode

Let's verify the impact of this change:


🏁 Script executed:

#!/bin/bash
# Description: Check how this change affects development mode detection in different scenarios

# Test 1: Check if any existing code relies on the old development mode detection
rg -A 2 'BuildInfo.Main.Version.*devel' || echo "No direct usage of old detection method found"

# Test 2: Check for potential documentation that needs updating
fd -e md -e txt | xargs rg -l 'development.*mode|dev.*mode' || echo "No documentation mentions found"

Length of output: 1669


Action: Verify the new development detection logic and update its documentation.

The updated logic now determines development mode by checking for a git setting in BuildInfo.Settings instead of using BuildInfo.Main.Version == "(devel)". Our tests indicate that no other parts of the codebase rely on the previous method. However, please ensure that:

  • This new assumption—that having VCS set to git correctly identifies a development environment—holds true in all scenarios.
  • Inline comments and any related documentation are updated to clearly explain why this change was made and its impact on downstream systems.
v3/pkg/application/internal/tests/utils.go (2)

16-16: LGTM!

The init function correctly locks the OS thread, which is essential for GUI applications.


54-65: Consider potential race conditions in error handling.

The select statement at line 56 will randomly choose between errChan and endChan if both have errors. This could lead to inconsistent error reporting if errors occur simultaneously.

Consider prioritizing fatal errors:

-	select {
-	case err := <-errChan:
-		return err
-	case fatal := <-endChan:
-		if fatal != nil {
-			t.Fatal(fatal)
-		}
-		return fatal
-	}
+	// Prioritize fatal errors over regular errors
+	select {
+	case fatal := <-endChan:
+		if fatal != nil {
+			t.Fatal(fatal)
+		}
+		return fatal
+	default:
+		select {
+		case err := <-errChan:
+			return err
+		case fatal := <-endChan:
+			if fatal != nil {
+				t.Fatal(fatal)
+			}
+			return fatal
+		}
+	}
v3/internal/commands/update_cli.go (1)

5-8: LGTM! Well-organized imports.

The imports are properly grouped and all are actively used in the code.

v3/examples/file-association/go.mod (21)

3-3: Update Go Version:
Line 3 now specifies Go version 1.24.0. Please ensure that your CI pipelines, tooling, and any third‑party integrations fully support this new version.


10-10: Updated ProtonMail Crypto Dependency:
The dependency has been updated to v1.1.5. Ensure that any API changes between the previous version and this one are reviewed, and that related functionality in the codebase remains compatible.


12-12: Updated Cloudflare Circl Dependency:
The update to v1.6.0 should bring improvements and/or bug fixes over the previous release. Please verify that any direct use (if applicable) aligns with the new version’s API.


13-13: Updated Filepath SecureJoin Dependency:
The dependency is now at v0.4.1. Check the changelog for any subtle API or behavior changes that might affect secure path joining.


14-14: Updated Purego Dependency:
The version of github.com/ebitengine/purego has been updated to v0.8.2, moving from an older alpha release. This should enhance stability; please verify its impact on any cryptographic or OS‑related calls.


18-18: Updated Go-Git Dependency:
The github.com/go-git/go-git/v5 dependency has been bumped to v5.13.2. It is advisable to test any Git operations for compatibility with this update.


27-27: Updated LeanAnthony/u Dependency:
The module github.com/leaanthony/u is now at v1.1.1. Review the release notes to confirm that no breaking changes affect its usage elsewhere in your project.


28-28: Updated Tint Dependency:
The github.com/lmittmann/tint dependency update to v1.0.7 might include bug fixes or performance improvements. Ensure that any color‐formatting or logging features using this package function as expected.


29-29: Updated Go-Colorable Dependency:
The github.com/mattn/go-colorable version has been updated to v0.1.14. Although this is a minor bump, please ensure that terminal output formatting remains consistent across environments.


31-31: Updated sha1cd Dependency:
The github.com/pjbgf/sha1cd dependency is updated to v0.3.2. Check for any improved security or performance fixes that might affect computing hash digests in your application.


32-32: Updated pkg/browser Dependency:
Upgrading github.com/pkg/browser to commit v0.0.0-20240102092130-5ac0b6a4141c is a notable change. Verify its behavior in opening URLs and integration with the OS default browser functions.


34-34: Updated Samber/lo Dependency:
The github.com/samber/lo library now uses version v1.49.1. Since this package offers utility functions, please check that any helper calls in your code continue to work as intended.


36-36: Updated Knownhosts Dependency:
The update to github.com/skeema/knownhosts v1.3.1 might include improvements to host key verification. It’s worthwhile to review the changelog to avoid unexpected behavior in SSH connections.


40-40: Updated Golang.org/x/crypto Dependency:
The change to golang.org/x/crypto v0.33.0 should provide enhanced security or bug fixes. Please ensure that all cryptographic operations in your project are tested thoroughly against this new version.


41-41: Updated Golang.org/x/exp Dependency:
The golang.org/x/exp module has been updated to a new experimental version v0.0.0-20250210185358-939b2ce775ac. Given its experimental status, carefully monitor its behavior in any feature that uses experimental APIs.


42-42: Updated Golang.org/x/mod Dependency:
The upgrade to golang.org/x/mod v0.23.0 might influence module management, especially if there are custom module operations in your build scripts.


43-43: Updated Golang.org/x/net Dependency:
The update to golang.org/x/net v0.35.0 could affect any networking components relying on the package. It’s recommended to check network-related functionalities and tests for full compatibility.


44-44: Updated Golang.org/x/sync Dependency:
Bumping golang.org/x/sync to v0.11.0 is a minor update; just ensure that no changes to concurrency primitives affect your application's synchronization logic.


45-45: Updated Golang.org/x/sys Dependency:
Updating golang.org/x/sys to v0.30.0 requires validation that all system calls and OS-specific functionalities still operate as expected.


46-46: Updated Golang.org/x/tools Dependency:
The update to golang.org/x/tools v0.30.0 could influence development tooling if used inline. Verify that all code analysis or generation tools remain functional.


50-50: Verify Replace Directive:
The replace directive on line 50 ensures that github.com/wailsapp/wails/v3 resolves to a local path (../..). Please confirm that this relative path is correct and works consistently in all development and CI environments.

v3/internal/templates/_common/go.mod.tmpl (3)

49-49: LGTM! More idiomatic template condition.

The simplified condition is more idiomatic in Go templates while maintaining the same functionality.


8-47:

❓ Verification inconclusive

Verify security of dependencies.

Several dependencies are using commit hashes or older versions. Let's verify there are no known security vulnerabilities.


🏁 Script executed:

#!/bin/bash
# Description: Check for known vulnerabilities in Go dependencies

# Install govulncheck
go install golang.org/x/vuln/cmd/govulncheck@latest

# Create a temporary go.mod file from the template
cat > go.mod << 'EOL'
module changeme

go 1.24

require (
	dario.cat/mergo v1.0.1
	github.com/Microsoft/go-winio v0.6.2
	github.com/ProtonMail/go-crypto v1.1.5
	github.com/adrg/xdg v0.5.3
	github.com/bep/debounce v1.2.1
	github.com/cloudflare/circl v1.6.0
	github.com/cyphar/filepath-securejoin v0.4.1
	github.com/ebitengine/purego v0.8.2
	github.com/emirpasic/gods v1.18.1
	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376
	github.com/go-git/go-billy/v5 v5.6.2
	github.com/go-git/go-git/v5 v5.13.2
	github.com/go-ole/go-ole v1.3.0
	github.com/godbus/dbus/v5 v5.1.0
	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8
	github.com/google/uuid v1.6.0
	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
	github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e
	github.com/kevinburke/ssh_config v1.2.0
	github.com/leaanthony/go-ansi-parser v1.6.1
	github.com/leaanthony/u v1.1.1
	github.com/lmittmann/tint v1.0.7
	github.com/mattn/go-colorable v0.1.14
	github.com/mattn/go-isatty v0.0.20
	github.com/pjbgf/sha1cd v0.3.2
	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
	github.com/pkg/errors v0.9.1
	github.com/rivo/uniseg v0.4.7
	github.com/samber/lo v1.49.1
	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
	github.com/skeema/knownhosts v1.3.1
	github.com/wailsapp/go-webview2 v1.0.19
	github.com/wailsapp/mimetype v1.4.1
	github.com/xanzy/ssh-agent v0.3.3
	golang.org/x/crypto v0.33.0
	golang.org/x/net v0.35.0
	golang.org/x/sys v0.30.0
	golang.org/x/text v0.22.0
	gopkg.in/ini.v1 v1.67.0
	gopkg.in/warnings.v0 v1.1.2
)
EOL

# Run vulnerability check
govulncheck ./...

Length of output: 423


Action Required: Verify Dependency Security with Govulncheck
The updated dependency list includes some commit-hash and older version references that may be unstable. We attempted to run a vulnerability scan using govulncheck, but the tool was not found in the environment. Please ensure that govulncheck is correctly installed and available in your PATH, then rerun the security check. Confirm that none of these dependencies have known vulnerabilities.

  • Dependencies to review:

    • Dependencies using commit hashes (e.g., github.com/go-git/gcfg and github.com/jchv/go-winloader)
    • Dependencies with older or specific version constraints
  • Next steps:

    • Verify installation of govulncheck (e.g., check if $GOPATH/bin is in your PATH)
    • Manually run the vulnerability check once govulncheck is available

3-3:

❓ Verification inconclusive

Verify Go version compatibility with user base.

The upgrade to Go 1.24 is very recent. Consider if this might impact users who haven't upgraded their Go installation yet.


🌐 Web query:

When was Go 1.24 released and what is its adoption rate?

💡 Result:

Go 1.24 was officially released on February 11, 2025[1][3][6][9]. As of February 17, 2025 (current date), adoption rate data isn't yet available due to its recent release[3][6]. However, several factors suggest strong potential adoption:

Key Features Driving Adoption

  • Swiss Table map implementation:
    25% faster lookups and 40% faster insertions compared to Go 1.23[2][3]
  • Runtime improvements:
    2–3% average CPU overhead reduction across benchmarks[1][3]
  • WebAssembly support:
    New go:wasmexport directive and WASI reactor/library builds[1][3]
  • Toolchain enhancements:
    go get -tool for managing tool dependencies[1][6][10]

Adoption Indicators

While direct metrics aren't available yet:

  1. Go ranked updated runtime assets #10 in Tiobe's language popularity index (1.24% share) pre-release[5]
  2. Major cloud providers typically adopt new Go versions within 30-60 days[6]
  3. Performance-critical domains (databases, networking tools) show early interest due to map/runtime optimizations[2][9]

Adoption patterns will become clearer in Q2 2025 as organizations complete upgrade cycles[6][9]. The release's backward compatibility ensures low migration barriers for existing projects[2][10].

Citations:


Ensure Compatibility with Go 1.24
In v3/internal/templates/_common/go.mod.tmpl (line 3) the Go version is now specified as 1.24. Note that Go 1.24 was officially released on February 11, 2025, and current adoption data isn’t available due to its recent release. Although the new version comes with significant performance improvements, you should verify that this upgrade aligns with your user base’s toolchain requirements. Consider adding documentation or a notice to inform users of the new minimum Go version requirement.

v3/internal/templates/_common/go.sum.tmpl (4)

1-147: LGTM! Dependencies align with PR objectives

The new dependencies and version updates align well with the PR objectives, particularly for service registration and error handling improvements. The changes include necessary packages for:

  1. Enhanced error handling (pkg/errors)
  2. Service management (mergo, godbus)
  3. Testing improvements (testify, matryer/is)

37-38:

❓ Verification inconclusive

Verify compatibility with go-git v5.13.2

The addition of go-git v5.13.2 is a significant new dependency that may affect the application's behavior.


🏁 Script executed:

#!/bin/bash
# Check if go-git is used in the codebase
rg "github.com/go-git/go-git/v5" -l

Length of output: 522


Ensure Thorough Compatibility Verification for go-git v5.13.2

The update in v3/internal/templates/_common/go.sum.tmpl (lines 37–38) introduces go-git v5.13.2, which is used extensively across the repository. A search confirms its presence in multiple files (e.g., v3/pkg/application/application_debug.go, v3/internal/commands/init.go, v3/internal/doctor/doctor.go, among others). Given the breadth of usage, please verify that the new version does not introduce any breaking changes or unintended side effects in any touchpoints.

  • Key Locations:

    • v3/internal/templates/_common/go.sum.tmpl (lines 37–38) – dependency declaration
    • Other modules where go-git is referenced (see search output for complete list)
  • Action:

    • Double-check integration points and run compatibility tests to ensure smooth functionality with go-git v5.13.2.

99-101:

❓ Verification inconclusive

Verify compatibility with testify v1.10.0

The update to testify v1.10.0 is a significant version jump that may introduce breaking changes.


🌐 Web query:

What are the breaking changes in testify v1.10.0 compared to v1.4.0?

💡 Result:

The transition from Testify v1.4.x to v1.x introduces several breaking changes due to API updates and behavioral adjustments[^[9]^][^[3]^][^[4]^]. Here are key breaking changes up through vx:

Assertion Behavior Changes

  • Float comparisons
    EqualValues now enforces identical types when comparing floats (float32 vs float64), causing failures where previous versions allowed mixed-type comparisons[^[9]^]. For example:

    assert.EqualValues(t, float32(123), float64(123)) // Fails in ≥vx  
  • Error message validation
    TestErrorIs/TestNotErrorIs now validate both error type and message content, whereas earlier versions only checked error types[^[4]^].

  • InEpsilonSlice parameter order
    Fixed expected/actual argument reversal[^[4]^]:

    assert.InEpsilonSlice(t, expectedSlice, actualSlice) // Corrected signature

Deprecated Features

Deprecated Feature Replacement Version
CompareType Removed ≥vx
EqualExportedValues Equal ≥vx

Suite Package Changes

  • Execution order
    Fixed SetupSubTest/TearDownSubTest sequencing to ensure proper nested test lifecycle management[^[4]^].

  • New interface requirements
    Added mandatory SetS(suite TestingSuite) method implementation for custom suites[^[7]^].

Mock Package Updates

  • Argument validation
    Introduced stricter type checking via IsTypeArgument, replacing older type assertion methods like AnythingOfType[^[10]^].

Build System Impact

  • YAML dependency
    Made YAML support optional via build tags (yaml or no_yaml)[^[4]^], potentially breaking projects relying on implicit YAML handling without explicit tag inclusion.

To mitigate upgrade issues:

go test -v ./... # Identify failing assertions
grep -r "EqualExportedValues\|CompareType" # Find deprecated usage

Citations:


Ensure Compatibility with Testify v1.10.0 Upgrade

We've updated Testify from v1.4.0 to v1.10.0 in the dependency file. This upgrade introduces several breaking changes that could affect the behavior of your tests. In particular, be aware of the following:

  • Assertion Behavior:

    • Float Comparisons: EqualValues now requires matching types (e.g., comparing a float32 to a float64 will fail).
    • Error Validations: Methods like TestErrorIs/TestNotErrorIs now check both the error type and its message content.
    • Argument Order Fixes: The parameters for methods such as assert.InEpsilonSlice have been corrected (ensure you pass the expected and actual values in the updated order).
  • Deprecated Features:

    • Certain functions like CompareType and EqualExportedValues have been removed or replaced, so examine your test implementations for any deprecated usage.

Action Required:
Please review your test suite to verify that all assertions and error validations align with Testify v1.10.0’s stricter behavior. Run comprehensive tests to catch any failures due to these changes.


108-110:

✅ Verification successful

Verify security implications of crypto package updates

The update to golang.org/x/crypto v0.33.0 may have security implications.


🌐 Web query:

Are there any known security vulnerabilities in golang.org/x/crypto v0.33.0?

💡 Result:

As of February 2025, there are no known security vulnerabilities specifically reported for golang.org/x/crypto v0.33.0 based on the provided search results[1][2][3][4][5]. Here's a breakdown of relevant findings:

Key Security Updates in Related Versions

  1. CVE-2024-45337 (Authorization Bypass)

    • Fixed in x/crypto v0.31.0
    • Affected: Applications misusing ServerConfig.PublicKeyCallback in SSH authentication[2][6].
  2. CVE-2024-45338 (Denial of Service)

    • Fixed in x/net v0.33.0
    • Affected: Non-linear parsing in HTML processing leading to resource exhaustion[1][10].

Analysis of x/crypto v0.33.0

  • The search results explicitly reference fixes for x/crypto vulnerabilities only up to v0.31.0 (CVE-2024-45337)[2][3][6].
  • No advisories or CVEs mention vulnerabilities introduced or unresolved in v0.33.0 of golang.org/x/crypto[7][9].
  • Projects like cert-manager and NanoProxy resolved risks by upgrading to v0.31.0[3][5].

Recommendations

While v0.33.0 itself shows no reported issues:

  1. Ensure dependencies like golang.org/x/net are also updated to v0.33+ to address CVE-2024-45338[1][5].
    2 Use tools like govulncheck or Trivy to verify if your code interacts with vulnerable functions[4].

In summary, x/crypto v0.33 appears secure based on available data, but confirm via automated scanning for full assurance[4].

Citations:


Security Verification Complete: golang.org/x/crypto v0.33.0 is Secure

Based on the latest web query results, there are no known security vulnerabilities specifically reported for golang.org/x/crypto v0.33.0. Notably, previous issues fixed in earlier releases (such as CVE-2024-45337 addressed in v0.31.0) are not present in this update. Please consider the following:

  • Ensure that related dependencies (e.g., golang.org/x/net) are updated accordingly to address any associated security advisories.
  • It is advisable to run automated security tools (e.g., govulncheck or Trivy) to further confirm that no vulnerabilities exist in the overall dependency chain.

The snippet in v3/internal/templates/_common/go.sum.tmpl (lines 108-110) accurately reflects these changes.

@fbbdev
Copy link
Collaborator Author

fbbdev commented Feb 17, 2025

Removed a redundant caching step in template tests that was causing errors and slowing down windows runs, the setup-go@v5 action is handling that automatically. Pipeline times down from ~21m to ~15m.
https://github.com/fbbdev/wails/actions/runs/13372810277

Fixed problems spotted by the rabbit.

This is now REALLY REALLY ready for review and merge 😅

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
.github/workflows/build-and-test-v3.yml (3)

74-83: Robust Ubuntu Testing via Xvfb and DBus Update
The Ubuntu test step now leverages xvfb-run alongside a dbus-update-activation-environment call to manage display requirements. This method should mitigate issues related to not opening a display on headless systems. As an enhancement, consider logging or verifying the success of the dbus-update-activation-environment command to ease troubleshooting if tests continue to hang.


139-145: Adjusted Linux Dependencies for Template Testing
The test template job installs a slightly trimmed set of Linux dependencies (omitting graphical packages like xvfb and x11-xserver-utils). Confirm that these packages are sufficient for the template builds. If any template requires graphical support, you might need to align this dependency list with that of the primary Go tests.


152-157: Reused Task Installation Step
The "Install Task" step is repeated here as in previous jobs. While this duplication is acceptable, consider abstracting or centralizing common setup steps into a reusable workflow to reduce maintenance overhead.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a23b94 and 3d42f88.

📒 Files selected for processing (6)
  • .github/workflows/build-and-test-v3.yml (4 hunks)
  • v3/internal/commands/update_cli.go (4 hunks)
  • v3/pkg/application/application.go (17 hunks)
  • v3/pkg/application/application_darwin.go (1 hunks)
  • v3/pkg/application/application_linux.go (1 hunks)
  • v3/pkg/application/webview_window.go (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • v3/pkg/application/application_darwin.go
  • v3/pkg/application/application_linux.go
  • v3/internal/commands/update_cli.go
  • v3/pkg/application/webview_window.go
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run Go Tests (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests (macos-latest, 1.24)
  • GitHub Check: Run Go Tests (windows-latest, 1.24)
🔇 Additional comments (16)
v3/pkg/application/application.go (8)

15-15: LGTM! Good use of sync primitives for thread safety.

The addition of the slices package and new lock fields improves thread safety in service lifecycle management.

Also applies to: 293-294, 330-331


107-107: LGTM! Improved error handling with proper error wrapping.

Error messages now use %w verb for proper error wrapping and include more context, making debugging easier.

Also applies to: 113-114, 117-118, 134-135, 159-160, 163-164


365-382: LGTM! Well-documented service registration with proper validation.

The new RegisterService method provides a clean API for late service registration with appropriate validation to prevent registration after app start.


690-716: LGTM! Comprehensive service startup with proper error handling.

The startupService method properly handles:

  1. Method binding
  2. HTTP handler setup
  3. Service startup hooks

718-738: LGTM! Robust service shutdown with proper ordering.

The shutdownServices method ensures:

  1. Services are shut down in reverse order
  2. Proper locking to prevent concurrent shutdowns
  3. Error handling during shutdown

581-688: LGTM! Robust application startup with proper state management.

The enhanced Run method includes:

  1. Double invocation prevention
  2. Service startup before event dispatching
  3. Proper cleanup on failures

876-917: LGTM! Comprehensive cleanup with proper ordering.

The enhanced cleanup process ensures:

  1. Application context cancellation
  2. Service shutdown
  3. Window and system tray cleanup
  4. Single instance cleanup
  5. Post-shutdown hooks

1087-1100: LGTM! Thread-safe runnable handling with proper lock management.

The runOrDeferToAppRun method demonstrates good lock management:

  1. Early unlock to prevent deadlocks
  2. Clear comments explaining TOC/TOU considerations
  3. Proper defer usage for panic tolerance
.github/workflows/build-and-test-v3.yml (8)

33-33: Go Version Update in the Matrix Specification
The updated go-version: [1.24] is in line with the changes made in the new go.mod files and overall upgrade of the toolchain. Ensure that all build and test scripts are fully compatible with 1.24.


43-44: Enhanced Linux Dependencies for Ubuntu Testing
The package list now includes additional dependencies (e.g., xvfb, x11-xserver-utils, at-spi2-core, xdg-desktop-portal-gtk), which should help support graphical tests on Ubuntu. Verify that these are available in the targeted CI environments.


69-73: Dedicated Windows Testing Step Added
Introducing a separate "Run tests (windows)" step clarifies the CI process on Windows. The use of go test -v ./... is appropriate.


84-87: Typechecking Binding Generator Output
Adding the "Typecheck binding generator output" step (running task generator:test:check) is a proactive measure that helps catch binding issues early in the CI pipeline.


122-133: Extensive Template Testing Coverage
The expanded matrix for template now includes a comprehensive list of frameworks and their TypeScript variations (e.g., svelte, vue, react, etc.). This extensive testing ensures that various front-end configurations are validated, though be mindful of potential matrix size and CI runtime.


134-134: Consistent Go Version in Test Templates
Specifying go-version: [1.24] within the test templates matrix maintains consistency with the other jobs and supports uniform toolchain behavior across different test environments.


158-163: CLI Build and Diagnostics Enhancement
The newly added "Build Wails3 CLI" step uses task install followed by wails3 doctor to verify the CLI build. This proactive diagnostic helps catch misconfigurations early, contributing to more robust CI outcomes.


170-170: Template Build Verification
The invocation of wails3 build after initializing a template ensures that each template not only initializes correctly but also builds successfully. This is an effective check to catch framework-specific issues.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d42f88 and d8f0cf2.

📒 Files selected for processing (1)
  • v3/pkg/application/application.go (22 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run Go Tests (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests (macos-latest, 1.24)
  • GitHub Check: Run Go Tests (windows-latest, 1.24)
🔇 Additional comments (6)
v3/pkg/application/application.go (6)

14-14: LGTM! Improved concurrency safety with new locks and state tracking.

The addition of starting flag and dedicated locks for shutdown operations enhances thread safety during application lifecycle transitions.

Also applies to: 292-293, 329-330


106-106: LGTM! Consistent error handling with proper error wrapping.

The use of %w verb for error wrapping ensures proper error propagation while maintaining the error chain, which aligns with Go's error handling best practices.

Also applies to: 112-112, 116-116, 133-133, 158-158, 162-162


364-381: LGTM! Well-implemented service registration with proper state checks.

The method ensures services can only be registered before the application starts running, with appropriate error handling.


581-589: LGTM! Robust startup state management.

The changes prevent double invocations and ensure proper state transitions during application startup.


602-617: LGTM! Well-structured service startup sequence.

The implementation ensures:

  • Services are started in registration order
  • Proper cleanup on startup failures
  • Clear error messages with service names

1088-1098: LGTM! Excellent locking strategy with clear documentation.

The implementation:

  • Uses proper lock/unlock patterns
  • Includes panic-safe unlocking with defer
  • Documents potential deadlock risks and mitigation

@@ -465,7 +465,7 @@ func (a *linuxApp) setIcon(icon []byte) {
var gerror *C.GError
pixbuf := C.gdk_pixbuf_new_from_stream(stream, nil, &gerror)
if gerror != nil {
a.parent.error("Failed to load application icon: " + C.GoString(gerror.message))
a.parent.error("failed to load application icon: %s", C.GoString(gerror.message))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory leak here (was there previously!)

@fbbdev fbbdev merged commit e7c134d into wailsapp:v3-alpha Feb 19, 2025
132 of 137 checks passed
@fbbdev fbbdev deleted the v3-alpha-feature/dynamic-bindings branch February 19, 2025 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants