Skip to content

feat: Add experimental wails3 setup wizard - #5601

Merged
leaanthony merged 67 commits into
masterfrom
setup-merge
Jun 14, 2026
Merged

leaanthony merged 67 commits into
masterfrom
setup-merge

Conversation

@leaanthony

@leaanthony leaanthony commented Jun 14, 2026 •

Copy link
Copy Markdown
Member

Replaces #4906 — the original branch was 498 commits behind master, making the diff unreadable. This PR is rebased on current master with all conflicts resolved and review comments addressed.

Summary

Adds an experimental interactive setup wizard (wails3 setup) that guides users through:

  • Dependency checking — verifies Go, npm, and platform tooling with one-click install suggestions
  • Project defaults — configure author info, bundle ID prefix, preferred templates
  • Cross-platform builds — optional Docker setup for building from any host OS
  • Code signing — configure signing identities for macOS, Windows, and Linux

The wizard runs in the browser and saves configuration to ~/.config/wails/config.yaml.

Also includes

  • wails3 setup signing and wails3 setup entitlements sub-commands
  • --json flag for wails3 doctor (machine-readable output)
  • Signing status section in wails3 doctor
  • TypeScript binding style selection (interfaces vs classes)
  • Documentation updates with setup instructions
  • Fun homepage morph effect (Desktop ↔ Server)

