Fortgeschrittene n8n-Workflows mit GitHub-Synchronisation

Experte

Dies ist ein DevOps, Multimodal AI-Bereich Automatisierungsworkflow mit 38 Nodes. Hauptsächlich werden If, N8n, Set, Code, Merge und andere Nodes verwendet. Automatisiertes Arbeitsablauf-Backup mit intelligenter Änderungserkennung über GitHub

Voraussetzungen
  • GitHub Personal Access Token
  • Telegram Bot Token
Workflow-Vorschau
Visualisierung der Node-Verbindungen, mit Zoom und Pan
Workflow exportieren
Kopieren Sie die folgende JSON-Konfiguration und importieren Sie sie in n8n
{
  "id": "dmvTNU9rfNdgTeSp",
  "meta": {
    "instanceId": "e7cc7f71b8002726158f14502c7243f892bdf0befb8af4790197437e4666e71e"
  },
  "name": "Advanced n8n Workflow Sync with GitHub",
  "tags": [],
  "nodes": [
    {
      "id": "09450fb6-e815-4c7b-88e9-a92da4d70d16",
      "name": "Über Elemente iterieren",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -368,
        912
      ],
      "parameters": {
        "options": {}
      },
      "executeOnce": false,
      "typeVersion": 3
    },
    {
      "id": "34869a4d-b687-44f7-81fd-5f71e8679a0e",
      "name": "Dateiinhalt aktualisieren und committen",
      "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": "Zeitplan-Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2912,
        864
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "34d67ff9-e25a-42a9-b751-dbde37ed575b",
      "name": "Alle Workflows abrufen",
      "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-Workflows kodieren",
      "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": "Änderungen entscheiden",
      "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": "Alte Datei löschen",
      "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": "Neue Datei erstellen (umbenennen)",
      "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": "Nach Erstellung zusammenführen (umbenennen)",
      "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": "Nach Aktualisierung zusammenführen",
      "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": "Dateien auflisten",
      "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": "Zusammenführen",
      "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": "Notiz",
      "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": "Notiz1",
      "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": "Notiz2",
      "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": "Notiz3",
      "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": "Notiz4",
      "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": "Zusammenfassungs-Arrays erstellen",
      "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": "Zusammenfassung rendern",
      "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": "Änderungen vorhanden?",
      "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": "Workflow-Parameter extrahieren",
      "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": "Dateien abrufen",
      "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": "Ist Telegram konfiguriert?",
      "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": "Nachricht senden",
      "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": "Neue Datei erstellen",
      "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": "Nach Erstellung zusammenführen",
      "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": "Notiz5",
      "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": "Notiz6",
      "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": "Keine Operation, nichts tun",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1056,
        2976
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "7e069ed2-7303-4db5-b0b3-f18edbfb88da",
      "name": "Notiz7",
      "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": "Stoppen und Fehler",
      "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": "Notiz8",
      "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": "Router",
      "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": "Nach Löschung zusammenführen (umbenennen)",
      "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": "Konfiguration",
      "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": "Notiz10",
      "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": "Bei leerer Konfiguration stoppen",
      "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-Konfiguration prüfen",
      "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
          }
        ]
      ]
    }
  }
}
Häufig gestellte Fragen

Wie verwende ich diesen Workflow?

Kopieren Sie den obigen JSON-Code, erstellen Sie einen neuen Workflow in Ihrer n8n-Instanz und wählen Sie "Aus JSON importieren". Fügen Sie die Konfiguration ein und passen Sie die Anmeldedaten nach Bedarf an.

Für welche Szenarien ist dieser Workflow geeignet?

Experte - DevOps, Multimodales KI

Ist es kostenpflichtig?

Dieser Workflow ist völlig kostenlos. Beachten Sie jedoch, dass Drittanbieterdienste (wie OpenAI API), die im Workflow verwendet werden, möglicherweise kostenpflichtig sind.

Workflow-Informationen
Schwierigkeitsgrad
Experte
Anzahl der Nodes38
Kategorie2
Node-Typen13
Schwierigkeitsbeschreibung

Für fortgeschrittene Benutzer, komplexe Workflows mit 16+ Nodes

Autor
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

Externe Links
Auf n8n.io ansehen

Diesen Workflow teilen

Kategorien

Kategorien: 34