8
n8n 한국어amn8n.com

고급 n8n 워크플로우와 GitHub 동기화

고급

이것은DevOps, Multimodal AI분야의자동화 워크플로우로, 38개의 노드를 포함합니다.주로 If, N8n, Set, Code, Merge 등의 노드를 사용하며. GitHub를 활용한 지능형 변경 감지 자동화 워크플로우 백업

사전 요구사항
  • GitHub Personal Access Token
  • Telegram Bot Token
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
  "id": "dmvTNU9rfNdgTeSp",
  "meta": {
    "instanceId": "e7cc7f71b8002726158f14502c7243f892bdf0befb8af4790197437e4666e71e"
  },
  "name": "Advanced n8n Workflow Sync with GitHub",
  "tags": [],
  "nodes": [
    {
      "id": "09450fb6-e815-4c7b-88e9-a92da4d70d16",
      "name": "항목 반복 처리",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -368,
        912
      ],
      "parameters": {
        "options": {}
      },
      "executeOnce": false,
      "typeVersion": 3
    },
    {
      "id": "34869a4d-b687-44f7-81fd-5f71e8679a0e",
      "name": "파일 내용 업데이트 및 커밋",
      "type": "n8n-nodes-base.github",
      "position": [
        1584,
        1872
      ],
      "webhookId": "f2d754dd-b68d-41e8-a662-7e91c1c3aa95",
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "",
          "value": "={{ $(\"Configuration\").item.json.repo.owner }}"
        },
        "filePath": "={{ $json.context.newFile.path }}",
        "resource": "file",
        "operation": "edit",
        "repository": {
          "__rl": true,
          "mode": "",
          "value": "={{ $(\"Configuration\").item.json.repo.name }}"
        },
        "fileContent": "={{ JSON.stringify($json.n8nWorkflowData.base64Decode().parseJson(), null, 2) }}",
        "commitMessage": "=update: {{ $json.context.newFile.name }}"
      },
      "credentials": {
        "githubApi": {
          "id": "7sCS6E9S2UWO6PFt",
          "name": "GitHub account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bbf77bc7-8c35-4177-be4f-44c91682fd1a",
      "name": "일정 트리거",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2912,
        864
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "34d67ff9-e25a-42a9-b751-dbde37ed575b",
      "name": "모든 워크플로우 가져오기",
      "type": "n8n-nodes-base.n8n",
      "position": [
        -1328,
        848
      ],
      "parameters": {
        "filters": {},
        "requestOptions": {}
      },
      "credentials": {
        "n8nApi": {
          "id": "M9BEPZyx4jMbY5tY",
          "name": "n8n account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "98fc6907-d188-4f9a-b68e-44592bfac95b",
      "name": "N8N 워크플로우 인코딩",
      "type": "n8n-nodes-base.code",
      "position": [
        -1104,
        848
      ],
      "parameters": {
        "jsCode": "// Encode workflow data to base64 to prevent data pollution\nconst items = $input.all();\n\nfor (const item of items) {\n  const originalWorkflow = item.json;\n\n  item.json = {\n    id: originalWorkflow.id,\n    name: originalWorkflow.name,\n    n8nWorkflowData: Buffer.from(JSON.stringify(originalWorkflow)).toString('base64')\n  };\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "3576b4b3-6692-4d08-9453-697d44ac0f56",
      "name": "변경 사항 결정",
      "type": "n8n-nodes-base.code",
      "position": [
        -112,
        928
      ],
      "parameters": {
        "jsCode": "// Helper function to ensure stable JSON serialization for reliable comparison.\nfunction sortKeysDeep(obj) {\n  if (obj === null || typeof obj !== 'object') return obj;\n  if (Array.isArray(obj)) return obj.map(sortKeysDeep);\n  const out = {};\n  Object.keys(obj).sort().forEach(k => { out[k] = sortKeysDeep(obj[k]); });\n  return out;\n}\n\nconst items = $input.all();\nconst WORKFLOWS_DIR = $node[\"Configuration\"].json.repo.path;\n\nfor (const item of items) {\n  const src = item.json || {};\n  const flags = {\n    fileExists: false,\n    nameChanged: false,\n    shouldCommit: false\n  };\n  // Initialize the context container\n  item.json.context = {\n    oldFile: { path: '', name: '' },\n    newFile: { path: '', name: '' },\n    operation: ''\n  };\n  const context = item.json.context;\n\n  // 1. Determine if the file exists on GitHub.\n  const hasGithub = typeof src.githubWorkflowData === 'string' && src.githubWorkflowData.length > 0;\n  flags.fileExists = hasGithub;\n\n  // 2. Extract the current workflow name from the N8N data.\n  const currentName = src.name || '';\n  context.newFile.name = currentName;\n\n  // 3. Detect renames and set file paths.\n  if (typeof src.filePath === 'string' && src.filePath.length > 0) {\n    const parts = src.filePath.split('/');\n    const filename = parts.pop() || '';\n    const githubName = filename.endsWith('.json') ? filename.slice(0, -5) : filename;\n    \n    flags.nameChanged = githubName !== currentName;\n    context.oldFile.path = src.filePath;\n    context.oldFile.name = githubName;\n    \n    const dirPath = parts.join('/');\n    context.newFile.path = `${dirPath}/${currentName}.json`;\n\n  } else {\n    flags.nameChanged = false;\n    context.newFile.path = `${WORKFLOWS_DIR}/${currentName}.json`.replace(/\\/+/g, '/');\n  }\n\n  // 4. Perform a stable comparison to see if a commit is needed.\n  try {\n    if (flags.fileExists) {\n      const n8nJsonStr = Buffer.from(src.n8nWorkflowData, 'base64').toString('utf8');\n      const githubJsonStr = Buffer.from(src.githubWorkflowData, 'base64').toString('utf8');\n      const n8nObj = JSON.parse(n8nJsonStr);\n      const githubObj = JSON.parse(githubJsonStr);\n      const stableN8nStr = JSON.stringify(sortKeysDeep(n8nObj));\n      const stableGithubStr = JSON.stringify(sortKeysDeep(githubObj));\n      flags.shouldCommit = stableN8nStr !== stableGithubStr;\n    } else {\n      flags.shouldCommit = true; // New file, always commit.\n    }\n  } catch (e) {\n    flags.shouldCommit = true;   // If parsing or comparison fails, better to commit.\n  }\n\n  // 5. Determine the final operation type.\n  if (flags.nameChanged) {\n    context.operation = 'rename';\n  } else if (!flags.fileExists) {\n    context.operation = 'create';\n  } else if (flags.shouldCommit) {\n    context.operation = 'update';\n  } else {\n    context.operation = 'skip';\n  }\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "1cc5a35b-8e52-46dd-ac69-03e320f66fc8",
      "name": "기존 파일 삭제",
      "type": "n8n-nodes-base.github",
      "position": [
        1744,
        1408
      ],
      "webhookId": "1bd59af3-c8ee-4664-9cfd-df3ab4b6793d",
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "",
          "value": "={{ $(\"Configuration\").item.json.repo.owner }}"
        },
        "filePath": "={{ $json.context.oldFile.path }}",
        "resource": "file",
        "operation": "delete",
        "repository": {
          "__rl": true,
          "mode": "",
          "value": "={{ $(\"Configuration\").item.json.repo.name }}"
        },
        "commitMessage": "=rename: {{ $json.context.oldFile.name }} -> {{ $json.context.newFile.name }} (step 1/2: remove old)"
      },
      "credentials": {
        "githubApi": {
          "id": "7sCS6E9S2UWO6PFt",
          "name": "GitHub account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c6464638-d7d4-4e36-b359-f70412db2bbb",
      "name": "새 파일 생성(이름 변경)",
      "type": "n8n-nodes-base.github",
      "position": [
        2144,
        1408
      ],
      "webhookId": "1b7a1463-d11a-4c0c-a596-a4b09e003d5a",
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "",
          "value": "={{ $(\"Configuration\").item.json.repo.owner }}"
        },
        "filePath": "={{ $json.context.newFile.path }}",
        "resource": "file",
        "repository": {
          "__rl": true,
          "mode": "",
          "value": "={{ $(\"Configuration\").item.json.repo.name }}"
        },
        "fileContent": "={{ JSON.stringify($json.n8nWorkflowData.base64Decode().parseJson(), null, 2) }}",
        "commitMessage": "=rename: {{ $json.context.oldFile.name }} -> {{ $json.context.newFile.name }} (step 2/2: create new)"
      },
      "credentials": {
        "githubApi": {
          "id": "7sCS6E9S2UWO6PFt",
          "name": "GitHub account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b470dd47-762a-4c8a-90e0-e4f535e9b41e",
      "name": "생성 후 병합(이름 변경)",
      "type": "n8n-nodes-base.merge",
      "notes": "Keeps the original context intact across the GitHub step. Prevents losing flags and fields.",
      "position": [
        2384,
        1392
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "clashHandling": {
            "values": {
              "resolveClash": "preferInput1"
            }
          }
        },
        "joinMode": "enrichInput1",
        "fieldsToMatchString": "id"
      },
      "typeVersion": 3.2
    },
    {
      "id": "be267762-77a4-4af5-ba26-7f3945d44b32",
      "name": "업데이트 후 병합",
      "type": "n8n-nodes-base.merge",
      "notes": "Keeps the original context intact across the GitHub step. Prevents losing flags and fields.",
      "position": [
        1872,
        1856
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "clashHandling": {
            "values": {
              "resolveClash": "preferInput1"
            }
          }
        },
        "joinMode": "enrichInput1",
        "fieldsToMatchString": "id"
      },
      "typeVersion": 3.2
    },
    {
      "id": "db1f95ae-42e5-4588-bb7d-5348011d9afe",
      "name": "파일 목록 조회",
      "type": "n8n-nodes-base.github",
      "notes": "An edge case handling. Do not stop the whole workflow if there's no such folder.",
      "onError": "continueErrorOutput",
      "position": [
        -1552,
        1040
      ],
      "webhookId": "2e1f9567-52d4-4047-980c-6b4a57d4bd40",
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Configuration\").item.json.repo.owner }}"
        },
        "filePath": "={{ $(\"Configuration\").item.json.repo.path }}",
        "resource": "file",
        "operation": "list",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Configuration\").item.json.repo.name }}"
        }
      },
      "credentials": {
        "githubApi": {
          "id": "7sCS6E9S2UWO6PFt",
          "name": "GitHub account"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "8c6f381b-caed-4267-abb4-6672c93f45e9",
      "name": "병합",
      "type": "n8n-nodes-base.merge",
      "position": [
        -880,
        928
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "clashHandling": {
            "values": {
              "resolveClash": "preferInput1"
            }
          }
        },
        "joinMode": "enrichInput1",
        "fieldsToMatchString": "id"
      },
      "typeVersion": 3.2
    },
    {
      "id": "8d8f0963-0b37-47d9-b263-ffc7da06d118",
      "name": "스티커 메모",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        1168
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 448,
        "content": "## Rename a file (two-step)\n1. Delete the old filename\n1. Create a new filename"
      },
      "typeVersion": 1
    },
    {
      "id": "371212bd-c19c-4908-b3cb-fe75034a95fb",
      "name": "스티커 메모1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1616,
        752
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 448,
        "content": "## Collect data\n- N8N workflows list\n- GitHub files list "
      },
      "typeVersion": 1
    },
    {
      "id": "fb300a39-10b0-48cf-a793-a96e1ee3014f",
      "name": "스티커 메모2",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        -2672,
        592
      ],
      "parameters": {
        "color": 4,
        "width": 326,
        "height": 448,
        "content": "## Set parameters\n### GitHub\n- Repo owner\n- Repo name\n- Repo folder to store workflow backups\n### Reports\n- Telegram Chat ID to send notifications to\n- Do you need a report each time or only if something changed"
      },
      "typeVersion": 1
    },
    {
      "id": "80dabce7-e36e-4ea7-bd7d-67fb3b3096f8",
      "name": "스티커 메모3",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        -3024,
        592
      ],
      "parameters": {
        "color": 4,
        "width": 326,
        "height": 448,
        "content": "## Tune the schedule\nYou could change the check interval here.\n\nDefault: every hour"
      },
      "typeVersion": 1
    },
    {
      "id": "802494dc-8c76-4a34-97d3-c414cb04a179",
      "name": "스티커 메모4",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        176,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 1174,
        "height": 272,
        "content": "## Execution report\nYou could send this report to Telegram. See parameters in `Configuration` node for details.\nIf you don't need this -- delete this part."
      },
      "typeVersion": 1
    },
    {
      "id": "d0af7c02-a586-4993-8c4b-8fb18c3053fe",
      "name": "요약 배열 구성",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        112
      ],
      "parameters": {
        "jsCode": "// Aggregate arrays and flags for summary (no rendering)\n\nfunction normalizeName(item) {\n  return String(\n    item.json?.name || 'unknown'\n  );\n}\n\nconst items = $input.all();\n\nconst buckets = { create: [], update: [], rename: [], skip: [] };\n\nfor (const it of items) {\n  const name = normalizeName(it);\n  const op = it.json?.context.operation;\n  const oldName = it.json?.context.oldFile.name || name;\n  const newName = it.json?.context.newFile.name || name;\n\n  if (op === 'rename') buckets.rename.push(`${oldName} -> ${newName}`);\n  else if (op === 'create') buckets.create.push(name);\n  else if (op === 'update') buckets.update.push(name);\n  else buckets.skip.push(name);\n}\n\nconst isAnythingChanged = buckets.create.length > 0 || buckets.update.length > 0 || buckets.rename.length > 0;\n\nreturn [{ json: {\n  isAnythingChanged,\n  created: buckets.create,\n  updated: buckets.update,\n  renamed: buckets.rename,\n  skipped: buckets.skip\n} }];"
      },
      "typeVersion": 2
    },
    {
      "id": "0c401771-b522-4091-a14f-aba4a06d6158",
      "name": "요약 렌더링",
      "type": "n8n-nodes-base.code",
      "notes": "## Connecting to a messenger\n\nUse {{$json.message}} as message text. 'isAnythingChanged' controls whether to send.",
      "position": [
        928,
        112
      ],
      "parameters": {
        "jsCode": "// Helper function to escape text for Telegram's MarkdownV2 parser\nconst escapeMarkdownV2 = (str) => {\n  // For the full list of characters, see https://core.telegram.org/bots/api#markdownv2-style\n  return String(str).replace(/([_\\[\\]()~`>#+=|{}.!*-])/g, '\\\\$1');\n};\n\nconst all = $input.all();\nconst data = (all[0] && all[0].json) || {};\n\nconst config = $('Configuration').first().json;\nconst repoOwner = config.repo.owner;\nconst repoName = config.repo.name;\nconst repoPath = config.repo.path;\n\n// Construct repository URL using /blob/-/ for a branch-agnostic link\nconst repoUrl = `https://github.com/${repoOwner}/${repoName}/blob/-/${repoPath}`;\n\n// The link's *text* must be escaped, but the URL must not be.\nconst repoLinkText = escapeMarkdownV2(`${repoOwner}/${repoName}/${repoPath}`);\nconst repoLink = `[${repoLinkText}](${repoUrl})`;\n\nconst getList = key => (Array.isArray(data[key]) ? data[key] : []);\nconst sortAsc = (a, b) => String(a).localeCompare(String(b));\n\nconst sections = [\n  { key: 'created', title: 'Created' },\n  { key: 'updated', title: 'Updated' },\n  { key: 'renamed', title: 'Renamed' },\n  { key: 'skipped', title: 'Skipped (no changes)' },\n];\n\nconst summaryParts = [\n  `created ${getList('created').length}`,\n  `updated ${getList('updated').length}`,\n  `renamed ${getList('renamed').length}`,\n  `skipped ${getList('skipped').length}`\n];\n\n// Construct the final message with the new header format\nconst messageLines = [\n  '*Backup N8N workflows to GitHub*', // Main title\n  '', // Blank line for spacing\n  `Repo: ${repoLink}`,\n  `Totals: ${escapeMarkdownV2(summaryParts.join(', '))}`\n];\n\n// Append detailed lists as before\nfor (const { key, title } of sections) {\n  const list = [...getList(key)].sort(sortAsc);\n  if (list.length) {\n    messageLines.push('', `*${escapeMarkdownV2(title)}:*`);\n    for (const item of list) {\n      let line;\n      if (key === 'renamed') {\n        const [oldName, newName] = item.split(' -> ');\n        line = `\\`${oldName}.json\\` ${escapeMarkdownV2('->')} \\`${newName}.json\\``;\n      } else {\n        line = `\\`${item}.json\\``;\n      }\n      messageLines.push(line);\n    }\n  }\n}\n\nconst message = messageLines.join('\\n');\nreturn [{ json: { message } }];"
      },
      "notesInFlow": false,
      "typeVersion": 2
    },
    {
      "id": "fdb5e4f7-8aa1-4273-8776-ac0248b893e4",
      "name": "변경 사항 확인",
      "type": "n8n-nodes-base.if",
      "position": [
        704,
        112
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "8148a3ce-16fe-4074-9f57-c49072be8a8f",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $node[\"Configuration\"].json.report.verbose }}",
              "rightValue": ""
            },
            {
              "id": "ee4ef204-341f-444a-a26a-299aa0cde573",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.isAnythingChanged }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "13ba1e58-2bac-44dc-9142-b61efef7c4e1",
      "name": "워크플로우 매개변수 추출",
      "type": "n8n-nodes-base.code",
      "position": [
        -1104,
        1040
      ],
      "parameters": {
        "jsCode": "let items = $input.all();\n\nfor (let item of items) {\n    try {\n        const contentBase64 = item.json.content;\n        const path = item.json.path;\n        const sha = item.json.sha;\n\n        // Decode and parse GitHub file content to extract name\n        const content = Buffer.from(contentBase64, 'base64').toString('utf8');\n        const workflow = JSON.parse(content);\n\n        // Keep only the fields we need from GitHub side, store as base64\n        item.json = {\n            id: workflow.id,\n            name: workflow.name,\n            filePath: path,\n            githubWorkflowData: contentBase64, // Store as base64 to match N8N side\n            sha: sha\n        };\n\n    } catch (error) {\n        // Non-JSON or invalid workflow file\n        console.log(`Error parsing file ${item.json.path}: ${error.message}`);\n        item.json = {\n            id: null,\n            name: null,\n            filePath: item.json.path,\n            error: error.message\n        };\n    }\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "3464840a-599d-4432-aade-bfd5a2930461",
      "name": "파일 가져오기",
      "type": "n8n-nodes-base.github",
      "notes": "An edge case handling. Do not stop the whole workflow if there's no such folder.",
      "onError": "continueErrorOutput",
      "position": [
        -1328,
        1040
      ],
      "webhookId": "93c8a2dd-ddad-4837-a062-25473eee1208",
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Configuration\").item.json.repo.owner }}"
        },
        "filePath": "={{ $json.path }}",
        "resource": "file",
        "operation": "get",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $(\"Configuration\").item.json.repo.name }}"
        },
        "asBinaryProperty": false,
        "additionalParameters": {}
      },
      "credentials": {
        "githubApi": {
          "id": "7sCS6E9S2UWO6PFt",
          "name": "GitHub account"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "d991e89f-d795-4559-9b3f-4ecce50723b5",
      "name": "Telegram 구성 여부 확인",
      "type": "n8n-nodes-base.if",
      "position": [
        256,
        112
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "03191658-7c7b-4f85-a07b-35d9749d91f3",
              "operator": {
                "type": "number",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $(\"Configuration\").item.json.report.tg.chatID }}",
              "rightValue": 0
            },
            {
              "id": "3277a7ef-0895-4fbf-beb2-432f54cc8efc",
              "operator": {
                "type": "number",
                "operation": "notEquals"
              },
              "leftValue": "={{ $(\"Configuration\").item.json.report.tg.chatID }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0705a90f-00e1-46e6-b5ef-4a4efe5255cd",
      "name": "메시지 전송",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1152,
        112
      ],
      "webhookId": "ea0e343d-7ffa-4dd7-a3aa-0e7e45ad5753",
      "parameters": {
        "text": "={{ $json.message }}",
        "chatId": "={{ $node[\"Configuration\"].json.report.tg.chatID }}",
        "additionalFields": {
          "parse_mode": "MarkdownV2",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "7sLcbl1lRhPnfzJI",
          "name": "Telegram account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "fdef22d8-7960-48e2-a046-e69b3f48ce15",
      "name": "새 파일 생성",
      "type": "n8n-nodes-base.github",
      "position": [
        1296,
        2416
      ],
      "webhookId": "66429ae4-4b7d-4fb5-8438-26cdf6c4faa8",
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "",
          "value": "={{ $(\"Configuration\").item.json.repo.owner }}"
        },
        "filePath": "={{ $json.context.newFile.path }}",
        "resource": "file",
        "repository": {
          "__rl": true,
          "mode": "",
          "value": "={{ $(\"Configuration\").item.json.repo.name }}"
        },
        "fileContent": "={{ JSON.stringify($json.n8nWorkflowData.base64Decode().parseJson(), null, 2) }}",
        "commitMessage": "=create: {{ $json.context.newFile.name }}"
      },
      "credentials": {
        "githubApi": {
          "id": "7sCS6E9S2UWO6PFt",
          "name": "GitHub account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "669101cb-2d23-41c7-8e07-6e282b556cdd",
      "name": "생성 후 병합",
      "type": "n8n-nodes-base.merge",
      "notes": "Keeps the original context intact across the GitHub step. Prevents losing flags and fields.",
      "position": [
        1584,
        2400
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "clashHandling": {
            "values": {
              "resolveClash": "preferInput1"
            }
          }
        },
        "joinMode": "enrichInput1",
        "fieldsToMatchString": "id"
      },
      "typeVersion": 3.2
    },
    {
      "id": "c5d3b5e4-9c2a-4cf8-8bae-fca50b9f884c",
      "name": "스티커 메모5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1328,
        1712
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 448,
        "content": "## Update an existing file"
      },
      "typeVersion": 1
    },
    {
      "id": "d58b3267-9510-4abd-b4df-14f5cc19f984",
      "name": "스티커 메모6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        2256
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 448,
        "content": "## Create a new file"
      },
      "typeVersion": 1
    },
    {
      "id": "2ababf2c-5315-4d6a-9f09-b2efaef4ab2c",
      "name": "작업 없음",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1056,
        2976
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "7e069ed2-7303-4db5-b0b3-f18edbfb88da",
      "name": "스티커 메모7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        2800
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 448,
        "content": "## Nothing to do"
      },
      "typeVersion": 1
    },
    {
      "id": "0ac9743d-298a-49d2-b391-4c34b9d5122b",
      "name": "중단 및 오류",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        336,
        3376
      ],
      "parameters": {
        "errorMessage": "=Invalid operation: \"{{ $json.context.operation }}\". You should look at the code in the \"Decide changes\" node."
      },
      "typeVersion": 1
    },
    {
      "id": "f7a498d9-900f-41ab-9b5f-8e849b85c38c",
      "name": "스티커 메모8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        752
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 448,
        "content": "## Controller"
      },
      "typeVersion": 1
    },
    {
      "id": "4d426ef2-c4db-4690-bd23-48c374fcf0e1",
      "name": "라우터",
      "type": "n8n-nodes-base.switch",
      "position": [
        128,
        880
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "=rename",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "5af9aafc-3ee1-4855-89b2-b0ceb83b3169",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.context.operation }}",
                    "rightValue": "rename"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "update",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "849881fc-2d0e-4154-b6c2-10ff6c2b5480",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.context.operation }}",
                    "rightValue": "update"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "create",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "85f4cce5-476e-4970-82b4-0b04cc67870f",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.context.operation }}",
                    "rightValue": "create"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "skip",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "51919025-e488-4557-9cd9-23f4be9bbf06",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.context.operation }}",
                    "rightValue": "skip"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "ignoreCase": true,
          "fallbackOutput": "extra",
          "renameFallbackOutput": "error"
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "921826cc-13da-4db8-8a18-fdffacbcf5bb",
      "name": "삭제 후 병합(이름 변경)",
      "type": "n8n-nodes-base.merge",
      "notes": "Keeps the original context intact across the GitHub step. Prevents losing flags and fields.",
      "position": [
        1968,
        1248
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "clashHandling": {
            "values": {
              "resolveClash": "preferInput1"
            }
          }
        },
        "joinMode": "enrichInput1",
        "fieldsToMatchString": "id"
      },
      "typeVersion": 3.2
    },
    {
      "id": "bda0d23c-f6fe-435c-8732-c1f97c313d80",
      "name": "구성",
      "type": "n8n-nodes-base.set",
      "position": [
        -2576,
        864
      ],
      "parameters": {
        "values": {
          "number": [
            {
              "name": "report.tg.chatID",
              "value": null
            }
          ],
          "string": [
            {
              "name": "repo.owner"
            },
            {
              "name": "repo.name"
            },
            {
              "name": "repo.path",
              "value": "workflows/"
            }
          ],
          "boolean": [
            {
              "name": "report.verbose"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "a9074602-25a9-4fec-a464-caf1459dd3fb",
      "name": "스티커 메모10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3024,
        1072
      ],
      "parameters": {
        "width": 672,
        "height": 960,
        "content": "## Advanced n8n Workflow Sync with GitHub\n\nThis workflow automatically backs up your n8n workflows to a GitHub repository. It intelligently detects changes, handles workflow renames, and commits only when actual modifications occur, providing a clean version history.\n\n### ✨ Key Features:\n- **Intelligent Sync**: Reliable backup of n8n workflows to GitHub.\n- **Rename Support**: Automatically handles workflow renames.\n- **Efficient Commits**: Only commits real changes, keeping your repo clean.\n- **Clear History**: Informative commit messages (create, update, rename).\n\n### 🚀 Quick Setup:\n1.  **Credentials**: Set up GitHub, n8n API, and optional Telegram credentials in n8n.\n2.  **Configuration Node**: Open the `Configuration` node (green) and update:\n    - `repo.owner`: Your GitHub username\n    - `repo.name`: Your GitHub repository name\n    - `repo.path`: Folder for backups (e.g., `workflows/`)\n    - `report.tg.chatID` (Optional): Telegram chat ID, or `0` to disable.\n3.  **Connect Credentials**: Link your created credentials to the respective GitHub, n8n, and Telegram nodes.\n4.  **Schedule Trigger**: Adjust the backup frequency in the `Schedule Trigger` node.\n5.  **Activate**: Save and activate the workflow.\n\n### ⚙️ How It Works (Simple Steps)\n\n1.  **Get n8n Workflows**: The workflow starts by fetching all your current workflows from n8n.\n2.  **Get GitHub Files**: At the same time, it lists all existing workflow files from your GitHub repository.\n3.  **Compare & Decide**: It then compares each n8n workflow with its GitHub counterpart. It checks if anything changed, if it was renamed, or if it's new.\n4.  **Take Action**:\n    *   If a workflow is **new**, it's created on GitHub.\n    *   If a workflow is **updated**, the file content is changed on GitHub.\n    *   If a workflow was **renamed**, the old file is deleted, and a new one is created.\n    *   If **nothing changed**, the workflow is skipped.\n5.  **Send Report**: Finally, it can send a summary report to Telegram about what happened.\n\n### 💡 What's Next?\nFuture updates will include automatic archiving of inactive workflows and performance optimizations. Follow my profile for new workflow publications!"
      },
      "typeVersion": 1
    },
    {
      "id": "999b6d5e-ba67-4b98-9637-1ca8c061113e",
      "name": "빈 구성 시 중단",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        -1920,
        1024
      ],
      "parameters": {
        "errorMessage": "Incomplete GitHub configuration. Please check \"Configuration\" node."
      },
      "typeVersion": 1
    },
    {
      "id": "99820b4c-3ad8-43ba-bafe-9ae5f919e39f",
      "name": "GitHub 구성 확인",
      "type": "n8n-nodes-base.if",
      "notes": "Pre-provisioning safe fuse",
      "position": [
        -2160,
        864
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0b285299-edd5-41a0-85e8-3d94246e1cff",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.repo.owner }}",
              "rightValue": ""
            },
            {
              "id": "c9f894e0-cf42-45e1-87bd-13c2bd024b48",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.repo.name }}",
              "rightValue": ""
            },
            {
              "id": "f1591996-df67-4caf-8171-a049993268d2",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.repo.path }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "02eb2192-2fce-4283-90c0-616f2e6ced5d",
  "connections": {
    "8c6f381b-caed-4267-abb4-6672c93f45e9": {
      "main": [
        [
          {
            "node": "09450fb6-e815-4c7b-88e9-a92da4d70d16",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4d426ef2-c4db-4690-bd23-48c374fcf0e1": {
      "main": [
        [
          {
            "node": "1cc5a35b-8e52-46dd-ac69-03e320f66fc8",
            "type": "main",
            "index": 0
          },
          {
            "node": "921826cc-13da-4db8-8a18-fdffacbcf5bb",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "34869a4d-b687-44f7-81fd-5f71e8679a0e",
            "type": "main",
            "index": 0
          },
          {
            "node": "be267762-77a4-4af5-ba26-7f3945d44b32",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "fdef22d8-7960-48e2-a046-e69b3f48ce15",
            "type": "main",
            "index": 0
          },
          {
            "node": "669101cb-2d23-41c7-8e07-6e282b556cdd",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "2ababf2c-5315-4d6a-9f09-b2efaef4ab2c",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "0ac9743d-298a-49d2-b391-4c34b9d5122b",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3464840a-599d-4432-aade-bfd5a2930461": {
      "main": [
        [
          {
            "node": "13ba1e58-2bac-44dc-9142-b61efef7c4e1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "db1f95ae-42e5-4588-bb7d-5348011d9afe": {
      "main": [
        [
          {
            "node": "3464840a-599d-4432-aade-bfd5a2930461",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "bda0d23c-f6fe-435c-8732-c1f97c313d80": {
      "main": [
        [
          {
            "node": "99820b4c-3ad8-43ba-bafe-9ae5f919e39f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3576b4b3-6692-4d08-9453-697d44ac0f56": {
      "main": [
        [
          {
            "node": "4d426ef2-c4db-4690-bd23-48c374fcf0e1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0c401771-b522-4091-a14f-aba4a06d6158": {
      "main": [
        [
          {
            "node": "0705a90f-00e1-46e6-b5ef-4a4efe5255cd",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "fdef22d8-7960-48e2-a046-e69b3f48ce15": {
      "main": [
        [
          {
            "node": "669101cb-2d23-41c7-8e07-6e282b556cdd",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "1cc5a35b-8e52-46dd-ac69-03e320f66fc8": {
      "main": [
        [
          {
            "node": "921826cc-13da-4db8-8a18-fdffacbcf5bb",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "09450fb6-e815-4c7b-88e9-a92da4d70d16": {
      "main": [
        [
          {
            "node": "d991e89f-d795-4559-9b3f-4ecce50723b5",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "3576b4b3-6692-4d08-9453-697d44ac0f56",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "bbf77bc7-8c35-4177-be4f-44c91682fd1a": {
      "main": [
        [
          {
            "node": "bda0d23c-f6fe-435c-8732-c1f97c313d80",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "fdb5e4f7-8aa1-4273-8776-ac0248b893e4": {
      "main": [
        [
          {
            "node": "0c401771-b522-4091-a14f-aba4a06d6158",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "34d67ff9-e25a-42a9-b751-dbde37ed575b": {
      "main": [
        [
          {
            "node": "98fc6907-d188-4f9a-b68e-44592bfac95b",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "669101cb-2d23-41c7-8e07-6e282b556cdd": {
      "main": [
        [
          {
            "node": "09450fb6-e815-4c7b-88e9-a92da4d70d16",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "be267762-77a4-4af5-ba26-7f3945d44b32": {
      "main": [
        [
          {
            "node": "09450fb6-e815-4c7b-88e9-a92da4d70d16",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "99820b4c-3ad8-43ba-bafe-9ae5f919e39f": {
      "main": [
        [
          {
            "node": "34d67ff9-e25a-42a9-b751-dbde37ed575b",
            "type": "main",
            "index": 0
          },
          {
            "node": "db1f95ae-42e5-4588-bb7d-5348011d9afe",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "999b6d5e-ba67-4b98-9637-1ca8c061113e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d0af7c02-a586-4993-8c4b-8fb18c3053fe": {
      "main": [
        [
          {
            "node": "fdb5e4f7-8aa1-4273-8776-ac0248b893e4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "98fc6907-d188-4f9a-b68e-44592bfac95b": {
      "main": [
        [
          {
            "node": "8c6f381b-caed-4267-abb4-6672c93f45e9",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d991e89f-d795-4559-9b3f-4ecce50723b5": {
      "main": [
        [
          {
            "node": "d0af7c02-a586-4993-8c4b-8fb18c3053fe",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "c6464638-d7d4-4e36-b359-f70412db2bbb": {
      "main": [
        [
          {
            "node": "b470dd47-762a-4c8a-90e0-e4f535e9b41e",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "2ababf2c-5315-4d6a-9f09-b2efaef4ab2c": {
      "main": [
        [
          {
            "node": "09450fb6-e815-4c7b-88e9-a92da4d70d16",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "13ba1e58-2bac-44dc-9142-b61efef7c4e1": {
      "main": [
        [
          {
            "node": "8c6f381b-caed-4267-abb4-6672c93f45e9",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "b470dd47-762a-4c8a-90e0-e4f535e9b41e": {
      "main": [
        [
          {
            "node": "09450fb6-e815-4c7b-88e9-a92da4d70d16",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "921826cc-13da-4db8-8a18-fdffacbcf5bb": {
      "main": [
        [
          {
            "node": "b470dd47-762a-4c8a-90e0-e4f535e9b41e",
            "type": "main",
            "index": 0
          },
          {
            "node": "c6464638-d7d4-4e36-b359-f70412db2bbb",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "34869a4d-b687-44f7-81fd-5f71e8679a0e": {
      "main": [
        [
          {
            "node": "be267762-77a4-4af5-ba26-7f3945d44b32",
            "type": "main",
            "index": 1
          }
        ]
      ]
    }
  }
}
자주 묻는 질문

이 워크플로우를 어떻게 사용하나요?

위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.

이 워크플로우는 어떤 시나리오에 적합한가요?

고급 - 데브옵스, 멀티모달 AI

유료인가요?

이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.

워크플로우 정보
난이도
고급
노드 수38
카테고리2
노드 유형13
난이도 설명

고급 사용자를 위한 16+개 노드의 복잡한 워크플로우

저자
Maksym Brashenko

Maksym Brashenko

@j2h4u

I’m a generalist engineer who thrives on messy systems, undocumented protocols, and problems no one wants to touch Over the past 6 years, I’ve built infrastructure for managing hardware, reverse-engineered flaky devices, automated workflows end to end, and connected product logic to business reality Use my link to book an initial consultation

외부 링크
n8n.io에서 보기

이 워크플로우 공유

카테고리

카테고리: 34