fix(electron): Fix broken symlinks in server bundle preventing app startup#743
fix(electron): Fix broken symlinks in server bundle preventing app startup#743
Conversation
…artup Fixes #742 This commit resolves two critical issues that prevented the Electron app from starting: 1. **Broken symlinks in server bundle** - After npm install, local @automaker/* packages were symlinked in node_modules - These symlinks broke after electron-builder packaging since relative paths no longer existed - Solution: Added Step 6b in prepare-server.mjs to replace symlinks with real directory copies - Added lstatSync and resolve imports to support symlink detection and replacement 2. **electronUserDataWriteFileSync fails on first launch** - The userData directory doesn't exist on first app launch - Writing .api-key file would fail with ENOENT error - Solution: Added directory existence check and creation with { recursive: true } before writing Files modified: - apps/ui/scripts/prepare-server.mjs: Added symlink replacement logic after npm install - libs/platform/src/system-paths.ts: Added parent directory creation in electronUserDataWriteFileSync Verification: After these fixes, npm run build:electron produces a working app that starts without ERR_MODULE_NOT_FOUND errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Summary of ChangesHello @Shironex, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses two critical issues that were preventing the Electron application from starting. It resolves problems related to broken symbolic links for local packages within the packaged server bundle and ensures that user data files can be written successfully on the very first launch, even when the application's dedicated data directory has not yet been created. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
📝 WalkthroughWalkthroughThis PR replaces symlinked local packages in the server bundle with real directory copies after npm install, and ensures electronUserData write calls create parent directories before writing files. Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces two important fixes for the Electron app startup. The first fix addresses broken symlinks in the server bundle by replacing them with actual directory copies, which is a solid approach. The second fix ensures that the userData directory is created on first launch, preventing file write errors.
I've identified a potential issue in the symlink replacement logic where broken symlinks might not be handled correctly. I've also suggested a minor simplification in the directory creation logic. Overall, these are great fixes that improve the robustness of the application startup.
- Use lstatSync with try/catch for robust broken symlink detection - Remove redundant existsSync check before mkdirSync with recursive: true Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
libs/platform/src/system-paths.ts (1)
744-758:⚠️ Potential issue | 🟠 MajorAdd path traversal protection to
electronUserDataWriteFileSync.The function currently accepts
relativePathwithout validation, allowing..sequences to escapeelectronUserDataPath. While existing call sites use hardcoded filenames, the API should enforce containment to prevent future misuse. Add normalized path validation before directory creation and write.🔒 Proposed fix
- const fullPath = path.join(electronUserDataPath, relativePath); - // Ensure parent directory exists (may not exist on first launch) - const dir = path.dirname(fullPath); - if (!fsSync.existsSync(dir)) { - fsSync.mkdirSync(dir, { recursive: true }); - } - fsSync.writeFileSync(fullPath, data, options); + const fullPath = path.resolve(electronUserDataPath, relativePath); + const normalizedUserData = path.resolve(electronUserDataPath); + if ( + fullPath !== normalizedUserData && + !fullPath.startsWith(normalizedUserData + path.sep) + ) { + throw new Error('[SystemPaths] Electron userData path traversal blocked'); + } + const dir = path.dirname(fullPath); + fsSync.mkdirSync(dir, { recursive: true }); + fsSync.writeFileSync(fullPath, data, options);
🧹 Nitpick comments (1)
apps/ui/scripts/prepare-server.mjs (1)
115-128: Harden symlink replacement against TOCTOU races and Windows junctions.The existing
existsSync+lstatSynccheck beforermSynccreates a race condition: the path state can change between the checks and the removal. On Windows, npm can create junctions in addition to symlinks. Wrap the logic in a try/catch block and usermSyncwith{ recursive: true, force: true }for robustness; also validate that the real package directory exists before copying to avoid leaving a missing target.♻️ Proposed change
const nodeModulesAutomaker = join(BUNDLE_DIR, 'node_modules', '@automaker'); for (const pkgName of LOCAL_PACKAGES) { const pkgDir = pkgName.replace('@automaker/', ''); const nmPkgPath = join(nodeModulesAutomaker, pkgDir); - if (existsSync(nmPkgPath) && lstatSync(nmPkgPath).isSymbolicLink()) { - const realPath = resolve(BUNDLE_DIR, 'libs', pkgDir); - rmSync(nmPkgPath); - cpSync(realPath, nmPkgPath, { recursive: true }); - console.log(` ✓ Replaced symlink: ${pkgName}`); - } + try { + if (!lstatSync(nmPkgPath).isSymbolicLink()) continue; + const realPath = resolve(BUNDLE_DIR, 'libs', pkgDir); + if (!existsSync(realPath)) { + console.warn(`⚠️ Missing real package dir for ${pkgName}: ${realPath}`); + continue; + } + rmSync(nmPkgPath, { recursive: true, force: true }); + cpSync(realPath, nmPkgPath, { recursive: true }); + console.log(` ✓ Replaced symlink: ${pkgName}`); + } catch { + // Not a symlink or disappeared between checks; skip. + } }
Fixes #742
This commit resolves two critical issues that prevented the Electron app from starting:
Broken symlinks in server bundle
electronUserDataWriteFileSync fails on first launch
Files modified:
Verification: After these fixes, npm run build:electron produces a working app that starts without ERR_MODULE_NOT_FOUND errors.
Summary by CodeRabbit