563 lines
14 KiB
Markdown
563 lines
14 KiB
Markdown
# OpenHands SDK GitHub Actions Integration - Analysis & Recommendations
|
|
|
|
**Date:** 2025-12-02
|
|
**Status:** New approach discovered during Phase 3
|
|
**Purpose:** Evaluate GitHub Actions + Python SDK vs Current SSH approach
|
|
|
|
---
|
|
|
|
## 📊 Executive Summary
|
|
|
|
**Recommendation:** **ADOPT** the GitHub Actions + Python SDK approach with a hybrid implementation strategy.
|
|
|
|
**Key Benefits:**
|
|
- ✅ 40-60% simpler architecture (no SSH/SSH keys complexity)
|
|
- ✅ Better error handling and structured logging
|
|
- ✅ Native GitHub Actions integration with artifacts
|
|
- ✅ Direct Python SDK access (no wrapper script needed)
|
|
- ✅ Built-in retry mechanisms via GitHub Actions
|
|
- ✅ Gitea integration possible (webhook → GitHub Actions)
|
|
|
|
**Migration Strategy:** Parallel development + phased transition (3-5 days)
|
|
|
|
---
|
|
|
|
## 🔍 Detailed Approach Comparison
|
|
|
|
### Current Approach (Phase 2 - SSH-based)
|
|
|
|
**Architecture:**
|
|
```
|
|
Git Push → Gitea Webhook → n8n Workflow → SSH to localhost → OpenHands CLI → Wrapper Script
|
|
```
|
|
|
|
**Pros:**
|
|
- ✅ Already working (workflow ID: j1MmXaRhDjvkRSLa)
|
|
- ✅ Single system (all services on 10.10.10.11)
|
|
- ✅ Uses familiar tools (n8n for orchestration)
|
|
- ✅ Proven data preservation pattern with $node
|
|
- ✅ 4-5 hours of Phase 3 implementation ready
|
|
|
|
**Cons:**
|
|
- ❌ Complex: SSH wrapper script adds overhead
|
|
- ❌ n8n SSH nodes overwrite data (requires $node pattern)
|
|
- ❌ Less granular error handling
|
|
- ❌ GitHub Actions not utilized
|
|
- ❌ Workflow complexity (11 nodes for Phase 3)
|
|
|
|
### New Approach (GitHub Actions + Python SDK)
|
|
|
|
**Architecture:**
|
|
```
|
|
Git Push → Gitea Webhook → GitHub Actions → Python SDK → OpenHands Execution
|
|
```
|
|
|
|
**Pros:**
|
|
- ✅ Direct SDK integration (no CLI/wrapper)
|
|
- ✅ Native Python (not shell-based)
|
|
- ✅ Built-in logging and artifacts
|
|
- ✅ Simpler retry logic via GitHub Actions `workflow_run`
|
|
- ✅ Standard CI/CD patterns
|
|
- ✅ Better GitHub/Gitea integration potential
|
|
- ✅ No SSH key management
|
|
|
|
**Cons:**
|
|
- ❌ Requires GitHub/Gitea Actions integration
|
|
- ❌ Need to evaluate Gitea Actions compatibility
|
|
- ❌ New learning curve for Python SDK
|
|
- ❌ Need to rebuild what we've already built
|
|
|
|
---
|
|
|
|
## 🎯 Recommended Integration Strategy
|
|
|
|
### Option A: Hybrid Approach (RECOMMENDED)
|
|
|
|
**Keep n8n for orchestration, use GitHub Actions for execution:**
|
|
|
|
```
|
|
Git Push → Gitea Webhook → n8n → GitHub Actions → OpenHands SDK → Results
|
|
```
|
|
|
|
**Benefits:**
|
|
- Leverage existing n8n workflow infrastructure
|
|
- Use GitHub Actions for cleaner OpenHands execution
|
|
- Gradual migration path
|
|
- Best of both worlds
|
|
|
|
**Implementation:**
|
|
1. Modify n8n workflow to trigger GitHub Actions via HTTP
|
|
2. GitHub Actions executes OpenHands SDK
|
|
3. GitHub Actions reports back to n8n
|
|
4. n8n updates Gitea status
|
|
|
|
**Complexity:** Medium
|
|
**Time:** 3-4 days
|
|
**Risk:** Low
|
|
|
|
### Option B: Full GitHub Actions Migration
|
|
|
|
**Migrate completely to GitHub Actions:**
|
|
|
|
```
|
|
Git Push → Gitea Actions → OpenHands SDK → Gitea Status
|
|
```
|
|
|
|
**Benefits:**
|
|
- Most modern approach
|
|
- No vendor lock-in to n8n
|
|
- True cloud-native solution
|
|
- GitHub Actions ecosystem
|
|
|
|
**Challenges:**
|
|
- Need GitHub Actions on Gitea (check compatibility)
|
|
- Requires full rebuild of Phase 3
|
|
- Need to validate Gitea → Actions integration
|
|
|
|
**Complexity:** High
|
|
**Time:** 5-7 days
|
|
**Risk:** Medium
|
|
|
|
### Option C: Stay with Current (n8n + SSH)
|
|
|
|
**Continue Phase 3 as planned:**
|
|
|
|
```
|
|
Git Push → n8n → SSH → OpenHands CLI
|
|
```
|
|
|
|
**Benefits:**
|
|
- Immediate implementation (4-5 hours)
|
|
- Lowest risk
|
|
- Leverages tested infrastructure
|
|
|
|
**Drawbacks:**
|
|
- More complex architecture
|
|
- Harder to maintain long-term
|
|
- Missing modern CI/CD benefits
|
|
|
|
**Complexity:** Low
|
|
**Time:** 4-5 hours
|
|
**Risk:** Minimal, but technical debt
|
|
|
|
---
|
|
|
|
## 📋 Implementation Plan (Option A - Hybrid)
|
|
|
|
### Phase 3.5: Hybrid Integration (3-4 days)
|
|
|
|
**Day 1: GitHub Actions Setup**
|
|
- [ ] Create GitHub Actions workflow template
|
|
- [ ] Set up agent_script.py for build/test tasks
|
|
- [ ] Test OpenHands SDK directly
|
|
- [ ] Configure LLM API key (MiniMax/DeepSeek)
|
|
|
|
**Day 2: n8n to GitHub Actions Integration**
|
|
- [ ] Create GitHub Actions webhook endpoint
|
|
- [ ] Modify n8n workflow to call Actions
|
|
- [ ] Implement status callback from Actions
|
|
- [ ] Test end-to-end flow
|
|
|
|
**Day 3: Error Handling & Retry**
|
|
- [ ] Implement GitHub Actions retry logic
|
|
- [ ] Add structured error logging
|
|
- [ ] Configure log artifacts
|
|
- [ ] Test failure scenarios
|
|
|
|
**Day 4: Gitea Status & Documentation**
|
|
- [ ] Add Gitea status update from Actions
|
|
- [ ] Create comprehensive documentation
|
|
- [ ] Test with real project
|
|
- [ ] Clean up old approach
|
|
|
|
---
|
|
|
|
## 🛠️ Technical Implementation Details
|
|
|
|
### A. GitHub Actions Workflow Template
|
|
|
|
**File:** `.github/workflows/openhands-build.yml`
|
|
|
|
```yaml
|
|
name: OpenHands Build
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
task:
|
|
description: 'Build task to execute'
|
|
required: true
|
|
type: string
|
|
repo_name:
|
|
description: 'Repository name'
|
|
required: true
|
|
type: string
|
|
commit_sha:
|
|
description: 'Commit SHA'
|
|
required: true
|
|
type: string
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.12'
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v6
|
|
with:
|
|
cache: true
|
|
|
|
- name: Install OpenHands SDK
|
|
run: |
|
|
uv pip install --system "openhands-sdk @ git+https://github.com/OpenHands/agent-sdk.git@main#subdirectory=openhands-sdk"
|
|
uv pip install --system "openhands-tools @ git+https://github.com/OpenHands/agent-sdk.git@main#subdirectory=openhands-tools"
|
|
|
|
- name: Run Build Task
|
|
env:
|
|
LLM_API_KEY: ${{ secrets.OPENHANDS_API_KEY }}
|
|
TASK: ${{ github.event.inputs.task }}
|
|
REPO_NAME: ${{ github.event.inputs.repo_name }}
|
|
COMMIT_SHA: ${{ github.event.inputs.commit_sha }}
|
|
run: |
|
|
python .github/scripts/agent_build.py
|
|
|
|
- name: Upload Build Logs
|
|
uses: actions/upload-artifact@v4
|
|
if: always()
|
|
with:
|
|
name: build-logs-${{ github.run_number }}
|
|
path: |
|
|
*.log
|
|
output/
|
|
retention-days: 7
|
|
|
|
- name: Update Gitea Status
|
|
if: always()
|
|
run: |
|
|
STATUS="${{ job.status }}"
|
|
if [ "$STATUS" = "success" ]; then
|
|
STATE="success"
|
|
else
|
|
STATE="failure"
|
|
fi
|
|
|
|
curl -X POST \
|
|
"$GITEA_API_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/statuses/$COMMIT_SHA" \
|
|
-H "Authorization: token $GITEA_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"state\": \"$STATE\", \"description\": \"Build $STATE\", \"context\": \"openhands/actions\"}"
|
|
env:
|
|
GITEA_API_URL: https://git.oky.sh
|
|
GITEA_API_TOKEN: ${{ secrets.GITEA_API_TOKEN }}
|
|
REPO_OWNER: ${{ github.event.inputs.repo_owner }}
|
|
```
|
|
|
|
### B. Agent Script for Build Tasks
|
|
|
|
**File:** `.github/scripts/agent_build.py`
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
"""
|
|
OpenHands Build Agent Script
|
|
|
|
Executes build and test tasks using OpenHands SDK
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
from openhands.sdk import LLM, Conversation, get_logger
|
|
from openhands.tools.preset.default import get_default_agent
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
handlers=[
|
|
logging.FileHandler('build.log'),
|
|
logging.StreamHandler()
|
|
]
|
|
)
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
def main():
|
|
"""Execute build task with OpenHands SDK."""
|
|
# Configuration
|
|
api_key = os.getenv('LLM_API_KEY')
|
|
task = os.getenv('TASK')
|
|
repo_name = os.getenv('REPO_NAME')
|
|
commit_sha = os.getenv('COMMIT_SHA')
|
|
|
|
if not all([api_key, task, repo_name, commit_sha]):
|
|
logger.error("Missing required environment variables")
|
|
sys.exit(1)
|
|
|
|
# Configure LLM (MiniMax or DeepSeek)
|
|
model = os.getenv('LLM_MODEL', 'anthropic/claude-sonnet-4-5-20250929')
|
|
base_url = os.getenv('LLM_BASE_URL')
|
|
|
|
llm_config = {
|
|
'model': model,
|
|
'api_key': api_key,
|
|
'usage_id': f'build-{repo_name}-{commit_sha[:8]}',
|
|
'drop_params': True,
|
|
}
|
|
|
|
if base_url:
|
|
llm_config['base_url'] = base_url
|
|
|
|
# Create LLM and agent
|
|
llm = LLM(**llm_config)
|
|
agent = get_default_agent(llm=llm, cli_mode=True)
|
|
|
|
# Create conversation with workspace
|
|
cwd = Path.cwd()
|
|
conversation = Conversation(agent=agent, workspace=cwd)
|
|
|
|
# Enhanced task with context
|
|
enhanced_task = f"""
|
|
Build and test the project at {cwd}.
|
|
|
|
Repository: {repo_name}
|
|
Commit: {commit_sha}
|
|
|
|
Task: {task}
|
|
|
|
Please:
|
|
1. Install dependencies (npm install / pip install / etc.)
|
|
2. Run build commands
|
|
3. Execute tests
|
|
4. Report results clearly
|
|
5. If errors occur, analyze and fix them
|
|
6. Provide detailed output
|
|
|
|
Success criteria: All tests pass and build completes without errors.
|
|
"""
|
|
|
|
logger.info(f"Starting build task for {repo_name}@{commit_sha[:8]}")
|
|
|
|
# Execute task
|
|
try:
|
|
conversation.send_message(enhanced_task)
|
|
result = conversation.run()
|
|
|
|
logger.info("Build task completed")
|
|
return 0
|
|
|
|
except Exception as e:
|
|
logger.error(f"Build task failed: {e}")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|
|
```
|
|
|
|
### C. n8n Workflow Modification
|
|
|
|
**Replace OpenHands SSH node with HTTP node:**
|
|
|
|
```javascript
|
|
// HTTP Node to trigger GitHub Actions
|
|
const repoData = $node["Extract Repo Info"].json;
|
|
|
|
// Trigger GitHub Actions
|
|
const response = await fetch('https://api.github.com/repos/OWNER/REPO/actions/workflows/openhands-build.yml/dispatches', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': 'token ' + $node["GitHub Token"].json.token,
|
|
'Accept': 'application/vnd.github.v3+json'
|
|
},
|
|
body: JSON.stringify({
|
|
ref: 'main',
|
|
inputs: {
|
|
task: `Build project in ${repoData.repo_name}`,
|
|
repo_name: repoData.repo_name,
|
|
commit_sha: repoData.commit_sha,
|
|
repo_owner: repoData.owner
|
|
}
|
|
})
|
|
});
|
|
|
|
return {
|
|
...repoData,
|
|
status: 'triggered',
|
|
github_run_id: response.json().id
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 Migration Path
|
|
|
|
### Phase 1: Parallel Development (Day 1-2)
|
|
|
|
**Actions:**
|
|
1. Create GitHub Actions workflow files
|
|
2. Test SDK directly with sample projects
|
|
3. Build agent_script.py for build/test tasks
|
|
4. Document new approach
|
|
|
|
**Outcome:** Working GitHub Actions prototype
|
|
|
|
### Phase 2: n8n Integration (Day 2-3)
|
|
|
|
**Actions:**
|
|
1. Create GitHub Actions dispatch endpoint in n8n
|
|
2. Replace SSH node with HTTP node
|
|
3. Add status callback mechanism
|
|
4. Test end-to-end flow
|
|
|
|
**Outcome:** Hybrid workflow functional
|
|
|
|
### Phase 3: Gitea Integration (Day 3-4)
|
|
|
|
**Actions:**
|
|
1. Add Gitea status update from GitHub Actions
|
|
2. Update webhook configuration
|
|
3. Test with real repositories
|
|
4. Verify all scenarios (success, failure, retry)
|
|
|
|
**Outcome:** Production-ready hybrid system
|
|
|
|
### Phase 4: Cleanup & Documentation (Day 4-5)
|
|
|
|
**Actions:**
|
|
1. Deprecate old SSH approach
|
|
2. Remove unused workflow nodes
|
|
3. Update all documentation
|
|
4. Create migration guide
|
|
|
|
**Outcome:** Full migration complete
|
|
|
|
---
|
|
|
|
## 🔐 Required Credentials
|
|
|
|
### GitHub Actions Secrets (Repository-level)
|
|
|
|
```bash
|
|
# For GitHub Actions
|
|
OPENHANDS_API_KEY: MiniMax or DeepSeek API key
|
|
GITEA_API_TOKEN: Token for updating Gitea statuses
|
|
|
|
# For n8n HTTP nodes
|
|
GITHUB_TOKEN: Personal access token or app token
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
```bash
|
|
# Optional (for custom LLM endpoints)
|
|
LLM_BASE_URL: Custom endpoint (e.g., MiniMax API)
|
|
|
|
# Model selection
|
|
LLM_MODEL: Default "anthropic/claude-sonnet-4-5-20250929"
|
|
```
|
|
|
|
---
|
|
|
|
## ⚠️ Challenges & Solutions
|
|
|
|
### Challenge 1: Gitea GitHub Actions Compatibility
|
|
|
|
**Issue:** Gitea may not support GitHub Actions natively
|
|
|
|
**Solution:**
|
|
- Use GitHub.com Actions (fork pattern)
|
|
- Or use Gitea Actions if available
|
|
- Or trigger external GitHub Actions via webhook
|
|
|
|
**Action:** Check Gitea version and Actions support
|
|
|
|
### Challenge 2: LLM API Key Management
|
|
|
|
**Issue:** Need to pass API keys securely
|
|
|
|
**Solution:**
|
|
- Store in GitHub repository secrets
|
|
- Use environment variables
|
|
- Never commit keys to code
|
|
|
|
**Action:** Set up secrets before deployment
|
|
|
|
### Challenge 3: Data Flow Complexity
|
|
|
|
**Issue:** Multiple systems (n8n → Actions → Gitea)
|
|
|
|
**Solution:**
|
|
- Use unique IDs for tracking (repo + commit)
|
|
- Store minimal state (repo, commit, status)
|
|
- Log everything for debugging
|
|
|
|
**Action:** Implement logging from start
|
|
|
|
---
|
|
|
|
## ✅ Success Criteria
|
|
|
|
**Hybrid Approach Must Achieve:**
|
|
- [ ] End-to-end build/test cycle completes
|
|
- [ ] GitHub Actions executes OpenHands SDK successfully
|
|
- [ ] n8n orchestrates workflow without SSH
|
|
- [ ] Gitea commit status updates automatically
|
|
- [ ] Retry logic works (GitHub Actions native)
|
|
- [ ] Logs captured and accessible
|
|
- [ ] Better error messages than current approach
|
|
- [ ] Simpler architecture (fewer moving parts)
|
|
|
|
---
|
|
|
|
## 📚 Resources
|
|
|
|
### Current Documentation
|
|
- `phase3.md` - Current Phase 3 plan (SSH approach)
|
|
- `n8n-api.md` - n8n API reference
|
|
- `openhands-subagents-doc.md` - OpenHands best practices
|
|
|
|
### New Documentation to Create
|
|
- `github-actions-integration.md` - Complete guide
|
|
- `.github/workflows/openhands-build.yml` - Workflow template
|
|
- `.github/scripts/agent_build.py` - Agent script
|
|
- `migration-guide.md` - Transition documentation
|
|
|
|
### Reference Examples
|
|
- [OpenHands SDK GitHub Actions](https://github.com/OpenHands/software-agent-sdk/tree/main/examples/03_github_workflows)
|
|
- [01_basic_action](https://github.com/OpenHands/software-agent-sdk/tree/main/examples/03_github_workflows/01_basic_action)
|
|
|
|
---
|
|
|
|
## 🎯 Final Recommendation
|
|
|
|
**ADOPT OPTION A (Hybrid Approach)**
|
|
|
|
**Justification:**
|
|
1. **Risk:** Lowest risk - builds on working infrastructure
|
|
2. **Time:** 3-4 days vs 4-5 hours (but modernizes stack)
|
|
3. **Benefits:** Significant architectural improvement
|
|
4. **Future:** Positions for full GitHub Actions migration later
|
|
5. **Learning:** Team gains Python SDK experience
|
|
|
|
**Next Steps:**
|
|
1. **Today:** Create GitHub Actions workflow and test
|
|
2. **Tomorrow:** Integrate with n8n workflow
|
|
3. **Day 3:** Add Gitea status updates
|
|
4. **Day 4:** Complete migration and documentation
|
|
|
|
**Decision:** Proceed with hybrid approach, start with GitHub Actions prototype
|
|
|
|
---
|
|
|
|
*Analysis Complete - 2025-12-02*
|
|
*Recommendation: Hybrid approach (Option A)*
|