Add n8n workflow JSON files

- openhands-workflow.json: Manual test workflow
- openhands-gitea-webhook-workflow.json: Production CI/CD workflow

Both ready to import directly into n8n
This commit is contained in:
Git Admin 2025-11-29 20:14:44 +00:00
parent fdabda64b2
commit e58778a4bb
2 changed files with 599 additions and 0 deletions

View File

@ -0,0 +1,350 @@
{
"name": "Gitea → OpenHands CI/CD",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "gitea-push",
"options": {}
},
"id": "webhook-trigger",
"name": "Gitea Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1.1,
"position": [240, 300],
"webhookId": "gitea-push"
},
{
"parameters": {
"jsCode": "// Extract repository and commit information from Gitea webhook\nconst payload = $input.item.json;\n\n// Extract key information\nconst repoName = payload.repository?.name || 'unknown';\nconst repoFullName = payload.repository?.full_name || 'unknown';\nconst repoCloneUrl = payload.repository?.clone_url || '';\nconst branch = payload.ref?.replace('refs/heads/', '') || 'main';\nconst commitSha = payload.after || '';\nconst commitMessage = payload.commits?.[0]?.message || 'No message';\nconst pusher = payload.pusher?.username || 'unknown';\n\n// Create task message for OpenHands\nconst task = `Build and test project ${repoFullName} on branch ${branch}. ` +\n `Latest commit: \"${commitMessage}\". ` +\n `Run the following commands: npm install && npm test && npm build. ` +\n `Report any errors found.`;\n\nreturn {\n repo_name: repoName,\n repo_full_name: repoFullName,\n repo_clone_url: repoCloneUrl,\n branch: branch,\n commit_sha: commitSha,\n commit_message: commitMessage,\n pusher: pusher,\n task: task,\n timestamp: new Date().toISOString()\n};"
},
"id": "extract-repo-info",
"name": "Extract Repo Info",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [460, 300]
},
{
"parameters": {
"url": "http://172.18.0.1:3000/api/conversations",
"method": "POST",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "initial_user_msg",
"value": "={{ $json.task }}"
},
{
"name": "repository",
"value": "={{ $json.repo_clone_url }}"
},
{
"name": "selected_branch",
"value": "={{ $json.branch }}"
}
]
},
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"id": "create-conversation",
"name": "Create OpenHands Session",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [680, 300]
},
{
"parameters": {
"amount": 10,
"unit": "seconds"
},
"id": "wait-initial",
"name": "Wait 10s",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [900, 300],
"webhookId": "wait-initial"
},
{
"parameters": {
"url": "=http://172.18.0.1:3000/api/conversations/{{ $json.conversation_id }}",
"method": "GET",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"id": "get-status",
"name": "Check Build Status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1120, 300]
},
{
"parameters": {
"conditions": {
"options": {
"combineOperation": "any"
},
"conditions": [
{
"id": "status-running",
"leftValue": "={{ $json.status }}",
"rightValue": "RUNNING",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "status-stopped",
"leftValue": "={{ $json.status }}",
"rightValue": "STOPPED",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "status-awaiting",
"leftValue": "={{ $json.status }}",
"rightValue": "AWAITING_USER_INPUT",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
},
"options": {}
},
"id": "check-ready",
"name": "Is Build Ready?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1340, 300]
},
{
"parameters": {
"amount": 15,
"unit": "seconds"
},
"id": "wait-retry",
"name": "Wait 15s Retry",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [1560, 180],
"webhookId": "wait-retry"
},
{
"parameters": {
"jsCode": "// Track retry count\nconst retries = $workflow.staticData.retries || 0;\nconst maxRetries = 20; // 5 minutes max (20 * 15s)\n\nif (retries >= maxRetries) {\n throw new Error('Build timeout: Max retries exceeded (5 minutes)');\n}\n\n$workflow.staticData.retries = retries + 1;\n\n// Pass through conversation_id\nconst convId = $input.item.json.conversation_id || $('Create OpenHands Session').item.json.conversation_id;\n\nreturn {\n conversation_id: convId,\n retry_count: retries + 1,\n max_retries: maxRetries\n};"
},
"id": "retry-counter",
"name": "Retry Counter",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1780, 180]
},
{
"parameters": {
"url": "=http://172.18.0.1:3000/api/conversations/{{ $('Create OpenHands Session').item.json.conversation_id }}/events",
"method": "GET",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"id": "get-events",
"name": "Get Build Events",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1560, 420]
},
{
"parameters": {
"jsCode": "// Analyze events to determine build success/failure\nconst events = $input.item.json.events || [];\nconst repoInfo = $('Extract Repo Info').item.json;\n\n// Look for error indicators\nconst hasErrors = events.some(e => \n e.message?.toLowerCase().includes('error') ||\n e.message?.toLowerCase().includes('failed') ||\n e.observation?.toLowerCase().includes('error')\n);\n\n// Look for success indicators\nconst hasSuccess = events.some(e =>\n e.message?.toLowerCase().includes('success') ||\n e.message?.toLowerCase().includes('passed') ||\n e.message?.toLowerCase().includes('completed')\n);\n\n// Get last event\nconst lastEvent = events[events.length - 1] || {};\n\nreturn {\n repo: repoInfo.repo_full_name,\n branch: repoInfo.branch,\n commit: repoInfo.commit_sha,\n build_status: hasErrors ? 'FAILED' : (hasSuccess ? 'SUCCESS' : 'UNKNOWN'),\n total_events: events.length,\n last_event_message: lastEvent.message || 'No message',\n has_errors: hasErrors,\n has_success: hasSuccess,\n events: events\n};"
},
"id": "analyze-results",
"name": "Analyze Build Results",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1780, 420]
},
{
"parameters": {
"mode": "raw",
"jsonOutput": "={{ {\n \"status\": $json.build_status,\n \"repo\": $json.repo,\n \"branch\": $json.branch,\n \"commit\": $json.commit.substring(0, 8),\n \"message\": $json.build_status === 'SUCCESS' ? '✅ Build passed' : ($json.build_status === 'FAILED' ? '❌ Build failed' : '⚠️ Build status unknown'),\n \"total_events\": $json.total_events,\n \"timestamp\": new Date().toISOString()\n} }}",
"options": {}
},
"id": "format-response",
"name": "Format Response",
"type": "n8n-nodes-base.set",
"typeVersion": 3.3,
"position": [2000, 420]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
},
"id": "webhook-response",
"name": "Webhook Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [2220, 420]
}
],
"connections": {
"Gitea Webhook": {
"main": [
[
{
"node": "Extract Repo Info",
"type": "main",
"index": 0
}
]
]
},
"Extract Repo Info": {
"main": [
[
{
"node": "Create OpenHands Session",
"type": "main",
"index": 0
}
]
]
},
"Create OpenHands Session": {
"main": [
[
{
"node": "Wait 10s",
"type": "main",
"index": 0
}
]
]
},
"Wait 10s": {
"main": [
[
{
"node": "Check Build Status",
"type": "main",
"index": 0
}
]
]
},
"Check Build Status": {
"main": [
[
{
"node": "Is Build Ready?",
"type": "main",
"index": 0
}
]
]
},
"Is Build Ready?": {
"main": [
[
{
"node": "Get Build Events",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 15s Retry",
"type": "main",
"index": 0
}
]
]
},
"Wait 15s Retry": {
"main": [
[
{
"node": "Retry Counter",
"type": "main",
"index": 0
}
]
]
},
"Retry Counter": {
"main": [
[
{
"node": "Check Build Status",
"type": "main",
"index": 0
}
]
]
},
"Get Build Events": {
"main": [
[
{
"node": "Analyze Build Results",
"type": "main",
"index": 0
}
]
]
},
"Analyze Build Results": {
"main": [
[
{
"node": "Format Response",
"type": "main",
"index": 0
}
]
]
},
"Format Response": {
"main": [
[
{
"node": "Webhook Response",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0,
"updatedAt": "2025-11-29T19:40:00.000Z",
"versionId": "1"
}

249
openhands-workflow.json Normal file
View File

@ -0,0 +1,249 @@
{
"name": "OpenHands API Test Workflow",
"nodes": [
{
"parameters": {},
"id": "manual-trigger",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [240, 300]
},
{
"parameters": {
"url": "http://172.18.0.1:3000/api/conversations",
"method": "POST",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "initial_user_msg",
"value": "Create a file named hello.txt with content: Hello from n8n automated workflow!"
}
]
},
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"id": "create-conversation",
"name": "Create Conversation",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [460, 300]
},
{
"parameters": {
"amount": 5,
"unit": "seconds"
},
"id": "wait-5s",
"name": "Wait 5s",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [680, 300],
"webhookId": "wait-5s"
},
{
"parameters": {
"url": "=http://172.18.0.1:3000/api/conversations/{{ $json.conversation_id }}",
"method": "GET",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"id": "get-status",
"name": "Get Conversation Status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [900, 300]
},
{
"parameters": {
"conditions": {
"options": {
"combineOperation": "any"
},
"conditions": [
{
"id": "status-running",
"leftValue": "={{ $json.status }}",
"rightValue": "RUNNING",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "status-awaiting",
"leftValue": "={{ $json.status }}",
"rightValue": "AWAITING_USER_INPUT",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "status-stopped",
"leftValue": "={{ $json.status }}",
"rightValue": "STOPPED",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
},
"options": {}
},
"id": "check-status",
"name": "Check If Ready",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1120, 300]
},
{
"parameters": {
"amount": 10,
"unit": "seconds"
},
"id": "wait-more",
"name": "Wait 10s More",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [1340, 180],
"webhookId": "wait-more"
},
{
"parameters": {
"url": "=http://172.18.0.1:3000/api/conversations/{{ $('Create Conversation').item.json.conversation_id }}/events?limit=20",
"method": "GET",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"id": "get-events",
"name": "Get Events",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1340, 420]
},
{
"parameters": {
"jsCode": "// Extract conversation ID and status from previous node\nconst convId = $input.item.json.conversation_id || $('Create Conversation').item.json.conversation_id;\nconst status = $input.item.json.status;\nconst runtimeStatus = $input.item.json.runtime_status;\n\nreturn {\n conversation_id: convId,\n status: status,\n runtime_status: runtimeStatus,\n message: `Status: ${status}, Runtime: ${runtimeStatus}`\n};"
},
"id": "format-status",
"name": "Format Status",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1560, 180]
}
],
"connections": {
"Manual Trigger": {
"main": [
[
{
"node": "Create Conversation",
"type": "main",
"index": 0
}
]
]
},
"Create Conversation": {
"main": [
[
{
"node": "Wait 5s",
"type": "main",
"index": 0
}
]
]
},
"Wait 5s": {
"main": [
[
{
"node": "Get Conversation Status",
"type": "main",
"index": 0
}
]
]
},
"Get Conversation Status": {
"main": [
[
{
"node": "Check If Ready",
"type": "main",
"index": 0
}
]
]
},
"Check If Ready": {
"main": [
[
{
"node": "Get Events",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 10s More",
"type": "main",
"index": 0
}
]
]
},
"Wait 10s More": {
"main": [
[
{
"node": "Format Status",
"type": "main",
"index": 0
}
]
]
},
"Format Status": {
"main": [
[
{
"node": "Get Conversation Status",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0,
"updatedAt": "2025-11-29T19:30:00.000Z",
"versionId": "1"
}