kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cd7cada2f86b4e866a15b4323bb8d6d7ab5bba8b 146 lines 3.6 kB view raw
1import { createOrUpdateExternalLink } from "../../github/services/link-manager"; 2import { 3 findTaskByNumber, 4 isTaskInFinalState, 5 updateTaskStatus, 6} from "../../github/services/task-service"; 7import type { GiteaConfig } from "../config"; 8import { 9 findAllIntegrationsByGiteaRepo, 10 repoOwnerLogin, 11} from "../services/integration-lookup"; 12import { extractTaskNumberFromBranchGitea } from "../utils/branch-matcher"; 13import { resolveTargetStatus } from "../utils/resolve-column"; 14import { baseUrlFromRepositoryHtmlUrl } from "../utils/webhook-repo"; 15 16type PushPayload = { 17 ref: string; 18 head_commit?: { 19 id: string; 20 message: string; 21 author?: { name: string }; 22 timestamp: string; 23 }; 24 commits?: Array<{ 25 id: string; 26 message: string; 27 author?: { name: string; username?: string }; 28 timestamp?: string; 29 }>; 30 repository: { 31 owner: { login?: string; username?: string }; 32 name: string; 33 html_url: string; 34 }; 35}; 36 37const PROTECTED_BRANCHES = [ 38 "main", 39 "master", 40 "develop", 41 "staging", 42 "production", 43]; 44 45export async function handleGiteaPush(payload: PushPayload) { 46 const { ref, repository } = payload; 47 48 if (!ref.startsWith("refs/heads/")) { 49 console.log(`[Gitea Push] Skipping non-branch ref: ${ref}`); 50 return; 51 } 52 53 const branchName = ref.slice("refs/heads/".length); 54 console.log(`[Gitea Push] Processing branch: ${branchName}`); 55 56 if (PROTECTED_BRANCHES.includes(branchName)) { 57 console.log(`[Gitea Push] Skipping protected branch: ${branchName}`); 58 return; 59 } 60 61 const origin = baseUrlFromRepositoryHtmlUrl(repository.html_url); 62 if (!origin) { 63 return; 64 } 65 const owner = repoOwnerLogin(repository); 66 const integrations = await findAllIntegrationsByGiteaRepo( 67 origin, 68 owner, 69 repository.name, 70 ); 71 72 if (integrations.length === 0) { 73 return; 74 } 75 76 const headCommit = 77 payload.head_commit ?? payload.commits?.[payload.commits.length - 1]; 78 79 for (const integration of integrations) { 80 if (!integration.project) { 81 continue; 82 } 83 84 let config: GiteaConfig; 85 try { 86 config = JSON.parse(integration.config) as GiteaConfig; 87 } catch (error) { 88 console.error("Invalid Gitea integration config for push webhook", { 89 integrationId: integration.id, 90 error, 91 }); 92 continue; 93 } 94 const projectSlug = integration.project.slug; 95 96 const taskNumber = extractTaskNumberFromBranchGitea( 97 branchName, 98 config, 99 projectSlug, 100 ); 101 102 if (!taskNumber) { 103 continue; 104 } 105 106 const task = await findTaskByNumber(integration.projectId, taskNumber); 107 108 if (!task) { 109 continue; 110 } 111 112 const treeUrl = `${repository.html_url}/src/branch/${branchName}`; 113 114 await createOrUpdateExternalLink({ 115 taskId: task.id, 116 integrationId: integration.id, 117 resourceType: "branch", 118 externalId: branchName, 119 url: treeUrl, 120 title: branchName, 121 metadata: { 122 lastCommit: headCommit 123 ? { 124 sha: headCommit.id, 125 message: headCommit.message, 126 author: headCommit.author?.name, 127 timestamp: 128 "timestamp" in headCommit ? headCommit.timestamp : undefined, 129 } 130 : null, 131 }, 132 }); 133 134 const targetStatus = await resolveTargetStatus( 135 integration.projectId, 136 "branch_push", 137 config.statusTransitions?.onBranchPush || "in-progress", 138 ); 139 140 const isTaskFinal = await isTaskInFinalState(task); 141 142 if (task.status !== targetStatus && !isTaskFinal) { 143 await updateTaskStatus(task.id, targetStatus); 144 } 145 } 146}