#!/usr/bin/env node
/**
* Chrome-launcher Sync Script
* Comprehensive integration tool for merging chrome-launcher updates
* with brave-real-launcher while preserving all Brave-specific features
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
class ChromeLauncherSyncManager {
constructor() {
this.projectRoot = process.cwd();
this.tempDir = '/tmp/chrome-launcher-sync';
this.backupDir = '/tmp/brave-backup-sync';
this.chromeVersion = null;
this.chromeCommit = null;
}
async run(targetVersion = 'latest') {
console.log('π Starting Chrome-launcher Comprehensive Sync...\n');
try {
// Step 1: Prepare environment
await this.prepareEnvironment();
// Step 2: Fetch chrome-launcher
await this.fetchChromeLauncher(targetVersion);
// Step 3: Backup Brave-specific files
await this.backupBraveFiles();
// Step 4: Integrate chrome-launcher code
await this.integrateChromeLauncher();
// Step 5: Restore and enhance Brave features
await this.restoreBraveFeatures();
// Step 6: Smart version increment
await this.incrementVersion();
// Step 7: Update configurations
await this.updateConfigurations();
// Step 8: Verify integration
await this.verifyIntegration();
console.log('\nπ Chrome-launcher sync completed successfully!');
console.log(`β
Updated to chrome-launcher v${this.chromeVersion}`);
console.log(`π¦ All Brave features preserved and enhanced`);
} catch (error) {
console.error('\nβ Sync failed:', error.message);
process.exit(1);
} finally {
// Cleanup
await this.cleanup();
}
}
async prepareEnvironment() {
console.log('π Preparing sync environment...');
// Remove old temp directories
if (fs.existsSync(this.tempDir)) {
execSync(`rm -rf "${this.tempDir}"`);
}
if (fs.existsSync(this.backupDir)) {
execSync(`rm -rf "${this.backupDir}"`);
}
// Create temp directories
fs.mkdirSync(this.tempDir, { recursive: true });
fs.mkdirSync(this.backupDir, { recursive: true });
console.log('β
Environment prepared');
}
async fetchChromeLauncher(targetVersion) {
console.log(`π₯ Fetching chrome-launcher ${targetVersion}...`);
process.chdir(this.tempDir);
// Clone chrome-launcher
execSync('git clone https://github.com/GoogleChrome/chrome-launcher.git .');
// Get version info
if (targetVersion !== 'latest') {
try {
execSync(`git checkout v${targetVersion}`);
this.chromeVersion = targetVersion;
} catch (e) {
execSync(`git checkout ${targetVersion}`);
this.chromeVersion = targetVersion;
}
} else {
// Get latest tag
const latestTag = execSync('git describe --tags --abbrev=0').toString().trim();
this.chromeVersion = latestTag.replace('v', '');
try {
execSync(`git checkout ${latestTag}`);
} catch (e) {
console.log('Using main branch');
}
}
this.chromeCommit = execSync('git rev-parse --short HEAD').toString().trim();
console.log(`β
Fetched chrome-launcher v${this.chromeVersion} (${this.chromeCommit})`);
process.chdir(this.projectRoot);
}
async backupBraveFiles() {
console.log('πΎ Backing up Brave-specific files...');
const braveFiles = [
'src/brave-finder.ts',
'src/brave-launcher.ts',
'src/flags.ts',
'src/utils.ts',
'src/index.ts',
'bin/',
'examples/',
'scripts/',
'test-ci.cjs',
'test-dual-modules.cjs',
'package.json',
'tsconfig.json',
'README.md',
'CHANGELOG.md',
'GITHUB_SETUP.md'
];
braveFiles.forEach(file => {
const srcPath = path.join(this.projectRoot, file);
const destPath = path.join(this.backupDir, file);
if (fs.existsSync(srcPath)) {
// Create directory if needed
const destDir = path.dirname(destPath);
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
}
// Copy file/directory
execSync(`cp -r "${srcPath}" "${destPath}"`);
console.log(` β
Backed up: ${file}`);
}
});
console.log('β
Brave files backed up');
}
async integrateChromeLauncher() {
console.log('π Integrating chrome-launcher code...');
// Clean up any existing chrome-chrome-* files
await this.cleanupDuplicateFiles();
// 1. Copy Reference Files
const chromePkgSrc = path.join(this.tempDir, 'package.json');
const chromePkgDest = path.join(this.projectRoot, 'chrome-launcher-package.json');
if (fs.existsSync(chromePkgSrc)) fs.copyFileSync(chromePkgSrc, chromePkgDest);
const chromeReadmeSrc = path.join(this.tempDir, 'README.md');
const chromeReadmeDest = path.join(this.projectRoot, 'chrome-launcher-README.md');
if (fs.existsSync(chromeReadmeSrc)) fs.copyFileSync(chromeReadmeSrc, chromeReadmeDest);
// 2. Copy Safe Files (Utilities that change rarely and can be synced)
const safeFiles = [
'src/random-port.ts',
'src/utils.ts' // Warning: Check if Brave utils has changes. Assuming upstream utils are better/newer.
// Note: Brave-Real-Launcher utils.ts has Brave specific code?
// If so, we should NOT overwrite utils.ts without checking.
// For safety, we ONLY merge flags.ts automatically for now.
];
// safeFiles.forEach... (Skipped for safety as per analysis, only flags is safe to merge if smart)
console.log('β
Chrome-launcher reference files integrated');
}
async restoreBraveFeatures() {
console.log('π¦ Restoring and enhancing Brave features...');
const currentVersion = this.getCurrentVersion();
console.log(` π¦ Preserving current version: ${currentVersion}`);
// Restore backup
if (fs.existsSync(this.backupDir)) {
const backupFiles = fs.readdirSync(this.backupDir);
backupFiles.forEach(file => {
if (file !== 'package.json') { // package.json handled separately
const srcPath = path.join(this.backupDir, file);
const destPath = path.join(this.projectRoot, file);
execSync(`cp -r "${srcPath}" "${destPath}"`);
}
});
}
// Merge package.json
await this.mergePackageJsonDependencies(currentVersion);
// SMART MERGE: Flags
await this.mergeFlags();
console.log('β
Brave features restored and enhanced');
}
async mergeFlags() {
console.log('π© Smart Merging flags.ts...');
const chromeFlagsPath = path.join(this.tempDir, 'src/flags.ts');
const braveFlagsPath = path.join(this.projectRoot, 'src/flags.ts');
if (fs.existsSync(chromeFlagsPath) && fs.existsSync(braveFlagsPath)) {
let chromeContent = fs.readFileSync(chromeFlagsPath, 'utf8');
// Inject Brave-specific flags
// We hardcode the essential Brave flags to ensure they are always present even if backup is weird
// But we should also respect the file structure.
const braveFeaturesToAdd = [
' // Brave-specific: disable Brave Ads',
" 'BraveAds',",
" 'BraveRewards',",
" 'BraveNews',",
" 'BraveWallet',",
" 'BraveP3A',",
" 'BraveP3ANotice',",
" 'BraveAnalyticsConsent',"
].join('\n');
const braveFlagsToAdd = [
' // Brave-specific flags',
" '--disable-brave-secure-dns',",
" '--disable-brave-update',",
" '--disable-tor-client-updater',",
" '--disable-brave-p3a',",
" '--disable-brave-p3a-notice',",
" '--disable-brave-stats-updater',",
" '--disable-brave-referral-ping',"
].join('\n');
// Inject Features
if (chromeContent.includes('.join(\',\')')) {
// Look for the end of the features array
chromeContent = chromeContent.replace(
/(\s*)\/\/ Disable all extensions/i,
`\n${braveFeaturesToAdd}\n$1// Disable all extensions` // Rough injection point before array close usually?
// Actually flags.ts structure is:
// array join(',') ...
// We need to inject BEFORE .join(',')
);
// Better regex finding the array end
// array of features ends with ].join(',')
chromeContent = chromeContent.replace(
/(\s*)\]\.join\(/,
`$1$1// Brave Specific Features\n$1$1'BraveAds',\n$1$1'BraveRewards',\n$1$1'BraveNews',\n$1$1'BraveWallet',\n$1$1'BraveP3A',\n$1$1'BraveP3ANotice',\n$1$1'BraveAnalyticsConsent',\n$1].join(`
);
}
// Inject Flags
// Insert before the last ];
chromeContent = chromeContent.replace(
/(\s*)\];\s*$/,
`\n${braveFlagsToAdd}\n$1];`
);
fs.writeFileSync(braveFlagsPath, chromeContent);
console.log(' β
flags.ts merged with Brave features');
}
}
async enhanceBraveFiles() {
// Deprecated by mergeFlags
}
async incrementVersion() {
console.log('π¦ Smart Version Increment...');
try {
// Use version increment utility
const VersionManager = require('./version-increment.cjs');
const versionManager = new VersionManager();
// Always increment with force flag for continuous updates
const result = await versionManager.run({
force: true,
strategy: 'auto'
});
if (result.success && result.incremented) {
console.log(` β
Version incremented: ${result.oldVersion} β ${result.newVersion}`);
console.log(` π Reason: ${result.reason}`);
// Update chrome version reference for consistency
if (result.newVersion !== this.chromeVersion) {
this.chromeVersion = result.newVersion;
}
} else {
console.log(' βΉοΈ Version increment skipped');
}
} catch (error) {
console.error(' β οΈ Version increment warning:', error.message);
console.log(' π Continuing with existing version...');
}
console.log('β
Version management completed');
}
async updateConfigurations() {
console.log('βοΈ Updating configurations...');
// Update dependencies
console.log(' π¦ Installing updated dependencies...');
execSync('npm install', { cwd: this.projectRoot });
// Update TypeScript build
console.log(' π¨ Updating TypeScript build...');
try {
execSync('npm run build:cjs', { cwd: this.projectRoot });
} catch (e) {
console.log(' β οΈ Build will be handled later');
}
console.log('β
Configurations updated');
}
async verifyIntegration() {
console.log('π§ͺ Verifying integration...');
const checks = [
{ name: 'Package.json', file: 'package.json' },
{ name: 'Brave Launcher', file: 'src/brave-launcher.ts' },
{ name: 'Brave Finder', file: 'src/brave-finder.ts' },
{ name: 'Brave Flags', file: 'src/flags.ts' },
{ name: 'Index Exports', file: 'src/index.ts' },
{ name: 'Utils', file: 'src/utils.ts' },
{ name: 'Chrome Reference', file: 'chrome-launcher-package.json', optional: true }
];
let allGood = true;
checks.forEach(check => {
if (fs.existsSync(path.join(this.projectRoot, check.file))) {
console.log(` β
${check.name}`);
} else if (check.optional) {
console.log(` βΉοΈ ${check.name} - Optional, not found`);
} else {
console.log(` β ${check.name} - Missing!`);
allGood = false;
}
});
if (!allGood) {
throw new Error('Integration verification failed - some files are missing');
}
console.log('β
Integration verified');
}
async cleanupDuplicateFiles() {
console.log('π§Ή Cleaning up duplicate chrome files...');
const srcDir = path.join(this.projectRoot, 'src');
const duplicatePatterns = [
'chrome-chrome-*.ts',
'chrome-chrome-*.js'
];
try {
duplicatePatterns.forEach(pattern => {
const files = require('glob').sync(pattern, { cwd: srcDir });
files.forEach(file => {
const fullPath = path.join(srcDir, file);
if (fs.existsSync(fullPath)) {
fs.unlinkSync(fullPath);
console.log(` ποΈ Removed duplicate: ${file}`);
}
});
});
console.log('β
Duplicate files cleaned up');
} catch (e) {
// If glob is not available, use alternative approach
console.log('βΉοΈ Using alternative cleanup method...');
this.alternativeCleanup();
}
}
alternativeCleanup() {
const srcDir = path.join(this.projectRoot, 'src');
try {
const files = fs.readdirSync(srcDir);
files.forEach(file => {
if (file.startsWith('chrome-chrome-') && file.endsWith('.ts')) {
const fullPath = path.join(srcDir, file);
fs.unlinkSync(fullPath);
console.log(` ποΈ Removed duplicate: ${file}`);
}
});
} catch (e) {
console.log('β οΈ Alternative cleanup had issues:', e.message);
}
}
async cleanup() {
console.log('π§Ή Cleaning up...');
try {
if (fs.existsSync(this.tempDir)) {
execSync(`rm -rf "${this.tempDir}"`);
}
if (fs.existsSync(this.backupDir)) {
execSync(`rm -rf "${this.backupDir}"`);
}
console.log('β
Cleanup completed');
} catch (e) {
console.log('β οΈ Cleanup had issues:', e.message);
}
}
}
// CLI interface
if (require.main === module) {
const version = process.argv[2] || 'latest';
const syncManager = new ChromeLauncherSyncManager();
syncManager.run(version).catch(console.error);
}
module.exports = ChromeLauncherSyncManager;