eigent/scripts/clean-symlinks.js
Tong Chen af93bb3065
feat: Add Lint & Format (#878)
Co-authored-by: a7m-1st <Ahmed.jimi.awelkeir500@gmail.com>
Co-authored-by: eigent-ai <camel@eigent.ai>
Co-authored-by: Wendong-Fan <133094783+Wendong-Fan@users.noreply.github.com>
Co-authored-by: Wendong-Fan <w3ndong.fan@gmail.com>
2026-02-01 23:16:18 +08:00

187 lines
5.4 KiB
JavaScript

#!/usr/bin/env node
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
/* global console, process */
/**
* Clean invalid symbolic links before packaging
* This script removes symbolic links that point outside the bundle
* or to non-existent files, which can cause codesign to fail
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const projectRoot = path.resolve(__dirname, '..');
// const VENV_DIR = path.join(projectRoot, 'resources', 'prebuilt', 'venv');
/**
* Check if a symlink is valid (points to an existing file within the bundle)
*/
function isValidSymlink(symlinkPath, bundleRoot) {
try {
const stats = fs.lstatSync(symlinkPath);
if (!stats.isSymbolicLink()) {
return true; // Not a symlink, so it's valid
}
const target = fs.readlinkSync(symlinkPath);
const resolvedPath = path.resolve(path.dirname(symlinkPath), target);
const bundlePath = path.resolve(bundleRoot);
// Check if target exists
if (!fs.existsSync(resolvedPath)) {
return false; // Target doesn't exist
}
// Check if target is within bundle
if (!resolvedPath.startsWith(bundlePath)) {
return false; // Target is outside bundle
}
return true;
} catch (error) {
console.error(`Error checking symlink: ${error}`);
return false;
}
}
/**
* Fix Python symlinks in venv/bin (Unix) or venv/Scripts (Windows)
* Remove symlinks that point outside the bundle (to cache directory)
*/
function fixPythonSymlinks(venvBinDir, bundleRoot) {
if (!fs.existsSync(venvBinDir)) {
return;
}
const bundlePath = path.resolve(bundleRoot);
const isWindows = process.platform === 'win32';
const pythonNames = isWindows
? [
'python.exe',
'python3.exe',
'python3.10.exe',
'python3.11.exe',
'python3.12.exe',
]
: ['python', 'python3', 'python3.10', 'python3.11', 'python3.12'];
for (const pythonName of pythonNames) {
const pythonSymlink = path.join(venvBinDir, pythonName);
if (fs.existsSync(pythonSymlink)) {
try {
const stats = fs.lstatSync(pythonSymlink);
if (stats.isSymbolicLink()) {
const target = fs.readlinkSync(pythonSymlink);
const resolvedPath = path.resolve(
path.dirname(pythonSymlink),
target
);
// If symlink points outside bundle (especially to cache), remove it
if (!resolvedPath.startsWith(bundlePath)) {
console.log(
`Removing invalid ${pythonName} symlink pointing to: ${target}`
);
fs.unlinkSync(pythonSymlink);
}
}
} catch (error) {
console.warn(
`Warning: Could not process ${pythonName} symlink: ${error.message}`
);
}
}
}
}
/**
* Remove invalid symlinks recursively
*/
function cleanSymlinks(dir, bundleRoot, removed = []) {
if (!fs.existsSync(dir)) {
return removed;
}
try {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
try {
if (entry.isSymbolicLink()) {
if (!isValidSymlink(fullPath, bundleRoot)) {
console.log(`Removing invalid symlink: ${fullPath}`);
fs.unlinkSync(fullPath);
removed.push(fullPath);
}
} else if (entry.isDirectory()) {
// Skip certain directories that might have many symlinks
if (entry.name === 'node_modules' || entry.name === '__pycache__') {
continue;
}
cleanSymlinks(fullPath, bundleRoot, removed);
}
} catch (error) {
// Ignore errors for individual files
console.warn(
`Warning: Could not process ${fullPath}: ${error.message}`
);
}
}
} catch (error) {
console.warn(`Warning: Could not read directory ${dir}: ${error.message}`);
}
return removed;
}
/**
* Main function
*/
function main() {
console.log('🧹 Cleaning invalid symbolic links...');
const bundleRoot = path.join(projectRoot, 'resources', 'prebuilt');
const isWindows = process.platform === 'win32';
const venvBinDir = path.join(
bundleRoot,
'venv',
isWindows ? 'Scripts' : 'bin'
);
// First, try to fix Python symlinks specifically
if (fs.existsSync(venvBinDir)) {
fixPythonSymlinks(venvBinDir, bundleRoot);
}
// Then clean all other invalid symlinks
const removed = cleanSymlinks(bundleRoot, bundleRoot);
if (removed.length > 0) {
console.log(`✅ Removed ${removed.length} invalid symbolic link(s)`);
} else {
console.log('✅ No invalid symbolic links found');
}
}
main();