UNPKG

repoweaver

Version:

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

191 lines 8.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebhookHandler = void 0; const crypto_1 = require("crypto"); const github_client_1 = require("./github-client"); const github_bootstrapper_1 = require("./github-bootstrapper"); class WebhookHandler { constructor(webhookSecret, appId, privateKey, database) { this.webhookSecret = webhookSecret; this.appId = appId; this.privateKey = privateKey; this.database = database; } async handleWebhook(req, res) { try { // Verify webhook signature if (!this.verifySignature(req)) { res.status(401).json({ error: 'Invalid signature' }); return; } const event = req.headers['x-github-event']; const payload = req.body; console.log(`Received webhook: ${event} - ${payload.action}`); switch (event) { case 'installation': await this.handleInstallation(payload); break; case 'installation_repositories': await this.handleInstallationRepositories(payload); break; case 'push': await this.handlePush(payload); break; case 'repository': await this.handleRepository(payload); break; case 'pull_request': await this.handlePullRequest(payload); break; default: console.log(`Unhandled event: ${event}`); } res.status(200).json({ message: 'Webhook processed successfully' }); } catch (error) { console.error('Webhook processing error:', error); res.status(500).json({ error: 'Internal server error' }); } } verifySignature(req) { const signature = req.headers['x-hub-signature-256']; if (!signature) { return false; } const hmac = (0, crypto_1.createHmac)('sha256', this.webhookSecret); hmac.update(JSON.stringify(req.body)); const expectedSignature = `sha256=${hmac.digest('hex')}`; return signature === expectedSignature; } async handleInstallation(payload) { if (!payload.installation) return; const { installation } = payload; switch (payload.action) { case 'created': await this.database.createInstallation({ id: installation.id, account: installation.account.login, accountType: installation.account.type }); console.log(`Installation created for ${installation.account.login}`); break; case 'deleted': await this.database.deleteInstallation(installation.id); console.log(`Installation deleted for ${installation.account.login}`); break; case 'suspend': await this.database.suspendInstallation(installation.id); console.log(`Installation suspended for ${installation.account.login}`); break; case 'unsuspend': await this.database.unsuspendInstallation(installation.id); console.log(`Installation unsuspended for ${installation.account.login}`); break; } } async handleInstallationRepositories(payload) { if (!payload.installation || !payload.repositories) return; const { installation, repositories } = payload; switch (payload.action) { case 'added': for (const repo of repositories) { await this.database.addRepositoryToInstallation(installation.id, repo.name, repo.full_name); } console.log(`Added ${repositories.length} repositories to installation ${installation.id}`); break; case 'removed': for (const repo of repositories) { await this.database.removeRepositoryFromInstallation(installation.id, repo.name); } console.log(`Removed ${repositories.length} repositories from installation ${installation.id}`); break; } } async handlePush(payload) { if (!payload.repository || !payload.installation) return; const { repository, installation } = payload; // Check if this is a push to a template repository const templateConfigs = await this.database.getTemplateConfigurations(repository.full_name); if (templateConfigs.length > 0) { console.log(`Template repository ${repository.full_name} was updated`); // Queue updates for all repositories using this template for (const config of templateConfigs) { await this.queueTemplateUpdate(installation.id, config.targetRepository, repository.full_name); } } } async handleRepository(payload) { if (!payload.repository || !payload.installation) return; const { repository, installation } = payload; switch (payload.action) { case 'created': // Check if this repository should be auto-configured with templates const installationConfig = await this.database.getInstallationConfig(installation.id); if (installationConfig?.autoConfigureTemplates) { await this.autoConfigureRepository(installation.id, repository); } break; case 'deleted': await this.database.deleteRepositoryConfig(repository.full_name); break; } } async handlePullRequest(payload) { if (!payload.repository || !payload.installation) return; // Handle PR events related to template updates // This could include auto-merging approved template updates console.log(`Pull request ${payload.action} in ${payload.repository.full_name}`); } async queueTemplateUpdate(installationId, targetRepository, templateRepository) { try { const client = new github_client_1.GitHubClient(this.appId, this.privateKey, installationId); const bootstrapper = new github_bootstrapper_1.GitHubBootstrapper(client); // Get repository configuration const [owner, repo] = targetRepository.split('/'); const templates = await bootstrapper.getRepositoryTemplates(owner, repo); // Find the template that was updated const templateConfig = templates.find(t => t.includes(templateRepository)); if (!templateConfig) { console.log(`No template configuration found for ${templateRepository}`); return; } // Queue the update job await this.database.queueJob({ type: 'template_update', installationId, targetRepository, templateRepository, status: 'pending', createdAt: new Date() }); console.log(`Queued template update for ${targetRepository} from ${templateRepository}`); } catch (error) { console.error(`Failed to queue template update: ${error}`); } } async autoConfigureRepository(installationId, repository) { try { const client = new github_client_1.GitHubClient(this.appId, this.privateKey, installationId); const bootstrapper = new github_bootstrapper_1.GitHubBootstrapper(client); // Get default templates for this installation const installationConfig = await this.database.getInstallationConfig(installationId); const defaultTemplates = installationConfig?.defaultTemplates || []; if (defaultTemplates.length > 0) { // Save default templates to the new repository await bootstrapper.saveRepositoryTemplates(repository.owner.login, repository.name, defaultTemplates); console.log(`Auto-configured ${repository.full_name} with ${defaultTemplates.length} templates`); } } catch (error) { console.error(`Failed to auto-configure repository: ${error}`); } } } exports.WebhookHandler = WebhookHandler; //# sourceMappingURL=webhook-handler.js.map