Créez un système de migration des rapports de bug de GitHub à GitLab

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
90
Mois de parution
mai 2017
Spécialité(s)


Résumé

Apprenez à manier les API de GitHub et GitLab afin d'effectuer des opérations courantes sur vos dépôts.


Body

Il est de nos jours très courant pour des services web de fournir à leurs utilisateurs une API REST permettant d'effectuer diverses opérations depuis la ligne de commandes (notamment avec curl), ou d'écrire des applications utilisables en dehors du navigateur. Python étant un langage très populaire, de nombreuses API bénéficient d'une bibliothèque Python permettant de les manipuler. Voyons deux d'entre elles, qui permettent d'interagir avec des services d'hébergement de dépôts Git au travers de leurs API.

1. Découverte de l'API GitHub

GitHub fournit une API dont la documentation est disponible sur le Web (voir https://developer.github.com/v3/). Elle utilise le format bien connu JSON.

1.1 Premiers pas

Nous pouvons commencer par lancer une requête GET sur la racine de l'API :

# Nous tronquons ici la sortie par souci de lisibilité, et afin de ne garder que

# les parties qui nous intéressent.

$ curl -s https://api.github.com/

{

  "current_user_url": "https://api.github.com/user",

  ...

  "repository_url": "https://api.github.com/repos/{owner}/{repo}",

  ...

}

On peut ensuite continuer notre exploration en utilisant les URL qui nous ont été retournées :

# Récupérons des informations sur le dépôt "GLMF175" de l'utilisateur "GLMF".

$ curl -s https://api.github.com/repos/GLMF/GLMF175

{

  "id": 23825643,

  "name": "GLMF175",

  "full_name": "GLMF/GLMF175",

  ...

  "description": "Codes sources du n°175 de GNU/Linux Magazine (Octobre 2014)",

  ...

  "created_at": "2014-09-09T08:32:32Z",

  "updated_at": "2014-11-03T10:12:27Z",

  "pushed_at": "2014-10-02T07:30:06Z",

  "git_url": "git://github.com/GLMF/GLMF175.git",

  "ssh_url": "git@github.com:GLMF/GLMF175.git",

  "clone_url": "https://github.com/GLMF/GLMF175.git",

  "svn_url": "https://github.com/GLMF/GLMF175",

  ...

}

Certaines requêtes nécessitent une authentification :

$ curl -s https://api.github.com/user

{

  "message": "Requires authentication",

  "documentation_url": "https://developer.github.com/v3"

}

$ curl -s https://api.github.com/user -u Steap

Enter host password for user 'Steap':

{

  "login": "Steap",

  "id": 416834,

  ...

  "html_url": "https://github.com/Steap",

  ...

  "repos_url": "https://api.github.com/users/Steap/repos",

  ...

  "name": "Cyril Roelandt",

  ...

}

Il existe plusieurs façons de s'authentifier, que nous détaillerons dans la partie suivante. Avant cela, force est de constater qu'effectuer toutes les requêtes avec curl va vite devenir relativement fastidieux ; il convient donc d'utiliser une bibliothèque Python rendant l'utilisation de l'API plus facile. Il en existe de nombreuses : nous avons choisi d'utiliser ici PyGithub (https://pypi.python.org/pypi/PyGithub), en version 1.32.

1.2 Authentification

Intéressons-nous à deux méthodes d'authentification : par mot de passe, et à l'aide d'un jeton privé. Notons qu'il est également possible d'enregistrer une application et de s'authentifier grâce au protocole OAuth, mais nous ne détaillerons pas ici cette méthode.

1.2.1 Par pseudo/mot de passe

Comme nous venons de le voir, il est possible de s'identifier en donnant tout simplement son pseudonyme (ou son adresse de courriel) et son mot de passe :

>>> import github

>>> gh = github.Github('Steap', 'motdepasse')

# On peut vérifier que nous sommes bien connectés :

>>> print(gh.get_user().login)

Steap

1.2.2 Par jeton privé

Il est également possible d'utiliser un jeton privé, ce qui nous permet de gérer plus finement les permissions. Pour ce faire, il suffit de se rendre sur la page https://github.com/settings/tokens/new et d'y sélectionner les permissions requises (cf. Figure 1).

git_figure_01

 

Fig. 1 : Liste des permissions disponibles lors de la création d'un jeton.

Créons un jeton en ne lui donnant pas la permission gist, puis vérifions qu'il est effectivement impossible de créer un gist :

>>> import github

>>> gh = github.Github('tokensecret')

>>> gh.get_user().create_gist(True,

... {'hello.txt': github.InputFileContent('Hello GLMF')},

... 'A test gist')

Traceback (most recent call last):

...

github.GithubException.UnknownObjectException: 404...

Créons maintenant un autre jeton, en lui donnant la permission de créer des gists. Nous pouvons le faire directement depuis notre console Python :

>>> t = gh.get_user().create_authorization(["gist"], "Token-with-gist")

>>> t.token

'nouveautoken'

Tentons à nouveau de créer un gist :

>>> gh = github.Github('nouveautoken')

>>> gh.get_user().create_gist(True,

... {'hello.txt': github.InputFileContent('Hello GLMF')},

... 'A test gist')

Gist(id="a9050b4027ee16111458a6b34f959a94")

Cette fois-ci, la commande a fonctionné : nous pouvons le vérifier en nous rendant sur https://gist.github.com/Steap/a9050b4027ee16111458a6b34f959a94.

1.3 Aller plus loin

Nous n'avons pas détaillé, dans la partie précédente, les paramètres des méthodes utilisées (create_authorization, create_gist). Leur fonctionnement est relativement intuitif, et nous n'avons de toute façon pas la place d'écrire ici la documentation complète de PyGithub. Nous pouvons toutefois donner deux conseils pour appréhender ce type de bibliothèques :

  • explorez les possibilités offertes par chaque objet Python dans l'interpréteur interactif : dir() et help() vous permettront de rapidement comprendre les subtilités de la bibliothèque utilisée ;
  • lisez la documentation de l'API, même si vous ne comptez pas l'utiliser directement : les bibliothèques sont en général très proches de cette API, et apportent principalement une plus grande facilité d'utilisation.

Nous verrons dans la troisième partie de cet article d'autres cas d'utilisation de l'API GitHub.

2. Découverte de l'API GitLab

La documentation de l'API GitLab est bien évidemment disponible sur le Web (https://docs.gitlab.com/ee/api/README.html#gitlab-api). Montrons ici quelques cas d'utilisation, en utilisant l'instance GitLab hébergée par Framasoft (https://framagit.org/).

2.1 Authentification

Comme pour l'API GitHub, il est nécessaire de savoir s'authentifier.

2.1.1 Par pseudo/mot de passe

Sur GitLab, l'authentification par pseudo/mot de passe permet uniquement de récupérer un jeton privé (également disponible dans l'interface web) :

$ curl -X POST -s "https://framagit.org/api/v3/session?login=Steap&password=xxx"

{

   "web_url" : "https://framagit.org/Steap",

   ...

   "private_token" : "secret-token",

   ...

   "username" : "Steap",

   ...

}

Voyons maintenant comment utiliser ce jeton privé.

2.1.2 Jeton privé

Le jeton privé que nous venons de récupérer peut maintenant être utilisé pour interroger l'API, par exemple afin de lister nos projets :

$ curl -s https://framagit.org/api/v3/projects -H 'PRIVATE-TOKEN: secret-token'

[

  {

    ...

    "web_url" : "https://framagit.org/Steap/quiparrainequi",

    "id" : 13198,

    "name" : "quiparrainequi",

    ...

  }

  ...

]

2.1.3 OAuth2

Il est également possible d'utiliser OAuth2 afin de bénéficier d'une gestion plus fine des permissions. Une documentation complète est disponible sur le Web (https://docs.gitlab.com/ee/api/README.html#oauth-2-tokens), mais voyons tout de même rapidement les étapes nécessaires à l'obtention d'un jeton.

Il faut tout d'abord créer une application en passant par l'interface web de GitLab (voir figure 2).

git_figure_02

 

Fig. 2 : Création d'une application dans GitLab.

GitLab affichera alors votre Application ID, votre secret ainsi que l'URL de redirection que vous avez indiquée. Utilisez ces informations pour construire l'URL suivante :

>>> url = "https://framagit.org/oauth/authorize"

>>> url+= "?client_id=%s" % APP_ID

>>> url+= "&redirect_uri=%s" % REDIRECT_URI

>>> url+= "&response_type=code"

Dégainez votre butineur et rendez-vous sur cette page, donnez à l'application les permissions qu'elle vous demande, et vous serez redirigé vers une page dont l'URL se terminera par code=$CODE. Une requête vous permettra d'obtenir (enfin !) votre jeton OAuth2 :

$ URL=https://framagit.org/oauth/token

$ URL="$URL?client_id=$APP_ID"

$ URL="$URL&client_secret=$SECRET"

$ URL="$URL&code=$CODE"

$ URL="$URL&grant_type=authorization_code"

$ URL="$URL&redirect_uri=$REDIRECT_URI"

$ curl -s -X POST $URL | json_pp

{

   "refresh_token" : "...",

   "scope" : "api",

   "created_at" : 1490500721,

   "token_type" : "bearer",

   "access_token" : "secret-access-token"

}

Vous pouvez ensuite utiliser ce jeton dans vos requêtes :

$ curl -H "Authorization: Bearer secret-access-token" \

    https://framagit.org/api/v3/projects/

[

  ...

]

2.2 Une bibliothèque : python-gitlab

Rendons-nous la vie plus simple en utilisant une bibliothèque Python : python-gitlab en version 0.20. S'authentifier en utilisant un jeton privé se fait très simplement :

>>> import gitlab

>>> gl = gitlab.Gitlab('https://framagit.org', 'secret-token')

Il est ensuite possible d'explorer la bibliothèque comme nous l'avons fait avec PyGithub :

>>> for project in gl.projects.list():

...     print(project.name)

...

quiparrainequi

framadate

sf2cf

Voyons dans la prochaine partie comment utiliser python-gitlab pour manipuler nos projets.

3. Exemple : migrations des rapports de bugs

Lorsqu'un utilisateur de GitLab veut créer un nouveau projet, il a la possibilité d'importer un projet existant depuis un autre site, notamment GitHub (voir figure 3). Le dépôt Git sera automatiquement importé, et les rapports de bugs seront copiés. Comment pourrions-nous utiliser les API de GitHub et de GitLab afin d'implémenter une fonctionnalité similaire ?

git_figure_03

 

Fig. 3 : GitLab permet d'importer un projet depuis un autre service.

3.1 Idée générale

L'idée de base est très simple : il suffit en effet de créer un nouveau projet, puis de migrer chacun des services associés (bugs, wiki, etc.) de GitHub vers GitLab. Nous nous contenterons ici de migrer les bugs. Regardons le point d'entrée de notre code :

import github

import gitlab

 

GITHUB_TOKEN = 'secret-github-token'

gh = github.Github(GITHUB_TOKEN)

 

GITLAB_URL = 'https://framagit.org'

GITLAB_TOKEN = 'secret-gitlab-token'

gl = gitlab.Gitlab(GITLAB_URL, GITLAB_TOKEN)

 

def migrate_gh_project(project_name):

    # Create a new project on Gitlab

    gl_project = gitlab_create_project(project_name)

 

    gh_repo = gh.get_user().get_repo(project_name)

 

    # Migrate all issues

    for gh_issue in gh_repo.get_issues():

        gl_issue = gitlab_clone_github_issue(gl_project, gh_issue)

 

    # TODO: Migrate everything else!

migrate_gh_project('quiparrainequi')

Pour l'instant, tout est facilement compréhensible. On notera bien évidemment que gl fait référence à GitLab, et gh à GitHub. Il ne nous reste plus qu'à regarder dans le détail comment définir les fonctions que nous venons d'introduire.

3.2 Créer un nouveau projet

Cette étape est sans la plus facile : elle consiste en un seul appel à l'API GitLab, et ne nécessite qu'un seul paramètre, le nom du projet :

def gitlab_create_project(project_name):

    return gl.projects.create({'name': project_name})

Après avoir exécuté cette fonction, un nouveau projet apparaît dans notre tableau de bord GitLab.

3.3 Créer les rapports de bugs

Dans la fonction migrate_gh_project, nous itérons sur les rapports de bugs afin de les cloner un par un. Idéalement, nous aimerions conserver cinq informations :

  • le titre du rapport ;
  • la description du bug ;
  • la date de création du bug ;
  • l'auteur du bug ;
  • les commentaires.

Comme nous pouvons le voir dans le code de la fonction gitlab_clone_github_issue, les trois premières informations peuvent être passées à gl_project.issues.create. Spécifier l'auteur du bug est plus difficile :

  • il a peut-être un compte sur GitHub, mais pas sur votre instance GitLab ;
  • un problème de permissions se pose : un utilisateur ne peut pas créer un bug en usurpant l'identité d'un autre.

Nous choisissons ici d'inclure le nom de l'auteur dans la description (c'est l'approche implémentée par l'outil officiel de migration fourni par GitLab).

def gitlab_clone_github_issue(gl_project, gh_issue):

    body = '[Created by %s] %s' % (gh_issue.user.login, gh_issue.body)

    gl_issue = gl_project.issues.create({

            'title': gh_issue.title,

            'description': body,

            'created_at': gh_issue.created_at.isoformat()

        })

 

    for gh_comment in gh_issue.get_comments():

        gitlab_clone_github_comment(gl_issue, gh_comment)

 

    return gl_issue

Il ne nous reste plus qu'à cloner les commentaires.

3.4 Créer les commentaires

Créer un commentaire est une opération relativement similaire à la création d'un rapport de bug. Nous rencontrons le même problème concernant les auteurs des commentaires, que nous contournons de la même façon que précédemment :

def gitlab_clone_github_comment(gl_issue, gh_comment):

    body = '[Created by %s] %s' % (gh_comment.user.login, gh_comment.body)

    gl.project_issue_notes.create({

            'body': body,

            'created_at': gh_comment.created_at.isoformat()

        }, project_id=gl_issue.project_id, issue_id=gl_issue.id)

Nous avons maintenant toutes les pièces du puzzle ! Si l'on exécute la fonction migrate_gh_project avec un nom de projet existant sur notre compte, un projet similaire apparaîtra dans GitLab, avec les bugs reproduits (presque) à l'identique.

3.5 Améliorations possibles

Bien entendu, ce code n'est qu'un simple exemple de l'utilisation combinée des API de GitHub et GitLab. Nous pourrions y apporter de nombreuses améliorations :

  • seuls les bugs sont migré (même le code du dépôt est absent de notre nouveau projet), il conviendrait de tout copier ;
  • seuls les bugs ouverts sont copiés (get_issues ne retourne par défaut que ceux-ci), il faudrait également copier ceux qui ont été résolus ;
  • la visibilité du projet est « privée » par défaut, mais nous pourrions corriger cela en modifiant gitlab_create_project ;  
  • afin de pouvoir migrer nos projets de n'importe quel service en ligne vers n'importe quel autre, il faudrait donner à notre code une meilleure architecture.

Tout ceci est possible grâce aux deux bibliothèques que nous avons utilisées jusqu'ici.

Conclusion

Nous avons montré à quel point il était facile d'utiliser les API de GitHub et GitLab grâce à des bibliothèques Python, et avons réussi à construire une application certes rudimentaire, mais tout à fait fonctionnelle, qui nous a permis de nous authentifier, de lire des données et d'en créer d'autres.

De nombreuses API fonctionnent de façon similaire, en proposant diverses méthodes d'authentification (par mot de passe, avec des jetons, avec OAuth2, etc.) et en permettant à l'utilisateur de récupérer et de modifier l'état du système. La plupart d'entre elles sont également utilisables au travers de bibliothèques Python tout aussi simples d'accès que celles dont il était question dans cet article. La méthodologie employée ici (exploration de l'API avec curl, authentification, découverte de la bibliothèque dans l'interpréteur Python...) aura donc sans doute l'occasion d'être réutilisée.



Article rédigé par

Par le(s) même(s) auteur(s)

Faut s’démener au FOSDEM !

Magazine
Marque
GNU/Linux Magazine
Numéro
213
Mois de parution
mars 2018
Résumé
Le FOSDEM (Free and OpenSource Developers European Meeting) est tellement incontournable qu’une part non négligeable des auteurs de GLMF s’y rend chaque année. De nombreuses mains et points de vue ont donc participé à ce compte-rendu, pour vous faire part du foisonnement de ce week-end intense.

Étendez Pandoc avec Lua

Magazine
Marque
GNU/Linux Magazine
Numéro
199
Mois de parution
décembre 2016
Spécialité(s)
Résumé
Il est parfois nécessaire de convertir un fichier d'un langage de balisage vers un autre : de Markdown vers du HTML, d'Org-mode vers LaTeX, etc. Pandoc est un outil permettant ce type de conversion, et il est capable de gérer un nombre impressionnant de formats différents. Que faire si l'on souhaite utiliser un format ésotérique inconnu de Pandoc ? Il est possible de l'étendre en Lua !

Les derniers articles Premiums

Les derniers articles Premium

Quarkus : applications Java pour conteneurs

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Initié par Red Hat, il y a quelques années le projet Quarkus a pris son envol et en est désormais à sa troisième version majeure. Il propose un cadre d’exécution pour une application de Java radicalement différente, où son exécution ultra optimisée en fait un parfait candidat pour le déploiement sur des conteneurs tels que ceux de Docker ou Podman. Quarkus va même encore plus loin, en permettant de transformer l’application Java en un exécutable natif ! Voici une rapide introduction, par la pratique, à cet incroyable framework, qui nous offrira l’opportunité d’illustrer également sa facilité de prise en main.

De la scytale au bit quantique : l’avenir de la cryptographie

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Imaginez un monde où nos données seraient aussi insaisissables que le célèbre chat de Schrödinger : à la fois sécurisées et non sécurisées jusqu'à ce qu'un cryptographe quantique décide d’y jeter un œil. Cet article nous emmène dans les méandres de la cryptographie quantique, où la physique quantique n'est pas seulement une affaire de laboratoires, mais la clé d'un futur numérique très sécurisé. Entre principes quantiques mystérieux, défis techniques, et applications pratiques, nous allons découvrir comment cette technologie s'apprête à encoder nos données dans une dimension où même les meilleurs cryptographes n’y pourraient rien faire.

Les nouvelles menaces liées à l’intelligence artificielle

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Sommes-nous proches de la singularité technologique ? Peu probable. Même si l’intelligence artificielle a fait un bond ces dernières années (elle est étudiée depuis des dizaines d’années), nous sommes loin d’en perdre le contrôle. Et pourtant, une partie de l’utilisation de l’intelligence artificielle échappe aux analystes. Eh oui ! Comme tout système, elle est utilisée par des acteurs malveillants essayant d’en tirer profit pécuniairement. Cet article met en exergue quelques-unes des applications de l’intelligence artificielle par des acteurs malveillants et décrit succinctement comment parer à leurs attaques.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 122 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous