高度な 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": "何もしない(No Operation)",
      "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からインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。

このワークフローはどんな場面に適していますか?

上級 - DevOps, マルチモーダルAI

有料ですか?

このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。

関連ワークフロー

GitHub 同期ダッシュボード - V2
コミット履歴とロールバック機能を備えた GitHub ワークフロー バージョン管理ダッシュボード
If
N8n
Set
+
If
N8n
Set
94 ノードEduard
DevOps
GitHub への自動化 n8n ワークフロー バックアップおよび削除の追跡
n8n のワーキングフローを GitHub へ自動バックアップし、削除を追跡
If
N8n
Set
+
If
N8n
Set
31 ノードMarcial Ambriz
DevOps
GPT-4駆動のカールドメールワークフロー(完全カスタマイズされた3本のメールフォロー付き)
GPT-4、Mailgun、Supabaseを使ってパーソナライズされたラグディ冷信Seriesを自動化
If
Set
Code
+
If
Set
Code
100 ノードPaul
リードナーチャリング
Google Drive、GitHub、メッセージ アラートを使用した自動作業フロー バックアップシステム
Google Drive、GitHub、そしてメッセージアラートを使用した自動化ワークフローバックアップシステム
If
N8n
Set
+
If
N8n
Set
20 ノードKhairul Muhtadin
コンテンツ作成
Typebot ワークフローと GitHub を相互同期、Typebot API を使用
TypebotフローとGitHubを双方向に同期し、Typebot APIを使用
If
Set
Code
+
If
Set
Code
31 ノードMarcial Ambriz
DevOps
完全な B2B セールスフロー:Apollo リード生成、Mailgun 外信、および AI 返信管理
完全なB2Bセールスフロー:Apolloリード生成、Mailgunアウト Reach、AI返信管理
If
Set
Code
+
If
Set
Code
116 ノードPaul
コンテンツ作成
ワークフロー情報
難易度
上級
ノード数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