Files
obsidian-vault/work/projects/vscode-migration/vscode-migration.md
T

111 lines
14 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VSCode/Cursor Builds & Debugging (macOS)
Status: **IN PROGRESS** · updated 2026-10-16
**Asana:** [Migration to fully VSCode-supported builds&debugging](https://app.asana.com/1/137249556945/project/1203301625297703/task/1209808486380252)
**Repo:** `~/DuckDuckGo/apple-browsers.git` (bare + worktrees)
**Findings:** [[projects/vscode-migration/findings]]
**Tech design (package step):** [[projects/vscode-migration/tech-design-spm-browser-package]]
**Editor via Xcode build server (no package needed):** [[projects/vscode-migration/xcode-build-server]] · [[projects/vscode-migration/tech-design-xcode-build-server|tech design]]
**Next:** [[projects/vscode-migration/modularization-roadmap]] — modularization after the browser package (incl. iOS/macOS sharing)
## Goal
Build, run and debug the macOS app from VSCode/Cursor: make most of the app
SPM-compatible, so `swift build` + SourceKit-LSP work, and configure
`.vscode` (tasks, launch).
## Status
| Step | Status |
|---|---|
| Move non-Swift code to local packages | ✅ Jun 2025 |
| Missing imports, unused files cleanup | ✅ Jun 2025 |
| Initial VSCode config | ✅ Jun 2025 (original SweetPad setup) |
| Xcode BSP local setup | ✅ 2026-10-16: generic harness at `~/Developer/xcode-editor-tools`, local Git hook migrated, all 97 existing worktrees preserved; fresh 98th worktree bootstrapped automatically. 47 unit tests and unrelated Xcode app/BSP smoke passed. New harness has docs/license/tests/CI and is public at [mallexxx/xcode-editor-tools](https://github.com/mallexxx/xcode-editor-tools), `main` commit `b4a839b`; local checkout clean. Details: [[projects/vscode-migration/xcode-build-server#Standalone harness extraction and deployment — 2026-10-16]] |
| BSP fork with required fixes | ✅ [mallexxx/sourcekit-xcode-bsp](https://github.com/mallexxx/sourcekit-xcode-bsp), pinned `afd0e3d`; 58 tests and strict SwiftLint pass. App-repository setup is local/uncommitted |
| Unify WebKit private methods (Sparkle/AppStore) | ✅ Feb 2026 |
| Drop `#if SPARKLE/APPSTORE` | ✅ Mar 2026 — DMG and App Store targets now compile the same 1398 files |
| Drop dead target flags (`SANDBOX_TEST_TOOL`, `ALPHA`, `NETWORK_EXTENSION` in shared files) | ✅ [#6983](https://github.com/duckduckgo/apple-browsers/pull/6983) |
| Replace sandbox-test-tool with App Store UI tests (`AppStoreSandboxUITests`, CI leg along full DMG runs) | ✅ [#6980](https://github.com/duckduckgo/apple-browsers/pull/6980) |
| Split app files shared with helper targets | 🔄 A+B in review ([#7029](https://github.com/duckduckgo/apple-browsers/pull/7029)), C ready, G done; the rest stays shared by path — see [Target split stages](#target-split-stages) |
| Asset resource initializers instead of asset symbol extensions | ✅ committed, `alex/asset-resource-symbols` (needs a PR) |
| Drop storyboards | ✅ last one (`NavigationBar`) is `alex/drop-navigationbar-storyboard` (PR open); `Feedback` #6866, `PasswordManager` #6950 merged |
| **Browser code as a SwiftPM package** | 🔄 builds and runs from Xcode and VSCode — `alex/spm-browser-package`, see [Browser package](#browser-package) |
| Core Data models in code | ❌ **Cancelled** 2026-10-01 — Xcode 27 builds these in SPM |
| Asset catalogs for `swift build` | ❌ **Cancelled** 2026-10-01 — Xcode 27 builds these in SPM |
| swift-build provisioning cache | ⏸ Existing 2025 fork located at `~/Developer/swift-build-custom`; caches provisioning inputs, not package resolution; not enabled on Xcode 27.1. Measured phase median 1.09 s. See [[projects/vscode-migration/xcode-build-server#Swift Build fork and cache findings — 2026-10-16]] |
| Remote debugging (VMs, old hardware) | ⏸ not started |
| AI instructions, GitHub MCP subtasks | ⏸ not started |
## Current decision and evidence (2026-10-04)
- Code-initialized Core Data models were proposed at Apple Weekly on 2026-09-29, then cancelled after Xcode 27 / Swift 6.4 validation. [Asana subtask 1218854852229979](https://app.asana.com/0/0/1218854852229979) is completed because the conversion became unnecessary, not because the new model code was implemented. Existing `.xcdatamodeld` files stay as SPM resources and load through `Bundle.module`; the project documents local Xcode 27.2 verification.
- [Cleanup task 1218987523298563](https://app.asana.com/0/0/1218987523298563) to remove runtime model compilation test hacks remains open. [PR #7080](https://github.com/duckduckgo/apple-browsers/pull/7080) packages the browser as SwiftPM, is currently Draft, and is separate from the cancelled code-defined model approach. GitHub shows it was temporarily closed and reopened on 2026-10-02.
- For cross-project focus and career analysis, use [[personal/projects/personal-os/project-truth-analyzer]] to distinguish proposals, decisions, implementation, and verification.
## Open items after the cancellation
- [x] Closed PR [#6976](https://github.com/duckduckgo/apple-browsers/pull/6976) (AssetCatalogPlugin) and the Core Data PR; their worktrees are removed.
- [ ] Check what Xcode 27 actually does for `swift build` (native) and SourceKit-LSP: asset symbols, `.car`, `.momd`, storyboards? Earlier finding on Xcode 26: only `--build-system swiftbuild` handles assets, native doesn't. See [[projects/vscode-migration/findings#Asset catalogs]].
- [ ] Re-check whether storyboards still need dropping with Xcode 27.
## Plan (next steps)
1. **PRs in order:** #7029 (A+B) → C (`move-vpn-pixel-event`) / G (`move-helper-only-files`) → `alex/asset-resource-symbols` → `alex/drop-navigationbar-storyboard` → `alex/spm-browser-package`.
2. **Browser package PR follow-ups** — see [Browser package](#browser-package): localization export (`loc_export.sh`), CI checks for the VSCode setup, dropping the duplicated app-target package product dependencies.
3. **Helper-target split, later:** D, E1, E2+H, F aren't needed for the package (helpers keep compiling the shared files by path); do them when there's a reason (F with the KeyedStoring migration).
4. **VSCode tasks with cached helper targets** (extensions/login items built by Xcode only when their inputs change) — the host app already contains them; revisit if host rebuilds are too slow. See [[projects/vscode-migration/findings#Extensions in VSCode tasks]].
## Browser package
Worktree `spm-browser-package`, branch `alex/spm-browser-package` (on top of `alex/move-helper-only-files` + `alex/asset-resource-symbols` + `alex/drop-navigationbar-storyboard`; package work uncommitted).
- `macOS/DuckDuckGo/Package.swift`: target `DuckDuckGo_Privacy_Browser` (same module name as the former app target: storyboards, archived class names, `@testable import`) with `path: "."` — files stay in place. Products `DuckDuckGoBrowser` (static, linked into the app targets) and `DuckDuckGoBrowserDynamic` (for `swift build`).
- Resources stay in the app targets (`Bundle.main`: strings, JSON, scripts, app icons); asset catalogs (for generated `ImageResource`/`ColorResource`) and the 8 Core Data models (codegen entity classes) are processed by the package — models now load from `Bundle.module`.
- App targets: only `DuckDuckGoApp/main.swift` (`AppMain.main()`), module `DuckDuckGoApp`; package reference + `DuckDuckGoBrowser` product added, existing product dependencies kept.
- `SPM_FORCE_DEBUG` in `Package.swift` (same as AppKitExtensions/SyncUI): the CI configuration maps to release for packages.
- `REVIEW` compilation condition can't reach the package → Browser Tools Panel debug menu item is gated at runtime (`isDebugBuild || isReviewBuild`).
- VSCode/Cursor: open `macOS/DuckDuckGo`; `.vscode/` has tasks (host app → `swift build --build-system swiftbuild` → sign), `launch.json`, `make-host-app.sh`, `sign-dylib.sh`, `launcher/launcher.c`.
- Verified (Xcode 27.2, offline): all product schemes Debug, `macOS Browser` Review, unit + integration tests `build-for-testing` Debug and CI (with `SPM_FORCE_DEBUG=1`), Xcode-signed Debug app and the VSCode host run without crashes, `swift build` of the dylib.
- Open: `loc_export.sh` / string catalog extraction now that sources are in a package; CI doesn't build the VSCode path; `Package.resolved` in `macOS/DuckDuckGo` (copied from the project) — keep in sync or generate.
## Target split stages
Goal: no file in `macOS/DuckDuckGo/` is compiled into a helper target (VPN agent, PIR agent, NetP extensions), so the app directory can become one SPM target. `InputFilesChecker` tracks the remaining files in `temporarilyAllowedMisplacedFiles` (one block per stage); a stale entry fails the build, so each stage PR must remove its block.
| Stage | Files | Used by | Home | New deps / project changes | Size & blockers | PR |
|---|---|---|---|---|---|---|
| A | `ApplicationBuildType`, `BuildFlags`, `DuckDuckGoUserAgent` | app + all 6 helpers | `AppKitExtensions` | none (all targets already link it) | 46 imports | [#7029](https://github.com/duckduckgo/apple-browsers/pull/7029) |
| B | `DBPFeatureFlagger`, `DataBrokerAuthenticationManagerBuilder`, `DataBrokerProtectionSettings+Environment`, `WebViewUserAgentProvider`, `SafariVersionReader`, `WebKitVersionProvider` | app + PIR agent | `DataBrokerProtection-macOS` | none (`Subscription` comes transitively, like elsewhere in the package) | 5 imports | [#7029](https://github.com/duckduckgo/apple-browsers/pull/7029) |
| C | `NetworkProtectionPixelEvent` (569 loc) | app, VPN agent, 3 extensions | `NetworkProtectionMac` → `VPNPixels` (new library product) | `VPNPixels` += `VPN`, `Configuration`; BSK package dep; product linked to 7 targets | 9 imports. Ready, uncommitted (`move-vpn-pixel-event`) | own PR, on top of #7029 |
| D | `SubscriptionPixel`, `SubscriptionPixelHandler`, `SubscriptionEnvironment+Default`, `WideEventFeatureFlagAdapter`, `SubscriptionFunnelOrigin` (836 loc) | app, VPN agent, PIR agent, 3 extensions | new local package (no existing one is linked by all consumers) | `Subscription`, `PixelKit`, `Networking`, `PrivacyConfig`, `WideEvent`, `AppKitExtensions`; product linked to ~8 targets | used in ~134 files — biggest diff | own PR (new package, Subscription reviewers) |
| E1 | TipKit ×4, `VPNIPCResources`, `VPNOperationErrorRecorder`, `NetworkProtectionVPNCountryLabelsModel`, `VPNSettings+Environment` (~470 loc) | app + VPN agent | `NetworkProtectionMac` (IPC / UI, or a small `TipKitSupport` target) | `Subscription` for `VPNSettings+Environment` | moderate, no strings | combine with G |
| G | 10 helper-only sources living in `DuckDuckGo/` (`Bundle+VPN`, `NetworkProtectionOptionKeyExtension`, `NetworkProtectionControllerErrorStore`, `NetworkProtectionTunnelController`, `MacPacketTunnelProvider`, `MacTransparentProxyProvider`, `NetworkProtectionNotificationsPresenterFactory`, `VPNFailureRecoveryPixel`, `Bundle+NetworkProtectionExtensions`, `SubscriptionManager+StandardConfiguration`) | helpers only, never the app | `DuckDuckGoVPN/`, new `HelperTargetsShared/` (allowed for helper targets in `InputFilesChecker`) | `git mv` + pbxproj group paths only | done | ✅ `alex/move-helper-only-files` |
| E2 | `UserText+NetworkProtection+Shared`, `DefaultVPNLocationFormatter` | app + VPN agent | `NetworkProtectionUI` (has `Localizable.xcstrings`) | strings → `Bundle.module` with translations; app and VPN agent each have their own `UserText` | blocked on the strings/resources decision | combine with H |
| H | 4 app icons, `macos-config.json`, 7 VPN Lottie JSONs, 2 NetP `.xcstrings` | app + helpers | package resources or per-target copies | resource bundles / `Bundle.module` | same decision as E2 and the app-package tech design | with E2 |
| F | `UserDefaultsWrapper` (415 loc) + `UserDefaults+NetworkProtectionShared` | app, VPN agent, all extensions; ~51 app files | don't move — deprecated in favour of `KeyedStoring` | follows the KeyedStoring migration | blocked on that migration (owner/timeline TBD) | after the migration |
| X (optional) | `NETP_SYSTEM_EXTENSION` in `MacPacketTunnelProvider`, `MacTransparentProxyProvider`, `NetworkProtectionNotificationsPresenterFactory` | extensions only | runtime check `Bundle.main.bundleURL.pathExtension == "systemextension"` | removes the last `FEATURE_FLAGS` from xcconfigs | changes keychain service / defaults selection — needs VPN team review | optional, separate |
Order: C → D → E1+G → strings/resources tech design → E2+H → F after KeyedStoring. X any time if the VPN team agrees.
## Worktrees
| Worktree | Branch | What |
|---|---|---|
| `trim-target-memberships` | `alex/trim-target-memberships` | Stages A+B + membership trim, PR #7029 |
| `move-vpn-pixel-event` | `alex/move-vpn-pixel-event` | Stage C, uncommitted, on top of #7029 |
| `move-helper-only-files` | `alex/move-helper-only-files` | Stage G (committed): helper-only sources → `DuckDuckGoVPN/`, `HelperTargetsShared/` |
| `asset-resource-symbols` | `alex/asset-resource-symbols` | Asset resource initializers (committed) |
| `spm-browser-package` | `alex/spm-browser-package` | Browser code as a SwiftPM package + VSCode setup (WIP, uncommitted) |
| `drop-sb-navigationbar` | `alex/drop-navigationbar-storyboard` | Last storyboard (PR open, merged into `spm-browser-package`) |
| `drop-sb-feedback` | `alex/drop-feedback-storyboard` | Merged (#6866); follow-up commits after the squash? |
| `coredata-spm-hacks-cleanup` | `alex/coredata-spm-hacks-cleanup` | no commits ahead of main yet |
| `netp-dependencies` | `alex/align-netp-dependencies` | Old draft: move files by target (superseded by the stages above) |
| `vscode`, `vscode2` | `alex/fix-imports-cleanup`, `alex/vscode2` | Old: SwiftGenPlugin draft (uncommitted), root `Package.swift` |
| `sweetpad` | `alex/cursor-fix-build` | Old `.vscode` (SweetPad) config |
Gone: `spm-asset-symbols-plugin`, `coredata-code-models*`, `drop-sb-bookmarksbar*` (BookmarksBar storyboard + xib are on main), `appstore-sandbox-ui-tests` (#6980), `drop-dead-target-flags` (#6983), `split-shared-target-files`.
Demo project for the (cancelled) asset approach: `~/Developer/SPMAssetSymbolsDemo` (`./verify.sh`).