Aller au contenu

GitHub-Odoo Sync

Le workflow GitHub-Odoo Sync transforme chaque événement GitHub (issue, PR, commit) en une opération Odoo correspondante : tâche projet créée, label appliqué, milestone synchronisé, commit historisé. Depuis le refactoring #274, l’architecture est passée d’un workflow monolithique de 138 nodes à un hub de 13 nodes qui dispatche vers 7 sous-workflows spécialisés.

WorkflowIDNodesRôle
GitHub Project Sync (parent)JR8ESduxKNSQ7iVs13Trigger + lookup mapping + dispatch par event type
SW-1 Issue Lifecycle8yU07chLmZuogA3j23opened / closed / reopened + sub-issues parent
SW-2 Issue LabelsOMeuYvh0B9XA2S1H11labeled / unlabeled
SW-3 Issue Properties0BoSdwR4d8S9Fq0C32assigned / edited / pinned / renamed / deleted
SW-4 Issue MilestoneEZiZqgZNpxEzof7F13milestoned / demilestoned
SW-5 PR Handler3o7IzU2brzJeedB718pull_request opened / merged / closed
SW-6 Git EventsO2jirkOJOpOIy5ez18push (commits) + create (branch stage transition)
SW-7 Repo EntityWVWY01qmw9uYzSl828Label + Milestone CRUD au niveau du repo

Un sous-workflow utilitaire odoo-get-or-create-tag (j7e2EzEtIJ42T2S4) est appelé par SW-1, SW-2 et SW-7 pour créer ou récupérer des tags Odoo correspondant aux labels GitHub.

Autres sub-workflows

Issues sub-workflows

GitHub Project Sync · 13 nodes

GitHub · webhooks

Data Table · github_project_mapping

Github Trigger

Lookup Mapping

Sync Enabled?

Switch · Event Type

SW-1 Lifecycle · 23n

SW-2 Labels · 11n

SW-3 Properties · 32n

SW-4 Milestone · 13n

SW-5 PR Handler · 18n

SW-6 Git Events · 18n

SW-7 Repo Entity · 28n

odoo-get-or-create-tag

Odoo 18 · guig_db

Tous les événements GitHub utiles à un workflow projet sont synchronisés :

CatégorieÉvénementsSous-workflow
Cycle de vie issueopened, closed, reopenedSW-1
Sub-issuesparent_added, parent_removedSW-1
Labels issuelabeled, unlabeledSW-2
Propriétés issueassigned, edited, pinned, renamed, deletedSW-3
Milestone issuemilestoned, demilestonedSW-4
Pull requestsopened, closed, mergedSW-5
Push & branchespush (commits), create (branch)SW-6
Repo-levellabel/milestone CRUDSW-7

Sans syncConséquence
Visibilité métierLes issues restent invisibles depuis Odoo
Suivi du tempsImpossible de lier une heure de timesheet à une issue précise
Historique commitsDispersé dans GitHub, pas de vue cumulée par tâche Odoo
Double saisieCréer une tâche Odoo manuellement pour chaque issue

Pourquoi un hub-spoke plutôt qu’un workflow unique ?

Section intitulée « Pourquoi un hub-spoke plutôt qu’un workflow unique ? »

Le workflow d’origine (138 nodes) avait grossi au fil des événements supportés. Trois symptômes ont déclenché le refactoring #274 :

SymptômeCauseConséquence
Édition lenteUI N8N peine à charger 138 nodesModifs ergonomiquement coûteuses
Tests rébarbatifsTouche un sub-flow → re-tester le toutRégressions silencieuses
CouplageCode IA tag, Odoo task, milestone partagésChangement local cassait des branches lointaines

L’architecture en hub avec 7 sous-workflows résout ces tensions : chaque SW a un périmètre étroit, peut être édité et testé isolément, et signe son contrat d’I/O via Execute Workflow Trigger.

CritèreChoix
Source de vérité du codeGitHub (là où vivent les commits, les PRs, les CI)
Source de vérité businessOdoo (timesheets, factures, projets clients)
Direction du déclenchementGitHub webhooks (temps réel, sub-second)
EnrichissementOdoo ajoute le contexte business (projet, tags, complexité, estimation)

La direction inverse (fermer une issue depuis Odoo) est techniquement faisable mais ajoute un risque de boucle. Pour le moment, GitHub reste source ; Odoo est sink + dashboard.


1. Webhook GitHub — Le trigger N8N reçoit l’événement avec sa signature HMAC X-Hub-Signature-256 (vérifiée par N8N).

2. Lookup mapping — Le hub consulte la Data Table github_project_mapping pour trouver le projet Odoo associé au repo (owner/repoodoo_project_id).

3. Switch event type — Le hub route vers le sous-workflow adapté selon headers.x-github-event et body.action.

4. Sub-workflow execution — Chaque SW reçoit en passthrough {body, headers, query, mapping} et applique sa logique métier.

5. Appels Odoo XML-RPC — Tous les SWs convergent vers le même endpoint Odoo : http://odoo:8069/xmlrpc/2/object, base de données guig_db.

L’addon custom project_github_sync (v18.0.5.6.0) ajoute les champs nécessaires à la synchro. Tous les champs custom utilisent le préfixe x_ pour ne pas entrer en collision avec les natifs Odoo :

