Skip to content

feat(v3): wails3 migrate - automated v2 to v3 project migration#5758

Draft
taliesin-ai wants to merge 4 commits into
masterfrom
feat/wails3-migrate-command
Draft

feat(v3): wails3 migrate - automated v2 to v3 project migration#5758
taliesin-ai wants to merge 4 commits into
masterfrom
feat/wails3-migrate-command

Conversation

@taliesin-ai

@taliesin-ai taliesin-ai commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Warning

This command is experimental. It handles the common v2 project shapes well, but your mileage may vary: generated projects should be reviewed and tested thoroughly. Please report anything it gets wrong via the issue tracker; PRs improving the tool are very welcome. The CLI, the generated MIGRATION.md and the docs all carry this notice.

wails3 migrate -d ./myv2project -o ./myv3project helps convert a Wails v2 project into a Wails v3 project.

The design principle: migrate what maps deterministically, document the rest, and never leave code half-rewritten. The tool does not modify application logic and does not generate compatibility layers - code that still uses the v2 API is copied untouched, every such location is enumerated in a generated MIGRATION.md with its concrete v3 replacement, and the compiler points at exactly those locations until they are ported.

What it migrates

Parse (syntax-only go/parser, works without the v2 module in the module cache): wails.json (with v2's defaulting rules), go.mod, the options.App literal inside wails.Run(...), the Bind entries, lifecycle callbacks and embed directives.

Generate:

  • main.go rewritten around application.New() + app.Window.NewWithOptions() via textual surgery: only the wails.Run statement and the import block are replaced, all other user code and comments are preserved byte-for-byte, then gofmt'd. The error-handling shape is preserved.
  • Data-driven option mapping (geometry, start state, background colour, frameless, asset server/handler/middleware, single instance, drag-and-drop, Windows/Mac/Linux platform options incl. enum/preset translation such as mac.TitleBarHiddenInset() -> application.MacTitleBarHiddenInset). Title maps to both Options.Name and the window title.
  • Bind -> Services. Lifecycle hooks are wired natively in the generated main file: OnStartup -> events.Common.ApplicationStarted, OnDomReady -> events.Common.WindowRuntimeReady, OnShutdown -> Options.OnShutdown, OnBeforeClose -> a ShouldQuit wrapper. Public v3 API only, no shims.
  • v3 build system scaffolded through the existing init/build-assets machinery: root Taskfile (package manager carried over from frontend:install), build/ assets and build/config.yml populated from the v2 metadata. File associations and protocols move from wails.json into config.yml (with an UpdateBuildAssets pass so plists/manifests pick them up). The bundle id keeps v2's com.wails.<name> convention so migrated apps keep their identity. The v2 build/appicon.png is preserved.
  • go.mod: wails/v2 -> wails/v3 (at version.LatestStable()), go directive raised to 1.24, all other requires kept.
  • Frontend copied over with @wailsio/runtime added to package.json. The generated wailsjs/ directory is not carried over - it is v2 build output that cannot work against a v3 backend.

What it documents instead of migrating

MIGRATION.md contains a Port these to the v3 API table:

  • every call into the v2 runtime package, by file:line, with the concrete v3 replacement (e.g. runtime.EventsEmit -> app.Event.Emit(name, data...); runtime.WindowSetTitle -> window.SetTitle(title) via app.Window.Current(); dialogs -> the v3 builder API; MessageDialog -> button callbacks). ~60 v2 functions are mapped.
  • every frontend import of wailsjs/runtime or wailsjs/go/..., with the @wailsio/runtime equivalent and the wails3 generate bindings workflow.
  • plus Manual steps for options needing a human (menus, custom loggers, EnumBind, custom Windows themes, ...) and notes on everything else the tool did.

Deliberate consequences:

  • A project with remaining v2 call sites does not compile until they are ported - that is the honest state, and the report lists exactly where to go.
  • go mod tidy is skipped (with a warning) while v2 call sites remain: tidying would re-add the v2 dependency and let the old calls compile, only to fail at runtime inside a v3 application. MIGRATION.md spells this out; after porting, the user runs tidy + wails3 generate bindings.

Verified

  • Unit tests for the parser, option mapper, main-file generator, go.mod transform, the call-site advisor (Go + frontend) and the frontend copy rules (internal/migrate), plus end-to-end command tests (internal/commands) covering the full run, the MIGRATION.md checklist contents, untouched-source guarantees, output-safety guards and non-v2 rejection.
  • Manually migrated a v2 template-derived fixture with lifecycle hooks, runtime calls and file associations: the checklist lists each call site correctly and the project refuses to build until they are ported (as designed).
  • A fixture without v2 runtime usage compiles and runs: the natively wired OnStartup (ApplicationStarted event) fired, confirmed via a marker file.
  • Options mapping verified end to end earlier in development, including a live webview round-trip of events and bindings against the published @wailsio/runtime@3.0.0-alpha.95.
  • gofmt/go vet clean; full internal/commands and internal/migrate suites pass.

Docs

The Migrating from v2 to v3 guide now leads with the automated path, describing exactly what is migrated and what is documented, under an Experimental caution.

Notes for review

  • Earlier iterations of this branch shipped a v2-runtime compatibility bridge (first as pkg/v2compat, then generated into the project) and generated wailsjs JS shims. Both were removed by design decision: compatibility layers invite code to stay on (or newly adopt) the v2 API, and half-automatic rewriting risks silently changing behaviour. The advise-first model keeps the tool honest. The history is in the branch commits if we ever want to revisit.
  • templates.Install mutates ProjectDir, chdirs and prints via bare fmt.Print; the migrate command works around all three (restores cwd, silences stdout for the scratch scaffold). Happy to refactor Install instead if preferred.
  • When run from a dev build, the generated go.mod uses version.LatestStable() (version.txt) rather than v3.0.0-dev, so the require always resolves.

… migration

wails3 migrate -d ./myv2project -o ./myv3project converts a Wails v2
project into a v3 project:

- Parses wails.json and the declarative options.App literal passed to
  wails.Run (syntax-only, no module downloads needed) and generates an
  equivalent programmatic main.go via application.New() +
  app.Window.NewWithOptions(), preserving user code and comments through
  textual surgery on just the wails.Run statement and imports.
- Maps v2 options to v3 (window geometry, start state, background
  colour, platform-specific options, single instance, asset server),
  converts Bind entries into v3 services and bridges the
  OnStartup/OnDomReady/OnShutdown/OnBeforeClose lifecycle callbacks.
- Adds pkg/v2compat/runtime: the v2 runtime API (context-first
  functions) implemented on the v3 application API, so migrated code
  only needs its import path rewritten. Covers events, window control,
  dialogs, clipboard, browser, screens, logging and app control; every
  function documents its v3 replacement for incremental migration.
- Migrates the frontend: regenerates wailsjs/ as a compatibility layer
  over @wailsio/runtime (runtime shim + Call.ByName binding shims from
  the parsed bound-struct methods) and adds the npm dependency.
- Scaffolds the v3 build system via the init machinery: Taskfile,
  build/ assets and build/config.yml populated from the v2 metadata
  (product info, file associations, protocols, bundle id kept as
  com.wails.<name> so the app keeps its identity).
- Transforms go.mod (wails/v2 -> wails/v3, go directive raised),
  preserving all other requires.
- Writes MIGRATION.md documenting every mapped option and any manual
  steps (menus, custom loggers, EnumBind, ...).

Docs: the v2-to-v3 migration guide now leads with the automated path.
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 95467bcf-4137-4d94-960c-b487b25fae79

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/wails3-migrate-command

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The migrate command now announces its experimental status in the CLI
output, the generated MIGRATION.md and the migration guide, with a
pointer to the issue tracker for reports and contributions.
The bridge is no longer a public package in the v3 module: its source
lives at internal/migrate/v2compat/runtime (compiled, vetted and tested
in-repo but not importable) and wails3 migrate copies it into the
output project as <module>/v2compat/runtime with a generated-code
header explaining it is temporary.

This means only migrated projects carry the v2-style API - nobody can
adopt it for new code - and there is no sunset obligation on the v3
module: each project deletes its own bridge functions as call sites are
ported to the v3 API, and removes the package when nothing imports it.

Import rewriting now targets the project-local path, and the bridge is
only emitted when the project actually needs it (v2 runtime imports or
lifecycle hooks).
…at layers

Half-migrated code helps nobody, and compatibility shims invite new code
onto the old API. The migrate command now draws a hard line:

Migrated fully (deterministic):
- project scaffold, Taskfile, build assets, config.yml from wails.json
- main.go rewritten around application.New()/NewWithOptions with the
  options mapped; lifecycle hooks wired natively (ApplicationStarted
  event, WindowRuntimeReady event, Options.OnShutdown, ShouldQuit)
- go.mod v2 -> v3; frontend copied with @wailsio/runtime added

Documented instead of migrated:
- every call into the v2 runtime package, listed in MIGRATION.md by
  file:line with its concrete v3 replacement; the sources are copied
  untouched, so the compiler points at exactly the listed locations
  until they are ported
- every frontend wailsjs import, with the @wailsio/runtime equivalent
  and the generate-bindings workflow; the generated wailsjs directory
  is not carried over (it is v2 build output and cannot work with v3)

Removed: the v2compat runtime bridge and the generated wailsjs shims.
go mod tidy is skipped (with a warning) while v2 call sites remain,
since tidying would re-add the v2 dependency and let old calls compile
only to fail at runtime; MIGRATION.md spells this out.
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