mvp-factory-openhands/GITHUB_ACTIONS_INTEGRATION_...

13 KiB

GitHub Actions Integration Guide - Hybrid Approach

Date: 2025-12-02 Approach: Hybrid (n8n → GitHub Actions → OpenHands SDK) Status: Ready for implementation


🎯 Overview

This guide shows how to integrate OpenHands SDK via GitHub Actions while keeping n8n for orchestration. This hybrid approach leverages the best of both worlds:

  • n8n: Workflow orchestration, webhook handling, data preservation
  • GitHub Actions: Clean OpenHands execution, logging, artifacts
  • OpenHands SDK: Direct Python integration (no SSH/wrapper)

🏗️ Architecture

┌──────────────┐
│   Git Push   │
│   (Gitea)    │
└──────┬───────┘
       │
       ▼
┌──────────────────────┐
│   Gitea Webhook      │
│  (push to main)      │
└──────┬───────────────┘
       │
       ▼
┌──────────────────────┐
│   n8n Workflow       │
│  ┌────────────────┐ │
│  │ Extract Info   │ │
│  │ Trigger Actions│ │ ← HTTP call to GitHub
│  │ Wait/Callback  │ │
│  │ Update Gitea   │ │
│  └────────────────┘ │
└──────┬───────────────┘
       │
       ▼
┌──────────────────────────────┐
│   GitHub Actions Workflow    │
│  ┌────────────────────────┐ │
│  │ 1. Checkout            │ │
│  │ 2. Setup Python        │ │
│  │ 3. Install SDK         │ │
│  │ 4. Run agent_build.py  │ │ ← OpenHands SDK
│  │ 5. Upload Logs         │ │
│  │ 6. Update Gitea Status │ │
│  └────────────────────────┘ │
└──────┬───────────────────────┘
       │
       ▼
┌──────────────────────┐
│   Gitea Commit       │
│   Status Updated     │
└──────────────────────┘

📝 Implementation Steps

Step 1: Set Up GitHub Repository (10 min)

Option A: Use GitHub.com

# Push to GitHub (if you have a GitHub account)
git remote add github https://github.com/username/repo.git
git push github main

Option B: Mirror from Gitea to GitHub

# Create GitHub repo, then mirror
git clone https://git.oky.sh/gitadmin/project.git
cd project
git remote add github https://github.com/username/project.git
git push github --all

Step 2: Configure GitHub Secrets (5 min)

In GitHub Repository → Settings → Secrets and variables → Actions:

  1. OPENHANDS_API_KEY

    • Value: MiniMax API key from /home/bam/openhands/.env
  2. GITEA_API_TOKEN

    • Value: Gitea API token (generate in Gitea settings)

Repository Variables:

  1. LLM_MODEL

    • Value: anthropic/claude-sonnet-4-5-20250929
  2. LLM_BASE_URL (optional)

    • Value: MiniMax API base URL if using custom endpoint
  3. GITEA_API_URL

    • Value: https://git.oky.sh

Step 3: Create GitHub Actions Workflow (5 min)

The workflow file is already created at:

  • .github/workflows/openhands-build.yml

Make sure it's in your repository root.

Step 4: Create Build Agent Script (5 min)

The agent script is already created at:

  • .github/scripts/agent_build.py

Make sure it's executable:

chmod +x .github/scripts/agent_build.py

Step 5: Test GitHub Actions Manually (15 min)

Trigger via GitHub UI:

  1. Go to GitHub → Actions tab
  2. Select "OpenHands Build & Test" workflow
  3. Click "Run workflow"
  4. Fill in parameters:
    • Task: "Build and test this project"
    • Repo Name: your-repo
    • Commit SHA: current commit SHA
    • Retry Count: 0

Expected Result:

  • Workflow runs successfully
  • Logs uploaded as artifacts
  • Gitea status updated (if configured)

Step 6: Modify n8n Workflow (30 min)

Replace the OpenHands SSH node with GitHub Actions HTTP node:

Node 1: Trigger GitHub Actions (HTTP Request)

