Intégration continue et autres joyeusetées en embarqué

a marqué ce sujet comme résolu.

Bonjour à tous,

Pour un projet professionnel en embarqué (STM32, AVR et FPGA), j’aimerai mettre en place toute une configuration me permettant d’avoir ma toolchain de compilation, mes tests automatisées et un git bien propre. Pour le moment je suis seul dessus mais je serai rejoins par d’autre développeurs et j’aimerai commencer proprement avant qu’ils arrivent.

Il me semble que ça rentre dans le domaine du "devops" mais je n’y connais absolument rien.

Docker

Pour la toolchain de compilation, je pense utiliser Docker, que je n’ai encore jamais utilisé. Mon objectif est d’avoir la même toolchain sur mon PC, sur le serveur et sur le PC des autres personnes qui me rejoindraient. Plusieurs questions cependant concernant Docker :

  • L’avez-vous déjà utilisé dans le domaine de l’embarqué ? Peut-on, depuis un container, flasher un microcontrôleur ?
  • Imaginons que j’ai deux applications à développer utilisant deux toolchains différentes (une STM32 et une autre AVR8), on est d’accord que je passerai par deux containers ? Maintenant si j’ai deux applications à développer en AVR8, j’utilise un seul container ou deux containers avec la même toolchain ?

Comment ça s’utilise dans la vie de tous les jours ? Je lance mon container contenant toolchain en mode intéractif, en montant un volume vers /app du container pour que je puisse y accéder depuis VSCode. Quand je veux compiler/flasher je fais juste un make dans mon container (en mode interactif du coup) et voilà ? Ça fonctionne aussi simplement que cela ?

Git

J’ai lu de nombreuses fois que pour avoir un git "propre", le mieux était de créer une branche pour chaque fonctionnalité/bug fix. Ça me permet d’utiliser un système de tickets c’est bien ça (Gerrit ?) ? J’ai lu aussi qu’il fallait désactiver les commits et les merges sur master. Autant les commits ça me semble logique autant les merges je vois pas pourquoi. Comment est-ce que j’applique les modifications de ma branche sur master ? Quelles pratiques utilisez-vous pour utiliser Git sur un gros projet avec de nombreuses personnes ?

CI

J’aimerai qu’à chaque fois que je souhaite appliquer des modifications sur master, les tests soient lancés. Il me semble que ça s’appelle l’intégration continue. Je le vois beaucoup dans le web mais pas grand chose pour l’embarqué. Comment est-ce que cela se passe ? Je branche mes boards au serveur directement et en utilisant un service de CI (comme Jenkins?) je peux définir mes règles ? ("S’il y a des modifications sur master, lance le container docker, flash les boards et effectue les tests"). Existe t-il des périphériques permettant de couper le courant aux boards qui seraient connectées au serveur en USB ? Je vais avoir besoin de tester le comportement de mon système lorsqu’il perd un FPGA ou un microcontroleur.

Je ne sais pas dans quelle catégorie ce question rentre (Git ou CI ?) mais je voudrais m’assurer qu’avant chaque commit, les fichiers C suivent bien les règles syntaxiques que nous aurions préalablement définies. Je l’ai déjà fait en JavaScript avec ESLint et c’était vraiment sympa. Quels outils me permettraient de faire ça en C et en C++ ? Dois-je mettre ça dans un git hook ou directement dans une pipeline CI ? J’imagine que les deux fonctionnent mais que la pipeline est plus flexible ?

Cela va faire une semaine que je me renseigne sur ce sujet mais ça m’a l’air d’être un domaine extrêmement vaste avec des nouveautés toutes les semaines que je préfère faire appel à votre expérience. Je sais que mon post peut faire un peu brouillon avec des idées qui partent dans tous les sens, je m’en excuse.

Je vous remercie pour vos réponses. :)

+0 -0

Bonjour,

[Docker] L’avez-vous déjà utilisé dans le domaine de l’embarqué ? Peut-on, depuis un container, flasher un microcontrôleur ?

