The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "j1fz8SLyD2kvPwCR",
"name": "Build Time Hotspot Tracker - Gradle/CocoaPods Analyzer-f",
"tags": [],
"nodes": [
{
"id": "fc6bb8da-653d-49ec-a755-138ee6347b00",
"name": "Set Configuration",
"type": "n8n-nodes-base.set",
"position": [
1168,
624
],
"parameters": {
"values": {
"string": [
{
"name": "regressionThreshold",
"value": "20"
},
{
"name": "historicalBuildsCount",
"value": "10"
},
{
"name": "excludeModules",
"value": "test"
}
]
},
"options": {}
},
"typeVersion": 1
},
{
"id": "0b1f3728-a01a-480b-8b63-7239e1769d9d",
"name": "Comment on PR",
"type": "n8n-nodes-base.github",
"position": [
4544,
640
],
"parameters": {
"body": "=## \ud83d\udd0d Build Time Analysis Report\n\n### \ud83d\udcca Summary\n**Severity**: {{ $('AI Build Analyzer').item.json.severity }}\n**Productivity Impact**: {{ $('AI Build Analyzer').item.json.productivityImpact }}\n\n### \u26a0\ufe0f Performance Regressions\n{{ $('AI Build Analyzer').item.json.regressions.map(r => `- **${r.module}**: ${r.percentSlower}% slower\\n Impact: ${r.impact}`).join('\\n') }}\n\n### \ud83d\udd0e Root Causes\n{{ $('AI Build Analyzer').item.json.rootCauses.map(rc => `- ${rc}`).join('\\n') }}\n\n### \ud83d\udca1 Recommendations\n{{ $('AI Build Analyzer').item.json.recommendations.map(rec => `- ${rec}`).join('\\n') }}\n\n---\n*\ud83e\udd16 Automated analysis by Build Time Hotspot Tracker*",
"owner": {
"__rl": true,
"mode": "name",
"value": "repo"
},
"operation": "createComment",
"repository": {
"__rl": true,
"mode": "name",
"value": "test_app"
},
"issueNumber": "={{ $('AI Build Analyzer').item.json.prNumber }}"
},
"typeVersion": 1
},
{
"id": "060c345c-18fa-414b-ae4e-c5b3bedd5bfa",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"width": 544,
"height": 992,
"content": "# How It Works\nThis workflow serves as an automated CI/CD Build Performance Monitor, analyzing metrics from Gradle or CocoaPods builds to detect regressions and identify performance \"hotspots\". It is initiated by a Webhook that receives a JSON payload containing task durations, module names, and build context. The system uses historical data from Airtable and an AI Agent (GPT-4o-mini) to compare current performance against baselines, categorize the severity of any slowdowns, and generate actionable optimization recommendations.\n# Setup Steps\n## Webhook Trigger\nConfigure your CI/CD pipeline (e.g., GitHub Actions, Jenkins) to send a POST request with build metrics to the workflow's Webhook URL.\n\n## Connect Accounts\nAirtable: Add a Personal Access Token to allow the workflow to fetch historical data and store new analysis records.\nGitHub: Provide API credentials with repository write access to enable automated commenting on Pull Requests.\nGmail: Set up OAuth2 credentials to send automated email alerts when critical performance regressions are detected.\nOpenAI: Ensure active credits are available for the AI Build Analyzer node to perform the regression analysis.\n\n## Configure Parameters\nSet Configuration: Adjust the regressionThreshold (default 20%) and define excludeModules (e.g., filtering out \"test\" tasks) to tailor analysis to your project.\n\nStorage Nodes: Verify the Airtable Base and Table IDs in both the \"Fetch Historical Builds\" and \"Store Build Data\" nodes to ensure data is routed to your specific tracking table.\n\n## Update Notifications\nComment on PR: Confirm that the GitHub account used has the necessary permissions to post comments on the target repository.\n\nNotify Email: Update the recipient email address in the Gmail node to ensure alerts reach the correct team members."
},
"typeVersion": 1
},
{
"id": "08401cf2-d707-451f-9ef6-ab9b84379b6e",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
272
],
"parameters": {
"color": 7,
"width": 816,
"height": 672,
"content": "## Webhook Trigger on Push/PR & Set Configuration\nThe Webhook node is the workflow's entry point, listening for a POST request at /webhook/build-hotspot-tracker sent by the CI/CD job upon build completion. It receives the critical JSON payload, including build ID, PR number, and task durations.\n\nThe subsequent Set Configuration node initializes static parameters for the analysis. This includes the regressionThreshold (default 20 or 20%) used to identify slow modules and excludeModules (default test), which filters out irrelevant tasks like unit tests from the performance calculation.\n"
},
"typeVersion": 1
},
{
"id": "509bdc28-275b-40a5-8955-cdf3eff0a694",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
1648,
272
],
"parameters": {
"color": 7,
"width": 1072,
"height": 688,
"content": "## Historical Data Aggregation & AI Preparation\nFetches the last 10 builds from Airtable to calculate average, maximum, and minimum performance baselines.\nStructures current build metrics and configuration parameters into a standardized format for processing.\nMerges real-time data with historical statistics to create a unified dataset for comparative analysis."
},
"typeVersion": 1
},
{
"id": "58c0a7a8-5cc4-46e1-8014-2c669148cc0b",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
2816,
272
],
"parameters": {
"color": 7,
"width": 1056,
"height": 704,
"content": "## AI-Powered Analysis & Severity Routing\nIntelligent Diagnosis: Uses an AI Agent (GPT-4o-mini) to identify build regressions (>20% slowdown), determine root causes, and suggest optimizations.\nContextual Awareness: Integrates the GitHub API and session memory to factor in PR details and historical build context.\nStructured Routing: Converts AI insights into a standardized JSON format and routes the workflow based on severity (Critical, Warning, or Info)."
},
"typeVersion": 1
},
{
"id": "a2441868-fd57-43f2-a6ed-912333e38fac",
"name": "Notify Email",
"type": "n8n-nodes-base.gmail",
"position": [
4544,
816
],
"parameters": {
"sendTo": "user@example.com",
"message": "=Build regression detected in PR #{{ $('Webhook').item.json.body.prNumber }} for repository {{ $('Webhook').item.json.body.repository }}.\n\nRegressions found:\n{{ $('AI Build Analyzer').item.json.regressions.map(r => `\u2022 ${r.module}: ${r.percentSlower}% slower - ${r.impact}`).join('\\n') }}\n\nRecommendations:\n{{ $('AI Build Analyzer').item.json.recommendations.map(r => `\u2022 ${r}`).join('\\n') }}\n\nView PR: https://github.com/{{ $('Webhook').item.json.body.repository }}/pull/{{ $('Webhook').item.json.body.prNumber }}",
"options": {},
"subject": "=\ud83d\udea8 Build Regression Detected - PR #{{ $('AI Build Analyzer').item.json.regressions[0].module }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "1240d698-d4bb-4048-a7f5-9e54531589e1",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
880,
624
],
"parameters": {
"path": "build-hotspot-tracker",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2.1
},
{
"id": "cfc8a0f2-2b73-49ac-9009-0ab1c1ad7238",
"name": "Store Build Data",
"type": "n8n-nodes-base.airtable",
"position": [
4528,
448
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appPH1qPPYCXj2YBO",
"cachedResultUrl": "https://airtable.com/appPH1qPPYCXj2YBO",
"cachedResultName": "host Base"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tbl7PAEPfvn2C590h",
"cachedResultUrl": "https://airtable.com/{app_id}",
"cachedResultName": "tblBuildHistory"
},
"columns": {
"value": {
"buildId": "={{ $('Webhook').item.json.body.buildId }}",
"prNumber": "={{ $('Webhook').item.json.body.prNumber }}",
"timestamp": "={{ $now.toISO() }}",
"repository": "={{ $('Webhook').item.json.body.repository }}",
"slowestModules": "={{ $('AI Build Analyzer').item.json.regressions.map(r => r.module + ': ' + r.percentSlower + '% slower').join(', ') }}",
"totalBuildTime": "={{ $('Webhook').item.json.body.totalDuration }}",
"recommendations": "={{ $('AI Build Analyzer').item.json.recommendations.join('; ') }}"
},
"schema": [
{
"id": "buildId",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "buildId",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "prNumber",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "prNumber",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "repository",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "repository",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "totalBuildTime",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "totalBuildTime",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "slowestModules",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "slowestModules",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "recommendations",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "recommendations",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "timestamp",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "create"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "61882b5d-27e7-4ec1-bf46-ef6faf5ff48f",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
2912,
768
],
"parameters": {
"model": "gpt-4.1-mini",
"options": {
"maxTokens": 2000,
"temperature": 0.2
}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "95cf853c-6e14-4d7f-adbb-207f0d7503a6",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
3328,
768
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"severity\": {\"type\": \"string\", \"enum\": [\"Critical\", \"Warning\", \"Info\"]},\n \"regressions\": {\"type\": \"array\", \"items\": {\"type\": \"object\", \"properties\": {\"module\": {\"type\": \"string\"}, \"percentSlower\": {\"type\": \"number\"}, \"impact\": {\"type\": \"string\"}}}},\n \"recommendations\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}},\n \"rootCauses\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}},\n \"productivityImpact\": {\"type\": \"string\"}\n },\n \"required\": [\"severity\", \"regressions\", \"recommendations\"]\n}"
},
"typeVersion": 1.3
},
{
"id": "3a103c03-43b3-474a-bf33-a84fe31ce63c",
"name": "Aggregate Historical Data",
"type": "n8n-nodes-base.aggregate",
"position": [
2144,
544
],
"parameters": {
"options": {},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"renameField": true,
"outputFieldName": "avgBuildTime",
"fieldToAggregate": "totalBuildTime"
},
{
"renameField": true,
"outputFieldName": "maxBuildTime",
"fieldToAggregate": "totalBuildTime"
},
{
"renameField": true,
"outputFieldName": "minBuildTime",
"fieldToAggregate": "totalBuildTime"
}
]
}
},
"typeVersion": 1
},
{
"id": "92c09b8f-b9dd-44da-b49c-fb4b9c0d14e6",
"name": "Compare with Historical Builds",
"type": "n8n-nodes-base.compareDatasets",
"position": [
2400,
624
],
"parameters": {
"options": {
"skipFields": "id, createdTime, timestamp"
},
"mergeByFields": {
"values": [
{
"field1": "buildId",
"field2": "buildId"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "3f258363-6faa-4e1e-88ab-ea1f6291b51d",
"name": "GitHub API Tool",
"type": "n8n-nodes-base.httpRequestTool",
"position": [
3184,
768
],
"parameters": {
"url": "=https://api.github.com/repos/{{ $fromAI('repository', 'GitHub repository in format owner/repo', 'string') }}/pulls/{{ $fromAI('prNumber', 'Pull request number', 'string') }}",
"options": {},
"sendHeaders": true,
"authentication": "predefinedCredentialType",
"toolDescription": "Fetch additional context from GitHub API including PR details, commit info, and file changes",
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/vnd.github+json"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "f0925af3-b451-4ba7-96d6-02e495cdaf90",
"name": "Build Context Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"position": [
3040,
768
],
"parameters": {
"sessionKey": "={{ $('Webhook').item.json.body.repository }}-{{ $('Webhook').item.json.body.prNumber }}",
"sessionIdType": "customKey"
},
"typeVersion": 1.3
},
{
"id": "b916a67d-2249-4faf-a991-d181900cb429",
"name": "Fetch Historical Builds",
"type": "n8n-nodes-base.airtable",
"position": [
1904,
544
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appPH1qPPYCXj2YBO",
"cachedResultName": "host Base"
},
"limit": 10,
"table": {
"__rl": true,
"mode": "list",
"value": "tbl7PAEPfvn2C590h",
"cachedResultName": "tblBuildHistory"
},
"options": {},
"operation": "search",
"returnAll": false,
"filterByFormula": "=AND({repository} = '{{ $('Webhook').item.json.body.repository }}', {prNumber} = '{{ $('Webhook').item.json.body.prNumber }}')"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "11568d1d-2e1f-4b89-8c43-1a3e7af32423",
"name": "Prepare AI Input",
"type": "n8n-nodes-base.set",
"position": [
1904,
736
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "currentBuild",
"type": "object",
"value": "={{ $('Webhook').item.json.body }}"
},
{
"id": "id-2",
"name": "config",
"type": "object",
"value": "={{ $('Set Configuration').item.json }}"
},
{
"id": "id-3",
"name": "buildMetrics",
"type": "object",
"value": "={{ { buildId: $('Webhook').item.json.body.buildId, prNumber: $('Webhook').item.json.body.prNumber, repository: $('Webhook').item.json.body.repository, totalDuration: $('Webhook').item.json.body.totalDuration, gradleTasks: $('Webhook').item.json.body.gradle.tasks.length } }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "2015f54b-c2a2-4317-91c4-5491a1a8c93d",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
3984,
272
],
"parameters": {
"color": 7,
"width": 1120,
"height": 704,
"content": "## Automated Reporting & Data Archiving\nGitHub Feedback: Posts a detailed performance report directly to the Pull Request, outlining regressions, root causes, and optimization steps.\nCritical Alerts: Sends real-time Gmail notifications to the team when significant build slowdowns are detected, including direct links to the PR.\nHistory Log: Archives all build metrics, detected \"hotspots,\" and AI suggestions in Airtable for long-term trend analysis and auditing."
},
"typeVersion": 1
},
{
"id": "de7d0eb1-c25c-4742-9df7-3d253fec43f4",
"name": "AI Build Analyzer",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
3024,
480
],
"parameters": {
"text": "=Analyze the following build performance data:\n\nCurrent Build:\n{{ $('Compare with Historical Builds').item.json.currentBuild }}\n\nHistorical Statistics:\n{{ $('Compare with Historical Builds').item.json.historicalStats }}\n\nComparison:\n{{ $('Compare with Historical Builds').item.json.comparison }}\n\nProvide detailed analysis including regressions (modules >20% slower), root causes, and specific recommendations.",
"options": {
"systemMessage": "You are an expert CI/CD build performance analyst specializing in Gradle and CocoaPods build optimization. Your role is to analyze build metrics, identify performance regressions, determine root causes, and provide actionable optimization recommendations. Always classify findings by severity (Critical/Warning/Info) and estimate productivity impact."
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 3
},
{
"id": "2f1cf676-1495-402f-a9c4-fcc673de96b4",
"name": "Switch",
"type": "n8n-nodes-base.switch",
"position": [
3632,
576
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Critical",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "774799b0-3a7b-4783-b3a8-20b02e4ec0b7",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.severity }}",
"rightValue": "Critical"
}
]
},
"renameOutput": true
},
{
"outputKey": "Warning",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "e0f70fdd-6fc5-44c2-936e-84bf84bf624c",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.severity }}",
"rightValue": "Warning"
}
]
},
"renameOutput": true
},
{
"outputKey": "Info",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "a25a6291-ecba-49b5-9083-82589ea11a3b",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.severity }}",
"rightValue": "Info"
}
]
},
"renameOutput": true
}
]
},
"options": {}
},
"typeVersion": 3.3
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "4119a9da-955c-47b5-b0bb-93234e7dc86c",
"connections": {
"Switch": {
"main": [
[
{
"node": "Store Build Data",
"type": "main",
"index": 0
},
{
"node": "Comment on PR",
"type": "main",
"index": 0
},
{
"node": "Notify Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Comment on PR",
"type": "main",
"index": 0
}
],
[
{
"node": "Store Build Data",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "Set Configuration",
"type": "main",
"index": 0
}
]
]
},
"GitHub API Tool": {
"ai_tool": [
[
{
"node": "AI Build Analyzer",
"type": "ai_tool",
"index": 0
}
]
]
},
"Prepare AI Input": {
"main": [
[
{
"node": "Compare with Historical Builds",
"type": "main",
"index": 0
}
]
]
},
"AI Build Analyzer": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Build Analyzer",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Set Configuration": {
"main": [
[
{
"node": "Fetch Historical Builds",
"type": "main",
"index": 0
},
{
"node": "Prepare AI Input",
"type": "main",
"index": 0
}
]
]
},
"Build Context Memory": {
"ai_memory": [
[
{
"node": "AI Build Analyzer",
"type": "ai_memory",
"index": 0
}
]
]
},
"Fetch Historical Builds": {
"main": [
[
{
"node": "Aggregate Historical Data",
"type": "main",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "AI Build Analyzer",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Aggregate Historical Data": {
"main": [
[
{
"node": "Compare with Historical Builds",
"type": "main",
"index": 1
}
]
]
},
"Compare with Historical Builds": {
"main": [
[
{
"node": "AI Build Analyzer",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
airtableTokenApigmailOAuth2openAiApi
About this workflow
Build Time Hotspot Tracker - Gradle/CocoaPods Analyzer-f. Uses github, stickyNote, gmail, airtable. Webhook trigger; 20 nodes.
Source: https://github.com/weblineindia/n8n-Analyze-app-build-time-hotspots-with-Gradle-CocoaPods-Airtable-GitHub-Gmail-and-OpenAI/blob/main/n8n-workflow-template.json — original creator credit. Request a take-down →