kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
at main 96 lines 2.8 kB view raw
1import type { PluginContext, TaskTitleChangedEvent } from "../../types"; 2import type { GitHubConfig } from "../config"; 3import { 4 findExternalLinksByTask, 5 updateExternalLink, 6} from "../services/link-manager"; 7import { getGithubApp, getInstallationIdForRepo } from "../utils/github-app"; 8 9export async function handleTaskTitleChanged( 10 event: TaskTitleChangedEvent, 11 context: PluginContext, 12): Promise<void> { 13 const githubApp = getGithubApp(); 14 if (!githubApp) { 15 return; 16 } 17 18 const config = context.config as GitHubConfig; 19 const { repositoryOwner, repositoryName } = config; 20 21 try { 22 const links = await findExternalLinksByTask(event.taskId); 23 const issueLink = links.find( 24 (link) => 25 link.integrationId === context.integrationId && 26 link.resourceType === "issue", 27 ); 28 29 if (!issueLink) { 30 return; 31 } 32 33 const metadata = issueLink.metadata ? JSON.parse(issueLink.metadata) : {}; 34 35 // LOOP PREVENTION: Check if this update originated from GitHub 36 const lastTitleSync = metadata.lastSync?.title; 37 if (lastTitleSync) { 38 // Skip if value unchanged and last sync was from GitHub 39 if ( 40 lastTitleSync.value === event.newTitle && 41 lastTitleSync.source === "github" 42 ) { 43 console.log("Skipping title sync - already synced from GitHub"); 44 return; 45 } 46 47 // Skip if recent sync (within 2 seconds) to prevent rapid loops 48 const timeSinceLastSync = 49 Date.now() - new Date(lastTitleSync.timestamp).getTime(); 50 if (timeSinceLastSync < 2000) { 51 console.log( 52 `Skipping title sync - recent sync detected (${timeSinceLastSync}ms ago)`, 53 ); 54 return; 55 } 56 } 57 58 let installationId = config.installationId; 59 if (!installationId) { 60 installationId = await getInstallationIdForRepo( 61 repositoryOwner, 62 repositoryName, 63 ); 64 } 65 66 const octokit = await githubApp.getInstallationOctokit(installationId); 67 const issueNumber = Number.parseInt(issueLink.externalId, 10); 68 69 await octokit.rest.issues.update({ 70 owner: repositoryOwner, 71 repo: repositoryName, 72 issue_number: issueNumber, 73 title: event.newTitle, 74 }); 75 76 // Update metadata to track this sync 77 await updateExternalLink(issueLink.id, { 78 title: event.newTitle, 79 metadata: { 80 ...metadata, 81 lastSync: { 82 ...metadata.lastSync, 83 title: { 84 timestamp: new Date().toISOString(), 85 source: "kaneo", 86 value: event.newTitle, 87 }, 88 }, 89 }, 90 }); 91 92 console.log(`Synced task title to GitHub issue #${issueNumber}`); 93 } catch (error) { 94 console.error("Failed to update GitHub issue title:", error); 95 } 96}