Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 52 additions & 7 deletions apps/ui/scripts/prepare-server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,40 @@ for (const pkgName of LOCAL_PACKAGES) {
console.log(` ✓ ${pkgName}`);
}

// Step 5: Create a minimal package.json for the server
// Step 5: Create a minimal package.json for the server (without local packages)
// Also collect external dependencies from local packages
console.log('📝 Creating server package.json...');
const serverPkg = JSON.parse(readFileSync(join(SERVER_DIR, 'package.json'), 'utf-8'));

// Replace local package versions with file: references
// Remove local packages from dependencies - we'll copy them directly to node_modules
// This avoids symlinks that break in packaged apps
const dependencies = { ...serverPkg.dependencies };
for (const pkgName of LOCAL_PACKAGES) {
if (dependencies[pkgName]) {
const pkgDir = pkgName.replace('@automaker/', '');
dependencies[pkgName] = `file:libs/${pkgDir}`;
delete dependencies[pkgName];
}

// Collect external dependencies from local packages
// These need to be installed via npm since we're copying local packages directly
console.log('📦 Collecting dependencies from local packages...');
for (const pkgName of LOCAL_PACKAGES) {
const pkgDir = pkgName.replace('@automaker/', '');
const pkgJsonPath = join(LIBS_DIR, pkgDir, 'package.json');

if (existsSync(pkgJsonPath)) {
const localPkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
if (localPkg.dependencies) {
for (const [depName, depVersion] of Object.entries(localPkg.dependencies)) {
// Skip other local packages - they're handled separately
if (depName.startsWith('@automaker/')) {
continue;
}
// Add external dependency if not already present
if (!dependencies[depName]) {
dependencies[depName] = depVersion;
console.log(` + ${depName}@${depVersion} (from ${pkgName})`);
}
Comment on lines +108 to +111
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current logic for collecting transitive dependencies from local packages doesn't handle version conflicts. If two local packages (or the server and a local package) depend on different versions of the same external dependency, the script will silently use the first version it encounters. This could lead to subtle bugs or runtime errors if the versions are incompatible.

It would be safer to add a warning when a version conflict is detected. This will make developers aware of potential issues during the build process.

        if (!dependencies[depName]) {
          dependencies[depName] = depVersion;
          console.log(`   + ${depName}@${depVersion} (from ${pkgName})`);
        } else if (dependencies[depName] !== depVersion) {
          console.warn(`   ⚠️  Version conflict for ${depName}: existing version is ${dependencies[depName]}, but ${pkgName} requires ${depVersion}. Using existing version.`);
        }

}
}
}
}

Expand All @@ -100,8 +124,9 @@ const bundlePkg = {

writeFileSync(join(BUNDLE_DIR, 'package.json'), JSON.stringify(bundlePkg, null, 2));

// Step 6: Install production dependencies
// Step 6: Install production dependencies (external only)
console.log('📥 Installing server production dependencies...');
// Note: execSync is used here with hardcoded commands (no user input) for build automation
execSync('npm install --omit=dev', {
cwd: BUNDLE_DIR,
stdio: 'inherit',
Expand All @@ -112,7 +137,27 @@ execSync('npm install --omit=dev', {
},
});

// Step 7: Rebuild native modules for current architecture
// Step 7: Copy local packages directly to node_modules (avoiding symlinks)
console.log('📦 Installing local packages to node_modules...');
const nodeModulesAutomaker = join(BUNDLE_DIR, 'node_modules', '@automaker');
mkdirSync(nodeModulesAutomaker, { recursive: true });

for (const pkgName of LOCAL_PACKAGES) {
const pkgDir = pkgName.replace('@automaker/', '');
const srcDir = join(bundleLibsDir, pkgDir);
const destDir = join(nodeModulesAutomaker, pkgDir);

if (existsSync(srcDir)) {
// Remove any existing symlink or directory
if (existsSync(destDir)) {
rmSync(destDir, { recursive: true });
}
cpSync(srcDir, destDir, { recursive: true });
console.log(` ✓ ${pkgName}`);
}
}

// Step 8: Rebuild native modules for current architecture
// This is critical for modules like node-pty that have native bindings
console.log('🔨 Rebuilding native modules for current architecture...');
try {
Expand Down
6 changes: 6 additions & 0 deletions libs/platform/src/system-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ export function electronUserDataReadFileSync(

/**
* Write a file to Electron userData directory
* Ensures parent directory exists before writing
*/
export function electronUserDataWriteFileSync(
relativePath: string,
Expand All @@ -750,6 +751,11 @@ export function electronUserDataWriteFileSync(
throw new Error('[SystemPaths] Electron userData path not initialized');
}
const fullPath = path.join(electronUserDataPath, relativePath);
// Ensure parent directory exists
const parentDir = path.dirname(fullPath);
if (!fsSync.existsSync(parentDir)) {
fsSync.mkdirSync(parentDir, { recursive: true });
}
fsSync.writeFileSync(fullPath, data, options);
}

Expand Down