517 lines
13 KiB
Markdown
517 lines
13 KiB
Markdown
# 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*
|