UNPKG

pgit-cli

Version:

Private file tracking with dual git repositories

273 lines (270 loc) 11.3 kB
import * as path from 'node:path'; import chalk from 'chalk'; import { DEFAULT_PATHS } from '../types/config.types.js'; import { ConfigManager } from '../core/config.manager.js'; import { FileSystemService } from '../core/filesystem.service.js'; import { GitService } from '../core/git.service.js'; import { PlatformDetector } from '../utils/platform.detector.js'; import { BaseError } from '../errors/base.error.js'; /** * Init command specific errors */ export class InitError extends BaseError { constructor() { super(...arguments); this.code = 'INIT_ERROR'; this.recoverable = true; } } export class AlreadyInitializedError extends BaseError { constructor() { super(...arguments); this.code = 'ALREADY_INITIALIZED'; this.recoverable = false; } } /** * Initialize private git tracking command */ export class InitCommand { constructor(workingDir) { this.workingDir = workingDir || process.cwd(); this.fileSystem = new FileSystemService(); this.configManager = new ConfigManager(this.workingDir, this.fileSystem); } /** * Execute the init command */ async execute(options = {}) { try { if (options.verbose) { console.log(chalk.blue('🔧 Initializing private git tracking...')); } // Check if already initialized await this.checkNotAlreadyInitialized(); // Validate environment await this.validateEnvironment(options.verbose); // Create directory structure await this.createDirectoryStructure(options.verbose); // Initialize private git repository await this.initializePrivateRepository(options.verbose); // Create configuration await this.createConfiguration(options.verbose); // Update .gitignore await this.updateGitignore(options.verbose); // Create initial commit in private repository await this.createInitialCommit(options.verbose); return { success: true, message: 'Private git tracking initialized successfully', exitCode: 0, }; } catch (error) { if (error instanceof BaseError) { return { success: false, message: error.message, error, exitCode: 1, }; } return { success: false, message: 'Failed to initialize private git tracking', error: error instanceof Error ? error : new Error(String(error)), exitCode: 1, }; } } /** * Check if private git tracking is already initialized */ async checkNotAlreadyInitialized() { if (await this.configManager.exists()) { throw new AlreadyInitializedError('Private git tracking is already initialized in this directory. Use "private status" to check current state.'); } // Check if directories already exist const privateRepoPath = path.join(this.workingDir, DEFAULT_PATHS.privateRepo); const storagePath = path.join(this.workingDir, DEFAULT_PATHS.storage); if (await this.fileSystem.pathExists(privateRepoPath)) { throw new AlreadyInitializedError(`Private repository directory already exists: ${DEFAULT_PATHS.privateRepo}`); } if (await this.fileSystem.pathExists(storagePath)) { throw new AlreadyInitializedError(`Private storage directory already exists: ${DEFAULT_PATHS.storage}`); } } /** * Validate the environment before initialization */ async validateEnvironment(verbose) { if (verbose) { console.log(chalk.gray(' Validating environment...')); } // Check if current directory is a git repository const mainGitService = new GitService(this.workingDir, this.fileSystem); if (!(await mainGitService.isRepository())) { throw new InitError('Current directory is not a git repository. Please run "git init" first or navigate to a git repository.'); } // Check symbolic link support const supportsSymlinks = await PlatformDetector.supportsSymlinks(); if (!supportsSymlinks) { console.log(chalk.yellow('⚠️ Warning: Your system may not support symbolic links. Some features may not work correctly.')); if (PlatformDetector.isWindows()) { console.log(chalk.yellow(' On Windows, you may need to run as Administrator or enable Developer Mode.')); } } // Check write permissions const permissions = await PlatformDetector.checkPermissions(this.workingDir); if (!permissions.writable) { throw new InitError('Cannot write to current directory. Please check permissions.'); } if (verbose) { console.log(chalk.green(' ✓ Environment validation passed')); } } /** * Create the directory structure for private git tracking */ async createDirectoryStructure(verbose) { if (verbose) { console.log(chalk.gray(' Creating directory structure...')); } const privateRepoPath = path.join(this.workingDir, DEFAULT_PATHS.privateRepo); const storagePath = path.join(this.workingDir, DEFAULT_PATHS.storage); try { // Create private repository directory await this.fileSystem.createDirectory(privateRepoPath); if (verbose) { console.log(chalk.gray(` ✓ Created ${DEFAULT_PATHS.privateRepo}/`)); } // Create private storage directory await this.fileSystem.createDirectory(storagePath); if (verbose) { console.log(chalk.gray(` ✓ Created ${DEFAULT_PATHS.storage}/`)); } } catch (error) { throw new InitError('Failed to create directory structure', error instanceof Error ? error.message : String(error)); } } /** * Initialize the private git repository */ async initializePrivateRepository(verbose) { if (verbose) { console.log(chalk.gray(' Initializing private git repository...')); } try { const storagePath = path.join(this.workingDir, DEFAULT_PATHS.storage); const privateGitService = new GitService(storagePath, this.fileSystem); // Initialize git repository in storage directory await privateGitService.initRepository(); if (verbose) { console.log(chalk.green(' ✓ Private git repository initialized')); } } catch (error) { throw new InitError('Failed to initialize private git repository', error instanceof Error ? error.message : String(error)); } } /** * Create the initial configuration */ async createConfiguration(verbose) { if (verbose) { console.log(chalk.gray(' Creating configuration...')); } try { await this.configManager.create(this.workingDir); if (verbose) { console.log(chalk.green(` ✓ Configuration created: ${DEFAULT_PATHS.config}`)); } } catch (error) { throw new InitError('Failed to create configuration', error instanceof Error ? error.message : String(error)); } } /** * Update .gitignore to exclude private system files */ async updateGitignore(verbose) { if (verbose) { console.log(chalk.gray(' Updating .gitignore...')); } try { const gitignorePath = path.join(this.workingDir, DEFAULT_PATHS.gitignore); // Read existing .gitignore or create empty content let gitignoreContent = ''; if (await this.fileSystem.pathExists(gitignorePath)) { gitignoreContent = await this.fileSystem.readFile(gitignorePath); } // Define private git entries const privateEntries = [ '', '# Private Git Tracking (auto-generated)', DEFAULT_PATHS.privateRepo, DEFAULT_PATHS.storage, DEFAULT_PATHS.config, '', ]; // Check if entries already exist const entriesToAdd = []; for (const entry of privateEntries) { if (entry.trim() && !gitignoreContent.includes(entry)) { entriesToAdd.push(entry); } } // Add entries if needed if (entriesToAdd.length > 0) { if (gitignoreContent && !gitignoreContent.endsWith('\n')) { gitignoreContent += '\n'; } gitignoreContent += entriesToAdd.join('\n'); await this.fileSystem.writeFileAtomic(gitignorePath, gitignoreContent); if (verbose) { console.log(chalk.green(' ✓ Updated .gitignore with private git exclusions')); } } else if (verbose) { console.log(chalk.gray(' ✓ .gitignore already contains private git exclusions')); } } catch (error) { // This is not critical, so we log a warning but don't fail console.log(chalk.yellow(` ⚠️ Warning: Could not update .gitignore: ${error instanceof Error ? error.message : String(error)}`)); } } /** * Create initial commit in private repository */ async createInitialCommit(verbose) { if (verbose) { console.log(chalk.gray(' Creating initial commit in private repository...')); } try { const storagePath = path.join(this.workingDir, DEFAULT_PATHS.storage); const privateGitService = new GitService(storagePath, this.fileSystem); // Create initial README file const readmePath = path.join(storagePath, 'README.md'); const readmeContent = `# Private Files Storage This directory contains private files that are excluded from the main repository. - Initialized: ${new Date().toISOString()} - Platform: ${PlatformDetector.getPlatformName()} - CLI Version: 1.0.0 Do not modify this directory manually. Use the \`private\` CLI commands instead. `; await this.fileSystem.writeFileAtomic(readmePath, readmeContent); // Add and commit the README await privateGitService.addFiles(['README.md']); const commitHash = await privateGitService.commit('Initial commit: Private files storage initialized'); if (verbose) { console.log(chalk.green(` ✓ Initial commit created: ${commitHash.substring(0, 8)}`)); } } catch (error) { throw new InitError('Failed to create initial commit', error instanceof Error ? error.message : String(error)); } } } //# sourceMappingURL=init.command.js.map