|
| 1 | +# Contributing |
| 2 | + |
| 3 | +Merci de contribuer a PatchworkAgent ! Ce guide explique comment ajouter un nouveau provider IA, modifier l'orchestrateur, ou corriger un bug. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Pre-requis |
| 8 | + |
| 9 | +- Docker |
| 10 | +- k3s (ou un cluster Kubernetes local) |
| 11 | +- `kubectl` configure sur le namespace `ai-bot` |
| 12 | +- Python 3.11+ (pour l'orchestrateur) |
| 13 | +- Les cles API des providers que vous souhaitez tester |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## Structure du projet |
| 18 | + |
| 19 | +``` |
| 20 | +. |
| 21 | +├── app/ |
| 22 | +│ └── app.py # Orchestrateur FastAPI |
| 23 | +├── providers/ |
| 24 | +│ ├── git_workflow.sh # Logique Git partagee (clone, branch, push, PR) |
| 25 | +│ ├── claude_code.sh # Provider Claude Code |
| 26 | +│ ├── openai.sh # Provider OpenAI Codex |
| 27 | +│ └── aider.sh # Provider Aider (OpenRouter) |
| 28 | +├── images/ |
| 29 | +│ ├── orchestrator/Dockerfile # Image orchestrateur |
| 30 | +│ ├── worker-claude/ # Image + run.sh worker Claude |
| 31 | +│ ├── worker-codex/ # Image + run.sh worker Codex |
| 32 | +│ └── worker-aider/ # Image + run.sh worker Aider |
| 33 | +├── k8s/ |
| 34 | +│ ├── namespace-rbac.yaml # Namespace + RBAC |
| 35 | +│ ├── orchestrator.yaml # Deployment + Service + Ingress |
| 36 | +│ ├── debug-claude.yaml # Job de debug Claude |
| 37 | +│ ├── debug-codex.yaml # Job de debug Codex |
| 38 | +│ └── debug-aider.yaml # Job de debug Aider |
| 39 | +└── README.md |
| 40 | +``` |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## Ajouter un nouveau provider IA |
| 45 | + |
| 46 | +Pour ajouter un provider `myprovider` avec le label de declenchement `ai-pr-myprovider` : |
| 47 | + |
| 48 | +### 1. Script provider : `providers/myprovider.sh` |
| 49 | + |
| 50 | +Suivre le pattern existant : |
| 51 | + |
| 52 | +```bash |
| 53 | +#!/usr/bin/env bash |
| 54 | +set -euo pipefail |
| 55 | + |
| 56 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 57 | + |
| 58 | +# 1. Verifier le token GitHub |
| 59 | +: "${GITHUB_TOKEN:?GITHUB_TOKEN is required}" |
| 60 | + |
| 61 | +# 2. Clone & branch (logique partagee) |
| 62 | +source "$SCRIPT_DIR/git_workflow.sh" |
| 63 | +git_clone_and_branch |
| 64 | + |
| 65 | +# 3. Appeler le CLI IA |
| 66 | +myprovider-cli run "Fix issue #${ISSUE_NUMBER}: ${GITHUB_ISSUE_TITLE:-no title}. ..." |
| 67 | + |
| 68 | +# 4. Push & PR (logique partagee) |
| 69 | +git_push_and_pr "Automated PR created by MyProvider for issue #${ISSUE_NUMBER}." |
| 70 | +``` |
| 71 | + |
| 72 | +Les fonctions `git_clone_and_branch` et `git_push_and_pr` sont dans `providers/git_workflow.sh`. Ne pas dupliquer cette logique. |
| 73 | + |
| 74 | +### 2. Wrapper d'entree : `images/worker-myprovider/run.sh` |
| 75 | + |
| 76 | +```bash |
| 77 | +#!/usr/bin/env bash |
| 78 | +set -euo pipefail |
| 79 | + |
| 80 | +echo "=== worker start ===" |
| 81 | +echo "TIME: $(date -u --iso-8601=seconds)" |
| 82 | +echo "AI_PROVIDER=${AI_PROVIDER:-myprovider}" |
| 83 | +echo "GITHUB_REPO=${GITHUB_REPO:-}" |
| 84 | +echo "GITHUB_ISSUE_NUMBER=${GITHUB_ISSUE_NUMBER:-}" |
| 85 | +echo "GITHUB_INSTALLATION_ID=${GITHUB_INSTALLATION_ID:-}" |
| 86 | +if [[ "${DEBUG_ENV:-0}" == "1" ]]; then |
| 87 | + echo "---- env (whitelist) ----" |
| 88 | + printenv | grep -E '^(AI_PROVIDER|GITHUB_REPO|GITHUB_ISSUE_NUMBER|GITHUB_INSTALLATION_ID|NAMESPACE|JOB_IMAGE|HOME|PATH)=' || true |
| 89 | + echo "---- end env ----" |
| 90 | +fi |
| 91 | + |
| 92 | +exec /app/providers/myprovider.sh |
| 93 | +``` |
| 94 | + |
| 95 | +### 3. Dockerfile : `images/worker-myprovider/Dockerfile` |
| 96 | + |
| 97 | +```dockerfile |
| 98 | +FROM ubuntu:22.04 |
| 99 | + |
| 100 | +ENV DEBIAN_FRONTEND=noninteractive |
| 101 | +WORKDIR /tmp |
| 102 | + |
| 103 | +RUN apt-get update && apt-get install -y \ |
| 104 | + curl ca-certificates git bash jq procps \ |
| 105 | + && rm -rf /var/lib/apt/lists/* |
| 106 | + |
| 107 | +# Installer les deps specifiques au CLI (Node.js, Go, etc.) |
| 108 | + |
| 109 | +# Creer un user non-root (certains CLIs le requierent) |
| 110 | +RUN useradd -m -s /bin/bash worker |
| 111 | +USER worker |
| 112 | + |
| 113 | +# Installer le CLI IA |
| 114 | +RUN curl -fsSL https://example.com/install.sh | bash |
| 115 | + |
| 116 | +# Copier les scripts |
| 117 | +WORKDIR /app |
| 118 | +COPY --chown=worker:worker images/worker-myprovider/run.sh /app/run.sh |
| 119 | +COPY --chown=worker:worker providers/ /app/providers/ |
| 120 | +RUN sed -i 's/\r$//' /app/run.sh /app/providers/*.sh \ |
| 121 | + && chmod +x /app/run.sh /app/providers/*.sh |
| 122 | + |
| 123 | +ENV PATH="/home/worker/.local/bin:${PATH}" |
| 124 | +WORKDIR /work |
| 125 | + |
| 126 | +ENTRYPOINT ["/app/run.sh"] |
| 127 | +CMD [] |
| 128 | +``` |
| 129 | + |
| 130 | +### 4. Job de debug : `k8s/debug-myprovider.yaml` |
| 131 | + |
| 132 | +Creer un Job qui : |
| 133 | +- Verifie l'installation du CLI (`command -v`, `--version`) |
| 134 | +- Verifie la cle API (longueur, presence) |
| 135 | +- Execute un test simple (ex: "Quelle est la capitale de la France ?") |
| 136 | +- Dort ensuite (`sleep 36000`) pour permettre `kubectl exec` |
| 137 | + |
| 138 | +### 5. Enregistrer le provider dans `app/app.py` |
| 139 | + |
| 140 | +```python |
| 141 | +# Ajouter la variable d'image |
| 142 | +MYPROVIDER_WORKER_IMAGE = os.getenv("MYPROVIDER_WORKER_IMAGE", "worker-myprovider:latest") |
| 143 | + |
| 144 | +# Ajouter dans PROVIDER_CONFIG |
| 145 | +"myprovider": ProviderConfig( |
| 146 | + image=MYPROVIDER_WORKER_IMAGE, |
| 147 | + ai_provider="myprovider", |
| 148 | + api_secret=ProviderSecretRef("MY_API_KEY", "myprovider-api-key", "MY_API_KEY"), |
| 149 | +), |
| 150 | +``` |
| 151 | + |
| 152 | +### 6. Mettre a jour le README.md |
| 153 | + |
| 154 | +- Table des secrets (section 1) |
| 155 | +- Table des images (section 2) |
| 156 | +- Commandes de build (section 2) |
| 157 | +- Section debug jobs (section 3) |
| 158 | +- Section "En cas de changement d'image" (section 4) |
| 159 | +- Troubleshooting si necessaire (section 5) |
| 160 | + |
| 161 | +### 7. Verification |
| 162 | + |
| 163 | +```shell |
| 164 | +# Syntaxe Python |
| 165 | +python -c "import ast; ast.parse(open('app/app.py').read())" |
| 166 | + |
| 167 | +# Build de l'image |
| 168 | +docker build -f images/worker-myprovider/Dockerfile -t worker-myprovider:latest . |
| 169 | + |
| 170 | +# Import dans k3s |
| 171 | +docker save worker-myprovider:latest | sudo k3s ctr images import - |
| 172 | + |
| 173 | +# Lancer le job de debug |
| 174 | +kubectl -n ai-bot apply -f k8s/debug-myprovider.yaml |
| 175 | +kubectl -n ai-bot logs -f job/debug-myprovider |
| 176 | + |
| 177 | +# Test end-to-end : ajouter le label ai-pr-myprovider sur une issue |
| 178 | +``` |
| 179 | + |
| 180 | +--- |
| 181 | + |
| 182 | +## Modifier l'orchestrateur |
| 183 | + |
| 184 | +L'orchestrateur est dans `app/app.py` (FastAPI). |
| 185 | + |
| 186 | +```shell |
| 187 | +# Verifier la syntaxe apres modification |
| 188 | +python -c "import ast; ast.parse(open('app/app.py').read())" |
| 189 | + |
| 190 | +# Rebuild + deploiement |
| 191 | +docker build -f images/orchestrator/Dockerfile -t ghcr.io/hey-intent/orchestrator:latest . |
| 192 | +docker save ghcr.io/hey-intent/orchestrator:latest | sudo k3s ctr images import - |
| 193 | +kubectl -n ai-bot rollout restart deployment/orchestrator |
| 194 | + |
| 195 | +# Verifier les logs |
| 196 | +kubectl -n ai-bot logs -f deploy/orchestrator --tail=200 |
| 197 | +``` |
| 198 | + |
| 199 | +--- |
| 200 | + |
| 201 | +## Conventions |
| 202 | + |
| 203 | +### Scripts shell |
| 204 | + |
| 205 | +- Shebang : `#!/usr/bin/env bash` |
| 206 | +- Toujours `set -euo pipefail` |
| 207 | +- Fin de ligne Unix (LF). Les Dockerfiles font `sed -i 's/\r$//'` par securite. |
| 208 | +- Ne jamais logger de secrets. Utiliser `GIT_ASKPASS` pour les tokens Git. |
| 209 | + |
| 210 | +### Dockerfiles |
| 211 | + |
| 212 | +- Base : `ubuntu:22.04` |
| 213 | +- Deps communes : `curl ca-certificates git bash jq procps` |
| 214 | +- User non-root `worker` (UID auto) |
| 215 | +- `WORKDIR /work` pour l'execution |
| 216 | +- `ENTRYPOINT ["/app/run.sh"]` |
| 217 | + |
| 218 | +### Secrets Kubernetes |
| 219 | + |
| 220 | +- Un secret par provider (isolation : compromission d'un secret n'affecte pas les autres) |
| 221 | +- Injection via `secretKeyRef` (jamais en clair dans les manifests) |
| 222 | +- Convention de nommage : `<provider>-api-key` |
| 223 | + |
| 224 | +### Nommage |
| 225 | + |
| 226 | +| Element | Convention | Exemple | |
| 227 | +|---------|-----------|---------| |
| 228 | +| Label GitHub | `ai-pr-<provider>` | `ai-pr-claude` | |
| 229 | +| Image Docker | `worker-<provider>:latest` | `worker-aider:latest` | |
| 230 | +| Secret K8s | `<service>-api-key` | `openrouter-api-key` | |
| 231 | +| Script provider | `providers/<provider>.sh` | `providers/aider.sh` | |
| 232 | +| Job de debug | `k8s/debug-<provider>.yaml` | `k8s/debug-aider.yaml` | |
| 233 | +| Cle dans `PROVIDER_CONFIG` | `<provider>` (suffixe du label) | `"aider"` | |
| 234 | + |
| 235 | +--- |
| 236 | + |
| 237 | +## Limitations connues (POC) |
| 238 | + |
| 239 | +Ce projet est un POC. Les contributions pour adresser ces limitations sont bienvenues : |
| 240 | + |
| 241 | +- Concurrence / idempotence (doubles declenchements, collisions de branches/jobs) |
| 242 | +- Timeout / cancellation des jobs (pods "zombies") |
| 243 | +- Gestion des conflits Git / PR deja existante |
| 244 | +- Monitoring / alerting (Prometheus, Grafana) |
| 245 | +- Dashboard de suivi des jobs |
| 246 | +- Gestion des quotas et du budget tokens par PR / par repo |
| 247 | +- Rate limiting sur les webhooks |
| 248 | +- Retry / dead-letter queue en cas d'echec |
| 249 | +- Traitement des images dans les issues (screenshots, diagrammes) |
| 250 | +- Gestion des commentaires dans les issues (contexte additionnel, instructions de suivi) |
| 251 | +- Support multi-cluster / haute disponibilite |
0 commit comments