mvp-factory-openhands/NEW_APPROACH_ANALYSIS.md

14 KiB

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

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

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

#!/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:

// 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)

# 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

# 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


🎯 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)