UNPKG

repoweaver

Version:

A GitHub App that skillfully weaves multiple templates together to create and update repositories with intelligent merge strategies

162 lines (156 loc) 7.25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GitHubTemplateManager = void 0; const merge_strategy_registry_1 = require("./merge-strategy-registry"); class GitHubTemplateManager { constructor(client) { this.client = client; this.mergeRegistry = new merge_strategy_registry_1.MergeStrategyRegistry(); } async processTemplate(template, targetOwner, targetRepo, excludePatterns = [], mergeStrategy = 'merge', mergeStrategies = [], plugins = []) { const result = { success: true, template, filesProcessed: 0, errors: [], }; try { // Load plugins for (const plugin of plugins) { await this.mergeRegistry.loadPlugin(plugin); } // Get template files from GitHub const templateFiles = await this.client.getTemplateFiles(template); // Filter out excluded files const filteredFiles = this.filterFiles(templateFiles, excludePatterns); // Process files based on merge strategy await this.processFiles(filteredFiles, targetOwner, targetRepo, mergeStrategy, mergeStrategies, result); } catch (error) { result.success = false; result.errors.push(`Template processing failed: ${error}`); } return result; } filterFiles(files, excludePatterns) { return files.filter((file) => { // Skip directories in processing if (file.type === 'dir') { return false; } // Apply exclude patterns return !this.shouldExclude(file.path, excludePatterns); }); } shouldExclude(filePath, excludePatterns) { return excludePatterns.some((pattern) => { const regex = new RegExp(pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*')); return regex.test(filePath); }); } async processFiles(files, targetOwner, targetRepo, mergeStrategy, mergeStrategies, result) { const branch = `boots-strapper-update-${Date.now()}`; try { // Create a new branch for the template updates await this.client.createBranch(targetOwner, targetRepo, branch); for (const file of files) { try { // Determine merge strategy for this file const defaultMergeStrategy = typeof mergeStrategy === 'string' ? { type: mergeStrategy } : mergeStrategy; const fileStrategy = await this.mergeRegistry.resolveStrategyForFile(file.path, mergeStrategies, defaultMergeStrategy); const shouldProcess = await this.shouldProcessFile(targetOwner, targetRepo, file.path, fileStrategy.name); if (shouldProcess) { let content = file.content; // Get existing content if file exists let existingContent = ''; try { const existingFiles = await this.client.getRepositoryContents(targetOwner, targetRepo, file.path); const existingFile = existingFiles.find((f) => f.path === file.path && f.type === 'file'); existingContent = existingFile?.content || ''; } catch (error) { // File doesn't exist, which is fine } // Apply merge strategy if (existingContent) { const mergeResult = await fileStrategy.merge({ filePath: file.path, templateName: result.template.name, existingContent, newContent: file.content, }); if (mergeResult.success) { content = mergeResult.content; // Track warnings and conflicts if (mergeResult.warnings) { result.errors.push(...mergeResult.warnings.map((w) => `Warning: ${w}`)); } if (mergeResult.conflicts) { result.errors.push(...mergeResult.conflicts.map((c) => `Conflict: ${c}`)); } } else { result.errors.push(`Merge failed for ${file.path}, using new content`); } } await this.client.createOrUpdateFile(targetOwner, targetRepo, file.path, content, `Update ${file.path} from template ${result.template.name} using ${fileStrategy.name} strategy`, branch); result.filesProcessed++; } } catch (error) { result.errors.push(`Failed to process file ${file.path}: ${error}`); } } // Create a pull request with the changes if (result.filesProcessed > 0) { const prNumber = await this.client.createPullRequest(targetOwner, targetRepo, `Update repository from template: ${result.template.name}`, this.generatePullRequestBody(result.template, result), branch, 'main'); result.pullRequestNumber = prNumber; } } catch (error) { result.success = false; result.errors.push(`Branch creation or PR failed: ${error}`); } } async shouldProcessFile(owner, repo, filePath, strategyName) { if (strategyName === 'overwrite') { return true; } try { // Check if file exists await this.client.getRepositoryContents(owner, repo, filePath); // File exists if (strategyName === 'skip') { return false; } // All other strategies process existing files return true; } catch (error) { // File doesn't exist, so we can create it return true; } } async cleanup() { await this.mergeRegistry.cleanup(); } generatePullRequestBody(template, result) { return ` ## Template Update This pull request updates the repository with changes from the template: **${template.name}** **Template Details:** - Repository: ${template.url} - Branch: ${template.branch || 'main'} ${template.subDirectory ? `- Subdirectory: ${template.subDirectory}` : ''} **Changes:** - ${result.filesProcessed} files processed ${result.errors.length > 0 ? `- ${result.errors.length} errors encountered` : ''} **Files Modified:** <!-- This will be populated with the actual file list --> --- *This PR was automatically generated by [RepoWeaver](https://github.com/apps/repoweaver)* `.trim(); } } exports.GitHubTemplateManager = GitHubTemplateManager; //# sourceMappingURL=github-template-manager.js.map