Changes since #4906

  • Resolved all merge conflicts with current master (498 commits)
  • Fixed Docker API fallback: reset PullStatus to "pulling" before CLI fallback so the SSE stream stays open
  • Fixed keychain save: LoadGlobalDefaults/SaveGlobalDefaults errors now surface to the frontend instead of silently returning success: true
  • Fixed resolveSigningDefaults early-return: was skipping Entitlements fill-in when Identity + KeychainProfile were already set
  • Fixed non-darwin hosts being sent into the notarization flow (gated behind selectedPlatform === 'darwin' && hostOS === 'darwin')
  • Added SHA-256 verification for Zig downloads in Dockerfile.cross (fetches expected hash from Zig's official index.json)
  • Fixed misleading test name in init_test.go
  • Removed 16 unrelated generated d2 SVGs accidentally committed from a local docs build

Test plan

  • Run wails3 setup on Linux — verify dependency detection and Docker setup
  • Run wails3 setup on macOS — verify Xcode tools and signing detection
  • Run wails3 setup on Windows — verify WebView2 and signing detection
  • Verify wails3 doctor --json outputs valid JSON
  • Verify saved config is used by wails3 init
  • Verify Docker pull falls back to CLI when API fails, and progress remains visible
  • Verify keychain profile save errors are shown in the UI

Notes

⚠️ Experimental — primarily tested on Linux. Users are directed to GitHub Issue #4904 to report issues.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added experimental wails3 setup wizard with guided code signing and macOS notarization.
    • Added wails3 doctor --json plus expanded reports for build environment and signing readiness.
    • Added an option to generate TypeScript interfaces instead of classes, reflected in generated bindings and project setup.
  • Bug Fixes
    • Improved dependency checks (Go minimum version, Linux npm detection via PATH, refreshed Windows PATH, and more reliable cross-image detection).
    • macOS signing now pulls missing identity settings from saved defaults.
  • Documentation
    • Updated installation/quick start and cross-platform build guides to recommend the setup wizard.
  • Style
    • Refreshed setup-wizard UI styling and homepage morphing visuals.

leaanthony and others added 30 commits January 25, 2026 14:43
- Add 'splash' step as the initial wizard page
- Scrolling montage background with Wails app screenshots
- Centered logo with red glow effect
- Apple-style welcome text
- Footer with theme toggle, sponsor link, and Get Started button
- Simplified button styling (rounded-lg, no glow)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TemplateFooter with theme toggle + sponsor on left, nav on right
- Redesign WelcomePage with logo on left, title/subtitle on right
- Add numbered setup steps (1, 2, 3) and links bar
- Footer styling now matches splash page design

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add logo (80px) top left with header/subheader next to it
- Footer grounded to bottom with theme toggle, sponsor, and nav buttons
- Remove WelcomePage (splash now goes directly to dependencies)
- Match splash page footer dimensions with template footer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move dependency checking from splash transition to dependencies page
- Show inline spinner above deps list while checking
- Show green success message above deps when all installed
- Disable Next button while checking
- Trigger check automatically via useEffect on page mount

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add imageBuilt field to DependencyStatus struct to track whether the
wails-cross Docker image exists. This allows the OOBE flow to properly
detect when Docker is installed but the cross-compilation image hasn't
been built yet, and prompt users to set up cross-platform builds.

Also moves the Docker build progress indicator to the center of the
footer for better visual placement.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Docker logo asset and updated frontend build output files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive test coverage for the applyGlobalDefaults function
that applies global defaults from ~/.config/wails/defaults.yaml to
init options when creating new projects.

Tests cover:
- Template default application
- Company default application
- Copyright generation with year/company placeholders
- Product identifier generation from prefix
- Description template with {name} placeholder
- Version default application
- Verification that non-default values are not overridden
- Combined defaults application

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move URLs from Message field to HelpURL field on Windows and Darwin
so the frontend can render them as clickable links.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…encies

On Windows, when software is installed, the PATH environment variable
is updated in the registry but running processes still have the old
PATH. This adds a refreshPath() function that reads the current PATH
from both system and user registry keys before checking for npm and
docker. This allows "Check Again" to work without restarting setup.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Show the cross-platform build question regardless of Docker install
status. Previously, the page was only shown when Docker was installed
but the wails-cross image wasn't built. Now it's shown when:
1. Docker is not installed (user might want to install it), OR
2. Docker is installed but wails-cross image is not built

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace all Docker logo SVGs with official path that includes container boxes
- Add "Some platforms may require a reboot" note on Docker install page
- Affects: Install Docker, Start Docker, Building image, Docker ready pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Combine Company and Bundle ID fields into one page called "Projects" in
the sidebar. Remove template selection from setup wizard as it's not
needed during initial setup.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…page

- Fix CheckingPage centering by using justify-start with pt-[30%] padding
- Replace h-full with flex-1 for proper flex container sizing
- Redesign CompletePage with compact terminal-style command display
- Add framework logos (JavaScript, TypeScript, React, Vue, Svelte, etc.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove terminal-style command display from complete page
- Remove "Read the documentation" link
- Change "Start Building" button to link to first-app quickstart guide
- Remove unused CopyableCommand component and handleClose function

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…asses)

- Add new wizard step after TypeScript selection for binding style choice
- Add UseInterfaces field to GlobalDefaults and pass through to templates
- Update Taskfile template to conditionally add -i flag for interfaces
- Improve button positioning to match SplashPage layout
- Remove "This sets the default template" text from template page
- Fix overflow issues when resizing window

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ebar

- Move Docker build progress indicator to sidebar (above bottom icons)
- Fix light mode text colors in ProjectsPage settings rows
- Add light mode CSS variants for settings-group and settings-row
- Improve button positioning consistency across pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update language selection cards with light mode text colors
- Update framework/template cards with light mode backgrounds and borders
- Use text-gray-900 dark:text-white pattern for proper contrast
- Add bg-gray-100 dark:bg-white/5 for unselected card backgrounds

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use Docker Engine API for accurate download progress with byte counts
- Add SSE streaming for smooth real-time UI updates (replaces polling)
- Show toast notification when Docker completes in background mode
- Fix progress calculation to use bytes instead of layer count
- Fix framework selection border clipping with padding
- Pull pre-built image from ghcr.io/wailsapp/wails-cross instead of building locally
- Add lipgloss-based styling (term/styles.go) with Wails brand colors
- Update init output to show Framework, Language, Bindings from config
- Display '(default)' suffix for values from global config
- Add Framework/Language fields to defaults for cleaner config
- New minimal banner style: 'Wails v3.x.x › Command'
- Add SigningDefaults to GlobalDefaults for macOS/Windows/Linux signing config
- Add signing status section to doctor command with platform detection
- Add --json flag to doctor command for machine-readable output
- Add /api/signing and /api/signing/status endpoints to setup wizard
- Add SigningStep component with platform tabs and status indicators
- Wire signing step into wizard flow after projects step

The signing step shows detected signing identities from:
- macOS: keychain codesigning identities, notarization config
- Windows: certificate file/store/cloud, SignTool availability
- Linux: GPG keys from keyring or config
- Add Configure button for each platform that opens a form
- Add configuration forms for macOS (identity, team ID, notarization profile)
- Add configuration forms for Windows (certificate path, thumbprint, timestamp)
- Add configuration forms for Linux (GPG key ID, key path)
- Use inline SVGs for platform icons (same as CrossPlatformPage)
- Forms save to defaults.yaml via /api/signing endpoint
- Include helpful command hints for finding signing identities
- Detect host OS and show platform-appropriate guidance
- On Mac: show 'security find-identity' and 'xcrun notarytool' commands
- On Linux/Windows: show rcodesign info and P12 certificate path field
- Add App Store Connect API fields for cross-platform notarization
- Link to Apple docs for API key creation
- Disable keychain profile field on non-Mac (not applicable)
… detection

- Add user-facing documentation at docs/getting-started/setup.mdx
- Remove internal design storyboard (docs/setup-wizard-storyboard.md)
- Wire up main `wails3 setup` command in CLI
- Add experimental warning with link to feedback issue #4904
- Add "Report Bug" button in wizard sidebar with clipboard template
- Fix npm detection on Linux to check PATH, not just package manager
- Use term.Warning and term.Hyperlink for styled terminal output

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tip about setup wizard in Dependencies section
- Add "Next Steps" section recommending `wails3 setup`
- Keep manual installation as fallback option
- Link to setup guide and feedback issue

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add setup step to quickstart code blocks on home page
- Add setup to TL;DR section in quick-start/installation
- Add new step 4 "Run Setup Wizard" with experimental warning
- Link to feedback issue #4904

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
leaanthony and others added 9 commits March 24, 2026 21:36
The UseInterfaces flag had default:"true", so the condition
`!options.UseInterfaces` was never true - meaning global defaults
could never set UseInterfaces=false.

Fix by always applying the global defaults value for UseInterfaces,
making `wails3 setup` the authoritative source for this preference.

Addresses: #4906 (comment)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ain permissions

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Add explicit permissions block (contents: read, packages: write)
at the workflow level so all jobs have restricted GITHUB_TOKEN scope.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Issue A: checkGo() now detects GOTOOLCHAIN (go env GOTOOLCHAIN ≠ "off")
and scans the module cache for a downloaded go1.25+ toolchain. Users
who installed wails3 via GOTOOLCHAIN auto-download will see "installed"
rather than "needs_update" for Go.

Issue B: gcc version now comes from `gcc --version` (e.g. "13.3.0")
instead of the build-essential meta-package version ("12.10ubuntu1").

Issue C: ProjectDefaults string fields gain omitempty YAML/JSON tags so
a partial POST to /api/defaults does not zero out CopyrightTemplate,
DescriptionTemplate, DefaultVersion, etc. in the saved YAML.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
- findToolchainGo: use GOMODCACHE instead of GOPATH+/pkg/mod to handle
  custom GOMODCACHE env var and multi-entry GOPATH values correctly
- findToolchainGo: scan for toolchain@v*-go* (not toolchain@v0.0.1-go*)
  so the detection stays correct if golang.org/toolchain is re-versioned
- POST /api/defaults: load existing defaults before decoding request body
  so fields absent from the partial POST retain their saved values
  (merge semantics) rather than being zeroed (replace semantics)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
Brings the setup wizard branch up to date with master (498 commits).
Resolves merge conflicts preserving setup wizard additions alongside
master's v3 improvements, removing accidental v2 regressions from the PR diff.

Key conflict resolutions:
- CI workflows: adopt master's target-branch and GOWORK=off env
- docs/astro.config.mjs: keep v3.wails.io URL + setup's @components alias
- v3/cmd/wails3/main.go: keep flags.Doctor (--json) + add doctor-ng from master
- Taskfile.tmpl.yml: merge -obfuscated (master) + -i UseInterfaces (setup)
- doctor.go: use master's git.HeadHash (go-git was removed in master)
- init_test.go: rename misleading test case — copyright IS normalized by
  applyGlobalDefaults even when global defaults are empty

- sign.go: fix resolveSigningDefaults early-return to also check Entitlements;
  previously --identity + --keychain-profile would skip filling in the default
  entitlements file from ~/.config/wails/defaults.yaml

- wizard.go: reset PullStatus to "pulling" before Docker CLI fallback so the
  SSE stream stays open and handleClose correctly detects the active build

- wizard.go: propagate LoadGlobalDefaults/SaveGlobalDefaults errors in
  handleNotarizeCreate; return success:false instead of silently swallowing them

- SigningStep.tsx: add loadError state — show retry button when initial data
  load fails instead of leaving users on a blank/stuck screen

- SigningStep.tsx: add saveError state — show error message inline when saving
  signing config fails

- SigningStep.tsx: gate macOS notarization flow to mac hosts only; non-mac users
  get a Save button that writes config and returns to status view, since
  handleNotarizeCreate rejects non-darwin hosts anyway

- Dockerfile.cross: add SHA-256 verification for Zig download by fetching the
  expected hash from Zig's official download manifest before extracting
These are astro-d2 build artifacts committed accidentally from a local
docs build. They don't exist in master and have no connection to the
setup wizard feature. The deployment pipeline regenerates them.
Copilot AI review requested due to automatic review settings June 14, 2026 03:03
@coderabbitai

coderabbitai Bot commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4a26581d-afef-4214-9709-3d64a0de7209

📥 Commits

Reviewing files that changed from the base of the PR and between f095a41 and 3808893.

📒 Files selected for processing (1)
  • v3/internal/setupwizard/wizard.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • v3/internal/setupwizard/wizard.go

Walkthrough

Introduces an experimental wails3 setup wizard signing step with cross-platform backend endpoints, a React SigningStep UI, and notarization profile management. Refactors wails3 doctor to emit structured JSON via a new --json flag. Adds UseInterfaces propagation through init/template/bindings. Replaces Docker build with a layer-progress Docker pull pipeline with SSE streaming. Expands per-OS dependency checks (Go 1.25+, PATH refresh on Windows). Updates docs, CI permissions, and Dockerfile integrity verification.

Changes

Backend features: defaults, doctor, wizard, signing, bindings

Layer / File(s) Summary
Signing and project defaults data contracts
v3/internal/defaults/defaults.go, v3/internal/setupwizard/defaults.go, v3/internal/flags/doctor.go, v3/internal/flags/init.go, v3/internal/setupwizard/frontend/src/types.ts
GlobalDefaults gains Signing SigningDefaults and per-OS signing structs; ProjectDefaults adds UseInterfaces, Framework, Language, and GetTemplateName(). Frontend types.ts mirrors these with new signing status/config interfaces, imageBuilt, richer DockerStatus progress fields, useInterfaces, and a signing wizard step.
Terminal styling library (lipgloss)
v3/internal/term/styles.go, v3/internal/term/term.go
Adds term/styles.go with exported lipgloss color constants, style instances, StatusIcon/StatusIconTri/RenderTable helpers. Updates term.go Header and Section to use the new lipgloss styles.
Doctor: structured report, JSON output, and signing status
v3/internal/doctor/doctor.go, v3/internal/doctor/signing.go, v3/internal/doctor/doctor_test.go, v3/internal/commands/doctor.go
Introduces DoctorReport/SystemReport/BuildReport structs; refactors Run() to accept jsonOutput bool and delegate to collectReport/renderReport. New doctor/signing.go implements CheckSigning() with per-OS identity discovery (macOS security find-identity, Windows signtool.exe, Linux GPG). commands.Doctor updated to accept *flags.Doctor and pass the JSON flag. Tests updated for Run(false)/Run(true) modes.
UseInterfaces flag: init, build-assets, and template wiring
v3/internal/flags/init.go, v3/internal/commands/build-assets.go, v3/internal/commands/build_assets/Taskfile.tmpl.yml, v3/internal/commands/init.go, v3/internal/commands/init_test.go, v3/internal/templates/templates.go, v3/cmd/wails3/main.go
Adds UseInterfaces/TemplateFromDefaults/UseInterfacesFromDefaults to flags.Init; applyGlobalDefaults calls GetTemplateName() and applies UseInterfaces from defaults. Taskfile conditionally appends -i when both are true. TemplateOptions gains UseInterfaces; install display switches to term.Section/RenderTable for framework/language/bindings. Comprehensive TestApplyGlobalDefaults table-driven test suite added. CLI wiring updates for doctor flags and setup subcommand.
Signing defaults integration: sign command, darwin Taskfile, setup warning
v3/internal/commands/sign.go, v3/internal/commands/build_assets/darwin/Taskfile.yml, v3/internal/commands/setup.go
Adds resolveSigningDefaults() to load ~/.config/wails/defaults.yaml and fill missing Identity/KeychainProfile/Entitlements. Darwin Taskfile sign/sign:notarize tasks simplified to use wails3 tool sign with {{.CLI_ARGS}} from wails3 setup defaults; vars-based config removed. Setup emits experimental warning and GitHub issue link via term.Println/term.Warning.
Setup wizard: Docker pull parser, status tracking, and configuration endpoints
v3/internal/setupwizard/wizard.go, v3/internal/setupwizard/pull_parser_test.go
Adds PullProgress type and regex-based pullParser with parseSize() for Docker pull output. Refactors Docker pull into startDockerPull() orchestrator: attempts Docker HTTP API streaming (per-layer progress) with CLI fallback. Expands DockerStatus with image metadata (size, version, SDK), pull accounting (bytes, layers), and message/error fields. Adds SSE /api/docker/status/stream endpoint for progress streaming, /api/docker/status refresh, /api/report-bug for issue generation. Detects cross-compilation image via crossImageName with ImageBuilt flag (handles [] and "Error" as not-built). Embeds assets/apple-sdk-license.pdf. Changes POST /api/defaults to merge into existing defaults instead of replace. Includes unit tests for parseSize (B/KB/MB/GB) and pull parser stage/progress tracking (streaming, real output, ANSI, no-size scenarios).
Setup wizard: signing/notarization backend endpoints and security hardening
v3/internal/setupwizard/wizard.go
Adds /api/signing/status GET, /api/signing GET/POST for loading/persisting signing defaults, /api/signing/notarize/validate and /api/signing/notarize/create for macOS notarization profile management (runs xcrun notarytool commands). Implements per-OS signing status computation combining stored defaults with local probing (macOS security find-identity for available identities, Windows signtool.exe presence check, Linux gpg --list-secret-keys for default key). Hardens /api/docker/build and dependency installer with allowlists for executable commands and sudo subcommands. Updates handleClose to report "closing" status and delay completion until background Docker work finishes via WaitGroup and sync.Once.
Setup wizard: signing frontend (types, API, UI, CSS)
v3/internal/setupwizard/frontend/src/api.ts, v3/internal/setupwizard/frontend/src/components/SigningStep.tsx, v3/internal/setupwizard/frontend/src/index.css, v3/internal/setupwizard/frontend/mockup.html, v3/internal/setupwizard/frontend/index.html
api.ts adds subscribeDockerStatus() SSE subscription with error retry and unsubscribe, updated close() returning CloseResponse, and signing/notarization/bug-report functions. SigningStep.tsx implements full multi-platform signing UI: status view (per-platform readiness rows), identity form (platform-specific inputs for macOS/Windows/Linux with cross-platform fallback for notarization), NotarizationSetup sub-flow, platform tabs with configured indicators, and animated transitions via framer-motion. Helper components StatusRow and PlatformTab included. index.css adds Wails blue theme, glassmorphic card/sidebar, settings-row/group layouts with hover/dark-mode, scrollbar-thin, scrolling-bg animation, sr-only utility, focus outline. Mockup HTML for Project Defaults step demo. Favicon links updated.
Per-OS dependency checks: Go version, crossImageName, npm PATH refresh
v3/internal/setupwizard/wizard_darwin.go, v3/internal/setupwizard/wizard_linux.go, v3/internal/setupwizard/wizard_windows.go
All platforms gain checkGo() enforcing Go 1.25+ minimum with version extraction and non-numeric suffix handling. Linux adds GOTOOLCHAIN support, findToolchainGo() to scan GOMODCACHE/golang.org/x/ for downloaded toolchain entries, and isGoMinVersion() for version comparison. Linux also skips npm in package-manager loop, derives gcc version via gcc --version, and discovers binaries via exec.LookPath. Windows adds refreshPath() to read/merge system and user PATH from registry (expanding REG_EXPAND_SZ), plus execCommandRefreshed() wrapper to refresh before command execution (enabling discovery of newly installed tools without restart). All platforms update Docker image detection to use crossImageName variable and interpret [] empty array or "Error" as not-built, setting ImageBuilt flag accordingly.

Documentation, CI, and infrastructure updates

Layer / File(s) Summary
Documentation: setup wizard pages and installation guides
docs/astro.config.mjs, docs/src/components/MorphText.astro, docs/src/content/docs/getting-started/installation.mdx, docs/src/content/docs/getting-started/setup.mdx, docs/src/content/docs/index.mdx, docs/src/content/docs/quick-start/installation.mdx
Adds new setup.mdx describing the experimental setup wizard, its features, sub-commands, and troubleshooting. Updates getting-started/installation.mdx with Aside tip on post-install wails3 setup, replaces "System Check" with "Next Steps" and manual verification guidance. Updates quick-start/installation.mdx to insert "Run Setup Wizard (Recommended)" step with experimental caveat and renumber following steps. Updates homepage index.mdx with asterisk on tagline, MorphText component import/render, and unified bash quickstart combining install, wails3 setup, and project init/dev. Adds astro.config.mjs with fileURLToPath import and @components Vite alias for ./src/components resolution. Adds MorphText.astro component with global CSS for .morph-word-title transitions (fade, blur, scale, slide) and DOM manipulation script that morphs "Desktop" ↔ "Server" text on ~800ms cycles after initialization.
Documentation: cross-platform build guide with full Dockerfile example
docs/src/content/docs/guides/build/cross-platform.mdx
Updates macOS SDK section to reference wailsapp/macosx-sdks source and clarifies SDK is not Wails-distributed. Replaces brief customization notes with comprehensive "Build Your Own Image" section: full Dockerfile template with parameterized ZIG_VERSION/MACOS_SDK_VERSION/IMAGE_VERSION, per-target Zig compiler wrapper scripts (darwin/linux/windows, amd64/arm64), and CGO-enabled build.sh entrypoint for Go app + optional frontend build. Adds separate instructions for building the image, overriding SDK version via --build-arg, and using a locally provided SDK tarball (COPY/extract instead of download).
CI, Dockerfile, and changelog
.github/workflows/build-cross-image.yml, v3/internal/commands/build_assets/docker/Dockerfile.cross, v3/.gitignore, v3/UNRELEASED_CHANGELOG.md
Moves GitHub Actions workflow permissions from job-level to workflow-level (contents: read, packages: write) and adds job-scoped read permissions to test-cross-compile job. Updates Dockerfile.cross to install python3 and replaces Zig streaming download/extract with architecture-aware approach: compute ZIG_ARCH from TARGETARCH, download .tar.xz to /tmp, fetch SHA-256 from Zig's index.json via python3, verify with sha256sum -c -, extract to /opt, symlink to /usr/local/bin/zig. Updates .gitignore systray bin path syntax. Records UNRELEASED_CHANGELOG.md entries for experimental wails3 setup wizard, --json output for wails3 doctor with signing status section, and Linux npm PATH detection fix.

Sequence Diagrams

sequenceDiagram
  participant User
  participant CLI as wails3 setup
  participant WizardServer as wizard.go (HTTP server)
  participant DockerAPI as Docker HTTP API
  participant DockerCLI as docker pull CLI
  participant Frontend as React UI

  User->>CLI: wails3 setup
  CLI->>WizardServer: wizard.Run()
  WizardServer->>Frontend: serve index.html
  Frontend->>WizardServer: POST /api/docker/start-background
  WizardServer->>WizardServer: startDockerPull()
  WizardServer->>DockerAPI: POST /images/create (streaming)
  alt Docker API streaming available
    DockerAPI-->>WizardServer: per-layer progress events
    WizardServer->>WizardServer: pullParser.ParseLine → PullProgress
  else fallback
    WizardServer->>DockerCLI: docker pull
    DockerCLI-->>WizardServer: stdout lines
  end
  Frontend->>WizardServer: GET /api/docker/status/stream (SSE)
  WizardServer-->>Frontend: DockerStatus JSON events
  Frontend->>WizardServer: GET /api/signing/status
  WizardServer-->>Frontend: SigningStatus (per-OS)
  Frontend->>WizardServer: POST /api/signing (save defaults)
  WizardServer-->>Frontend: saved confirmation
  Frontend->>WizardServer: POST /api/close
  WizardServer->>WizardServer: wait buildWg, close shutdown channel
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • wailsapp/wails#4171: Both PRs modify v3/internal/commands/build_assets/darwin/Taskfile.yml macOS signing tasks, with this PR simplifying sign/notarize to use {{.CLI_ARGS}} from wails3 setup defaults.
  • wailsapp/wails#4934: Both PRs extend v3/internal/commands/build-assets.go BuildAssetsOptions struct with new fields (UseInterfaces in this PR, CFBundleIconName in the related PR).

Suggested labels

Enhancement, Documentation, cli, Implemented in v3, v3-alpha, size:XXL

Poem

🐇 Hop hop, the wizard wakes anew,
Signing keys in macOS, Linux, Windows too!
A pull-parser tracks each Docker layer,
While --json makes the doctor fairer.
UseInterfaces blinks to life with style,
The bunny typed in Go for quite a while! ✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch setup-merge

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds an experimental wails3 setup browser-based wizard and extends v3 tooling/docs around environment setup, diagnostics, cross-build Docker, and signing configuration. This fits into the v3 CLI onboarding and “doctor” diagnostics flow by making setup interactive and making diagnostics machine-readable.

Changes:

  • Introduces/expands setup-wizard dependency checks across platforms (Go/npm/Docker/WebView2) plus Docker image status details.
  • Adds wails3 doctor --json and a new signing-status section in doctor output.
  • Adds global defaults for signing + TypeScript binding style selection (interfaces vs classes), and updates docs accordingly.

Reviewed changes

Copilot reviewed 40 out of 76 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
v3/UNRELEASED_CHANGELOG.md Changelog entry for setup wizard, doctor JSON, signing status, and npm detection fix
v3/internal/term/term.go Updates CLI header/section rendering; adds hyperlink/println helpers
v3/internal/term/styles.go Adds shared lipgloss styles and small rendering helpers
v3/internal/templates/templates.go Switches init output to new term table formatting; surfaces binding style
v3/internal/setupwizard/wizard_windows.go Windows dependency checks incl. PATH refresh and Go detection
v3/internal/setupwizard/wizard_linux.go Linux dependency checks incl. Go detection and PATH-based npm check
v3/internal/setupwizard/wizard_darwin.go macOS dependency checks incl. Go detection and Docker image inspection tweaks
v3/internal/setupwizard/pull_parser_test.go Adds tests for Docker pull progress parsing
v3/internal/setupwizard/frontend/src/types.ts Extends wizard types for signing status/details and Docker progress metadata
v3/internal/setupwizard/frontend/src/index.css UI styling updates incl. reduced-motion and focus-visible outlines
v3/internal/setupwizard/frontend/src/assets/docker-logo.svg Adds Docker logo asset
v3/internal/setupwizard/frontend/src/api.ts Adds SSE subscription for Docker status, signing APIs, and bug reporting
v3/internal/setupwizard/frontend/public/logos/vue.svg Adds framework logo asset
v3/internal/setupwizard/frontend/public/logos/typescript.svg Adds language logo asset
v3/internal/setupwizard/frontend/public/logos/svelte.svg Adds framework logo asset
v3/internal/setupwizard/frontend/public/logos/solid.svg Adds framework logo asset
v3/internal/setupwizard/frontend/public/logos/react.svg Adds framework logo asset
v3/internal/setupwizard/frontend/public/logos/qwik.svg Adds framework logo asset
v3/internal/setupwizard/frontend/public/logos/preact.svg Adds framework logo asset
v3/internal/setupwizard/frontend/public/logos/lit.svg Adds framework logo asset
v3/internal/setupwizard/frontend/public/logos/javascript.svg Adds language logo asset
v3/internal/setupwizard/frontend/public/favicon.svg Adds wizard favicon asset
v3/internal/setupwizard/frontend/mockup.html Adds UI mockup HTML (design reference)
v3/internal/setupwizard/frontend/index.html Adds favicon links
v3/internal/setupwizard/frontend/dist/logos/vue.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/logos/typescript.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/logos/svelte.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/logos/solid.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/logos/react.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/logos/qwik.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/logos/preact.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/logos/lit.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/logos/javascript.svg Built dist asset update
v3/internal/setupwizard/frontend/dist/index.html Built dist entry update (asset hashes + favicon links)
v3/internal/setupwizard/frontend/dist/favicon.svg Built dist favicon asset
v3/internal/setupwizard/frontend/dist/assets/index-CiqVA0q3.css Built dist CSS update
v3/internal/setupwizard/frontend/dist/assets/index-CCNHCwJO.css Removes old built CSS asset
v3/internal/setupwizard/frontend/.gitignore Ignores frontend build artifacts
v3/internal/setupwizard/defaults.go Re-exports defaults types incl. signing types
v3/internal/flags/init.go Adds UseInterfaces init flag and tracking fields for defaults provenance
v3/internal/flags/doctor.go Adds doctor flags type with --json
v3/internal/doctor/signing.go Adds signing detection/formatting for doctor output and JSON
v3/internal/doctor/doctor.go Refactors doctor to support JSON output and adds signing section
v3/internal/doctor/doctor_test.go Updates/extends tests for new doctor signature and JSON mode
v3/internal/defaults/defaults.go Adds signing defaults + template selection helpers + interfaces preference
v3/internal/commands/sign.go Loads signing defaults for sign tool + improves error messaging
v3/internal/commands/setup.go Adds experimental wizard warning + links to feedback issue
v3/internal/commands/init.go Applies global defaults for template selection and interfaces; passes through to build-assets
v3/internal/commands/init_test.go Adds tests for applying global defaults
v3/internal/commands/doctor.go Wires CLI doctor command to new flags-driven doctor runner
v3/internal/commands/build-assets.go Adds UseInterfaces to build-assets options
v3/internal/commands/build_assets/Taskfile.tmpl.yml Adds binding generation flag when interfaces are selected
v3/internal/commands/build_assets/docker/Dockerfile.cross Adds Zig SHA-256 verification logic for downloads
v3/internal/commands/build_assets/darwin/Taskfile.yml Updates signing tasks to rely on setup/defaults and CLI overrides
v3/cmd/wails3/main.go Adds doctor --json flags wiring and registers setup action
v3/.gitignore Fixes systray bin ignore entry formatting
docs/src/content/docs/quick-start/installation.mdx Recommends wails3 setup and keeps doctor as manual verification path
docs/src/content/docs/index.mdx Updates homepage tagline and quickstart to include setup wizard
docs/src/content/docs/guides/build/cross-platform.mdx Expands cross-build docs and adds “build your own image” details
docs/src/content/docs/getting-started/setup.mdx New setup wizard guide page
docs/src/content/docs/getting-started/installation.mdx Adds setup wizard as next step and reframes doctor as manual verification
docs/src/components/MorphText.astro Adds homepage morph text effect and footnote insertion
docs/astro.config.mjs Adds Vite alias for components
.github/workflows/build-cross-image.yml Adjusts permissions scoping for GHCR build workflow

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread v3/internal/commands/build_assets/docker/Dockerfile.cross
Comment thread v3/internal/defaults/defaults.go
Comment thread v3/internal/doctor/doctor.go
Comment thread v3/internal/doctor/doctor.go
Comment thread docs/src/content/docs/guides/build/cross-platform.mdx
Comment thread v3/internal/commands/init.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
v3/internal/commands/sign.go (1)

73-75: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Resolve defaults before macOS binary dispatch.

Line 73 requires options.Identity before signMacOSBinary() is called, but defaults are only loaded later at Line 126. If identity is set only in ~/.config/wails/defaults.yaml, binary signing incorrectly falls through to unsupported file type.

Suggested fix
-	// macOS binary (no extension typically)
-	if runtime.GOOS == "darwin" && options.Identity != "" {
+	// macOS binary (no extension typically)
+	if runtime.GOOS == "darwin" {
 		return signMacOSBinary(options)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/commands/sign.go` around lines 73 - 75, The macOS binary signing
dispatch check on line 73 evaluates options.Identity before the defaults are
resolved, but the defaults are loaded later at line 126. Move the default
configuration resolution (which loads from ~/.config/wails/defaults.yaml) to
execute before the macOS binary dispatch condition so that identity values from
the defaults config file are available when deciding whether to call
signMacOSBinary(). This ensures that signing falls through correctly instead of
hitting an unsupported file type error when identity is only specified in the
defaults config.
v3/internal/commands/init.go (1)

128-132: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Recompute TypeScript mode after applying global template defaults.

Line 129 computes isTypescript before Line 146 can change options.TemplateName. This can generate non-TS build assets for a defaults-selected *-ts template.

Proposed fix
-	// Check if the template is a typescript template
-	isTypescript := false
-	if strings.HasSuffix(options.TemplateName, "-ts") {
-		isTypescript = true
-	}
-
 	if options.ProjectName == "" {
 		return errors.New("please use the -n flag to specify a project name")
 	}
@@
 	} else {
 		applyGlobalDefaults(options, globalDefaults)
 	}
+
+	// Derive TS mode from the final template selection (after defaults).
+	isTypescript := strings.HasSuffix(options.TemplateName, "-ts")

Also applies to: 140-147, 180-181

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/commands/init.go` around lines 128 - 132, The isTypescript
variable is being computed before options.TemplateName can be modified by
applying global template defaults. Move the isTypescript computation (the check
using strings.HasSuffix for the "-ts" suffix) to occur AFTER the template
defaults have been applied to options.TemplateName, ensuring that if the
template name is changed to a TypeScript variant through defaults, the
isTypescript flag will be correctly set to true and generate the proper TS build
assets.
🧹 Nitpick comments (10)
docs/src/components/MorphText.astro (1)

3-42: ⚡ Quick win

Respect prefers-reduced-motion for the morph animation.

The current animation always runs, which can be problematic for users who explicitly request reduced motion.

Proposed change
 <style is:global>
+  `@media` (prefers-reduced-motion: reduce) {
+    .morph-word-title span {
+      transition: none !important;
+    }
+  }
+
   .morph-word-title {
     display: inline-block;
@@
 <script is:inline>
   (function() {
     const words = ['Desktop', 'Server'];
     const transitions = ['fade-out', 'blur-out', 'scale-out', 'slide-out'];
+    const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
@@
-      scheduleNext();
+      if (!reduceMotion) scheduleNext();
     }

Also applies to: 116-143

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/components/MorphText.astro` around lines 3 - 42, The animation
styles for the morph effect in the .morph-word-title and related classes do not
respect user preferences for reduced motion, which can cause discomfort for
those with vestibular disorders or motion sensitivity. Wrap all transition and
transform-based animation rules (the transition property on .morph-word-title
span, and the fade-out, blur-out, scale-out, and slide-out classes) with a
`@media` (prefers-reduced-motion: no-preference) media query so animations only
apply when users haven't requested reduced motion. For users who prefer reduced
motion, ensure the visual states still work correctly but without the
transitions and transforms—this may mean providing alternative rules outside the
media query that achieve the final state without animation.
v3/internal/setupwizard/frontend/src/index.css (2)

176-188: 💤 Low value

Keyframe name does not follow kebab-case convention.

The keyframe scrollBackground should be scroll-background to follow CSS naming conventions flagged by Stylelint.

♻️ Suggested fix
-@keyframes scrollBackground {
+@keyframes scroll-background {
   0% {
     transform: translateY(0);
   }
   100% {
     transform: translateY(-50%);
   }
 }

 .scrolling-bg {
-  animation: scrollBackground 60s linear infinite;
+  animation: scroll-background 60s linear infinite;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/setupwizard/frontend/src/index.css` around lines 176 - 188,
Rename the `@keyframes` scrollBackground to `@keyframes` scroll-background to follow
CSS kebab-case naming conventions, and update the animation property in the
.scrolling-bg class to reference the renamed keyframe scroll-background instead
of scrollBackground.

Source: Linters/SAST tools


198-208: 💤 Low value

The clip property is deprecated.

The clip: rect(0, 0, 0, 0) property is deprecated. Use clip-path: inset(50%) instead for the .sr-only utility.

♻️ Suggested fix
 .sr-only {
   position: absolute;
   width: 1px;
   height: 1px;
   padding: 0;
   margin: -1px;
   overflow: hidden;
-  clip: rect(0, 0, 0, 0);
+  clip-path: inset(50%);
   white-space: nowrap;
   border: 0;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/setupwizard/frontend/src/index.css` around lines 198 - 208, The
`clip` property used in the `.sr-only` class is deprecated and should be
replaced with the modern equivalent. Remove the `clip: rect(0, 0, 0, 0)` line
from the `.sr-only` utility class and add `clip-path: inset(50%)` in its place
to achieve the same visual hiding effect while using the current CSS standard.

Source: Linters/SAST tools

v3/internal/setupwizard/frontend/src/components/SigningStep.tsx (2)

40-43: 💤 Low value

Missing cleanup or dependency for headingRef focus.

The useEffect accesses headingRef.current but the ref is not a dependency. While this works in practice for initial focus, the ESLint exhaustive-deps rule would flag this. Consider adding a comment to suppress or restructure if linting is strict.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/setupwizard/frontend/src/components/SigningStep.tsx` around lines
40 - 43, The useEffect hook in the SigningStep component accesses
headingRef.current without including headingRef in the dependency array, which
violates ESLint exhaustive-deps rules. Add an ESLint disable comment above the
useEffect to suppress the warning, since the intention is to focus the heading
only on initial mount and the ref itself is stable and doesn't require
dependency tracking.

534-549: 💤 Low value

Notarization onDone reloads data but does not update local config state with the new keychainProfile.

The comment says "Backend already saved keychainProfile - just reload", but setConfig at line 666-669 in NotarizationSetup updates a local copy that gets discarded when loadData() runs. This works correctly because loadData fetches fresh data, but the intermediate setConfig call is effectively dead code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/setupwizard/frontend/src/components/SigningStep.tsx` around lines
534 - 549, The NotarizationSetup component contains a dead code path where
setConfig is called at lines 666-669 to update the keychainProfile, but this
intermediate state update is immediately overwritten when loadData() runs in the
onDone callback of the parent SigningStep component. Remove the unnecessary
setConfig call in NotarizationSetup since the subsequent loadData() call in the
onDone handler will fetch the fresh data from the backend anyway, making the
intermediate state update redundant.
v3/internal/setupwizard/frontend/src/api.ts (2)

143-184: ⚡ Quick win

API functions do not handle HTTP error responses.

All fetch calls assume success and directly call response.json(). If the server returns a 4xx/5xx status, parsing may fail or return unexpected data. Consider checking response.ok before parsing.

♻️ Example for getSigningStatus
 export async function getSigningStatus(): Promise<SigningStatus> {
   const response = await fetch(`${API_BASE}/signing/status`);
+  if (!response.ok) {
+    throw new Error(`Failed to get signing status: ${response.status}`);
+  }
   return response.json();
 }

The same pattern should be applied to getSigning, saveSigning, validateNotarizationProfile, createNotarizationProfile, and reportBug.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/setupwizard/frontend/src/api.ts` around lines 143 - 184, The API
functions getSigningStatus, getSigning, saveSigning,
validateNotarizationProfile, createNotarizationProfile, and reportBug all assume
successful HTTP responses without checking the response status. For each
function, add a check for response.ok after the fetch call and before calling
response.json(). If response.ok is false, throw an error with appropriate
context (such as the status code and status text) to prevent invalid data from
being returned or parsed incorrectly.

20-52: ⚡ Quick win

SSE reconnection lacks backoff and max retry limit.

The reconnection logic uses a fixed 1-second delay without exponential backoff or a maximum retry count. If the server is down or the endpoint is unreachable, this will retry indefinitely, potentially flooding logs and wasting resources.

♻️ Suggested improvement with backoff and retry limit
 export function subscribeDockerStatus(onUpdate: (status: DockerStatus) => void): () => void {
   let eventSource: EventSource | null = null;
   let closed = false;
+  let retryCount = 0;
+  const maxRetries = 10;
+  const baseDelay = 1000;

   const connect = () => {
     if (closed) return;
+    if (retryCount >= maxRetries) {
+      console.error('Max SSE reconnection attempts reached');
+      return;
+    }
     
     eventSource = new EventSource(`${API_BASE}/docker/status/stream`);
     
     eventSource.onmessage = (event) => {
       try {
         const status = JSON.parse(event.data) as DockerStatus;
         onUpdate(status);
+        retryCount = 0; // Reset on successful message
       } catch (e) {
         console.error('Failed to parse docker status:', e);
       }
     };

     eventSource.onerror = () => {
       eventSource?.close();
       if (!closed) {
-        setTimeout(connect, 1000);
+        const delay = Math.min(baseDelay * Math.pow(2, retryCount), 30000);
+        retryCount++;
+        setTimeout(connect, delay);
       }
     };
   };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/setupwizard/frontend/src/api.ts` around lines 20 - 52, The
subscribeDockerStatus function's reconnection logic in the connect nested
function needs exponential backoff and a maximum retry limit. Add a retry
counter variable that tracks reconnection attempts, implement exponential
backoff in the setTimeout call within eventSource.onerror (calculate delay as
initialDelay multiplied by 2 raised to the power of retryCount, with a
reasonable cap like 30 seconds), and stop retrying once the retry count exceeds
a maximum threshold (such as 10). Reset the retry counter to 0 when a successful
connection and message reception occurs (in the eventSource.onmessage handler)
so subsequent disconnections start fresh.
v3/internal/defaults/defaults.go (1)

233-240: ⚡ Quick win

Avoid exported mutable config catalogs for framework/language lists.

Frameworks and Languages are exported mutable vars, so any importing package can modify them at runtime and silently change IsValidFramework behavior. Prefer unexported vars (or exported getter returning a copy) to keep validation deterministic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/defaults/defaults.go` around lines 233 - 240, The exported
variables Frameworks and Languages are mutable slices that any importing package
can modify at runtime, compromising the deterministic behavior of
IsValidFramework. Make these variables unexported by renaming them to frameworks
and languages (lowercase), and provide an exported getter function (such as
GetFrameworks and GetLanguages) that returns a copy of each slice to allow
read-only access while preventing external modifications. Update any internal
references within the same package to use the new unexported variable names.
v3/internal/doctor/doctor.go (1)

262-270: ⚡ Quick win

Signing section map iteration order is non-deterministic.

Unlike other sections in renderReport that sort map keys before iteration (lines 206, 233, 249), the signing section iterates directly over signingMap. This causes inconsistent output ordering across runs.

Proposed fix to sort signing keys
 term.Section("Signing")
 signingTableData := pterm.TableData{}
 signingMap := formatSigningStatus(report.Signing)
+signingKeys := lo.Keys(signingMap)
+slices.Sort(signingKeys)
-for key, value := range signingMap {
+for _, key := range signingKeys {
+    value := signingMap[key]
     signingTableData = append(signingTableData, []string{key, value})
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/doctor/doctor.go` around lines 262 - 270, The signing section
iterates directly over signingMap without sorting keys, causing
non-deterministic output ordering. Sort the keys from signingMap before
iteration (similar to how other sections in renderReport at lines 206, 233, and
249 handle their maps). Extract the sorted keys from the signingMap returned by
formatSigningStatus, sort them, then iterate over the sorted keys in the for
loop to build signingTableData in a consistent order.
v3/internal/commands/init_test.go (1)

122-130: ⚡ Quick win

Add assertions for the new default-provenance and bindings fields.

TestApplyGlobalDefaults doesn’t assert TemplateFromDefaults, UseInterfaces, or UseInterfacesFromDefaults, so regressions in the new behavior can pass silently.

Minimal assertion extension
 			if options.ProductVersion != tt.wantOptions.ProductVersion {
 				t.Errorf("ProductVersion = %q, want %q", options.ProductVersion, tt.wantOptions.ProductVersion)
 			}
+			if options.TemplateFromDefaults != tt.wantOptions.TemplateFromDefaults {
+				t.Errorf("TemplateFromDefaults = %v, want %v", options.TemplateFromDefaults, tt.wantOptions.TemplateFromDefaults)
+			}
+			if options.UseInterfaces != tt.wantOptions.UseInterfaces {
+				t.Errorf("UseInterfaces = %v, want %v", options.UseInterfaces, tt.wantOptions.UseInterfaces)
+			}
+			if options.UseInterfacesFromDefaults != tt.wantOptions.UseInterfacesFromDefaults {
+				t.Errorf("UseInterfacesFromDefaults = %v, want %v", options.UseInterfacesFromDefaults, tt.wantOptions.UseInterfacesFromDefaults)
+			}
 		})
 	}
 }

Also applies to: 367-391

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/commands/init_test.go` around lines 122 - 130, The
TestApplyGlobalDefaults test is missing assertions for the fields
TemplateFromDefaults, UseInterfaces, and UseInterfacesFromDefaults in its
table-driven test cases. For each test case in the tests slice, add assertions
in the test execution to verify that these fields are correctly set on the
wantOptions after calling the function being tested. This ensures that
regressions in the handling of these new default-provenance and bindings fields
are caught during testing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/build-cross-image.yml:
- Around line 33-35: The `packages: write` permission at the workflow scope is
over-granting token rights to all jobs. Remove `packages: write` from the
workflow-level permissions block (lines 33-35) and keep only `contents: read`.
Then, add a `permissions` block specifically to the `build` job that performs
the image publishing, granting `packages: write` only there. This ensures the
write permission is narrowly scoped to just the job that needs it, following the
principle of least privilege.

In `@docs/src/content/docs/getting-started/installation.mdx`:
- Line 123: Fix the internal documentation links by removing the `/docs` prefix
from root-relative paths. In the file
docs/src/content/docs/getting-started/installation.mdx at line 123, change the
link in the text "See the [Setup Guide](/docs/getting-started/setup) for more
details" to use `/getting-started/setup` instead of
`/docs/getting-started/setup`. Apply the same fix to
docs/src/content/docs/getting-started/setup.mdx at line 13 by removing the
`/docs` prefix from any similar internal documentation links in that location.

In `@docs/src/content/docs/guides/build/cross-platform.mdx`:
- Around line 414-417: The COPY and RUN commands in the "Use Your Own SDK"
replacement block hard-code MacOSX14.5, which will cause the mv command to fail
if readers used a different MACOS_SDK_VERSION value (like 15.0) as shown in the
earlier example. Add explicit guidance text immediately before or after this
code block instructing readers to replace both occurrences of "14.5" in the COPY
command path and the mv command source directory with the SDK version they chose
(matching their MACOS_SDK_VERSION value).

In `@v3/internal/commands/build_assets/Taskfile.tmpl.yml`:
- Line 191: The `-i` flag (TypeScript interfaces) is currently emitted based
solely on the `.UseInterfaces` condition, but this flag is only valid when
TypeScript generation is enabled via the `.Typescript` condition. Modify the
conditional that emits the `-i` flag to require both `.Typescript` and
`.UseInterfaces` to be true simultaneously, so the flag is only included when
both conditions are met. This prevents the command from failing when
`.UseInterfaces` is true but `.Typescript` is false.

In `@v3/internal/setupwizard/frontend/src/components/SigningStep.tsx`:
- Around line 80-89: The handleSave function should return a boolean indicating
success or failure instead of relying on state updates that may be stale when
read by callers. Change the return type of handleSave from void to boolean,
returning true when saveSigning completes successfully and false when an error
is caught. This allows callers to immediately know the result of the save
operation without waiting for async state updates.

In `@v3/internal/setupwizard/wizard.go`:
- Around line 1309-1315: The exec.Command call in the notarytool
store-credentials invocation exposes the password as a command-line argument,
making it visible in process listings. Replace the --password flag and its value
in the exec.Command arguments with --password-stdin instead, then configure the
command's Stdin to receive the password by creating an io.Pipe, writing
req.Password to the write end of the pipe, and assigning the read end to
cmd.Stdin to pass the password securely through standard input rather than as a
command-line argument.

In `@v3/internal/term/term.go`:
- Around line 15-21: The Header function (around line 21) and Section function
(around line 90) are directly applying lipgloss ANSI styling without checking
whether colors are disabled. To fix this, both functions need to check the color
disable setting before applying lipgloss styles: if colors are disabled, render
the output as plain text without any styling; if colors are enabled, apply the
current lipgloss styling. This ensures that DisableColor() can fully suppress
colorized output across both functions, not just the pterm output.

---

Outside diff comments:
In `@v3/internal/commands/init.go`:
- Around line 128-132: The isTypescript variable is being computed before
options.TemplateName can be modified by applying global template defaults. Move
the isTypescript computation (the check using strings.HasSuffix for the "-ts"
suffix) to occur AFTER the template defaults have been applied to
options.TemplateName, ensuring that if the template name is changed to a
TypeScript variant through defaults, the isTypescript flag will be correctly set
to true and generate the proper TS build assets.

In `@v3/internal/commands/sign.go`:
- Around line 73-75: The macOS binary signing dispatch check on line 73
evaluates options.Identity before the defaults are resolved, but the defaults
are loaded later at line 126. Move the default configuration resolution (which
loads from ~/.config/wails/defaults.yaml) to execute before the macOS binary
dispatch condition so that identity values from the defaults config file are
available when deciding whether to call signMacOSBinary(). This ensures that
signing falls through correctly instead of hitting an unsupported file type
error when identity is only specified in the defaults config.

---

Nitpick comments:
In `@docs/src/components/MorphText.astro`:
- Around line 3-42: The animation styles for the morph effect in the
.morph-word-title and related classes do not respect user preferences for
reduced motion, which can cause discomfort for those with vestibular disorders
or motion sensitivity. Wrap all transition and transform-based animation rules
(the transition property on .morph-word-title span, and the fade-out, blur-out,
scale-out, and slide-out classes) with a `@media` (prefers-reduced-motion:
no-preference) media query so animations only apply when users haven't requested
reduced motion. For users who prefer reduced motion, ensure the visual states
still work correctly but without the transitions and transforms—this may mean
providing alternative rules outside the media query that achieve the final state
without animation.

In `@v3/internal/commands/init_test.go`:
- Around line 122-130: The TestApplyGlobalDefaults test is missing assertions
for the fields TemplateFromDefaults, UseInterfaces, and
UseInterfacesFromDefaults in its table-driven test cases. For each test case in
the tests slice, add assertions in the test execution to verify that these
fields are correctly set on the wantOptions after calling the function being
tested. This ensures that regressions in the handling of these new
default-provenance and bindings fields are caught during testing.

In `@v3/internal/defaults/defaults.go`:
- Around line 233-240: The exported variables Frameworks and Languages are
mutable slices that any importing package can modify at runtime, compromising
the deterministic behavior of IsValidFramework. Make these variables unexported
by renaming them to frameworks and languages (lowercase), and provide an
exported getter function (such as GetFrameworks and GetLanguages) that returns a
copy of each slice to allow read-only access while preventing external
modifications. Update any internal references within the same package to use the
new unexported variable names.

In `@v3/internal/doctor/doctor.go`:
- Around line 262-270: The signing section iterates directly over signingMap
without sorting keys, causing non-deterministic output ordering. Sort the keys
from signingMap before iteration (similar to how other sections in renderReport
at lines 206, 233, and 249 handle their maps). Extract the sorted keys from the
signingMap returned by formatSigningStatus, sort them, then iterate over the
sorted keys in the for loop to build signingTableData in a consistent order.

In `@v3/internal/setupwizard/frontend/src/api.ts`:
- Around line 143-184: The API functions getSigningStatus, getSigning,
saveSigning, validateNotarizationProfile, createNotarizationProfile, and
reportBug all assume successful HTTP responses without checking the response
status. For each function, add a check for response.ok after the fetch call and
before calling response.json(). If response.ok is false, throw an error with
appropriate context (such as the status code and status text) to prevent invalid
data from being returned or parsed incorrectly.
- Around line 20-52: The subscribeDockerStatus function's reconnection logic in
the connect nested function needs exponential backoff and a maximum retry limit.
Add a retry counter variable that tracks reconnection attempts, implement
exponential backoff in the setTimeout call within eventSource.onerror (calculate
delay as initialDelay multiplied by 2 raised to the power of retryCount, with a
reasonable cap like 30 seconds), and stop retrying once the retry count exceeds
a maximum threshold (such as 10). Reset the retry counter to 0 when a successful
connection and message reception occurs (in the eventSource.onmessage handler)
so subsequent disconnections start fresh.

In `@v3/internal/setupwizard/frontend/src/components/SigningStep.tsx`:
- Around line 40-43: The useEffect hook in the SigningStep component accesses
headingRef.current without including headingRef in the dependency array, which
violates ESLint exhaustive-deps rules. Add an ESLint disable comment above the
useEffect to suppress the warning, since the intention is to focus the heading
only on initial mount and the ref itself is stable and doesn't require
dependency tracking.
- Around line 534-549: The NotarizationSetup component contains a dead code path
where setConfig is called at lines 666-669 to update the keychainProfile, but
this intermediate state update is immediately overwritten when loadData() runs
in the onDone callback of the parent SigningStep component. Remove the
unnecessary setConfig call in NotarizationSetup since the subsequent loadData()
call in the onDone handler will fetch the fresh data from the backend anyway,
making the intermediate state update redundant.

In `@v3/internal/setupwizard/frontend/src/index.css`:
- Around line 176-188: Rename the `@keyframes` scrollBackground to `@keyframes`
scroll-background to follow CSS kebab-case naming conventions, and update the
animation property in the .scrolling-bg class to reference the renamed keyframe
scroll-background instead of scrollBackground.
- Around line 198-208: The `clip` property used in the `.sr-only` class is
deprecated and should be replaced with the modern equivalent. Remove the `clip:
rect(0, 0, 0, 0)` line from the `.sr-only` utility class and add `clip-path:
inset(50%)` in its place to achieve the same visual hiding effect while using
the current CSS standard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c841cff-9589-42ca-825b-41de476622ae

📥 Commits

Reviewing files that changed from the base of the PR and between 9bf12ea and 904cd32.

⛔ Files ignored due to path filters (33)
  • docs/public/d2/docs/DEVELOPER_GUIDE-0.svg is excluded by !**/*.svg
  • docs/public/d2/docs/DEVELOPER_GUIDE-1.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/assets/apple-sdk-license.pdf is excluded by !**/*.pdf
  • v3/internal/setupwizard/frontend/dist/assets/index-BmL8398X.js is excluded by !**/dist/**
  • v3/internal/setupwizard/frontend/dist/assets/index-C9VCVRfM.js is excluded by !**/dist/**
  • v3/internal/setupwizard/frontend/dist/assets/index-CCNHCwJO.css is excluded by !**/dist/**
  • v3/internal/setupwizard/frontend/dist/assets/index-CiqVA0q3.css is excluded by !**/dist/**
  • v3/internal/setupwizard/frontend/dist/favicon.ico is excluded by !**/dist/**, !**/*.ico
  • v3/internal/setupwizard/frontend/dist/favicon.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/index.html is excluded by !**/dist/**
  • v3/internal/setupwizard/frontend/dist/logos/javascript.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/logos/lit.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/logos/preact.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/logos/qwik.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/logos/react.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/logos/solid.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/logos/svelte.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/logos/typescript.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/logos/vue.svg is excluded by !**/dist/**, !**/*.svg
  • v3/internal/setupwizard/frontend/dist/showcase/montage.png is excluded by !**/dist/**, !**/*.png
  • v3/internal/setupwizard/frontend/public/favicon.ico is excluded by !**/*.ico
  • v3/internal/setupwizard/frontend/public/favicon.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/javascript.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/lit.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/preact.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/qwik.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/react.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/solid.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/svelte.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/typescript.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/logos/vue.svg is excluded by !**/*.svg
  • v3/internal/setupwizard/frontend/public/showcase/montage.png is excluded by !**/*.png
  • v3/internal/setupwizard/frontend/src/assets/docker-logo.svg is excluded by !**/*.svg
📒 Files selected for processing (43)
  • .github/workflows/build-cross-image.yml
  • docs/astro.config.mjs
  • docs/src/components/MorphText.astro
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/getting-started/setup.mdx
  • docs/src/content/docs/guides/build/cross-platform.mdx
  • docs/src/content/docs/index.mdx
  • docs/src/content/docs/quick-start/installation.mdx
  • v3/.gitignore
  • v3/UNRELEASED_CHANGELOG.md
  • v3/cmd/wails3/main.go
  • v3/internal/commands/build-assets.go
  • v3/internal/commands/build_assets/Taskfile.tmpl.yml
  • v3/internal/commands/build_assets/darwin/Taskfile.yml
  • v3/internal/commands/build_assets/docker/Dockerfile.cross
  • v3/internal/commands/doctor.go
  • v3/internal/commands/init.go
  • v3/internal/commands/init_test.go
  • v3/internal/commands/setup.go
  • v3/internal/commands/sign.go
  • v3/internal/defaults/defaults.go
  • v3/internal/doctor/doctor.go
  • v3/internal/doctor/doctor_test.go
  • v3/internal/doctor/signing.go
  • v3/internal/flags/doctor.go
  • v3/internal/flags/init.go
  • v3/internal/setupwizard/defaults.go
  • v3/internal/setupwizard/frontend/.gitignore
  • v3/internal/setupwizard/frontend/index.html
  • v3/internal/setupwizard/frontend/mockup.html
  • v3/internal/setupwizard/frontend/src/App.tsx
  • v3/internal/setupwizard/frontend/src/api.ts
  • v3/internal/setupwizard/frontend/src/components/SigningStep.tsx
  • v3/internal/setupwizard/frontend/src/index.css
  • v3/internal/setupwizard/frontend/src/types.ts
  • v3/internal/setupwizard/pull_parser_test.go
  • v3/internal/setupwizard/wizard.go
  • v3/internal/setupwizard/wizard_darwin.go
  • v3/internal/setupwizard/wizard_linux.go
  • v3/internal/setupwizard/wizard_windows.go
  • v3/internal/templates/templates.go
  • v3/internal/term/styles.go
  • v3/internal/term/term.go

Comment thread .github/workflows/build-cross-image.yml Outdated
Comment thread docs/src/content/docs/getting-started/installation.mdx Outdated
Comment thread docs/src/content/docs/guides/build/cross-platform.mdx
Comment thread v3/internal/commands/build_assets/Taskfile.tmpl.yml Outdated
Comment thread v3/internal/setupwizard/frontend/src/components/SigningStep.tsx Outdated
Comment thread v3/internal/setupwizard/wizard.go
Comment thread v3/internal/term/term.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/src/content/docs/index.mdx (1)

195-195: ⚡ Quick win

Consider adding prefers-reduced-motion support to the MorphText component.

The MorphText component applies animated word morphing and transitions without checking the user's motion preferences. Users with vestibular disorders or motion sensitivity may find this disruptive. Consider updating the component to respect the prefers-reduced-motion media query by disabling or reducing animations when the preference is set.

♿ Suggested accessibility enhancement

Add this to the MorphText.astro component's style block:

`@media` (prefers-reduced-motion: reduce) {
  .morph-word-title span {
    transition: none;
  }
  
  .morph-word-title span.fade-out,
  .morph-word-title span.blur-out,
  .morph-word-title span.scale-out,
  .morph-word-title span.slide-out {
    opacity: 1;
    filter: none;
    transform: none;
  }
}

Or skip the animation entirely in the script when the preference is detected:

if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  return; // Skip animation setup
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/index.mdx` at line 195, The MorphText component applies
animations without respecting the user's motion preferences, which can be
disruptive for users with vestibular disorders. Update the MorphText component
to detect the prefers-reduced-motion media query and disable animations when the
user has that preference set. You can accomplish this either by adding a CSS
media query block in the component's style section that overrides
animation-related properties (transition, opacity, filter, transform) with
no-motion alternatives, or by detecting the preference in the component's script
and skipping animation setup entirely when prefers-reduced-motion is detected.
Use the approach that best fits your component architecture.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/src/content/docs/index.mdx`:
- Around line 216-217: The link text "manual setup guide" in the anchor element
does not match the documentation it points to. The href="/getting-started/setup"
target documents the experimental setup wizard, not manual installation. Either
change the href attribute to point to "/getting-started/installation" (the
actual manual installation guide) to align with the link text, or change the
link text to "setup guide" or "setup wizard documentation" to match the current
target. Option 1 (updating the href to /getting-started/installation) is
recommended since users encountering issues with wails3 setup would benefit from
being directed to the manual installation instructions.

---

Nitpick comments:
In `@docs/src/content/docs/index.mdx`:
- Line 195: The MorphText component applies animations without respecting the
user's motion preferences, which can be disruptive for users with vestibular
disorders. Update the MorphText component to detect the prefers-reduced-motion
media query and disable animations when the user has that preference set. You
can accomplish this either by adding a CSS media query block in the component's
style section that overrides animation-related properties (transition, opacity,
filter, transform) with no-motion alternatives, or by detecting the preference
in the component's script and skipping animation setup entirely when
prefers-reduced-motion is detected. Use the approach that best fits your
component architecture.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2a78e287-3185-4fd4-8c8e-7180513d87a0

📥 Commits

Reviewing files that changed from the base of the PR and between 904cd32 and 72b07e4.

📒 Files selected for processing (1)
  • docs/src/content/docs/index.mdx

Comment thread docs/src/content/docs/index.mdx Outdated
- Dockerfile.cross: add python3 to apt-get (required for Zig SHA lookup)
- Taskfile.tmpl.yml: only emit -i when both UseInterfaces and Typescript are true
- init.go: don't override --useinterfaces=false with global defaults
- doctor.go: sort dependency keys; use fixed order for signing table rows
- wizard.go: pass notarytool password via stdin, not --password arg
- SigningStep.tsx: return boolean from handleSave; callers use return value
  instead of reading stale saveError state
- term.go: DisableColor() now also disables lipgloss ANSI via termenv.Ascii
- build-cross-image.yml: narrow packages:write to build job only
- docs: fix /docs/ prefix on internal links in setup/installation pages
- docs: note SDK version must be replaced in "Use Your Own SDK" snippet

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
v3/internal/setupwizard/wizard.go (1)

1091-1132: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Arbitrary command execution endpoint - verify this is the intended design.

This endpoint executes whatever command the client sends. While the server is bound to 127.0.0.1 only, a malicious local process or browser extension could invoke this API to run arbitrary commands with the user's privileges.

Given this is an experimental local-only setup wizard, the trade-off may be acceptable. Consider documenting this behavior or adding a simple allowlist of known install commands (e.g., brew install, apt install, winget install) if you want to reduce the attack surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/setupwizard/wizard.go` around lines 1091 - 1132, The
handleInstallDependency function executes arbitrary commands from the request
without any validation, creating a security risk. Add validation before
executing the command by implementing an allowlist check that restricts
execution to only known install commands such as "brew install", "apt install",
"winget install", and other safe package management commands. Create this
validation before the exec.Command call to verify that the command in the
InstallRequest matches one of the allowed patterns, and return an appropriate
error response if it does not match the allowlist.
🧹 Nitpick comments (1)
v3/internal/setupwizard/wizard.go (1)

742-758: 💤 Low value

Minor: Windows socket path is dead code.

The socketPath assignment for Windows (line 745) is never used because DialContext immediately returns an error on Windows (line 752-753), causing the fallback to CLI. Consider removing the Windows-specific path assignment for clarity:

♻️ Suggested simplification
 func (w *Wizard) pullViaDockerAPI() error {
-	socketPath := "/var/run/docker.sock"
-	if runtime.GOOS == "windows" {
-		socketPath = "//./pipe/docker_engine"
+	if runtime.GOOS == "windows" {
+		return fmt.Errorf("Windows named pipes not supported, falling back to CLI")
 	}
+	socketPath := "/var/run/docker.sock"

 	client := &http.Client{
 		Transport: &http.Transport{
 			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
-				if runtime.GOOS == "windows" {
-					return nil, fmt.Errorf("windows named pipes not supported, falling back to CLI")
-				}
 				return net.Dial("unix", socketPath)
 			},
 		},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/setupwizard/wizard.go` around lines 742 - 758, The
Windows-specific socketPath assignment in the pullViaDockerAPI method is
unreachable dead code because the DialContext function immediately returns an
error when runtime.GOOS is "windows", preventing the Windows path from ever
being used. Remove the conditional block that assigns the Windows named pipe
path (the if runtime.GOOS == "windows" check that sets socketPath to
"//./pipe/docker_engine"), leaving only the Unix socket path assignment as the
default, since that is the only path that will actually be executed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@v3/internal/setupwizard/wizard.go`:
- Around line 1091-1132: The handleInstallDependency function executes arbitrary
commands from the request without any validation, creating a security risk. Add
validation before executing the command by implementing an allowlist check that
restricts execution to only known install commands such as "brew install", "apt
install", "winget install", and other safe package management commands. Create
this validation before the exec.Command call to verify that the command in the
InstallRequest matches one of the allowed patterns, and return an appropriate
error response if it does not match the allowlist.

---

Nitpick comments:
In `@v3/internal/setupwizard/wizard.go`:
- Around line 742-758: The Windows-specific socketPath assignment in the
pullViaDockerAPI method is unreachable dead code because the DialContext
function immediately returns an error when runtime.GOOS is "windows", preventing
the Windows path from ever being used. Remove the conditional block that assigns
the Windows named pipe path (the if runtime.GOOS == "windows" check that sets
socketPath to "//./pipe/docker_engine"), leaving only the Unix socket path
assignment as the default, since that is the only path that will actually be
executed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 29c786fd-f79a-411e-9509-b5926a00e1c8

📥 Commits

Reviewing files that changed from the base of the PR and between 13726c8 and f095a41.

📒 Files selected for processing (11)
  • .github/workflows/build-cross-image.yml
  • docs/src/content/docs/getting-started/installation.mdx
  • docs/src/content/docs/getting-started/setup.mdx
  • docs/src/content/docs/guides/build/cross-platform.mdx
  • v3/internal/commands/build_assets/Taskfile.tmpl.yml
  • v3/internal/commands/build_assets/docker/Dockerfile.cross
  • v3/internal/commands/init.go
  • v3/internal/doctor/doctor.go
  • v3/internal/setupwizard/frontend/src/components/SigningStep.tsx
  • v3/internal/setupwizard/wizard.go
  • v3/internal/term/term.go
✅ Files skipped from review due to trivial changes (1)
  • docs/src/content/docs/getting-started/setup.mdx
🚧 Files skipped from review as they are similar to previous changes (8)
  • v3/internal/commands/build_assets/Taskfile.tmpl.yml
  • docs/src/content/docs/getting-started/installation.mdx
  • .github/workflows/build-cross-image.yml
  • v3/internal/commands/build_assets/docker/Dockerfile.cross
  • v3/internal/term/term.go
  • v3/internal/commands/init.go
  • v3/internal/setupwizard/frontend/src/components/SigningStep.tsx
  • docs/src/content/docs/guides/build/cross-platform.mdx

…d code

- handleInstallDependency now validates the command executable against an
  allowlist of known package managers (apt, brew, winget, pacman, etc.)
  and additionally validates the sudo subcommand, preventing an adversarial
  local process from invoking arbitrary binaries through the wizard API.
- pullViaDockerAPI now returns early on Windows instead of deferring the
  error to a DialContext closure that could never succeed, removing the
  unreachable socketPath assignment for Windows named pipes.
@leaanthony

Copy link
Copy Markdown
Member Author

Both issues from the latest review are fixed in 3808893:

Arbitrary command execution in handleInstallDependency — added an allowlist of known package managers (apt, apt-get, dnf, yum, pacman, zypper, emerge, xbps-install, eopkg, nix-env, brew, winget, choco, scoop). When the executable is sudo, a second allowlist validates the subcommand, so e.g. sudo rm -rf / is rejected. Every installCommand value the wizard emits is covered by these lists.

Dead code in pullViaDockerAPI — moved the Windows check to an early return at the top of the function; removed the unreachable socketPath = "//./pipe/docker_engine" assignment and the redundant runtime.GOOS == "windows" branch inside DialContext.

@leaanthony
leaanthony merged commit 9733553 into master Jun 14, 2026
59 checks passed
@leaanthony
leaanthony deleted the setup-merge branch June 14, 2026 06:52
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.

3 participants