Skip to main content

Bonnes pratiques relatives aux intégrateurs

Créez une application qui interagit de manière fiable avec l’API GitHub Enterprise Server et offre la meilleure expérience pour vos utilisateurs.

Vous souhaitez intégrer la plateforme GitHub ? Vous êtes au bon endroit. Ce guide vous aidera à créer une application offrant une expérience optimale à vos utilisateurs et à vérifier qu’elle interagit de manière fiable avec l’API.

Sécuriser les charges utiles fournies à partir de GitHub

Il est très important de sécuriser les charges utiles qui sont envoyées à partir de GitHub. Même si aucune information personnelle (comme les mots de passe) n’est transmise dans une charge utile, la fuite d’informations, quel que soit leur type, n’est pas une bonne chose. Parmi les informations susceptibles d’être sensibles figurent l’adresse e-mail du commiteur ou le nom des dépôts privés.

Vous pouvez effectuer plusieurs étapes pour sécuriser la réception des charges utiles fournies par GitHub :

  1. Vérifiez que votre serveur de réception utilise une connexion HTTPS. Par défaut, GitHub vérifie les certificats SSL lors de la livraison des charges utiles.
  2. Fournissez un jeton secret pour être sûr que les charges utiles proviennent de GitHub. En appliquant un jeton secret, vous garantissez que toutes les données reçues par votre serveur proviennent uniquement de GitHub. Dans l’idéal, vous devez fournir un jeton secret différent pour chaque utilisateur de votre service. De cette façon, si un jeton est compromis, aucun autre utilisateur ne sera affecté.

Favoriser le travail asynchrone par rapport au travail synchrone

GitHub s’attend à ce que les intégrations répondent sous 30 secondes à compter de la réception de la charge utile webhook. Si votre service prend plus de temps que cela, GitHub met fin à la connexion et la charge utile est perdue.

Étant donné qu’il est impossible de prédire la rapidité de votre service, vous devez tout réaliser dans un travail en arrière-plan. Resque (pour Ruby), RQ (pour Python) ou RabbitMQ (pour Java) sont des exemples de bibliothèques qui peuvent gérer la mise en file d’attente et le traitement des travaux en arrière-plan.

Notez que même si un travail en arrière-plan est en cours d’exécution, GitHub s’attend toujours à ce que votre serveur réponde sous 30 secondes. Votre serveur doit confirmer qu’il a reçu la charge utile en envoyant une réponse. Il est essentiel que votre service effectue toutes les validations d’une charge utile dès que possible, afin que vous puissiez signaler correctement si votre serveur va poursuivre ou non avec la requête.

Utiliser les codes d’état HTTP appropriés dans une réponse à GitHub

Chaque webhook comprend sa propre section « Livraisons récentes », qui indique si un déploiement a réussi ou non.

Vue Livraisons récentes

Vous devez utiliser des codes d’état HTTP appropriés pour informer les utilisateurs. Vous pouvez utiliser des codes comme 201 ou 202 pour accuser réception d’une charge utile qui ne sera pas traitée (par exemple, une charge utile fournie par une branche qui n’est pas celle par défaut). Réservez le code d’erreur 500 aux erreurs graves.

Fournir autant d’informations que possible à l’utilisateur

Les utilisateurs peuvent explorer les réponses du serveur que vous renvoyez à GitHub. Assurez-vous que vos messages sont clairs et informatifs.

Vue d’une réponse de charge utile

Suivre les redirections que l’API envoie

GitHub est explicite lorsqu’il indique à quel moment une ressource a été déplacée. Pour cela, il fournit un code d’état de redirection. Vous devez suivre ces redirections. Chaque réponse de redirection définit l’en-tête Location avec le nouvel URI auquel accéder. Si vous recevez une redirection, il est préférable de mettre à jour votre code pour suivre le nouvel URI, dans le cas où vous demanderiez un chemin déprécié susceptible d’être supprimé.

Nous avons fourni la liste des codes d’état HTTP à surveiller lorsque vous concevez votre application en vue de suivre les redirections.

N’analysez pas manuellement les URL

Souvent, les réponses d’API contiennent des données sous la forme d’URL. Par exemple, lors d’une demande de dépôt, nous allons envoyer une clé appelée clone_url avec une URL que vous pourrez utiliser pour cloner le dépôt.

Pour la stabilité de votre application, vous ne devez pas essayer d’analyser ces données, ni de deviner et de construire le format des futures URL. Votre application sera susceptible de ne plus fonctionner si nous décidons de modifier l’URL.