FamilleChampTypeSource
GitHubx_github_issue_idInteger (indexé)numéro issue/PR
GitHubx_github_urlChar(500)URL GitHub
GitHubx_github_repoChar (indexé)owner/repo
GitHubx_github_milestone_idIntegernuméro milestone
GitHubx_github_parent_issue_idIntegerparent (sub-issues)
GitHubx_github_commit_idsOne2manycommits liés
Effort (#188)x_estimated_hoursFloat(10,2)estimation à la création
Effort (#188)x_complexitySelectiontrivial / simple / moderate / complex
AI triage (#296)x_ai_project_triaged_atDatetimedernière analyse projet (TTL 7j)
AI triage (#296)x_ai_personal_triaged_atDatetimedernière analyse perso (TTL 7j)
Telemetryx_claude_time_totalFloat(10,2)temps actif Claude Code
Telemetryx_claude_cost_totalFloat(10,4)coût API USD
Telemetryx_claude_token_totalIntegertokens cumulés
Telemetryx_claude_sessionsIntegernombre de sessions
Telemetryx_claude_lines_added/removedIntegerLOC delta
Telemetryx_claude_session_idsOne2manyhistorique sessions
Telemetryx_claude_categorySelectioncatégorie auto pour tâches génériques
ModèleRôle
project.task.github.commitUn enregistrement par commit lié à une tâche (sha, message, auteur, date)
project.task.claude.sessionUn enregistrement par session Claude Code (id, durée, coût, tldr, category)

Le sous-workflow le plus chargé est SW-1 Issue Lifecycle, qui gère 5 actions distinctes :

parent_added · parent_removed

reopened

closed

opened

Execute Workflow Trigger · passthrough

Switch · body.action

Prepare Odoo Data + complexity + estimate

Convert MD to HTML

Detect Area Tag · area:xxx, feat(xxx)

Get/Create Tag

Create Task · stage Backlog

Prepare Closed Data

Find Task by x_github_issue_id

Is Project Task?

Close Project Task · stage 25 Done

Close Private Task · personal_stage 6 Terminé

Prepare Reopened Data

Find Task

Reopen → default_stage_id

Reopen Private → personal_stage 2 Aujourd'hui

Prepare Link/Unlink

Find Sub + Parent Tasks

Set parent_id + x_github_parent_issue_id

Clear parent_id

Quand une nouvelle issue est créée, SW-1 dérive automatiquement deux champs depuis le titre + body :

ChampCalcul
x_complexityHeuristique sur la longueur du body, mots-clés (refactor, migration, multi-step)
x_estimated_hoursModèle déterministe basé sur la cohorte de complexité (cf. Effort Estimator V1)

L’estimation initiale est une borne basse qui sert de référence. Le champ effective_hours (timesheets agrégés) permet ensuite de mesurer l’écart estimation vs réalité, alimentant un weekly coverage report.

En parallèle de la synchro événementielle, un workflow planifié quotidien (61uBaaFQssxT1HJ0, 25 nodes) effectue un rattrapage et un triage AI :

ÉtapeAction
1. Sync de rattrapageRécupère les issues GitHub modifiées depuis 24h, met à jour Odoo
2. Stage promotionSi une issue est in progress côté GitHub mais Backlog côté Odoo → promu à In Progress
3. AI Issue Triage (#296)Sub-workflow AwqOyyAC3sUg9tSm (12n) — codex-yolo analyse l’issue et propose stage projet + horizon perso
4. Cache TTL 7jLes champs x_ai_*_triaged_at empêchent de re-triager une issue déjà analysée récemment

Le triage AI est aussi exposé en mode interactif via l’API triage-interactive-api consommée par Claude Code lors d’une session ouverte (commande /triage).

Le hub passe à chaque sous-workflow un objet uniforme :

{
"body": { /* GitHub webhook payload */ },
"headers": {
"x-github-event": "issues",
"x-github-delivery": "..."
},
"query": {},
"mapping": {
"github_repo": "owner/repo",
"odoo_project_id": 5,
"default_stage_id": 21,
"sync_enabled": true
}
}

Le contrat d’entrée est strict (Execute Workflow Trigger en mode passthrough désactivé pour SW-3 à SW-7 qui utilisent des workflowInputs typés).

Pour intégrer un repo existant qui contient déjà des centaines d’issues, le workflow Initial Sync (#50) permet une importation one-shot : pagination GitHub API + création séquentielle des tâches Odoo + setup des tags. À utiliser uniquement à la mise en place ; ensuite, le sync événementiel suffit.


LimiteImpactMitigation
UnidirectionnelFermer côté Odoo ne ferme pas l’issue GitHubSync inverse à concevoir avec flag anti-boucle
Mapping manuelChaque repo doit être ajouté à la Data TableAuto-discovery par organisation à terme
Estimation initiale = borne basseIssues complexes sous-estiméesRecalibrage hebdomadaire prévu via cohort matching
Triage AI 24h de retard maxRe-triage le lendemain si issue ignoréeAcceptable, le triage est un assistant pas un oracle

Si besoin de sync bidirectionnelle :

  • Webhook Odoo automation.action quand une tâche passe en stage Done
  • Le webhook fait un commentaire GitHub Closed via Odoo task #N
  • Flag synced_from_odoo dans la table de mapping pour bloquer la rétro-propagation

Si beaucoup de repos à gérer :

  • Auto-discovery des repos d’une organisation GitHub
  • Templates de mapping par convention de nom (*-backend → projet Backend)
  • UI Odoo dédiée pour configurer les mappings sans toucher la Data Table

Si le triage AI devient critique :

  • Activer un mode confiance haute (auto-set du stage si confidence > 0.9)
  • Logger les décisions dans claude_code_active_sessions pour audit
  • Permettre un override manuel via /triage <issue> <stage>

  • Glossaire — XML-RPC, Webhook, HMAC, Sub-workflow