8.4 KiB
🚀 Gitea → n8n → OpenHands SDK Integration Guide
Last Updated: 2025-12-01
Status: ✅ WORKING (with variable fix needed)
Total Time: ~8 hours across multiple sessions
🎯 OVERVIEW
This guide documents the successful integration of:
- Gitea (Git hosting)
- n8n (Workflow automation)
- OpenHands SDK v1.3.0 (AI-powered build/test agent)
Flow: git push → Gitea webhook → n8n workflow → SSH to OpenHands SDK
✅ WHAT WORKS
1. Repository Setup
# New clean repository with only 8 essential files
# No .env files in git history (security)
# Deploy keys configured for SSH access
2. n8n Workflow Import
# Import command
docker exec n8n n8n import:workflow --input=/tmp/workflow.json
# Must include "active": true in JSON
3. Webhook Configuration
- URL:
https://n8n.oky.sh/webhook-test/openhands-sdk - Method: POST
- Content-Type: application/json
- Active: ✅
4. SSH Authentication
- Key:
/home/bam/.ssh/n8n_key - Mounted: In n8n container as
/home/node/.ssh - Credentials ID:
v2BMXeCFGpXaoIyb
5. OpenHands SDK Wrapper
- Location:
/home/bam/claude/mvp-factory/openhands-sdk-wrapper-sh.sh - Env File:
/home/bam/openhands/.env - SDK Path:
/tmp/software-agent-sdk - Python Wrapper:
/home/bam/openhands-sdk-wrapper-fixed.py
🔧 WORKFLOW STRUCTURE
Node 1: Webhook Trigger
{
"type": "n8n-nodes-base.webhook",
"parameters": {
"httpMethod": "POST",
"path": "openhands-sdk"
}
}
Node 2: Extract Repo Info (JavaScript)
const payload = $input.item.json;
const repoName = payload.repository?.name || 'unknown';
const repoFullName = payload.repository?.full_name || 'unknown';
const branch = payload.ref?.replace('refs/heads/', '') || 'main';
// ... extract more fields
const task = 'Build and test project ' + repoFullName + ' on branch ' + branch + '. ' +
'Clone the repository and run: npm install && npm test && npm build.';
return { repo_name: repoName, task: task, ... };
Node 3: Execute OpenHands SDK (SSH)
{
"type": "n8n-nodes-base.ssh",
"parameters": {
"command": "={{ 'sh /home/bam/claude/mvp-factory/openhands-sdk-wrapper-sh.sh \"' + $json.task + '\"' }}",
"sessionId": "gitea-session"
}
}
Node 4: Webhook Response
{
"type": "n8n-nodes-base.respondToWebhook",
"parameters": {
"respondWith": "text",
"responseBody": "✅ Build triggered successfully!"
}
}
⚠️ CRITICAL NOTES
n8n Test Mode vs Production
-
Test URL:
https://n8n.oky.sh/webhook-test/openhands-sdk- Requires clicking "Execute workflow" button in UI
- Webhook active for ONE call only
- Variables work properly
-
Production URL:
https://n8n.oky.sh/webhook/openhands-sdk- Always active (if workflow set to active: true)
- DOES NOT WORK with Gitea webhook node (known limitation)
Variable Substitution in SSH
❌ DOESN'T WORK:
"command": "sh /home/bam/claude/mvp-factory/openhands-sdk-wrapper-sh.sh \"{{ $json.task }}\""
✅ WORKS:
"command": "={{ 'sh /home/bam/claude/mvp-factory/openhands-sdk-wrapper-sh.sh \"' + $json.task + '\"' }}"
Why? n8n needs explicit expression evaluation for SSH node commands.
🔨 STEP-BY-STEP WORKFLOW
Step 1: Import Workflow
# Copy workflow JSON to n8n container
docker cp /path/to/workflow.json n8n:/tmp/workflow.json
# Import
docker exec -e N8N_BASIC_AUTH_ACTIVE=false n8n \
n8n import:workflow --input=/tmp/workflow.json
Step 2: Activate Workflow
- Open n8n UI: https://n8n.oky.sh
- Find workflow "Gitea → OpenHands SDK"
- Toggle to activate (make it green)
- OR for test mode: Click "Execute workflow" button
Step 3: Configure Gitea Webhook
- Go to Gitea repository
- Settings → Webhooks → Add Webhook
- Configure:
- URL:
https://n8n.oky.sh/webhook-test/openhands-sdk - Method: POST
- Content Type: application/json
- Push Events: ✅
- Active: ✅
- URL:
- Test webhook (optional)
Step 4: Test Integration
# Make a change and push
echo "test" > test.txt
git add test.txt
git commit -m "Test webhook"
git push origin main
Step 5: Monitor Execution
# Watch n8n logs
docker logs -f n8n
# Should see:
# - "Workflow was started"
# - OpenHands SDK output
# - Task with actual repo/branch values
📁 KEY FILES
Workflow Files
/tmp/openhands-sdk-webhook-FINAL.json- ✅ Working workflow with fixes/home/bam/claude/mvp-factory/openhands-sdk-n8n-CLEAN.json- Reference workflow
Wrapper Scripts
/home/bam/claude/mvp-factory/openhands-sdk-wrapper-sh.sh- Main wrapper (sh-compatible)/home/bam/openhands-sdk-wrapper-fixed.py- Python wrapper/home/bam/openhands/.env- API keys (NOT in git)
Configuration
/home/bam/.ssh/n8n_key- SSH private key for n8n/home/bam/services/services-stack/docker-compose.yml- n8n config with SSH mount
🧪 TESTING CHECKLIST
✅ Verified Working
- Workflow imports to n8n
- Webhook receives Gitea events
- n8n parses webhook payload
- SSH connects to localhost
- OpenHands SDK starts
- Task sent to OpenHands
🔧 Still Needs Fixing
- Variable substitution in SSH command (use expression syntax)
- Production webhook URL (test URL works, production doesn't)
- OpenHands task parsing (sometimes shows "unknown")
🚨 TROUBLESHOOTING
"Webhook not registered"
- Cause: Workflow not active
- Fix: Toggle workflow to active in n8n UI
- Or: Click "Execute workflow" for test mode
"unknown webhook" error
- Cause: Wrong URL path
- Fix: Use
/webhook-test/not/webhook/for n8n+Gitea
SSH authentication fails
- Cause: Key not mounted or wrong permissions
- Fix: Verify in docker-compose.yml:
volumes: - /home/bam/.ssh:/home/node/.ssh:ro
Variables show "{{ $json.field }}"
- Cause: n8n expression not evaluated
- Fix: Use explicit expression:
"={{ 'text ' + $json.field + ' more' }}"
OpenHands SDK not found
- Cause: Virtual environment not activated
- Fix: Wrapper script activates venv before running Python
📚 REFERENCE COMMANDS
Check n8n Logs
docker logs --tail 100 n8n 2>&1 | grep -i "openhands\|ssh\|webhook"
Test Webhook Directly
curl -X POST https://n8n.oky.sh/webhook-test/openhands-sdk \
-H "Content-Type: application/json" \
-d '{"repository":{"full_name":"test/repo"},"ref":"refs/heads/main"}'
Verify Workflow Active
curl -s https://n8n.oky.sh/api/v1/workflows | jq '.data[] | select(.name=="Gitea → OpenHands SDK") | .active'
Restart n8n
docker compose -f /home/bam/services/services-stack/docker-compose.yml restart n8n
🎓 KEY LEARNINGS
-
Test URLs are required for n8n+Gitea integration
- Production
/webhook/doesn't work with Gitea - Must use test mode
/webhook-test/
- Production
-
SSH variable substitution requires explicit expressions
- Can't use
{{ }}syntax directly in SSH command field - Must wrap in expression:
"={{ 'prefix' + $json.field + 'suffix' }}"
- Can't use
-
Workflows deactivate on import
- Always re-activate after importing new version
- Set
"active": truein JSON before import
-
SSH keys need proper mounting
- n8n container needs
/home/bam/.sshmounted as/home/node/.ssh - Use read-only mount:
:ro
- n8n container needs
-
OpenHands wrapper must be sh-compatible
- Use
#!/bin/shnot#!/bin/bash - Load env vars with
sourcenot dot (.works for both)
- Use
🚀 NEXT STEPS
Immediate
- Import
/tmp/openhands-sdk-webhook-FINAL.json - Activate workflow in n8n UI
- Test with new git push
Future Enhancements
- Production webhook support - Find workaround for n8n+Gitea limitation
- Retry logic - Add loop for build completion checking
- Commit status updates - Post results back to Gitea
- Error handling - Better error messages and recovery
- Multiple repos - Support for different repository patterns
📞 SUPPORT
If issues occur:
- Check n8n logs:
docker logs n8n - Verify workflow is active in n8n UI
- Test webhook manually with curl
- Confirm SSH key mounting in docker-compose.yml
- Check OpenHands wrapper script permissions
Status: Integration Complete ✅
Last Test: 2025-12-01 17:30:00
Commit: 290b79c (E2E test successful)