The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "jump-section: Auto Fix Pipeline",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "pr-auto-fix",
"responseMode": "onReceived",
"responseData": "noData"
},
"id": "webhook-trigger",
"name": "Webhook: PR Review Event",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1.1,
"position": [
240,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": false
},
"conditions": [
{
"id": "action-check",
"leftValue": "={{ $json.body.action }}",
"rightValue": "submitted",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "gemini-check",
"leftValue": "={{ $json.body.review.user.login }}",
"rightValue": "gemini-code-assist[bot]",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
"id": "check-gemini-review",
"name": "IF: Gemini \ub9ac\ubdf0\uc778\uc9c0 \ud655\uc778",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
460,
300
]
},
{
"parameters": {
"jsCode": "const body = $input.first().json.body;\nconst pr = body.pull_request;\nconst review = body.review;\nconst repo = body.repository;\n\nreturn [{\n json: {\n prNumber: pr.number,\n prTitle: pr.title,\n prUrl: pr.html_url,\n headBranch: pr.head.ref,\n headSha: pr.head.sha,\n repoOwner: repo.owner.login,\n repoName: repo.name,\n reviewBody: review.body || '',\n reviewState: review.state,\n reviewId: review.id\n }\n}];"
},
"id": "extract-info",
"name": "PR \uc815\ubcf4 \ucd94\ucd9c",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
680,
300
]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.github.com/repos/{{ $json.repoOwner }}/{{ $json.repoName }}/pulls/{{ $json.prNumber }}/comments",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/vnd.github+json"
},
{
"name": "X-GitHub-Api-Version",
"value": "2022-11-28"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.GITHUB_TOKEN }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "per_page",
"value": "50"
}
]
},
"options": {}
},
"id": "get-review-comments",
"name": "GitHub: \ub9ac\ubdf0 \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc870\ud68c",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
900,
200
]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.github.com/repos/{{ $('PR \uc815\ubcf4 \ucd94\ucd9c').first().json.repoOwner }}/{{ $('PR \uc815\ubcf4 \ucd94\ucd9c').first().json.repoName }}/pulls/{{ $('PR \uc815\ubcf4 \ucd94\ucd9c').first().json.prNumber }}/files",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/vnd.github+json"
},
{
"name": "X-GitHub-Api-Version",
"value": "2022-11-28"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.GITHUB_TOKEN }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "per_page",
"value": "30"
}
]
},
"options": {}
},
"id": "get-pr-files",
"name": "GitHub: PR \ubcc0\uacbd \ud30c\uc77c \uc870\ud68c",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
900,
400
]
},
{
"parameters": {
"jsCode": "const prInfo = $('PR \uc815\ubcf4 \ucd94\ucd9c').first().json;\nconst inlineComments = $('GitHub: \ub9ac\ubdf0 \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc870\ud68c').all().map(i => i.json);\nconst prFiles = $input.all().map(f => f.json);\n\n// Gemini \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \ud544\ud130\ub9c1\nconst geminiComments = inlineComments.filter(c =>\n c.user && c.user.login === 'gemini-code-assist[bot]'\n);\n\n// \ucf54\uba58\ud2b8 \ud14d\uc2a4\ud2b8 \uc815\ub9ac\nconst commentsText = geminiComments.length > 0\n ? geminiComments.map(c => `\ud83d\udccd ${c.path} (line ${c.original_line || c.line || '?'}):\\n${c.body}`).join('\\n\\n')\n : '(\uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc5c6\uc74c)';\n\n// \ubcc0\uacbd \ud30c\uc77c \ubaa9\ub85d\nconst filePaths = prFiles.map(f => f.filename);\n\n// \ub9ac\ubdf0 \uc804\uccb4 \uc694\uc57d\nconst reviewSummary = [\n `## Gemini \ub9ac\ubdf0 \uc694\uc57d\\n${prInfo.reviewBody || '(\uc5c6\uc74c)'}`,\n `## \uc778\ub77c\uc778 \ucf54\uba58\ud2b8\\n${commentsText}`\n].join('\\n\\n');\n\nreturn [{\n json: {\n ...prInfo,\n reviewSummary,\n filePaths,\n hasComments: geminiComments.length > 0 || prInfo.reviewBody.length > 0\n }\n}];"
},
"id": "prepare-context",
"name": "\ub9ac\ubdf0 \ucee8\ud14d\uc2a4\ud2b8 \uc900\ube44",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
300
]
},
{
"parameters": {
"conditions": {
"conditions": [
{
"id": "has-comments",
"leftValue": "={{ $json.hasComments }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
]
}
},
"id": "check-has-comments",
"name": "IF: \uc218\uc815\ud560 \ucf54\uba58\ud2b8 \uc788\ub294\uc9c0 \ud655\uc778",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1340,
300
]
},
{
"parameters": {
"jsCode": "// \ubcc0\uacbd\ub41c \ud30c\uc77c\ub4e4\uc744 \uc77d\uae30 \uc704\ud574 \uac01 \ud30c\uc77c \uacbd\ub85c\ub97c \uac1c\ubcc4 \uc544\uc774\ud15c\uc73c\ub85c \ubd84\ub9ac\nconst ctx = $input.first().json;\nreturn ctx.filePaths.map(path => ({\n json: {\n path,\n branch: ctx.headBranch,\n repoOwner: ctx.repoOwner,\n repoName: ctx.repoName,\n // \uc804\uccb4 \ucee8\ud14d\uc2a4\ud2b8\ub3c4 \ud568\uaed8 \uc804\ub2ec\n _ctx: ctx\n }\n}));"
},
"id": "split-files",
"name": "\ud30c\uc77c \ubaa9\ub85d \ubd84\ub9ac",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1560,
300
]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.github.com/repos/{{ $json.repoOwner }}/{{ $json.repoName }}/contents/{{ $json.path }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/vnd.github+json"
},
{
"name": "X-GitHub-Api-Version",
"value": "2022-11-28"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.GITHUB_TOKEN }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "ref",
"value": "={{ $json.branch }}"
}
]
},
"options": {}
},
"id": "read-files",
"name": "GitHub: \ud30c\uc77c \ub0b4\uc6a9 \uc77d\uae30",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1780,
300
]
},
{
"parameters": {
"jsCode": "const fileItems = $('GitHub: \ud30c\uc77c \ub0b4\uc6a9 \uc77d\uae30').all();\nconst splitItems = $('\ud30c\uc77c \ubaa9\ub85d \ubd84\ub9ac').all();\nconst ctx = splitItems[0].json._ctx;\n\nconst files = fileItems.map((item, i) => {\n const content = Buffer.from((item.json.content || '').replace(/\\n/g, ''), 'base64').toString('utf8');\n return {\n path: item.json.path,\n content,\n sha: item.json.sha\n };\n});\n\nconst filesContext = files.map(f =>\n `### ${f.path}\\n\\`\\`\\`typescript\\n${f.content}\\n\\`\\`\\``\n).join('\\n\\n');\n\n// CI \uc2e4\ud328 \uc815\ubcf4 \uc870\ud68c\nconst token = $env.GITHUB_TOKEN;\nfunction ghGet(path) {\n return new Promise((resolve, reject) => {\n const https = require('https');\n const req = https.request({\n hostname: 'api.github.com',\n path,\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Accept': 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n 'User-Agent': 'n8n-jump-section'\n }\n }, (res) => {\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => {\n if (res.statusCode >= 400) reject(new Error(`HTTP ${res.statusCode}: ${data}`));\n else resolve(JSON.parse(data));\n });\n });\n req.setTimeout(15000, () => { req.destroy(); reject(new Error('Request timeout')); });\n req.on('error', reject);\n req.end();\n });\n}\n\nlet ciSection = '';\ntry {\n const checkData = await ghGet(`/repos/${ctx.repoOwner}/${ctx.repoName}/commits/${ctx.headSha}/check-runs?per_page=30`);\n const failed = (checkData.check_runs || []).filter(c =>\n c.conclusion === 'failure' || c.conclusion === 'timed_out'\n );\n if (failed.length > 0) {\n const details = failed.map(c => {\n const lines = [`### \u274c ${c.name}`];\n if (c.output?.summary) lines.push(c.output.summary);\n if (c.output?.text) lines.push(c.output.text.slice(0, 8000));\n return lines.join('\\n');\n }).join('\\n\\n');\n ciSection = `## CI \uc2e4\ud328 \uc815\ubcf4\\n\\n${details}\\n\\n`;\n }\n} catch (e) {\n ciSection = `## CI \uc2e4\ud328 \uc815\ubcf4\\n\\n(\uc870\ud68c \uc2e4\ud328: ${e.message})\\n\\n`;\n}\n\nconst userPrompt = `## PR #${ctx.prNumber}: ${ctx.prTitle}\\n\ube0c\ub79c\uce58: \\`${ctx.headBranch}\\`\\n\\n## Gemini \ub9ac\ubdf0\\n${ctx.reviewSummary}\\n\\n${ciSection}---\\n\\n## \ud604\uc7ac \ucf54\ub4dc\\n\\n${filesContext}\\n\\n---\\n\\nGemini \ub9ac\ubdf0\ub97c \ubc18\uc601\ud558\uace0, CI \uc2e4\ud328\uac00 \uc788\ub2e4\uba74 \uc6d0\uc778\uc744 \ud30c\uc545\ud558\uc5ec \ud568\uaed8 \uc218\uc815\ud558\uc138\uc694.\\n\\n**\uc911\uc694**: CI \uc5d0\ub7ec \ubaa9\ub85d\uc5d0 \ubcf4\uc774\ub294 \ubaa8\ub4e0 \uc5d0\ub7ec\ub97c \uc774\ubc88 \ud55c \ubc88\uc5d0 \uc804\ubd80 \uc218\uc815\ud558\uc138\uc694. \ud558\ub098\ub9cc \uace0\uce58\uace0 \ub05d\ub0b4\uc9c0 \ub9c8\uc138\uc694. \uac01 \uc5d0\ub7ec\ub97c \ubd84\uc11d\ud574\uc11c \uc5f0\uad00\ub41c \ud30c\uc77c\uc744 \ubaa8\ub450 write_file\ub85c \uc81c\ucd9c\ud558\uc138\uc694. \ud0c0\ub2f9\ud55c \uc9c0\uc801\ub9cc \ubc18\uc601\ud558\uace0, \uc218\uc815\uc774 \ud544\uc694\ud55c \ubaa8\ub4e0 \ud30c\uc77c\uc744 write_file\ub85c \uc81c\ucd9c\ud558\uc138\uc694.`;\n\nconst geminiPayload = {\n system_instruction: {\n parts: [{ text: 'TypeScript \uc804\ubb38 \uac1c\ubc1c\uc790\uc785\ub2c8\ub2e4. Gemini\uc758 \ucf54\ub4dc \ub9ac\ubdf0 \ucf54\uba58\ud2b8\uc640 CI \uc2e4\ud328 \uc815\ubcf4\ub97c \ubc14\ud0d5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc218\uc815\ud569\ub2c8\ub2e4.\\n\\n## \ud544\uc218 \uaddc\uce59\\n\\n### Prettier \ud3ec\ub9f7 (\ubc18\ub4dc\uc2dc \uc900\uc218 \u2014 format:check CI\uac00 \ub3cc\uace0 \uc788\uc74c)\\n- \ub4e4\uc5ec\uc4f0\uae30: \uc2a4\ud398\uc774\uc2a4 2\uce78\\n- \uc138\ubbf8\ucf5c\ub860: \ud56d\uc0c1 \ubd99\uc784\\n- \ub530\uc634\ud45c: \uc2f1\uae00\ucffc\ud2b8 (\\'\\')\\n- \ud55c \uc904 \ucd5c\ub300 \uae38\uc774: 100\uc790\\n- trailing comma: \ud56d\uc0c1 \ubd99\uc784 (\ud568\uc218 \uc778\uc790 \ub9c8\uc9c0\ub9c9 \ud3ec\ud568)\\n- \uc904\ubc14\uafc8: LF (\\\\n)\\n\\n### \ucf54\ub4dc \uc218\uc815\\n- Gemini \ucf54\uba58\ud2b8\uc5d0\uc11c \ud0c0\ub2f9\ud55c \uc9c0\uc801\ub9cc \uc218\uc815\\n- CI \uc2e4\ud328\uac00 \uc788\uc73c\uba74 \ubc18\ub4dc\uc2dc \uc6d0\uc778\uc744 \ud30c\uc545\ud558\uace0 **\ubaa8\ub4e0 \uc5d0\ub7ec\ub97c \ud55c \ubc88\uc5d0** \uc218\uc815 (format check, type error, test failure, build error \ub4f1) \u2014 \uc77c\ubd80\ub9cc \uc218\uc815\ud558\uace0 \ub05d\ub0b4\uc9c0 \ub9d0 \uac83\\n- \uae30\uc874 Public API \ud558\uc704 \ud638\ud658\uc131 \uc720\uc9c0\\n- \uc218\uc815\uc774 \ud544\uc694 \uc5c6\ub294 \ud30c\uc77c\uc740 write_file \ud638\ucd9c \uc0dd\ub7b5\\n- \uc6d0\uc778\uc774 \uba85\ud655\ud558\uace0 \uc218\uc815 \ubc29\ud5a5\uc774 \ud655\uc2e4\ud55c \uacbd\uc6b0\uc5d0\ub9cc write_file\ub85c \uc81c\ucd9c\ud558\uc138\uc694. \uc6d0\uc778\uc774 \ubd88\ubd84\uba85\ud558\uac70\ub098 \uc758\ub3c4\uce58 \uc54a\uc740 \ub3d9\uc791 \ubcc0\uacbd \uac00\ub2a5\uc131\uc774 \uc788\uc73c\uba74 \ud574\ub2f9 \ud30c\uc77c\uc740 \uac74\ub108\ub701\ub2c8\ub2e4.' }]\n },\n contents: [{ role: 'user', parts: [{ text: userPrompt }] }],\n tools: [{\n functionDeclarations: [{\n name: 'write_file',\n description: '\uc218\uc815\ub41c \ud30c\uc77c\uc758 \uc804\uccb4 \ub0b4\uc6a9\uc744 \uc81c\ucd9c\ud569\ub2c8\ub2e4.',\n parameters: {\n type: 'OBJECT',\n properties: {\n path: { type: 'STRING', description: '\ud30c\uc77c \uacbd\ub85c' },\n content: { type: 'STRING', description: '\ud30c\uc77c \uc804\uccb4 \ub0b4\uc6a9' },\n description: { type: 'STRING', description: '\ubcc0\uacbd \uc124\uba85' }\n },\n required: ['path', 'content', 'description']\n }\n }]\n }],\n toolConfig: { functionCallingConfig: { mode: 'ANY' } },\n generationConfig: { maxOutputTokens: 16384 }\n};\n\nreturn [{\n json: {\n ...ctx,\n files,\n hasCiFailures: ciSection !== '',\n geminiPayload\n }\n}];"
},
"id": "decode-files",
"name": "\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2000,
300
]
},
{
"parameters": {
"method": "POST",
"url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={{ $env.GEMINI_API_KEY }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ $json.geminiPayload }}",
"options": {}
},
"id": "gemini-fix",
"name": "Gemini: \ucf54\ub4dc \uc218\uc815",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2220,
300
]
},
{
"parameters": {
"jsCode": "const response = $input.first().json;\nconst ctx = $('\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529').first().json;\n\nif (!response.candidates || response.candidates.length === 0) {\n return [{ json: { noChanges: true, geminiError: response.promptFeedback || response.error || 'no candidates', ...ctx } }];\n}\n\nconst candidate = response.candidates[0];\nif (!candidate.content || !candidate.content.parts || candidate.finishReason === 'MALFORMED_FUNCTION_CALL') {\n return [{ json: { noChanges: true, geminiError: candidate.finishReason || 'no content', ...ctx } }];\n}\n\nconst parts = candidate.content.parts;\nconst functionCalls = parts.filter(p => p.functionCall && p.functionCall.name === 'write_file');\n\nif (functionCalls.length === 0) {\n // \uc218\uc815 \ud544\uc694 \uc5c6\uc74c\n return [{ json: { noChanges: true, ...ctx } }];\n}\n\nconst fixedFiles = functionCalls.map(p => {\n const original = ctx.files.find(f => f.path === p.functionCall.args.path);\n return {\n path: p.functionCall.args.path,\n content: p.functionCall.args.content,\n description: p.functionCall.args.description,\n sha: original?.sha || null\n };\n});\n\nconst filesSummary = fixedFiles.map(f => `\u2022 \\`${f.path}\\`: ${f.description}`).join('\\n');\n\nreturn [{\n json: {\n noChanges: false,\n fixedFiles,\n filesSummary,\n prNumber: ctx.prNumber,\n prTitle: ctx.prTitle,\n prUrl: ctx.prUrl,\n headBranch: ctx.headBranch,\n repoOwner: ctx.repoOwner,\n repoName: ctx.repoName\n }\n}];"
},
"id": "parse-fix",
"name": "\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2440,
300
]
},
{
"parameters": {
"conditions": {
"conditions": [
{
"id": "has-changes",
"leftValue": "={{ $json.noChanges }}",
"rightValue": false,
"operator": {
"type": "boolean",
"operation": "false"
}
}
]
}
},
"id": "check-has-changes",
"name": "IF: \uc218\uc815\ub41c \ud30c\uc77c \uc788\ub294\uc9c0 \ud655\uc778",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
2660,
300
]
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nif (data.noChanges || !data.fixedFiles || data.fixedFiles.length === 0) return [];\nconst fixedFiles = data.fixedFiles.filter(f => f.path && f.content && f.content.trim().length > 0);\nif (fixedFiles.length === 0) return [];\nconst { headBranch, repoOwner, repoName, filesSummary } = data;\nconst token = $env.GITHUB_TOKEN;\n\nconst headers = {\n 'Authorization': `Bearer ${token}`,\n 'Accept': 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n 'Content-Type': 'application/json',\n 'User-Agent': 'n8n-jump-section'\n};\n\nconst baseUrl = `https://api.github.com/repos/${repoOwner}/${repoName}`;\n\nfunction request(method, url, body) {\n return new Promise((resolve, reject) => {\n const https = require('https');\n const path = url.replace('https://api.github.com', '');\n const options = { hostname: 'api.github.com', path, method, headers };\n const req = https.request(options, (res) => {\n let d = '';\n res.on('data', chunk => d += chunk);\n res.on('end', () => {\n if (res.statusCode >= 400) reject(new Error(`HTTP ${res.statusCode}: ${d}`));\n else resolve(JSON.parse(d));\n });\n });\n req.setTimeout(15000, () => { req.destroy(); reject(new Error('Request timeout')); });\n req.on('error', reject);\n if (body) req.write(JSON.stringify(body));\n req.end();\n });\n}\n\n// 1. HEAD SHA\nconst refData = await request('GET', `${baseUrl}/git/refs/heads/${headBranch}`);\nconst baseSha = refData.object.sha;\n\n// 2. \ud604\uc7ac \ud2b8\ub9ac SHA\nconst commitData = await request('GET', `${baseUrl}/git/commits/${baseSha}`);\nconst baseTreeSha = commitData.tree.sha;\n\n// 3. \uc0c8 \ud2b8\ub9ac \uc0dd\uc131 (\ubaa8\ub4e0 \ud30c\uc77c \ud55c\ubc88\uc5d0)\nconst newTree = await request('POST', `${baseUrl}/git/trees`, {\n base_tree: baseTreeSha,\n tree: fixedFiles.map(f => ({ path: f.path, mode: '100644', type: 'blob', content: f.content }))\n});\n\n// 4. \ucee4\ubc0b \uc0dd\uc131\nconst newCommit = await request('POST', `${baseUrl}/git/commits`, {\n message: `fix: apply Gemini review suggestions\\n\\n${filesSummary}`,\n tree: newTree.sha,\n parents: [baseSha]\n});\n\n// 5. \ube0c\ub79c\uce58 ref \uc5c5\ub370\uc774\ud2b8\nawait request('PATCH', `${baseUrl}/git/refs/heads/${headBranch}`, { sha: newCommit.sha });\n\nreturn [{ json: { ...data, commitSha: newCommit.sha } }];"
},
"id": "commit-fixes",
"name": "GitHub: Trees API \ucee4\ubc0b",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2880,
200
]
},
{
"parameters": {
"method": "POST",
"url": "=https://api.github.com/repos/{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.repoOwner }}/{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.repoName }}/issues/{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.prNumber }}/comments",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/vnd.github+json"
},
{
"name": "X-GitHub-Api-Version",
"value": "2022-11-28"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.GITHUB_TOKEN }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"body\": \"## \ud83e\udd16 AI \uc790\ub3d9 \uc218\uc815 \uc644\ub8cc\\n\\nGemini \ub9ac\ubdf0 \ucf54\uba58\ud2b8\ub97c \ubc18\uc601\ud574\uc11c \ucf54\ub4dc\ub97c \uc218\uc815\ud588\uc2b5\ub2c8\ub2e4.\\n\\n### \uc218\uc815\ub41c \ud30c\uc77c\\n{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.filesSummary }}\\n\\n---\\n*Gemini AI\uac00 \ub9ac\ubdf0\ub97c \ubd84\uc11d\ud558\uc5ec \uc790\ub3d9 \uc218\uc815\ud588\uc2b5\ub2c8\ub2e4. \ubcc0\uacbd\uc0ac\ud56d\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694.*\"\n}",
"options": {}
},
"id": "post-pr-comment",
"name": "GitHub: PR \ucf54\uba58\ud2b8 \uac8c\uc2dc",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3100,
400
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.DISCORD_WEBHOOK_URL }}",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"embeds\": [\n {\n \"title\": \"{{ $('\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529').first().json.hasCiFailures ? '\ud83d\udd27 Gemini \ub9ac\ubdf0 + CI \uc2e4\ud328 \uc218\uc815 \uc644\ub8cc' : '\ud83d\udd27 Gemini \ub9ac\ubdf0 \uae30\ubc18 \uc790\ub3d9 \uc218\uc815 \uc644\ub8cc' }}\",\n \"description\": \"[PR #{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.prNumber }}: {{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.prTitle }}]({{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.prUrl }})\",\n \"color\": 15105570,\n \"fields\": [\n {\n \"name\": \"\uc218\uc815\ub41c \ud30c\uc77c\",\n \"value\": \"{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.filesSummary }}\",\n \"inline\": false\n },\n {\n \"name\": \"\ube0c\ub79c\uce58\",\n \"value\": \"`{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.headBranch }}`\",\n \"inline\": true\n },\n {\n \"name\": \"CI \uc2e4\ud328 \ud3ec\ud568\",\n \"value\": \"{{ $('\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529').first().json.hasCiFailures ? '\u2705 CI \uc2e4\ud328 \uc815\ubcf4 \ubc18\uc601\ub428' : '\uc5c6\uc74c' }}\",\n \"inline\": true\n }\n ],\n \"footer\": { \"text\": \"jump-section AI Pipeline \u2022 Gemini \uc790\ub3d9 \uc218\uc815\" },\n \"timestamp\": \"{{ new Date().toISOString() }}\"\n }\n ]\n}",
"options": {}
},
"id": "discord-fix",
"name": "Discord: \uc790\ub3d9 \uc218\uc815 \uc54c\ub9bc",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3320,
300
]
}
],
"connections": {
"Webhook: PR Review Event": {
"main": [
[
{
"node": "IF: Gemini \ub9ac\ubdf0\uc778\uc9c0 \ud655\uc778",
"type": "main",
"index": 0
}
]
]
},
"IF: Gemini \ub9ac\ubdf0\uc778\uc9c0 \ud655\uc778": {
"main": [
[
{
"node": "PR \uc815\ubcf4 \ucd94\ucd9c",
"type": "main",
"index": 0
}
]
]
},
"PR \uc815\ubcf4 \ucd94\ucd9c": {
"main": [
[
{
"node": "GitHub: \ub9ac\ubdf0 \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc870\ud68c",
"type": "main",
"index": 0
}
]
]
},
"GitHub: \ub9ac\ubdf0 \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc870\ud68c": {
"main": [
[
{
"node": "GitHub: PR \ubcc0\uacbd \ud30c\uc77c \uc870\ud68c",
"type": "main",
"index": 0
}
]
]
},
"GitHub: PR \ubcc0\uacbd \ud30c\uc77c \uc870\ud68c": {
"main": [
[
{
"node": "\ub9ac\ubdf0 \ucee8\ud14d\uc2a4\ud2b8 \uc900\ube44",
"type": "main",
"index": 0
}
]
]
},
"\ub9ac\ubdf0 \ucee8\ud14d\uc2a4\ud2b8 \uc900\ube44": {
"main": [
[
{
"node": "IF: \uc218\uc815\ud560 \ucf54\uba58\ud2b8 \uc788\ub294\uc9c0 \ud655\uc778",
"type": "main",
"index": 0
}
]
]
},
"IF: \uc218\uc815\ud560 \ucf54\uba58\ud2b8 \uc788\ub294\uc9c0 \ud655\uc778": {
"main": [
[
{
"node": "\ud30c\uc77c \ubaa9\ub85d \ubd84\ub9ac",
"type": "main",
"index": 0
}
]
]
},
"\ud30c\uc77c \ubaa9\ub85d \ubd84\ub9ac": {
"main": [
[
{
"node": "GitHub: \ud30c\uc77c \ub0b4\uc6a9 \uc77d\uae30",
"type": "main",
"index": 0
}
]
]
},
"GitHub: \ud30c\uc77c \ub0b4\uc6a9 \uc77d\uae30": {
"main": [
[
{
"node": "\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529",
"type": "main",
"index": 0
}
]
]
},
"\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529": {
"main": [
[
{
"node": "Gemini: \ucf54\ub4dc \uc218\uc815",
"type": "main",
"index": 0
}
]
]
},
"Gemini: \ucf54\ub4dc \uc218\uc815": {
"main": [
[
{
"node": "\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1",
"type": "main",
"index": 0
}
]
]
},
"\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1": {
"main": [
[
{
"node": "IF: \uc218\uc815\ub41c \ud30c\uc77c \uc788\ub294\uc9c0 \ud655\uc778",
"type": "main",
"index": 0
}
]
]
},
"IF: \uc218\uc815\ub41c \ud30c\uc77c \uc788\ub294\uc9c0 \ud655\uc778": {
"main": [
[
{
"node": "GitHub: Trees API \ucee4\ubc0b",
"type": "main",
"index": 0
},
{
"node": "GitHub: PR \ucf54\uba58\ud2b8 \uac8c\uc2dc",
"type": "main",
"index": 0
}
]
]
},
"GitHub: Trees API \ucee4\ubc0b": {
"main": [
[
{
"node": "Discord: \uc790\ub3d9 \uc218\uc815 \uc54c\ub9bc",
"type": "main",
"index": 0
}
]
]
},
"GitHub: PR \ucf54\uba58\ud2b8 \uac8c\uc2dc": {
"main": [
[
{
"node": "Discord: \uc790\ub3d9 \uc218\uc815 \uc54c\ub9bc",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": [
{
"name": "jump-section"
},
{
"name": "auto-fix"
}
]
}
About this workflow
jump-section: Auto Fix Pipeline. Uses httpRequest. Webhook trigger; 16 nodes.
Source: https://github.com/bae080311/jump-section/blob/c566471882fa8b8f0e4d00704693b7c92d62c519/.n8n/workflows/auto-fix-pipeline.json — original creator credit. Request a take-down →