On peut tout à fait flasher un micro-contrôleur depuis un container. C’est un peu chiant car il faut donner les accès (montage + droits) sur /dev (Exemple - j’ai donné l’accès dans le docker-compose.yml). Après y’a peut-être d’autres méthodes … en tout cas c’est possible !

[Docker][...] Maintenant si j’ai deux applications à développer en AVR8, j’utilise un seul container ou deux containers avec la même toolchain ?

Deux GIT, deux containers différents j’ai envie de dire.

[Docker] Comment ça s’utilise dans la vie de tous les jours ? Je lance mon container contenant toolchain en mode intéractif, en montant un volume vers /app du container pour que je puisse y accéder depuis VSCode. Quand je veux compiler/flasher je fais juste un make dans mon container (en mode interactif du coup) et voilà ? Ça fonctionne aussi simplement que cela ?

Alors pour l’instant, j’ai que l’expérience d’une connexion Raspberry-Arduino. Le Raspberry j’y accède en SSH et interface noVNC (voir le lien d’exemple vers le GIT). J’ai été obligé de flasher mon code via le logiciel Arduino pour établir la connexion Firmata et ensuite je contrôle les instructions directement depuis Python.

[GIT] J’ai lu de nombreuses fois que pour avoir un git "propre", le mieux était de créer une branche pour chaque fonctionnalité/bug fix.

C’est plus facile pour s’y retrouver en effet. Pratique pour Ajouter/Enlever une fonctionnalité ; surtout si celle-ci est limite au niveau stabilité de l’application (try tests ou mise à jour d’un paquet sensible par exemple).

[GIT] J’ai lu aussi qu’il fallait désactiver les commits et les merges sur master.

Tu fais référence au pull request. Alors oui dans la mesure où ton application serait potentiellement utilisé par beaucoup de dévs, dont externes - ça peut valoir le coup. Autrement pour un dév perso, je ne vois pas bien l’intérêt.

Quant tu as une CI configuré sur la master/main ( 1 commit = test & build ), il vaut mieux opérer sur la branche principale par merge plutôt que par commit. Logique :p

[CI]

Au sujet des CI et corrections syntaxes en C/C++, je vais laisser le soin à d’autres plus compétent dans ce domaine de répondre. Je n’ai pas non plus testé de CI sur des objets connectés.

Edit : sinon je suis tombé récemment sur BalenaOS, à voir ce que ça vaut ! :)

+1 -0

Merci @Yarflam pour ta réponse !

Ça me rassure de voir qu’il ne devrait pas y avoir de problème avec docker ! Je n’avais vraiment pas envie de mettre en place vagrant qui est beaucoup plus lourd (mais que je sais fonctionnel).

Pour git c’est bien ce qu’il me semblait. De plus, comme on risque de se retrouver à plusieurs à travailler dessus, ça m’a l’air d’être le plus propre. :)

Je ne pense pas que BalenaOS soit intéressant pour moi comme je vais travailler sur de l’AVR ou du STM32. Mais projet à suivre.

Merci encore !

Salut,

Je peux pas te dire ce qu’il faut faire idéalement, mais j’ai une solution qui me satisfait avec mes contraintes:

Mon entreprise utilise les outils Atlassian, donc bitbucket pour gérer les dépôts git et droits d’accès, et bamboo pour la CI, mais rien n’empêche de faire la même chose avec d’autres outils, genre Gerrit+Jenkins. Quand bitbucket voit des modification sur un dépôt git, il notifie le serveur bamboo, qui déclenche les builds utilisant la branche modifiée, (ce sont des scripts arbitraires, il peut aussi bien s’agir de compilation, de test, ou d’envoi de spam). Ils ne sont pas fait sur le serveur, qui ne sert que d’orchestrateur, mais sur des agents, des machines distantes dédiées à ces tâches.

Pour la compilation, j’utilise les mêmes machines que les gens qui développent pour PC, et je fais le build dans un docker, parce que c’était chiant d’avoir les outils installés nativement sur les serveurs de compilation. Le résultat de ma compilation est appelé artefact par bamboo, et peut être passé en entrée à un autre job, celui de test.