Configuration:

  • Method: POST
  • URL: https://api.github.com/repos/{owner}/{repo}/actions/workflows/openhands-build.yml/dispatches
  • Headers:
    {
      "Authorization": "token {{ $node['GitHub Token'].json.token }}",
      "Accept": "application/vnd.github.v3+json",
      "Content-Type": "application/json"
    }
    
  • Body:
    {
      "ref": "main",
      "inputs": {
        "task": "Build and test the project",
        "repo_name": "{{ $node['Extract Repo Info'].json.repo_name }}",
        "commit_sha": "{{ $node['Extract Repo Info'].json.commit_sha }}",
        "retry_count": "{{ $workflow.staticData.retry_count || 0 }}",
        "previous_errors": "{{ $json.error_message || '' }}"
      }
    }
    

Node 2: Wait for Completion (Wait)

  • Amount: 5 (minutes)

Node 3: Check Build Status (HTTP Request)

Configuration:

  • Method: GET
  • URL: https://api.github.com/repos/{owner}/{repo}/actions/runs/{{ $json.id }}
  • Headers:
    {
      "Authorization": "token {{ $node['GitHub Token'].json.token }}",
      "Accept": "application/vnd.github.v3+json"
    }
    

Node 4: Process Results (Code)

const runData = $json;
const repoData = $node["Extract Repo Info"].json;

const status = runData.status; // completed
const conclusion = runData.conclusion; // success or failure

let result;
if (conclusion === 'success') {
  result = {
    status: 'SUCCESS',
    ...repoData,
    message: 'Build completed successfully',
    github_run_id: runData.id,
    github_url: runData.html_url
  };
} else {
  // Check if we should retry
  const currentRetry = $workflow.staticData.retry_count || 0;

  if (currentRetry < 3) {
    // Retry with error feedback
    result = {
      status: 'RETRY',
      ...repoData,
      retry_count: currentRetry + 1,
      error_message: `Build failed. Check logs: ${runData.html_url}`,
      github_run_id: runData.id,
      github_url: runData.html_url
    };
  } else {
    // Max retries reached
    result = {
      status: 'FAILED',
      ...repoData,
      retry_count: currentRetry,
      error_message: `Build failed after 3 attempts. Check logs: ${runData.html_url}`,
      github_run_id: runData.id,
      github_url: runData.html_url
    };
  }
}

return result;

Step 7: Test End-to-End Flow (30 min)

Push Test Repository:

# Make a small change and push
git add .
git commit -m "Test build"
git push origin main

Expected Flow:

  1. Gitea webhook triggers n8n
  2. n8n extracts repo info
  3. n8n triggers GitHub Actions
  4. GitHub Actions runs OpenHands SDK
  5. GitHub Actions updates Gitea status
  6. n8n receives result and notifies

Check:

  • GitHub Actions runs successfully
  • Build logs uploaded
  • Gitea commit status updated
  • n8n workflow completes

🔧 Configuration Details

GitHub Token Requirements

Create Personal Access Token:

  1. GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
  2. Generate new token (classic)
  3. Scopes:
    • repo (Full control of private repositories)
    • workflow (Update GitHub Actions workflows)
    • write:packages (Upload packages)

Use token in n8n:

  • Store as credential: "GitHub Token"
  • Use in HTTP nodes: {{ $node['GitHub Token'].json.token }}

Gitea API Token

Generate Token:

  1. Gitea → Settings → Applications
  2. Generate Access Token
  3. Copy token (save immediately - only shown once)

Use in n8n:

  • Store as credential: "Gitea API Token"
  • Reference in HTTP nodes

OpenHands API Key

Sources:

  • MiniMax: /home/bam/openhands/.envMINIMAX_API_KEY
  • DeepSeek: /home/bam/openhands/.envDEEPSEEK_API_KEY

Use in GitHub:

  • Store as repository secret: OPENHANDS_API_KEY

📊 Complete n8n Workflow Structure

Hybrid Workflow Nodes

[1] Gitea Webhook
     ↓
[2] Extract Repo Info (Code)
     ↓
[3] Initialize Retry (Code)
     → Set $workflow.staticData.retry_count
     ↓
[4] Trigger GitHub Actions (HTTP)
     → POST to GitHub Actions API
     ↓
[5] Wait for Completion (Wait)
     → 5 minutes
     ↓
[6] Check Build Status (HTTP)
     → GET GitHub Actions status
     ↓
[7] Process Results (Code)
     → Parse success/failure
     → Increment retry counter
     ↓
[8] Decision: Continue?
     ├─ YES (retry_count < 3) → [4]
     └─ NO (retry_count >= 3) → [9]
     ↓
