Journal git-bug: un bug tracker distribué intégré dans git

Posté par (page perso) . Licence CC by-sa.
46
5
déc.
2018
Ce journal a été promu en dépêche : git-bug: un bug tracker distribué intégré dans git.

Cher journal,

À l'occasion de la sortie de la version 0.4 de git-bug, je me suis dit que je pourrais te raconter un peu de quoi il en retourne.

Le logo de git-bug

git-bug est un bug tracker distribué intégré dans git, c'est-à-dire que l'ensemble des données des bugs est stocké sous forme d'objet git, sans polluer les branches et fichiers normaux. Mais pour quoi faire ? Et bien par rapport à un bug tracker classique, il y a plusieurs avantages :

  • Pas de dépendance à un service en particulier (par exemple si ton bugtracker préféré se fait racheter par tousse Microsoft tousse) ;
  • Pas besoin d'infrastructure particulière : si tu utilises déjà un remote git pour collaborer, tu as un bugtracker ;
  • Tu peux consulter et éditer les bugs hors-ligne, avec l'interface que tu préfères, sans latence.

Mais alors, comment on fait un bugtracker distribué ? Le plus gros problème c'est la gestion des conflits. Comment faire si Alice et Bob modifient le même bug et s'échangent les données ensuite ?

La solution choisie, plutôt que de stocker simplement l'état d'un bug, est de stocker chaque opération d'édition individuellement.

      +-------------+    +-----------+    +-------------+   +--------+
* --->| ADD_COMMENT |--->| SET_TITLE |--->| ADD_COMMENT |-->| CREATE |
      +-------------+    +-----------+    +-------------+   +--------+

Quand une fusion doit avoir lieu, l'une des deux versions voit ses nouvelles opérations placées à la fin de la chaine, à la manière d'un rebase. On a donc la garantie d'avoir un état final cohérent puisque c'est git-bug qui dérive l'état final de la chaine d'opération. Mais que se passe-t-il si deux personnes supprime la même étiquette ? Elle sera simplement supprimée une fois et l'action des deux personnes sera visible dans l'historique.

Voilà à quoi pourrait ressembler une opération :

{
  "type": "SET_TITLE",
  "author": {
    "name": "René Descartes",
    "email": "rene.descartes@example.com"
  },
  "timestamp": 1533640589,
  "title": "This title is better"
}

Ces opérations sont ensuite sérialisées et stockées dans des blob git et reliées dans une chaine de commit. Chaque bug a sa propre chaine de commit, accessible via une référence git. À chaque fois que le bug est modifié, une nouvelle série d'opérations est ajoutée à la chaine et la référence est mise à jour.

 refs/bugs/<bug-id>
       |
       |
       v
 +-----------+    +-----------+             "ops"    +-----------+
 |  Commit   |--->|   Tree    |---------+----------->|   Blob    | (OperationPack)
 +-----------+    +-----------+         |            +-----------+
       |                                |
       |                                |
       v                                |   "root"   +-----------+ 
 +-----------+    +-----------+         +----------->|   Blob    | (OperationPack)
 |  Commit   |--->|   Tree    |-- ...   |            +-----------+
 +-----------+    +-----------+         |
       |                                |
       |                                |   "media"  +-----------+        +-----------+
       v                                +----------->|   Tree    |---+--->|   Blob    | bug.jpg
 +-----------+    +-----------+                      +-----------+   |    +-----------+
 |  Commit   |--->|   Tree    |-- ...                                |
 +-----------+    +-----------+                                      |    +-----------+
                                                                     +--->|   Blob    | demo.mp4
                                                                          +-----------+

Comme tout est stocké sous forme d'objet git, on peut pousser tout ça vers un remote et collaborer, comme si on s'échangeait du code source.

Une autre propriété intéressante de la méthode, c'est qu'on peut stocker et lier les médias (typiquement, une capture d'écran) à un bug et les diffuser de la même façon.

Alors, à quoi ça ressemble tout ça ? Et bien ça dépend des goûts. Si tu préfères la ligne de commande, le scripting ou intégrer ça dans ton Vim, c'est possible. Voilà un aperçu:

# Ajouter un nouveau bug
git bug add

# Liste les bugs ouverts, trié par dernière édition
git bug ls "status:open sort:edit"

# Affiche l'état du bug f3781aee
git bug show f3

# Selectionne le bug f3781aee parce que je sais bien que tu es un peu flemmard
git bug select f3

# Ajoute un nouveau commentaire
git bug comment add

# Pousse les modifications vers le remote par défaut
git bug push

Pour un usage plus efficace, il y a aussi l'interface interactive en terminal :

Capture d'écran de git-bug en terminal interactif

Ou sinon, pour les amateurs de vrai pixels et de clic-clic, git-bug embarque une interface web, mais il faut avouer qu'il y a encore du boulot :

Capture d'écran de git-bug en interface web
Capture d'écran de git-bug en interface web

Techniquement, le binaire de git-bug lance un serveur web local qui sert les fichiers de l'application js/react qui ont été compilé et inclus dans ce même binaire. L'application web s'interface avec le binaire grâce à une API GraphQL. Le déploiement reste donc extrêmement simple avec un unique binaire à copier, sans configuration.

La nouvelle version 0.4 apporte une fonctionalité qui a été beaucoup demandée, un importeur incrémental pour Github ! Après avoir utilisé git bug bridge configure pour saisir ses identifiants, un token d'API est généré automatiquement et git-bug devient capable d'aspirer les issues en local dans son modèle de donnée.

Et le futur ? Une de mes priorités est d'implémenter un exporteur vers Github. Quand ça sera fait, il sera possible d'utiliser git-bug comme un Github déporté, qui fonctionne en local et offline.

Une autre idée qui me trotte dans la tête est d'avoir une véritable gestion d'identité en embarquant les données publiques dans des objets git, clé de chiffrement incluse. De cette façon, il devient possible de les distribuer facilement, avec les données des bugs, de signer ses éditions, les vérifier, et même pourquoi pas de vérifier les commits git normaux sans avoir à chercher et importer les clés GPG.

Mais tout ça, c'est quand même beaucoup pour, en gros, un seul bonhomme. Mais peut-être, mon cher journal, que je trouverai des âmes vaillantes pour m'accompagner dans cette aventure…

  • # Définition du « rebase » / relevé de coquilles

    Posté par . Évalué à -1 (+5/-6). Dernière modification le 06/12/18 à 00:53.

    Merci, c'est intéressant.

    Quand une fusion doit avoir lieu, l'une des deux versions voit ses nouvelles opérations placées à la fin de la chaine, à la manière d'un rebase.

    Qu'est-ce que « rebaser » (explications détaillées, en français, dans le contexte de Git) ? Je résumé l'explication introductive du lien précité :

    dans le contexte de Git où l'on a une branche principale et une autre expérimentale, là où la fusion (merge) des deux branches consiste à rassembler en un nouvel instantané (et un commit) les deux derniers instantanés de chaque branche et l'ancêtre commun le plus récent, le rebase, lui, consiste à prendre la modification (le patch) — ou la série de modifications — introduite dans la branche expérimentale et à la réappliquer sur le dernier instantané de la branche principale.

    Je relève quelques coquilles, anglicismes non traduits et je propose des modifications :

    M'est d'avis que ce journal pourrait finir en dépêche…

    remote git

    --> Git distant [traduction de remote, suppression du gras pour Git avec passage de la première lettre en majuscule, cf. l'avant-dernier paragraphe du présent commentaire pour un conseil de renommage quasi général de « git » en « Git »]

    Quand une fusion doit avoir lieu, l'une des deux versions voit ses nouvelles opérations placées à la fin de la chaine, à la manière d'un rebase.

    --> à la manière d'un rebase. [mise en italique]

    …ou bien, en version plus développée, en ajoutant une phrase explicative :

    --> à la manière d'un rebase. Dans le contexte de Git où l'on a une branche principale et une autre expérimentale, le rebase consiste à prendre la modification (le patch) — ou la série de modifications — introduite dans la branche expérimentale et à la réappliquer sur le dernier instantané de la branche principale (cf. explications en français pour des détails).

    c'est git-bug qui dérive l'état final de la chaine d'opération

    --> d'opérations [pluriel]

    Mais que se passe-t-il si deux personnes supprime la même étiquette ?

    --> suppriment

    un token d'API est généré automatiquement et git-bug devient capable d'aspirer les issues en local dans son modèle de donnée

    --> tocken [mise en italique]
    --> aspirer les issues problèmes référencés (issues)

    Les occurrences de « bug tracker » (on trouve aussi « bugtracker » dans le texte), hormis peut-être celle qui apparaît dans le titre, pourraient — à mon avis — avantageusement être remplacées par « traceur de bogues (bug tracker) » pour la première occurrence, et simplement « traceur de bogues » pour les suivantes. Je note que Wikipédia propose l'expression plus longue « logiciel de suivi de problèmes » (ou « système de suivi de problèmes ») pour traduire bug tracker. Dans la littérature, on trouve aussi l'expression « traqueur de bogues », qui est valide en français, mais renvoie plutôt à celui qui traque les bogues qu'au système de suivi de bogues à proprement parler.

    Les occurrences de « bug » / « bugs » (hormis celles qui apparaissent dans l'expression « bug tracker » / « bugtracker » / « git-bug » / toutes les expressions correspondant à des commandes, un répertoire dans un chemin, un nom de fichier) pourraient avantageusement être remplacées par « bogue » / « bogues ».

    Les occurrences de « git » (hormis celles qui apparaissent dans l'expression « git-bug » / « Github » / la série de commandes git lancées  dans un terminal, y compris l'expression isolée encadrée en Teletype git bug bridge configure) pourraient avantageusement être renommées en « Git » (avec une majuscule, cf. Git), y compris l'occurrence qui est dans le titre.

    Les occurrences de « commit » pourraient être modifiées en « modifications (commits)» pour la première (qui doit être au pluriel), « modifications » (au pluriel) pour la seconde — je passe le schéma avec une arborescence qui comprends trois occurrences — « commits » (mise en italique) pour la dernière occurrence que je propose de laisser en anglais (pour éviter une ambiguïté avec l'idée de modifier Git lui-même).

    • [^] # Re: Définition du « rebase » / relevé de coquilles

      Posté par (page perso) . Évalué à 10 (+9/-1).

      D'une manière générale, c'est une mauvaise idée de traduire le jargon (ici le vocabulaire propre à git) quand la communication s'adresse à des gens censés comprendre ce jargon (ici les utilisateurs de l'outil, qui ont besoin de connaitre git). Pourquoi ? Parce que le jargon, s'il est hermétique aux non-initiés, est le moyen le plus efficace de communication entre deux personnes qui ont les codes pour le comprendre.

      La connaissance libre : https://zestedesavoir.com

      • [^] # Re: Définition du « rebase » / relevé de coquilles

        Posté par (page perso) . Évalué à 9 (+9/-2).

        Je ne suis absolument pas d'accord avec ça. Le jargon rend la compréhension plus compliquée, et favorise l'exclusion. Même dans le milieu informatique que je connais pourtant bien, je suis régulièrement obligé de chercher la signification de mots ou abréviations (dont certaines peuvent en plus changer de sens selon le contexte). Alors dans l'info je suis d'accord que parfois il est plus compréhensible de garder le mot technique ou anglais (parce que utilisé dans une commande, ou parce qu'il n'y a pas de bon équivalent), mais c'est, de mon expérience, très rare.

        En plus de ça, on voit de plus en plus de contresens avec des mots anglais utilisés dans une autre langue (ici le français) alors qu'il veut dire tout autre chose. Par exemple « issue » qui veut dire « problème » en anglais et qui a un tout autre sens en français. Quand on me dit « regarde cette issue » j'ai plus tendance à regarder vers la porte que mon écran d'ordinateur.

        Autant je ne suis pas pour être trop à cheval sur les règles d'orthographe et de grammaire (enfin ça dépend du contexte), autant l'utilisation de jargon et/ou systématiquement de mots anglais rend la compréhension plus difficile et élitiste.

        Bref utiliser le terme adapté à la langue et l'audience quand il existe, ou expliquer un terme entre parenthèses faute de mieux est une bonne pratique à mon avis.

        • [^] # Re: Définition du « rebase » / relevé de coquilles

          Posté par . Évalué à -2 (+3/-5). Dernière modification le 06/12/18 à 14:24.

          En considérant la dépêche publiée, j'observe ceci :

          • les deux fautes d'accord en nombres ont été corrigées ;
          • remote git est resté tel quel (même pas mis en italique) ;
          • le passage en italique de rebase et token n'a pas été effectué ;
          • issues (de secours ?) est resté tel quel ;
          • bug tracker reste identique (non traduit, non mis en italique) ;
          • « bug » / « bugs » ne sont pas traduits en « bogue » / « bogues », alors que c'est la recommandation de la page de Wiki traductions classiques de Linuxfr.org ;
          • Git n'a pas sa majuscule ;
          • commit n'est pas traduit, ni même mis en italique.

          Voilà, je pense qu'effectivement, mon commentaire proposant des corrections méritait bien de prendre une telle note négative car il aurait pu se résumer aux deux fautes d'accord. Après, c'est peut-être le syndrome Paris Open Source Summit 2018 des 5 et 6 décembre, combiné aux vingt ans de LinuxFr.org,  qui ont amené les modérateurs à faire au plus vite, jusqu'à oublier le rituel « Merci, corrigé ».

          • [^] # Re: Définition du « rebase » / relevé de coquilles

            Posté par . Évalué à 1 (+1/-2). Dernière modification le 06/12/18 à 22:30.

            « bug » / « bugs » ne sont pas traduits en « bogue » / « bogues », alors que c'est la recommandation de la page de Wiki traductions classiques de Linuxfr.org ;

            Il faut reconnaître que cette traduction phonique est moche: d'un côté, celui du «bug» il y a une raison historique et répulsive (la plupart des gens n'aiment pas les cafards), de l'autre, le côté traduit, il n'y à… rien? Juste une ressemblance phonique pour dire que oui, y'a un mot français bien de chez nous?
            C'est de la branlette intellectuelle, ça. Et, oui, j'aurai préféré les termes de «cafard» ou «vermine», puisque j'ai lu a plus d'un endroit le terme «déverminage» d'un logiciel qui m'a semblé bien plus approprié que celui de «bogue», qui dans mon esprit est l'enveloppe de la châtaigne ou du marron… si un arbre pousse dans un ordinateur, on devrait plutôt parler de green IT, non?

            Pour le reste de ta tirade, je suis d'accord. Le texte qui doit être préservé hors traduction devrait être signalé par un balisage, et je te remercie d'avoir contribué à améliorer les choses, même si certains points n'ont pas été adoptés.
            Notes cependant que je ne suis pas d'accord avec toi sur au moins l'un d'eux :)

            jusqu'à oublier le rituel « Merci, corrigé ».

            Pour ta note négative, tu sais, je pense que ton historique doit vachement jouer. On dirait que tes comportements passés (que je n'ai pas connus, mais… j'ai l'impression que tu es dans les fortunes de debian? T'es une star, si ça se trouve) on développé des réflexes pavloviens chez certains.
            Malgré ça, moi j'apprécie le fait que tu continues de donner ton point de vue malgré l'opprobe (firefox me mets le doute sur l'orthographe…), et sans attaquer en plus.

          • [^] # Re: Définition du « rebase » / relevé de coquilles

            Posté par (page perso) . Évalué à 2 (+1/-1). Dernière modification le 06/12/18 à 23:30.

            jusqu'à oublier le rituel « Merci, corrigé ».

            Pas d'oubli. C'est moi qui a corrigé la plupart des fautes, sans m'appuyer sur ton commentaire (nous aussi on sait lire). J'ai préféré garder les anglicismes à cause du nombre de commandes Git qui sont en anglais (comme rebase). Quant aux italiques, ça nuisait à la lecture : la dépêche contient déjà trop de polices différentes (caractères, couleur, corps et graisse) pour en ajouter.

            "La liberté est à l'homme ce que les ailes sont à l'oiseau" Jean-Pierre Rosnay

        • [^] # Re: Définition du « rebase » / relevé de coquilles

          Posté par . Évalué à 6 (+5/-0).

          Si on remplace le terme jargon, connoté, par vocabulaire métier, ça change l'idée. Le vocabulaire métier sert à utiliser le même vocabulaire quand on parle d'une même chose. Le fait qu'il soit anglais n'est qu'un détail. Le but d'un langage commun est d'être précis, pas inné. Et s'il n'est pas inné c'est parce qu'il faut l'apprendre.
          Le but n'est pas d'être élitiste mais d'être précis. Avoir un langage commun précis, c'est un peu la base de la communication en entreprise.

    • [^] # Re: Définition du « rebase » / relevé de coquilles

      Posté par . Évalué à 4 (+2/-0). Dernière modification le 06/12/18 à 11:20.

      Qu'est-ce que « rebaser » (explications détaillées, en français, dans le contexte de Git) ? Je résumé l'explication introductive du lien précité :

      dans le contexte de Git où l'on a une branche principale et une autre expérimentale, là où la fusion (merge) des deux branches consiste à rassembler en un nouvel instantané (et un commit) les deux derniers instantanés de chaque branche et l'ancêtre commun le plus récent, le rebase, lui, consiste à prendre la modification (le patch) — ou la série de modifications — introduite dans la branche expérimentale et à la réappliquer sur le dernier instantané de la branche principale.

      À noter que le rebase peut aussi se faire sur une seule branche. Cela permet de remodeler (en réordonnançant, fusionnant, scindant, …) son historique de commits, pour le rendre plus propre, avant de le fusionner (merge ou rebase) avec une autre branche.

  • # Permissions ? Merge-requests ? import/export ?

    Posté par (page perso) . Évalué à 6 (+4/-0).

    Bravo, c'est super chouette.

    Quid des formats de données et permissions ? Par exemple si sur un projet je ne veux que les étiquettes "WIP", "v1.2.3" et "DEV" ? Et si je ne veux autoriser que toto et tata à modifier l'état du bug ? Est-ce qu'on peut modifier les commentaires des autres ?

    Est-ce que ça aurait du sens d'étendre pour des merge requests ? Git permet bien entendu d'utiliser une branche pour ça, l'intérêt serait de permettre de commenter le code avant de pouvoir réellement merger la branche.

    Ça se rapproche de ce que fait Fossil si je ne m'abuse (je n'ai jamais utilisé moi même, mais il me semble qu'il intègre tout directement, tickets, wiki, etc.), c'est vraiment une bonne idée et la possibilité de travailler hors ligne est super (même si on n'utile pas comme système de tickets principal, ça permet de faire tampon quand on est hors ligne).

    Bon je pourrais vérifier moi même mais je fais mon fainéant là :), comment ça marche pour créer un importeur/exporteur ? Ça a l'air d'être du JSON, y'a une API pour récupérer directement les opérations assemblées et le ticket final ? Je travaille sur un gestionnaire de tickets et de merge-requests décentralisé (basé sur XMPP), et ça m'intéresserait de pouvoir faire les tickets en local quand internet n'est pas dispo, et de pouvoir synchroniser après coup.

    Bonne continuation, c'est très prometteur.

    • [^] # Re: Permissions ? Merge-requests ? import/export ?

      Posté par (page perso) . Évalué à 3 (+3/-0). Dernière modification le 06/12/18 à 11:33.

      Par exemple si sur un projet je ne veux que les étiquettes "WIP", "v1.2.3" et "DEV" ?

      L'idée fondamentale de l'ergonomie de git-bug, c'est de fonctionner de base avec zéro configuration, et un ensemble de règle de base raisonnable qu'il sera possible d'ajuster pour des besoins particuliers. Pour les étiquettes, l'utilisateur est libre de saisir ce qu'il veut et git-bug propose dans son interface de sélectionner facilement une étiquettes existante. Dans le futur il sera possible d'ajouter des contraintes et de le limiter à un ensemble particulier.

      Mais tu va me dire, comment faire pour faire respecter des règles dans un système distribué ? Au final c'est assez simple. A la manière d'une blockchain, chaque client git-bug vérifie les nouveaux blocs de données et ignore simplement ceux qui ne respectent pas les règles. Tu peux librement faire ce que tu veux avec tes données locales, mais personne ne va les accepter.

      Et si je ne veux autoriser que toto et tata à modifier l'état du bug ? Est-ce qu'on peut modifier les commentaires des autres ?

      Actuellement, il n'y a pas de gestion forte des identités ni de gestion de droit. N'importe qui avec les droits d'écriture sur le dépôt peut créer, modifier ou éditer un bug et ses commentaires. Mais comme expliqué dans mon journal, c'est la suite du programme pour moi: avoir des profiles utilisateurs distribués avec les données des bugs, signer les opérations d'éditions et avoir un système de règles pour déterminer qui a le droit de faire quoi.

      Est-ce que ça aurait du sens d'étendre pour des merge requests ? Git permet bien entendu d'utiliser une branche pour ça, l'intérêt serait de permettre de commenter le code avant de pouvoir réellement merger la branche.

      Oui c'est tout à fait possible, mais largement en dehors de la portée du projet actuellement. Si il y a des volontaires, pas de soucis ;-)

      Ça se rapproche de ce que fait Fossil si je ne m'abuse

      Correct

      Comment ça marche pour créer un importeur/exporteur ? Ça a l'air d'être du JSON, y'a une API pour récupérer directement les opérations assemblées et le ticket final ? Je travaille sur un gestionnaire de tickets et de merge-requests décentralisé (basé sur XMPP), et ça m'intéresserait de pouvoir faire les tickets en local quand internet n'est pas dispo, et de pouvoir synchroniser après coup.

      Un importeur va simplement récupérer les informations depuis l'API du bug tracker distant et utiliser les fonctions de haut niveau de git-bug (créer un bug, ajouter un commentaire) pour répliquer les changements. Par exemple, l'importeur Github interroge l'API GraphQL de Github qui expose l'historique des éditions, compare avec les données existantes en local, insère les nouveaux changements tout en les taguant avec les identifiants unique Github pour pouvoir faire le lien pendant un futur import.

      Un exporteur va faire l'inverse, et répliquer les changements locaux non présent sur le tracker distant. Bien sûr, seul les changements dont tu es l'auteur pourront être répliqués, à moins de construire une vrai passerelle qui a des jetons d'API Github pour tout les auteurs, mais c'est pas au programme.

      Bonne continuation, c'est très prometteur.

      Merci :)

    • [^] # Re: Permissions ? Merge-requests ? import/export ?

      Posté par (page perso) . Évalué à 2 (+1/-0).

      Je viens de réaliser que ta question sur les imports/exports c'est plutôt pour interroger git-bug depuis un autre programme. L'idée c'est que les outils en ligne de commande permette de s'interfacer pour faire des scripts ou autre. Par exemple, git bug comment <id> permet de lister les commentaires d'un bug. Actuellement, c'est uniquement une sortie en mode texte pour un humain, mais un flag pour sortir en JSON ou autre, c'est parfaitement faisable. Ou alors il y a l'API GraphQL :-)

      Au passage, le format de sérialisation dans les blobs git a très peu d'importance et a été choisis uniquement pour des raisons de performance et de taux de compression (cf https://github.com/MichaelMure/git-bug/issues/5#issuecomment-419228672). Le format d'import/export peut très bien être différent.

      • [^] # Re: Permissions ? Merge-requests ? import/export ?

        Posté par (page perso) . Évalué à 2 (+1/-0).

        En parlant de l’import/export, est-ce qu’un importeur/exporteur pour GitLab est prévu ?

        Et une autre question (peut-être bête…) : pourquoi avoir une commande push dédiée ? Un git push normal, potentiellement accompagné des options appropriées, ne peut-il pas pousser tout le contenu du dépôt ?

        Bravo pour tout ce travail, c’est déjà super !

        • [^] # Re: Permissions ? Merge-requests ? import/export ?

          Posté par (page perso) . Évalué à 3 (+2/-0).

          En parlant de l’import/export, est-ce qu’un importeur/exporteur pour GitLab est prévu ?

          Un ingé de Gitlab semblait intéressé: https://gitlab.com/gitlab-org/gitlab-ce/issues/50435

          Mais essentiellement, il suffit que quelqu'un s'y colle. N'hésitez pas, promo sur les pull-requests !

          pourquoi avoir une commande push dédiée ?

          Un push normal avec les bonnes options marcherait, mais pas le pull parce que git-bug doit vérifier et fusionner les nouveaux changements à se sauce. Par soucis de cohérence, il y a les deux commandes spéciales dans git-bug. Au delà de ça, il y a aussi la volonté de découpler entièrement le workflow du code et des bugs.

  • # A coupler avec une système de revue de code intégré à git

    Posté par . Évalué à 2 (+1/-0).

    Ton billet m'a tout de suite fait penser à ça : https://github.com/google/git-appraise qui permet de faire de la revue de code intégré à git. (Meme idée d'utiliser des refsspec dédiés).

    A voir si on peut combiner les deux système pour avoir un bugtracker et de la revue de code directement dans git.

  • # Méprise

    Posté par (page perso) . Évalué à 7 (+14/-8).

    J'ai cru que c'était le nouveau nom de github suite au rachat par Microsoft.

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.