Le job de test s’éxécute sur une machine dédiée, dans le labo élec. Elle n’est pas gérée par l’IT, c’est pas du matériel de serveur, autrement dit elle a plus de chance de tomber en panne, et si ça arrive, c’est à mon équipe de s’occuper de la remplacer. C’est pas forcément idéal, mais on peut s’en passer le temps de la remettre en marche, si besoin est. Cette machine exécute une instance d’agent bamboo, pour recevoir les ordres de build, et contrôle les alims du labo ainsi que des modules relais pour couper l’alimentation sur USB: je travaille sur des produits qui s’alimentent par USB, il me fallait un moyen de les redémarrer en ROM BOOT USB. Tous les outils de flashage sont installés sur la machine (je ne teste pas les outils host, ils ne sont pas de moi), Le script de flashage allume la caméra (je bosse sur des capteurs vidéo), elle est reconnue par son vid:pid et udev donne les droits d’accès à l’utilisateur bamboo, qui peut flasher, executer ses tests, et envoyer le résultat au serveur, qui rassemble tous les résultats de test. Tout n’est pas couvert par les tests: quand la caméra doit filmer le labo, on ne vérifie que la présence de données, on vérifie l’intégrité des données avec des générateurs de pattern, donc si il y a un problème avec la partie analogique du capteur, il y a peu de chance que les tests le détectent. Mais reproduire la partie physique/mécanique des tests, et valider les résultats, ça coûte cher, et les tests que j’ai détectent les problèmes qui ont le plus de chances d’arriver. Le seul problème, c’est que les tests sur tous les systèmes sont trop longs pour être faits à chaque commit. On a donc des règles pour ne tester qu’une partie des résultats de compilation.

Quant à git, les développements sont fait dans des branches, mais les merge sont fait via rebase + fast-forward, sans commit de merge. Ca facilite la lecture de la branche, en minimisant le nombre d’embranchement, mais ça masque les début et fin de chaque développement. De fait, ce n’est viable qu’avec une certaine rigueur sur les commits. Un développement ça ressemble souvent à "je fais A", "je fais B", "je corrige A pour les architecture Big-endian", "je corrige B pour les archi avec des pointeurs de 3 octets". Avant de demander à le merger, on va réécrire les commits, pour que A existe directement dans sa version corrigée, et soit suivi d’une version de B elle aussi corrigée, de telle sorte que chaque commit soit fonctionnel. Le principal intérêt de faire ça est que, quand on détecte une régression, on peut la rechercher avec git bissect ce qu’on ne peut plus faire si on autorise des commits non fonctionnels au milieu des développements, mais, en faisant sérieusement l’exercice de réécrire ses commits pour qu’ils soient logiques et décrivent directement la solution, plutôt que de décrire par quels détours on y est parvenus, on doit aussi avoir des modifications plus faciles à relire, et où chaque commit est indépendant, de telle sorte qu’il ne soit pas utile de sauvegarder qu’ils ont été écrits au sein du même développement. Cela dit, ça repose sur le jugement des développeurs et maintainers, et ça demande à tous les contributeurs d’être à l’aise pour réécrire l’historique de leurs commits, il y a des contextes dans lesquelles cette façon de travailler n’est pas viable; elle n’est d’ailleurs pas appliquée dans toute mon entreprise, alors que travailler avec plusieurs méthodes différentes au sein de la même entreprise peut entrainer des difficultés. Je l’avais annoncé, je n’ai pas de solution parfaite à proposer.

Quant aux règles de formatage du code, on peut soit les vérifier quand on commit sur sa machine, avec des pre-commit-hook, soit sur le serveur, qui peut refuser le push de commits mal formatés, ou le merge de commits mal formatés. Pour ce qui est côté serveur, ça va dépendre de la solution utilisée (gerrit/bitbucket/github/gitlab/…), mais c’est un cas d’usage assez courant, j’imagine qu’ils ont tous une solution à proposer.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte