# 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)*