UNPKG

qraft

Version:

A powerful CLI tool to qraft structured project setups from GitHub template repositories

243 lines • 12.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.updateCommand = updateCommand; const chalk_1 = __importDefault(require("chalk")); const contentComparison_1 = require("../core/contentComparison"); const directoryScanner_1 = require("../core/directoryScanner"); const manifestManager_1 = require("../core/manifestManager"); const repositoryManager_1 = require("../core/repositoryManager"); const confirmationWorkflows_1 = require("../interactive/confirmationWorkflows"); const manifestUtils_1 = require("../utils/manifestUtils"); function createUserPrompt(question) { const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); }); }); } async function showUpdatePreview(preview) { console.log(chalk_1.default.cyan('\nšŸ“‹ Preview: What will be updated\n')); console.log(chalk_1.default.cyan('Current Box:')); console.log(chalk_1.default.gray(` šŸ“¦ Name: ${preview.boxName}`)); console.log(chalk_1.default.gray(` šŸ“ Version: ${preview.currentVersion}`)); console.log(chalk_1.default.gray(` šŸ“ Local Path: ${preview.localPath}`)); console.log(chalk_1.default.cyan('\nProposed Changes:')); console.log(chalk_1.default.gray(` šŸ”„ New Version: ${preview.suggestedVersion}`)); console.log(chalk_1.default.gray(` šŸ  Registry: ${preview.registry}`)); if (preview.hasChanges) { console.log(chalk_1.default.cyan('\nContent Analysis:')); if (preview.conflictCount > 0) { console.log(chalk_1.default.yellow(` āš ļø ${preview.conflictCount} file(s) with conflicts`)); } if (preview.safeFileCount > 0) { console.log(chalk_1.default.green(` āœ… ${preview.safeFileCount} file(s) safe to update`)); } } else { console.log(chalk_1.default.green('\n✨ No content changes detected')); } console.log(chalk_1.default.cyan('\nā“ Confirmation Required')); const answer = await createUserPrompt('Do you want to proceed with updating this box? (y/N): '); return answer.toLowerCase() === 'y'; } async function promptForManifestUpdates(currentManifest) { console.log(chalk_1.default.cyan('\nšŸ“ Update Box Information')); console.log(chalk_1.default.gray('Press Enter to keep current values, or type new values:\n')); const updates = {}; // Description const newDescription = await createUserPrompt(`Description (${chalk_1.default.gray(currentManifest.description)}): `); if (newDescription.trim()) { updates.description = newDescription.trim(); } // Author const currentAuthor = currentManifest.author || 'Not set'; const newAuthor = await createUserPrompt(`Author (${chalk_1.default.gray(currentAuthor)}): `); if (newAuthor.trim()) { updates.author = newAuthor.trim(); } // Tags const currentTags = currentManifest.tags?.join(', ') || 'None'; const newTags = await createUserPrompt(`Tags (${chalk_1.default.gray(currentTags)}): `); if (newTags.trim()) { updates.tags = newTags.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0); } // Version (with smart suggestion) const currentVersion = currentManifest.version; const suggestedVersion = suggestNextVersion(currentVersion); const newVersion = await createUserPrompt(`Version (current: ${chalk_1.default.gray(currentVersion)}, suggested: ${chalk_1.default.cyan(suggestedVersion)}): `); if (newVersion.trim()) { updates.version = newVersion.trim(); } else { updates.version = suggestedVersion; } return updates; } function suggestNextVersion(currentVersion) { // Simple semantic versioning increment const versionParts = currentVersion.split('.'); if (versionParts.length >= 3) { // Increment patch version const patch = parseInt(versionParts[2]) || 0; versionParts[2] = (patch + 1).toString(); return versionParts.join('.'); } else if (versionParts.length === 2) { // Increment minor version const minor = parseInt(versionParts[1]) || 0; versionParts[1] = (minor + 1).toString(); return versionParts.join('.'); } else { // Fallback: append .1 return `${currentVersion}.1`; } } async function updateCommand(boxManager, localPath, options = {}) { console.log(chalk_1.default.blue.bold('šŸ”„ Updating Box from Local Directory')); if (options.interactive !== false) { console.log(chalk_1.default.gray('šŸŽÆ Interactive mode enabled - you\'ll be guided through the process\n')); } try { // Step 1: Validate that this is an existing box console.log(chalk_1.default.cyan('šŸ” Checking for existing box...')); if (!(await manifestUtils_1.ManifestUtils.qraftDirectoryExists(localPath))) { throw new Error(`No .qraft directory found in ${localPath}. Use 'qraft create' for new boxes.`); } if (!(await manifestUtils_1.ManifestUtils.hasCompleteLocalManifest(localPath))) { throw new Error(`Incomplete manifest found in ${localPath}. Please run 'qraft create' to recreate the box.`); } const manifestManager = new manifestManager_1.ManifestManager(); const localManifestEntry = await manifestManager.getLocalManifest(localPath); if (!localManifestEntry) { throw new Error(`Could not read local manifest from ${localPath}`); } const currentManifest = localManifestEntry.manifest; console.log(chalk_1.default.green(`āœ… Found existing box: ${currentManifest.name} v${currentManifest.version}\n`)); // Step 2: Get registry information const effectiveRegistry = options.registry || localManifestEntry.metadata?.sourceRegistry || await boxManager.getConfigManager().getConfig().then(c => c.defaultRegistry); if (!effectiveRegistry) { throw new Error('No registry specified. Use --registry option or configure a default registry.'); } // Step 3: Analyze current content console.log(chalk_1.default.cyan('🧠 Analyzing current content...')); const directoryScanner = new directoryScanner_1.DirectoryScanner(); const structure = await directoryScanner.scanDirectory(localPath); console.log(chalk_1.default.green('āœ… Content analysis complete\n')); // Step 4: Interactive manifest updates (if enabled) let manifestUpdates = {}; if (options.interactive !== false) { manifestUpdates = await promptForManifestUpdates(currentManifest); } else { // Non-interactive: just increment version manifestUpdates.version = suggestNextVersion(currentManifest.version); } // Step 5: Create updated manifest const updatedManifest = { ...currentManifest, ...manifestUpdates }; // Step 6: Content comparison and conflict detection console.log(chalk_1.default.cyan('šŸ” Checking for conflicts...')); const contentComparison = new contentComparison_1.ContentComparison(); // For update workflow, we compare current structure with itself to detect any changes // This is a simplified approach - in a full implementation, you'd compare with the last known state const comparisonResult = await contentComparison.compareDirectories(structure, // old structure (simplified) structure, // new structure (same for now) localPath); const conflictingFiles = contentComparison.getConflictingFiles(comparisonResult); const safeFiles = contentComparison.getSafeFiles(comparisonResult); console.log(chalk_1.default.green('āœ… Conflict analysis complete\n')); // Step 7: Show preview and get confirmation const preview = { localPath, boxName: currentManifest.name, currentVersion: currentManifest.version, suggestedVersion: updatedManifest.version, registry: effectiveRegistry, hasChanges: Object.keys(manifestUpdates).length > 1, // More than just version conflictCount: conflictingFiles.length, safeFileCount: safeFiles.length }; const shouldProceed = await showUpdatePreview(preview); if (!shouldProceed) { console.log(chalk_1.default.yellow('ā¹ļø Update cancelled by user')); return; } // Step 8: Handle conflicts if any if (conflictingFiles.length > 0 && !options.skipConflictResolution) { console.log(chalk_1.default.cyan('\nāš ļø Resolving conflicts...')); const confirmationWorkflows = new confirmationWorkflows_1.ConfirmationWorkflows(); // Show conflicts to user const shouldContinue = await confirmationWorkflows.confirmConflictResolution(conflictingFiles.map(file => { const filePath = typeof file === 'string' ? file : file.path || String(file); return { type: 'overwrite', description: `File will be updated: ${filePath}`, riskLevel: 'medium', affectedFiles: [filePath], recommendations: ['Review changes before proceeding'] }; })); if (!shouldContinue) { console.log(chalk_1.default.yellow('ā¹ļø Update cancelled due to conflicts')); return; } } // Step 9: Update the box console.log(chalk_1.default.cyan('\nšŸš€ Updating box...')); const [registryOwner, registryRepo] = effectiveRegistry.split('/'); const repositoryManager = new repositoryManager_1.RepositoryManager(await boxManager.getGitHubToken(effectiveRegistry)); const updateResult = await repositoryManager.createBox(registryOwner, registryRepo, updatedManifest.name, localPath, updatedManifest, updatedManifest.remotePath, { commitMessage: `feat: update ${updatedManifest.name} to v${updatedManifest.version}`, createPR: true }); if (!updateResult.success) { throw new Error(`Failed to update box in repository: ${updateResult.message}`); } // Step 10: Update local manifest await manifestManager.storeLocalManifest(localPath, updatedManifest, effectiveRegistry, `${effectiveRegistry}/${updatedManifest.name}`, true // isUpdate = true ); // Success! console.log(chalk_1.default.green('\nšŸŽ‰ Box updated successfully!')); console.log(chalk_1.default.cyan('\nšŸ“‹ Summary:')); console.log(chalk_1.default.gray(` šŸ“¦ Name: ${updatedManifest.name}`)); console.log(chalk_1.default.gray(` šŸ”„ Version: ${currentManifest.version} → ${updatedManifest.version}`)); console.log(chalk_1.default.gray(` šŸ“ Description: ${updatedManifest.description}`)); console.log(chalk_1.default.gray(` šŸ  Registry: ${effectiveRegistry}`)); if (updateResult.commitSha) { console.log(chalk_1.default.gray(` šŸ”— Commit: ${updateResult.commitSha.substring(0, 8)}`)); } console.log(chalk_1.default.cyan('\nšŸŽÆ Next Steps:')); console.log(chalk_1.default.gray(` • Use: qraft copy ${updatedManifest.name}`)); console.log(chalk_1.default.gray(' • Share the updated box with others')); if (updateResult.prUrl) { console.log(chalk_1.default.cyan('\nšŸ”— Pull Request:')); console.log(chalk_1.default.gray(` ${updateResult.prUrl}`)); } } catch (error) { console.error(chalk_1.default.red('\nāŒ Error:'), error.message); if (error.code) { console.error(chalk_1.default.gray('Code:'), error.code); } console.error(chalk_1.default.cyan('\nšŸ’” Help:')); console.error(chalk_1.default.gray(' • Run: qraft update --help')); console.error(chalk_1.default.gray(' • Check that the directory contains a valid .qraft manifest')); console.error(chalk_1.default.gray(' • Use: qraft create for new boxes')); process.exit(1); } } //# sourceMappingURL=update.js.map