{
  "id": "Wyw2TmhKVujBRhkZ",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Sync Android drawable assets from Figma to GitHub",
  "tags": [],
  "nodes": [
    {
      "id": "b9ed0516-1d34-499b-85ba-7e304129b116",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        660,
        -280
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "8c08ddf1-e081-425c-bdb1-b46e28f48ebf",
      "name": "Download Each Image from the Figma Export URL",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1320,
        -355
      ],
      "parameters": {
        "url": "={{ $json[\"url\"] }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "139ce28e-570d-443d-a347-bef3089e6e1e",
      "name": "get figma Url",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        -355
      ],
      "parameters": {
        "url": "=https://api.figma.com/v1/images/FIGMA_PROJECT_ID?ids={{$json[\"id\"]}}&format=png&scale={{$json[\"scale\"]}}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Figma-Token",
              "value": "FIGMA_TOKEN"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "383243fc-1bd0-4968-af5e-fb6ba09e80f9",
      "name": "Merge1",
      "type": "n8n-nodes-base.merge",
      "position": [
        1560,
        -220
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2,
      "alwaysOutputData": true
    },
    {
      "id": "087cce4c-2102-48f8-bee1-69468fceeec9",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        1840,
        -220
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "4f1fd504-3119-4fe0-bb8f-6af824494d41",
              "operator": {
                "type": "number",
                "operation": "equals"
              },
              "leftValue": "={{$itemIndex}}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "258614f5-1704-4972-bbd4-e0bf8f4126d3",
      "name": "Get Figma Export URL",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        220,
        -280
      ],
      "parameters": {
        "url": "https://api.figma.com/v1/files/FIGMA_PROJECT_ID?ids=2-20347",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Figma-Token",
              "value": "FIGMA_TOKEN"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "417bd211-81b9-4f1c-9cdf-15b01a2d39ab",
      "name": "Execute workflow",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        0,
        -280
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "03ff18dd-880b-4930-9d61-6e16d5e08078",
      "name": "Find Icons & Buttons",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        -380
      ],
      "parameters": {
        "jsCode": "function findIconsAndButtons(nodes, parentChain = []) {\n  let matches = [];\n\n  for (const node of nodes) {\n    const fullPath = [...parentChain, node.name].join('/');\n\n    // If current node is a match\n    if (typeof node.name === 'string' && /icon|button/i.test(node.name)) {\n      matches.push({\n        id: node.id,\n        name: node.name,\n        type: node.type,\n        fullPath: fullPath,\n        exportSettings: node.exportSettings || null,\n      });\n    }\n\n    // Recurse into children if they exist\n    if (node.children && Array.isArray(node.children)) {\n      matches.push(...findIconsAndButtons(node.children, [...parentChain, node.name]));\n    }\n  }\n\n  return matches;\n}\n\n// Start from the root document children\nconst rootNode = items[0].json.document;\nconst canvas = rootNode.children.find(child => child.type === 'CANVAS');\nconst frame = canvas.children.find(child => child.type === 'FRAME' && /forgot password/i.test(child.name));\n\nif (!frame || !frame.children) {\n  return [];\n}\n\n// Recursively collect icons and buttons\nconst results = findIconsAndButtons(frame.children);\n\nreturn results.map(node => ({\n  json: node\n}));"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "6ea0e89f-aeef-46b0-b26b-22dcace571de",
      "name": "Predefine drawable folders",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        -180
      ],
      "parameters": {
        "jsCode": "return [\n  { json: { folder: 'drawable-mdpi', scale: '1' } },\n  { json: { folder: 'drawable-hdpi', scale: '1.5' } },\n  { json: { folder: 'drawable-xhdpi', scale: '2' } },\n  { json: { folder: 'drawable-xxhdpi', scale: '3' } }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "1ac6faec-8395-4f4e-bea1-82344b1a4fd8",
      "name": "Filter nullable url of nodes",
      "type": "n8n-nodes-base.code",
      "position": [
        1100,
        -360
      ],
      "parameters": {
        "jsCode": "// Filter valid images and map to { nodeId, url }\nconst results = [];\n\nfor (const item of items) {\n  const images = item.json.images;\n\n  for (const nodeId in images) {\n    const url = images[nodeId];\n\n    if (url) {\n      results.push({\n        json: {\n          nodeId,\n          url\n        }\n      });\n    }\n  }\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "053297b8-705d-42b9-9531-16bf91629f93",
      "name": "Edit File names",
      "type": "n8n-nodes-base.code",
      "position": [
        1700,
        -360
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const name = item.json.name || \"asset\";\n  const folder = item.json.folder || \"drawable-mdpi\";\n  \n  item.json.path = `app/src/main/res/${folder}/${name.toLowerCase().replace(/[^a-z0-9]/g, \"_\")}.png`;\n  item.json.commitMessage = `Add asset: ${name}.png to ${folder}`;\n  item.json.branch = \"add-assets-from-figma\";\n  item.json.repoOwner = \"REPO_OWNER\";\n  item.json.repoName = \"REPO_NAME\";\n\n  return item;\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "1678d236-4bab-4b39-a553-cd67f9cffe1a",
      "name": "Prepare Pull Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2040,
        -220
      ],
      "parameters": {
        "url": "https://api.github.com/repos/REPO_OWNER/REPO_NAME/pulls",
        "method": "POST",
        "options": {},
        "jsonBody": "{\n  \"title\": \"Add drawable assets from Figma\",\n  \"head\": \"add-assets-from-figma\",\n  \"base\": \"main\",\n  \"body\": \"This PR contains Android drawable assets exported from Figma in all resolutions.\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            },
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4ccbac73-ffba-45a7-a6fe-a77b8021480f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        -460
      ],
      "parameters": {
        "width": 2280,
        "height": 440,
        "content": "\nSync Android drawable assets from Figma to GitHub via PR (multi\u2011density PNG)"
      },
      "typeVersion": 1
    },
    {
      "id": "09523583-b2db-40d7-97df-798608513226",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        60
      ],
      "parameters": {
        "width": 2280,
        "height": 760,
        "content": "#\nNode Name\nDescription\n\n1 \ud83d\udd01 Execute Flow\nTrigger node to start the automation manually or via webhook/schedule.\n\n2 \ud83c\udfa8 Get Figma Export URLs\nFetches Figma export URLs (PNG/SVG) for all components from a specified file or parent node.\n\n3 \ud83e\udde0 Find Icons & Buttons\nFilters nodes by name or type to extract only relevant UI components like \u201cIcon\u201d and \u201cButton\u201d.\n\n4 \ud83d\udcc2 Predefine Drawable Folders\nCreates static Android drawable folder list (mdpi, hdpi, etc.) as JSON for merging later.\n\n5 \ud83d\udd17 Merge Metadata & Folders\nMerges filtered Figma nodes with predefined Android folder structure for each resolution.\n\n6 \ud83c\udf10 Get Figma Image URLs\nCalls Figma export API with IDs from merged metadata to retrieve actual exportable image URLs.\n\n7 \ud83d\udeab Filter Empty Image URLs\nRemoves any nodes where export URL is missing/null to avoid failed downloads or commits.\n\n8 \ud83d\udce5 Download Figma Images\nDownloads binary image files from export URLs for all filtered nodes across densities.\n\n9 \ud83e\uddec Merge Metadata & Images\nMerges previously prepared metadata (drawable folder, file name) with actual downloaded image files.\n\n10 \ud83d\udcdd Edit File Names\nRenames files based on naming conventions (e.g., lowercase, no spaces, append _icon if needed).\n\n11 \ud83d\udd04 If: Pull Request Created?\nPrevents duplicate pull requests by checking if one already exists for the same branch or purpose.\n\n12 \ud83d\ude80 Prepare Pull Request\nCommits all images to GitHub in proper folders and creates a clean Pull Request into the main branch.\n\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "da71a6f8-28a7-467f-998e-a4fef0700dc8",
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Prepare Pull Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "get figma Url",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Edit File names",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "get figma Url": {
      "main": [
        [
          {
            "node": "Filter nullable url of nodes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit File names": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute workflow": {
      "main": [
        [
          {
            "node": "Get Figma Export URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Icons & Buttons": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Figma Export URL": {
      "main": [
        [
          {
            "node": "Find Icons & Buttons",
            "type": "main",
            "index": 0
          },
          {
            "node": "Predefine drawable folders",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Predefine drawable folders": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Filter nullable url of nodes": {
      "main": [
        [
          {
            "node": "Download Each Image from the Figma Export URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Each Image from the Figma Export URL": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}