This workflow follows the Executecommand → HTTP Request recipe pattern — see all workflows that pair these two integrations.
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 →
{
"name": "TestFixer CI Integration",
"nodes": [
{
"id": "webhook-trigger",
"name": "CI Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
250,
300
],
"parameters": {
"path": "testfixer",
"httpMethod": "POST",
"responseMode": "lastNode"
},
"typeVersion": 1
},
{
"id": "validate-payload",
"name": "Validate Payload",
"type": "n8n-nodes-base.if",
"position": [
450,
300
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.repo_path }}",
"operation": "isNotEmpty"
},
{
"value1": "={{ $json.test_command }}",
"operation": "isNotEmpty"
}
]
}
},
"typeVersion": 1
},
{
"id": "run-tests",
"name": "Run Tests",
"type": "n8n-nodes-base.executeCommand",
"position": [
650,
200
],
"parameters": {
"command": "={{ 'cd ' + $json.repo_path + ' && ' + $json.test_command + ' --maxfail=1 2>&1' }}"
},
"typeVersion": 1
},
{
"id": "check-test-result",
"name": "Tests Passed?",
"type": "n8n-nodes-base.if",
"position": [
850,
200
],
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.exitCode }}",
"operation": "equal",
"value2": 0
}
]
}
},
"typeVersion": 1
},
{
"id": "success-response",
"name": "Success Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1050,
100
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ status: 'success', message: 'All tests passed' }) }}"
},
"typeVersion": 1
},
{
"id": "extract-failure",
"name": "Extract Failure",
"type": "n8n-nodes-base.code",
"position": [
1050,
300
],
"parameters": {
"jsCode": "// Parse pytest output to extract first failure\nconst output = $input.first().json.stdout || '';\nconst stderr = $input.first().json.stderr || '';\nconst combined = output + '\\n' + stderr;\n\n// Extract failed test info\nconst failedMatch = combined.match(/FAILED\\s+([^\\s:]+)::([^\\s]+)/);\nconst errorMatch = combined.match(/(\\w+Error|\\w+Exception):\\s*(.+?)(?:\\n|$)/);\n\nlet failure = {\n test_file: '',\n test_name: '',\n error_type: 'Unknown',\n error_message: 'Could not parse failure',\n stack_trace: combined.slice(-2000)\n};\n\nif (failedMatch) {\n failure.test_file = failedMatch[1];\n failure.test_name = failedMatch[2];\n}\n\nif (errorMatch) {\n failure.error_type = errorMatch[1];\n failure.error_message = errorMatch[2].trim();\n}\n\nreturn [{\n json: {\n ...failure,\n repo_path: $('CI Webhook').first().json.repo_path,\n test_command: $('CI Webhook').first().json.test_command,\n iteration: $('CI Webhook').first().json.iteration || 1,\n max_iterations: $('CI Webhook').first().json.max_iterations || 10\n }\n}];"
},
"typeVersion": 2
},
{
"id": "analyze-failure",
"name": "Analyze Failure",
"type": "n8n-nodes-base.httpRequest",
"position": [
1250,
300
],
"parameters": {
"url": "={{ $env.ARAGORA_API_URL || 'http://localhost:8000' }}/api/testfixer/analyze",
"method": "POST",
"bodyParameters": {
"parameters": [
{
"name": "failure",
"value": "={{ JSON.stringify($json) }}"
}
]
},
"options": {}
},
"typeVersion": 4.1
},
{
"id": "propose-fix",
"name": "Propose Fix (Debate)",
"type": "n8n-nodes-base.httpRequest",
"position": [
1450,
300
],
"parameters": {
"url": "={{ $env.ARAGORA_API_URL || 'http://localhost:8000' }}/api/testfixer/propose",
"method": "POST",
"bodyParameters": {
"parameters": [
{
"name": "analysis",
"value": "={{ JSON.stringify($json.analysis) }}"
},
{
"name": "repo_path",
"value": "={{ $json.repo_path }}"
}
]
},
"options": {}
},
"typeVersion": 4.1
},
{
"id": "check-confidence",
"name": "Confidence OK?",
"type": "n8n-nodes-base.if",
"position": [
1650,
300
],
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.proposal.confidence }}",
"operation": "largerEqual",
"value2": 0.5
}
]
}
},
"typeVersion": 1
},
{
"id": "low-confidence",
"name": "Low Confidence Alert",
"type": "n8n-nodes-base.slack",
"position": [
1850,
400
],
"parameters": {
"channel": "={{ $env.SLACK_CHANNEL || '#ci-alerts' }}",
"text": "\u26a0\ufe0f TestFixer low confidence\n\nTest: {{ $json.failure.test_name }}\nError: {{ $json.failure.error_type }}\nConfidence: {{ ($json.proposal.confidence * 100).toFixed(0) }}%\n\nManual review recommended.",
"otherOptions": {}
},
"typeVersion": 2.1
},
{
"id": "apply-fix",
"name": "Apply Fix",
"type": "n8n-nodes-base.httpRequest",
"position": [
1850,
200
],
"parameters": {
"url": "={{ $env.ARAGORA_API_URL || 'http://localhost:8000' }}/api/testfixer/apply",
"method": "POST",
"bodyParameters": {
"parameters": [
{
"name": "proposal_id",
"value": "={{ $json.proposal.id }}"
},
{
"name": "repo_path",
"value": "={{ $json.repo_path }}"
}
]
},
"options": {}
},
"typeVersion": 4.1
},
{
"id": "commit-fix",
"name": "Commit Fix",
"type": "n8n-nodes-base.executeCommand",
"position": [
2050,
200
],
"parameters": {
"command": "={{ 'cd ' + $json.repo_path + ' && git add -A && git commit -m \"fix: Auto-fix ' + $json.proposal.description.replace(/\"/g, '\\\\\"') + '\\n\\nCo-Authored-By: Aragora TestFixer <testfixer@aragora.ai>\"' }}"
},
"typeVersion": 1
},
{
"id": "check-iteration",
"name": "More Iterations?",
"type": "n8n-nodes-base.if",
"position": [
2250,
200
],
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.iteration }}",
"operation": "smaller",
"value2": "={{ $json.max_iterations }}"
}
]
}
},
"typeVersion": 1
},
{
"id": "increment-loop",
"name": "Increment & Loop",
"type": "n8n-nodes-base.httpRequest",
"position": [
2450,
200
],
"parameters": {
"url": "={{ $env.N8N_WEBHOOK_URL || 'http://localhost:5678' }}/webhook/testfixer",
"method": "POST",
"bodyParameters": {
"parameters": [
{
"name": "repo_path",
"value": "={{ $json.repo_path }}"
},
{
"name": "test_command",
"value": "={{ $json.test_command }}"
},
{
"name": "iteration",
"value": "={{ $json.iteration + 1 }}"
},
{
"name": "max_iterations",
"value": "={{ $json.max_iterations }}"
}
]
},
"options": {}
},
"typeVersion": 4.1
},
{
"id": "max-iterations-reached",
"name": "Max Iterations Alert",
"type": "n8n-nodes-base.slack",
"position": [
2450,
350
],
"parameters": {
"channel": "={{ $env.SLACK_CHANNEL || '#ci-alerts' }}",
"text": "\ud83d\udd34 TestFixer max iterations reached\n\nRepo: {{ $json.repo_path }}\nIterations: {{ $json.iteration }}\nLast failure: {{ $json.failure.test_name }}\n\nManual intervention required.",
"otherOptions": {}
},
"typeVersion": 2.1
},
{
"id": "create-linear-issue",
"name": "Create Linear Issue",
"type": "n8n-nodes-base.httpRequest",
"position": [
2650,
350
],
"parameters": {
"url": "https://api.linear.app/graphql",
"method": "POST",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"bodyParameters": {
"parameters": [
{
"name": "query",
"value": "mutation { issueCreate(input: { title: \"TestFixer: Manual fix needed for {{ $json.failure.test_name }}\", description: \"Automated fixing reached max iterations.\\n\\nError: {{ $json.failure.error_type }}\\nMessage: {{ $json.failure.error_message }}\\n\\nLast proposal confidence: {{ ($json.proposal.confidence * 100).toFixed(0) }}%\", teamId: \"{{ $env.LINEAR_TEAM_ID }}\" }) { success issue { id identifier url } } }"
}
]
},
"options": {}
},
"typeVersion": 4.1
}
],
"connections": {
"webhook-trigger": {
"main": [
[
{
"node": "validate-payload",
"type": "main",
"index": 0
}
]
]
},
"validate-payload": {
"main": [
[
{
"node": "run-tests",
"type": "main",
"index": 0
}
],
[]
]
},
"run-tests": {
"main": [
[
{
"node": "check-test-result",
"type": "main",
"index": 0
}
]
]
},
"check-test-result": {
"main": [
[
{
"node": "success-response",
"type": "main",
"index": 0
}
],
[
{
"node": "extract-failure",
"type": "main",
"index": 0
}
]
]
},
"extract-failure": {
"main": [
[
{
"node": "analyze-failure",
"type": "main",
"index": 0
}
]
]
},
"analyze-failure": {
"main": [
[
{
"node": "propose-fix",
"type": "main",
"index": 0
}
]
]
},
"propose-fix": {
"main": [
[
{
"node": "check-confidence",
"type": "main",
"index": 0
}
]
]
},
"check-confidence": {
"main": [
[
{
"node": "apply-fix",
"type": "main",
"index": 0
}
],
[
{
"node": "low-confidence",
"type": "main",
"index": 0
}
]
]
},
"apply-fix": {
"main": [
[
{
"node": "commit-fix",
"type": "main",
"index": 0
}
]
]
},
"commit-fix": {
"main": [
[
{
"node": "check-iteration",
"type": "main",
"index": 0
}
]
]
},
"check-iteration": {
"main": [
[
{
"node": "increment-loop",
"type": "main",
"index": 0
}
],
[
{
"node": "max-iterations-reached",
"type": "main",
"index": 0
}
]
]
},
"max-iterations-reached": {
"main": [
[
{
"node": "create-linear-issue",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
"ci",
"testing",
"automation",
"aragora"
],
"triggerCount": 0,
"updatedAt": "2026-02-03T00:00:00.000Z",
"versionId": "1"
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
TestFixer CI Integration. Uses executeCommand, httpRequest, slack. Webhook trigger; 16 nodes.
Source: https://github.com/synaptent/aragora/blob/5b875649149156b49a0fda1cc8da33f478dbba35/templates/n8n/testfixer-ci.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Development teams and project maintainers who receive high volumes of GitHub issues and want to automate classification and team notifications. Perfect for open source projects, product teams, and Dev
Use cases Auto-generate subtitles for training or educational videos Translate videos into multiple languages for global reach Create accessibility-friendly content with minimal effort Build a backend
PRAGMAS - Main Analysis. Uses httpRequest, executeCommand. Webhook trigger; 6 nodes.
MallanooSploit. Uses openAi, executeCommand, httpRequest. Webhook trigger; 44 nodes.
Venafi Presentation - Watch Video