[9] Update Gitea Status (HTTP)
     → Success or failure
     ↓
[10] Format Response (Code)
     ↓
[11] HTTP Response

🎯 Testing Checklist

Unit Tests

  • GitHub Actions workflow runs manually
  • Agent script executes successfully
  • OpenHands SDK initializes
  • Build task completes
  • Logs uploaded as artifacts

Integration Tests

  • n8n triggers GitHub Actions
  • GitHub Actions receives parameters
  • Build executes in Actions
  • n8n receives completion status
  • Gitea status updated

End-to-End Tests

  • Git push triggers webhook
  • Workflow completes successfully
  • Gitea status shows "success"
  • All retries tested (success path)
  • All retries tested (failure path, max 3)
  • Error messages properly formatted

🐛 Troubleshooting

Issue: GitHub Actions Not Triggered

Symptoms:

  • n8n HTTP request returns 404 or 403

Solutions:

  1. Check token has workflow scope
  2. Verify workflow file exists: .github/workflows/openhands-build.yml
  3. Check workflow file has workflow_dispatch trigger
  4. Verify correct repository owner/name in URL

Issue: OpenHands API Key Error

Symptoms:

  • Actions log: "LLM_API_KEY environment variable is not set"

Solutions:

  1. Check secret exists: OPENHANDS_API_KEY
  2. Verify secret value is correct
  3. Restart workflow (secrets require new run)

Issue: Build Hangs

Symptoms:

  • Actions runs but never completes

Solutions:

  1. Check agent timeout settings
  2. Review task complexity (make simpler)
  3. Add timeout to agent script
  4. Check OpenHands logs for errors

Issue: Gitea Status Not Updated

Symptoms:

  • Actions completes but Gitea status unchanged

Solutions:

  1. Check GITEA_API_TOKEN secret
  2. Verify Gitea API URL format
  3. Check repository owner/name
  4. Test API token manually:
    curl -H "Authorization: token YOUR_TOKEN" \
      https://git.oky.sh/api/v1/user
    

📈 Benefits Over SSH Approach

Complexity Reduction

Aspect SSH Approach GitHub Actions Approach
Setup n8n + SSH + Wrapper n8n + GitHub Actions
Authentication SSH keys GitHub token
Data Flow Complex $node pattern Standard HTTP
Logging Basic stdout/stderr Structured + Artifacts
Error Handling Custom retry logic GitHub Actions native
Lines of Code ~300 (11 nodes) ~150 (5 nodes)

Developer Experience

Before (SSH):

// Need to preserve data with $node pattern
const repoData = $node["Extract Repo Info"].json;
const sshOutput = $json;
return {
  ...repoData,  // Manual data merging
  code: sshOutput.code,
  stdout: sshOutput.stdout
};

After (GitHub Actions):

// Standard HTTP response
const response = await fetch(url);
const result = await response.json();
return result;

Observability

SSH Approach:

  • Logs: Only stdout/stderr in n8n
  • Artifacts: None
  • Debugging: Complex

GitHub Actions Approach:

  • Logs: Structured logging to files
  • Artifacts: Automatic upload
  • Debugging: Full GitHub Actions UI

📚 Additional Resources

Files Created

  • /home/bam/claude/mvp-factory/.github/workflows/openhands-build.yml
  • /home/bam/claude/mvp-factory/.github/scripts/agent_build.py
  • /home/bam/claude/mvp-factory/NEW_APPROACH_ANALYSIS.md
  • /home/bam/claude/mvp-factory/GITHUB_ACTIONS_INTEGRATION_GUIDE.md (this file)

Documentation


Next Steps

Immediate (Today):

  1. Create GitHub repository
  2. Add workflow and agent script files
  3. Configure GitHub secrets
  4. Test GitHub Actions manually

Tomorrow:

  1. Modify n8n workflow
  2. Add GitHub Actions trigger
  3. Test end-to-end flow
  4. Fix any issues

Day 3:

  1. Test retry logic
  2. Test failure scenarios
  3. Verify Gitea integration
  4. Document configuration

Success Criteria:

  • GitHub Actions runs successfully
  • n8n orchestrates without SSH
  • Gitea status updates automatically
  • Simpler than SSH approach
  • Better error handling and logging

Integration Guide - 2025-12-02 Ready for implementation