Par exemple, lors d’une utilisation de résultats paginés, il est souvent tentant de construire des URL avec ?page=<number> à la fin. Ne cédez pas à cette tentation. Pour plus d’informations sur le suivi fiable des résultats paginés, consultez « Utilisation de la pagination dans l’API REST ».

Vérifier le type d’événement et l’action avant de traiter l’événement

Il existe plusieurs types d’événements webhook, et chaque événement peut avoir plusieurs actions. À mesure que le nombre de fonctionnalités GitHub augmente, nous pouvons parfois ajouter de nouveaux types d’événements ou ajouter de nouvelles actions aux types d’événements existants. Assurez-vous que votre application vérifie explicitement le type et l’action d’un événement avant d’effectuer un traitement webhook. L’en-tête de demande X-GitHub-Event peut être utilisé pour savoir quel événement a été reçu afin que le traitement puisse être géré de manière appropriée. De même, la charge utile a une clé de niveau supérieur action qui peut être utilisée pour savoir quelle action a été effectuée sur l’objet concerné.

Par exemple, si vous avez configuré un webhook GitHub pour tout recevoir (« Send me everything »), votre application recevra les nouveaux types d’événements et les nouvelles actions à mesure qu’ils sont ajoutés. Il n’est donc pas recommandé d’utiliser n’importe quelle sorte de clause catch-all else. Prenez l’exemple de code suivant :

# Not recommended: a catch-all else clause
def receive
  event_type = request.headers["X-GitHub-Event"]
  payload    = request.body

  case event_type
  when "repository"
    process_repository(payload)
  when "issues"
    process_issues(payload)
  else
    process_pull_requests
  end
end

Dans cet exemple de code, les méthodes process_repository et process_issues seront correctement appelées si un événement repository ou issues a été reçu. Toutefois, tout autre type d’événement entraînera l’appel de process_pull_requests. Si de nouveaux types d’événements sont ajoutés, cela entraînera un comportement incorrect et les nouveaux types d’événements seront traités de la même façon qu’un événement pull_request.

Au lieu de cela, nous vous suggérons de vérifier explicitement les types d’événements et d’agir en conséquence. Dans l’exemple de code suivant, nous vérifions explicitement un événement pull_request, et la clause else journalise simplement que nous avons reçu un nouveau type d’événement :

# Recommended: explicitly check each event type
def receive
  event_type = request.headers["X-GitHub-Event"]
  payload    = JSON.parse(request.body)

  case event_type
  when "repository"
    process_repository(payload)
  when "issues"
    process_issue(payload)
  when "pull_request"
    process_pull_requests(payload)
  else
    puts "Oooh, something new from GitHub: #{event_type}"
  end
end

Étant donné que chaque événement peut également avoir plusieurs actions, il est recommandé que les actions soient vérifiées de la même manière. Par exemple, IssuesEvent a plusieurs actions possibles. Elles incluent notamment opened quand le problème est créé, closed quand le problème est fermé et assigned quand le problème est affecté à une personne.

Comme pour l’ajout de types d’événements, nous pouvons ajouter de nouvelles actions aux événements existants. Il n’est donc pas recommandé d’utiliser n’importe quelle clause catch-all else lorsque vous vérifiez l’action d’un événement. Au lieu de cela, nous vous suggérons de vérifier explicitement les actions d’événement comme nous l’avons fait avec le type d’événement. Voici un exemple très similaire à ce que nous avons suggéré pour les types d’événements ci-dessus :

# Recommended: explicitly check each action
def process_issue(payload)
  case payload["action"]
  when "opened"
    process_issue_opened(payload)
  when "assigned"
    process_issue_assigned(payload)
  when "closed"
    process_issue_closed(payload)
  else
    puts "Oooh, something new from GitHub: #{payload["action"]}"
  end
end

Dans cet exemple, l’action closed est vérifiée avant l’appel de la méthode process_closed. Toutes les actions non identifiées sont journalisées en vue d’une consultation future.

Gestion des erreurs d’API

Même si votre code ne peut pas générer de bogue, vous pouvez rencontrer des erreurs successives lorsque vous tentez d’accéder à l’API.

Au lieu d’ignorer les codes d’état 4xx et 5xx, vérifiez que vous interagissez correctement avec l’API. Par exemple, si un point de terminaison demande une chaîne et que vous transmettez une valeur numérique, vous recevrez une erreur de validation 5xx et votre appel échouera. De même, toute tentative d’accès à un point de terminaison non autorisé ou inexistant entraîne une erreur 4xx.

Le fait d’ignorer intentionnellement toutes les erreurs de validation peut entraîner la suspension de votre application pour abus.