# 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** ```bash # 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** ```bash # 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: ```bash 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:** ```json { "Authorization": "token {{ $node['GitHub Token'].json.token }}", "Accept": "application/vnd.github.v3+json", "Content-Type": "application/json" } ``` - **Body:** ```json { "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:** ```json { "Authorization": "token {{ $node['GitHub Token'].json.token }}", "Accept": "application/vnd.github.v3+json" } ``` #### Node 4: Process Results (Code) ```javascript 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:** ```bash # 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/.env` β†’ `MINIMAX_API_KEY` - **DeepSeek:** `/home/bam/openhands/.env` β†’ `DEEPSEEK_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: ```bash 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):** ```javascript // 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):** ```javascript // 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 - [OpenHands SDK Documentation](https://docs.openhands.dev/sdk/) - [GitHub Actions API](https://docs.github.com/en/rest/actions) - [Gitea API Documentation](https://docs.gitea.io/en-us/api-usage/) - [n8n HTTP Request Node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/) --- ## βœ… 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*