Ă propos dâOctokit.rb
Si vous souhaitez Ă©crire un script avec Ruby pour interagir avec lâAPI REST de GitHub, GitHub vous recommande dâutiliser le SDK Octokit.rb. Octokit.rb est maintenu par GitHub. Le SDK implĂ©mente les meilleures pratiques et vous permet dâinteragir plus facilement avec lâAPI REST via Ruby. Octokit.rb fonctionne avec tous les navigateurs modernes, Node.rb et Deno. Pour plus dâinformations sur Octokit.rb, consultez le fichier LISEZMOI Octokit.rb.
Prérequis
Ce guide suppose que vous ĂȘtes familiarisĂ© avec Ruby et lâAPI REST GitHub REST API. Pour plus dâinformations sur lâAPI REST, consultez Prise en main de lâAPI REST.
Vous devez installer et importer le gemme octokit
pour utiliser la bibliothĂšque Octokit.rb. Ce guide utilise des instructions import conformĂ©ment aux conventions Ruby. Pour plus dâinformations sur les diffĂ©rentes mĂ©thodes dâinstallation, consultez la section dâinstallation du fichier LISEZMOI Octokit.rb.
Instanciation et authentification
Avertissement
GĂ©rez vos informations dâauthentification comme un mot de passe.
Pour que vos informations dâidentification restent sĂ©curisĂ©es, vous pouvez les stocker sous forme de secret et exĂ©cuter votre script par le biais de GitHub Actions. Pour plus dâinformations, consultez « Utilisation de secrets dans GitHub Actions ».
Vous pouvez Ă©galement stocker vos informations d'identification en tant que secret Codespaces et exĂ©cuter votre script dans Codespaces. Pour plus dâinformations, consultez « Gestion des secrets spĂ©cifiques Ă votre compte pour GitHub Codespaces ».
Si ces options ne sont pas possibles, envisagez dâutiliser un autre service CLI pour stocker vos informations dâidentification de façon sĂ©curisĂ©e.
Authentification avec un personal access token
Si vous voulez utiliser lâAPI REST GitHub Ă des fins personnelles, vous pouvez crĂ©er un personal access token. Pour plus dâinformations sur la crĂ©ation dâun personal access token, consultez Gestion de vos jetons d'accĂšs personnels.
Tout dâabord, exigez la bibliothĂšque octokit
. Ensuite, créez une instance de Octokit
en faisant passer votre personal access token comme lâoption access_token
. Dans lâexemple suivant, remplacez YOUR-TOKEN
par vos personal access token.
require 'octokit' octokit = Octokit::Client.new(access_token: 'YOUR-TOKEN')
require 'octokit'
octokit = Octokit::Client.new(access_token: 'YOUR-TOKEN')
Authentification avec une GitHub App
Si vous voulez utiliser lâAPI au nom dâune organisation ou dâun autre utilisateur, GitHub vous recommande dâutiliser une GitHub App. Si un point de terminaison est disponible pour GitHub Apps, la documentation de rĂ©fĂ©rence de REST pour ce point de terminaison indique quel type de jeton GitHub App est nĂ©cessaire. Pour plus dâinformations, consultez « Inscription dâune application GitHub » et « Ă propos de lâauthentification avec une application GitHub ».
Au lieu dâexiger octokit
, créez une instance de Octokit::Client
en transmettant vos informations GitHub App en tant quâoptions. Dans lâexemple suivant, remplacez APP_ID
par lâID dâapplication, PRIVATE_KEY
avec la clé privée de votre application et INSTALLATION_ID
avec lâID de lâinstallation de votre application pour laquelle vous souhaitez vous authentifier. Vous pouvez trouver lâID de votre application et gĂ©nĂ©rer une clĂ© privĂ©e dans la page des paramĂštres de votre application. Pour plus dâinformations, consultez « Gestion des clĂ©s privĂ©es pour les applications GitHub ». Vous pouvez obtenir un ID dâinstallation avec les points de terminaison GET /users/{username}/installation
, GET /repos/{owner}/{repo}/installation
ou GET /orgs/{org}/installation
. Pour plus dâinformations, consultez Points de terminaison dâAPI REST pour GitHub Apps.
require 'octokit' app = Octokit::Client.new( client_id: APP_ID, client_secret: PRIVATE_KEY, installation_id: INSTALLATION_ID ) octokit = Octokit::Client.new(bearer_token: app.create_app_installation.access_token)
require 'octokit'
app = Octokit::Client.new(
client_id: APP_ID,
client_secret: PRIVATE_KEY,
installation_id: INSTALLATION_ID
)
octokit = Octokit::Client.new(bearer_token: app.create_app_installation.access_token)
Authentification dans GitHub Actions
Si vous voulez utiliser lâAPI dans un workflow GitHub Actions, GitHub vous recommande dâĂȘtre authentifiĂ© avec le jeton GITHUB_TOKEN
intégré au lieu de créer un jeton. Vous pouvez accorder des autorisations au GITHUB_TOKEN
avec la clé permissions
. Pour plus dâinformations sur GITHUB_TOKEN
, consultez Utiliser GITHUB_TOKEN pour lâauthentification dans les flux de travail.
Si votre workflow doit accéder à des ressources en dehors du dépÎt du workflow, vous ne pourrez pas utiliser GITHUB_TOKEN
. Dans ce cas, stockez vos informations dâidentification sous forme de secret et remplacez GITHUB_TOKEN
dans les exemples ci-dessous par le nom de votre secret. Pour plus dâinformations sur les secrets, consultez « Utilisation de secrets dans GitHub Actions ».
Si vous utilisez le mot clé run
pour exécuter votre script Ruby dans vos flux de travail GitHub Actions, vous pouvez stocker la valeur de GITHUB_TOKEN
en tant que variable dâenvironnement. Votre script peut accĂ©der Ă la variable dâenvironnement en tant que ENV['VARIABLE_NAME']
.
Par exemple, cette étape de workflow stocke GITHUB_TOKEN
dans une variable dâenvironnement appelĂ©e TOKEN
:
- name: Run script
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ruby .github/actions-scripts/use-the-api.rb
Le script quâexĂ©cute le workflow utilise ENV['TOKEN']
pour lâauthentification :
require 'octokit' octokit = Octokit::Client.new(access_token: ENV['TOKEN'])
require 'octokit'
octokit = Octokit::Client.new(access_token: ENV['TOKEN'])
Instanciation sans authentification
Vous pouvez utiliser lâAPI REST sans authentification, mais vous aurez une limite de dĂ©bit infĂ©rieure et vous ne pourrez pas utiliser certains points de terminaison. Pour crĂ©er une instance de Octokit
sans authentification, nâignorez pas lâoption access_token
.
require 'octokit' octokit = Octokit::Client.new
require 'octokit'
octokit = Octokit::Client.new
CrĂ©ation de requĂȘtes
Octokit prend en charge plusieurs façons de crĂ©er des requĂȘtes. Vous pouvez utiliser la mĂ©thode request
pour crĂ©er des requĂȘtes si vous connaissez le verbe HTTP et le chemin du point de terminaison. Vous pouvez utiliser la mĂ©thode rest
si vous souhaitez tirer parti de lâautocomplĂ©tion dans votre IDE et de la saisie. Pour les points de terminaison paginĂ©s, vous pouvez utiliser la mĂ©thode paginate
pour demander plusieurs pages de données.
Utilisation de la méthode request
pour crĂ©er des requĂȘtes
Pour utiliser la méthode request
pour crĂ©er des requĂȘtes, passez la mĂ©thode HTTP et le chemin comme premier argument. Passez tous les paramĂštres de corps, de requĂȘte ou de chemin dâaccĂšs dans un code de hachage en tant que deuxiĂšme argument. Par exemple, pour crĂ©er une requĂȘte GET
dans /repos/{owner}/{repo}/issues
et passer les paramĂštres owner
, repo
et per_page
:
octokit.request("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 2)
octokit.request("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 2)
La méthode request
passe automatiquement lâen-tĂȘte Accept: application/vnd.github+json
. Pour passer des en-tĂȘtes supplĂ©mentaires ou un en-tĂȘte Accept
différent, ajoutez une option headers
au code de hachage passĂ© en tant que deuxiĂšme argument. La valeur de lâoption headers
est un code de hachage avec les noms dâen-tĂȘte en tant que clĂ©s et les valeurs dâen-tĂȘte en tant que valeurs. Par exemple, pour envoyer un en-tĂȘte content-type
avec la valeur de text/plain
:
octokit.request("POST /markdown/raw", text: "Hello **world**", headers: { "content-type" => "text/plain" })
octokit.request("POST /markdown/raw", text: "Hello **world**", headers: { "content-type" => "text/plain" })
Utilisation des méthodes de point de terminaison rest
pour crĂ©er des requĂȘtes
Chaque point de terminaison dâAPI REST a une mĂ©thode de point de terminaison rest
associĂ©e dans Octokit. Ces mĂ©thodes appliquent gĂ©nĂ©ralement lâautocomplĂ©tion dans votre IDE pour des raisons pratiques. Vous pouvez passer nâimporte quel paramĂštre en tant code de hachage Ă la mĂ©thode.
octokit.rest.issues.list_for_repo(owner: "github", repo: "docs", per_page: 2)
octokit.rest.issues.list_for_repo(owner: "github", repo: "docs", per_page: 2)
CrĂ©ation de requĂȘtes paginĂ©es
Si le point de terminaison est paginé et que vous souhaitez extraire plusieurs pages de résultats, vous pouvez utiliser la méthode paginate
. paginate
rĂ©cupĂšre la page de rĂ©sultats suivante jusquâĂ atteindre la derniĂšre page, puis retourne tous les rĂ©sultats sous la forme dâun tableau. Quelques points de terminaison retournent les rĂ©sultats paginĂ©s sous forme de tableau dans un objet, au lieu de les retourner sous forme de tableau. paginate
retourne toujours un tableau dâĂ©lĂ©ments mĂȘme si le rĂ©sultat brut Ă©tait un objet.
Par exemple, lâexemple suivant rĂ©cupĂšre tous les problĂšmes du dĂ©pĂŽt github/docs
. Bien quâelle demande 100 problĂšmes Ă la fois, la fonction nâest pas retournĂ©e tant que la derniĂšre page de donnĂ©es nâest pas atteinte.
issue_data = octokit.paginate("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100)
issue_data = octokit.paginate("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100)
La méthode paginate
accepte un bloc facultatif, que vous pouvez utiliser pour traiter chaque page de rĂ©sultats. Cela vous permet de collecter uniquement les donnĂ©es souhaitĂ©es Ă partir de la rĂ©ponse. Par exemple, lâexemple suivant continue dâextraire les rĂ©sultats jusquâĂ ce quâun problĂšme incluant « test » dans le titre soit retournĂ©. Pour les pages de donnĂ©es qui ont Ă©tĂ© retournĂ©es, seuls le titre et lâauteur du problĂšme sont stockĂ©s.
issue_data = octokit.paginate("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100) do |response, done| response.data.map do |issue| if issue.title.include?("test") done.call end { title: issue.title, author: issue.user.login } end end
issue_data = octokit.paginate("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100) do |response, done|
response.data.map do |issue|
if issue.title.include?("test")
done.call
end
{ title: issue.title, author: issue.user.login }
end
end
Au lieu dâextraire tous les rĂ©sultats Ă la fois, vous pouvez utiliser octokit.paginate.iterator()
pour itĂ©rer au sein dâune seule page Ă la fois. Par exemple, lâexemple suivant extrait une page de rĂ©sultats Ă la fois et traite chaque objet de la page avant dâextraire la page suivante. Une fois quâun problĂšme qui inclut « test » dans le titre est atteint, le script arrĂȘte lâitĂ©ration et retourne le titre du problĂšme et lâauteur du problĂšme de chaque objet qui a Ă©tĂ© traitĂ©. LâitĂ©rateur est la mĂ©thode la plus efficace en mĂ©moire pour extraire des donnĂ©es paginĂ©es.
iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100) issue_data = [] break_loop = false iterator.each do |data| break if break_loop data.each do |issue| if issue.title.include?("test") break_loop = true break else issue_data << { title: issue.title, author: issue.user.login } end end end
iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100)
issue_data = []
break_loop = false
iterator.each do |data|
break if break_loop
data.each do |issue|
if issue.title.include?("test")
break_loop = true
break
else
issue_data << { title: issue.title, author: issue.user.login }
end
end
end
Vous pouvez également utiliser la méthode paginate
avec les méthodes de point de terminaison rest
. Transmettez la méthode de point de terminaison rest
en tant que premier argument et tous les paramĂštres comme deuxiĂšme argument.
iterator = octokit.paginate.iterator(octokit.rest.issues.list_for_repo, owner: "github", repo: "docs", per_page: 100)
iterator = octokit.paginate.iterator(octokit.rest.issues.list_for_repo, owner: "github", repo: "docs", per_page: 100)
Pour plus dâinformations sur la pagination, consultez Utilisation de la pagination dans lâAPI REST.
Interception des erreurs
Interception de toutes les erreurs
Parfois, lâAPI REST GitHub retourne une erreur. Par exemple, vous obtenez une erreur si votre jeton dâaccĂšs a expirĂ© ou si vous avez omis un paramĂštre obligatoire. Octokit.rb retente automatiquement la demande quand il obtient une erreur autre que 400 Bad Request
, 401 Unauthorized
, 403 Forbidden
, 404 Not Found
et 422 Unprocessable Entity
. Si une erreur dâAPI se produit mĂȘme aprĂšs plusieurs tentatives, Octokit.rb lĂšve une erreur qui inclut le code dâĂ©tat HTTP de la rĂ©ponse (response.status
) et les en-tĂȘtes de rĂ©ponse (response.headers
). Vous devez gérer ces erreurs dans votre code. Par exemple, vous pouvez utiliser un bloc try/catch pour intercepter les erreurs :
begin files_changed = [] iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/pulls/{pull_number}/files", owner: "github", repo: "docs", pull_number: 22809, per_page: 100) iterator.each do | data | files_changed.concat(data.map { | file_data | file_data.filename }) end rescue Octokit::Error => error if error.response puts "Error! Status: #{error.response.status}. Message: #{error.response.data.message}" end puts error end
begin
files_changed = []
iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/pulls/{pull_number}/files", owner: "github", repo: "docs", pull_number: 22809, per_page: 100)
iterator.each do | data |
files_changed.concat(data.map {
| file_data | file_data.filename
})
end
rescue Octokit::Error => error
if error.response
puts "Error! Status: #{error.response.status}. Message: #{error.response.data.message}"
end
puts error
end
Gestion des codes dâerreur prĂ©vus
Parfois, GitHub utilise un code dâĂ©tat 4xx pour indiquer une rĂ©ponse de non-erreur. Si le point de terminaison que vous utilisez le fait, vous pouvez ajouter une gestion supplĂ©mentaire pour des erreurs spĂ©cifiques. Par exemple, le point de terminaison GET /user/starred/{owner}/{repo}
retourne une erreur 404
si le dĂ©pĂŽt nâa pas dâĂ©toile. Lâexemple suivant utilise la rĂ©ponse 404
pour indiquer que le rĂ©fĂ©rentiel nâest pas marquĂ© dâĂ©toile, tous les autres codes dâerreur sont traitĂ©s comme des erreurs.
begin octokit.request("GET /user/starred/{owner}/{repo}", owner: "github", repo: "docs") puts "The repository is starred by me" rescue Octokit::NotFound => error puts "The repository is not starred by me" rescue Octokit::Error => error puts "An error occurred while checking if the repository is starred: #{error&.response&.data&.message}" end
begin
octokit.request("GET /user/starred/{owner}/{repo}", owner: "github", repo: "docs")
puts "The repository is starred by me"
rescue Octokit::NotFound => error
puts "The repository is not starred by me"
rescue Octokit::Error => error
puts "An error occurred while checking if the repository is starred: #{error&.response&.data&.message}"
end
Gestion des erreurs de limite de débit
Si vous recevez une erreur de limite de débit, vous pouvez retenter votre demande aprÚs avoir attendu. Quand votre taux est limité, GitHub répond avec une erreur 403 Forbidden
et la valeur dâen-tĂȘte de rĂ©ponse x-ratelimit-remaining
sera "0"
. Les en-tĂȘtes de rĂ©ponse incluent un en-tĂȘte x-ratelimit-reset
, qui vous indique lâheure Ă laquelle la fenĂȘtre de limite de dĂ©bit actuelle est rĂ©initialisĂ©e, en secondes dâĂ©poque UTC. Vous pouvez retenter votre demande aprĂšs lâheure spĂ©cifiĂ©e par x-ratelimit-reset
.
def request_retry(route, parameters) begin response = octokit.request(route, parameters) return response rescue Octokit::RateLimitExceeded => error reset_time_epoch_seconds = error.response.headers['x-ratelimit-reset'].to_i current_time_epoch_seconds = Time.now.to_i seconds_to_wait = reset_time_epoch_seconds - current_time_epoch_seconds puts "You have exceeded your rate limit. Retrying in #{seconds_to_wait} seconds." sleep(seconds_to_wait) retry rescue Octokit::Error => error puts error end end response = request_retry("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 2)
def request_retry(route, parameters)
begin
response = octokit.request(route, parameters)
return response
rescue Octokit::RateLimitExceeded => error
reset_time_epoch_seconds = error.response.headers['x-ratelimit-reset'].to_i
current_time_epoch_seconds = Time.now.to_i
seconds_to_wait = reset_time_epoch_seconds - current_time_epoch_seconds
puts "You have exceeded your rate limit. Retrying in #{seconds_to_wait} seconds."
sleep(seconds_to_wait)
retry
rescue Octokit::Error => error
puts error
end
end
response = request_retry("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 2)
Utilisation de la réponse
La méthode request
retourne un objet de rĂ©ponse si la demande a rĂ©ussi. Lâobjet de rĂ©ponse contient data
(le corps de la réponse retourné par le point de terminaison), status
(le code de réponse HTTP), url
(lâURL de la requĂȘte) et headers
(un code de hachage contenant les en-tĂȘtes de rĂ©ponse). Sauf indication contraire, le corps de rĂ©ponse est au format JSON. Certains points de terminaison ne retournent pas de corps de rĂ©ponse, auquel cas, la propriĂ©tĂ© data
est omise.
response = octokit.request("GET /repos/{owner}/{repo}/issues/{issue_number}", owner: "github", repo: "docs", issue_number: 11901) puts "The status of the response is: #{response.status}" puts "The request URL was: #{response.url}" puts "The x-ratelimit-remaining response header is: #{response.headers['x-ratelimit-remaining']}" puts "The issue title is: #{response.data['title']}"
response = octokit.request("GET /repos/{owner}/{repo}/issues/{issue_number}", owner: "github", repo: "docs", issue_number: 11901)
puts "The status of the response is: #{response.status}"
puts "The request URL was: #{response.url}"
puts "The x-ratelimit-remaining response header is: #{response.headers['x-ratelimit-remaining']}"
puts "The issue title is: #{response.data['title']}"
De mĂȘme, la mĂ©thode paginate
retourne un objet de réponse. Si request
a rĂ©ussi, lâobjet response
contient des donnĂ©es, un Ă©tat, une URL et des en-tĂȘtes.
response = octokit.paginate("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100) puts "#{response.data.length} issues were returned" puts "The title of the first issue is: #{response.data[0]['title']}"
response = octokit.paginate("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100)
puts "#{response.data.length} issues were returned"
puts "The title of the first issue is: #{response.data[0]['title']}"
Exemple de script
Voici un exemple de script complet qui utilise Octokit.rb. Le script importe Octokit
et crée une nouvelle instance Octokit
. Si vous souhaitez vous authentifier avec une GitHub App au lieu dâun personal access token, vous devez importer et instancier App
Ă la place de Octokit
. Pour plus dâinformations, consultez Authentification avec une GitHub App dans ce guide.
La fonction get_changed_files
obtient tous les fichiers modifiés pour une demande de tirage. La fonction comment_if_data_files_changed
appelle la fonction get_changed_files
. Si lâun des fichiers modifiĂ©s par la demande de tirage inclut /data/
dans le chemin, la fonction commente la demande de tirage.
require "octokit" octokit = Octokit::Client.new(access_token: "YOUR-TOKEN") def get_changed_files(octokit, owner, repo, pull_number) files_changed = [] begin iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/pulls/{pull_number}/files", owner: owner, repo: repo, pull_number: pull_number, per_page: 100) iterator.each do | data | files_changed.concat(data.map { | file_data | file_data.filename }) end rescue Octokit::Error => error if error.response puts "Error! Status: #{error.response.status}. Message: #{error.response.data.message}" end puts error end files_changed end def comment_if_data_files_changed(octokit, owner, repo, pull_number) changed_files = get_changed_files(octokit, owner, repo, pull_number) if changed_files.any ? { | file_name | /\/data\//i.match ? (file_name) } begin comment = octokit.create_pull_request_review_comment(owner, repo, pull_number, "It looks like you changed a data file. These files are auto-generated. \n\nYou must revert any changes to data files before your pull request will be reviewed.") comment.html_url rescue Octokit::Error => error if error.response puts "Error! Status: #{error.response.status}. Message: #{error.response.data.message}" end puts error end end end # Example usage owner = "github" repo = "docs" pull_number = 22809 comment_url = comment_if_data_files_changed(octokit, owner, repo, pull_number) puts "A comment was added to the pull request: #{comment_url}"
require "octokit"
octokit = Octokit::Client.new(access_token: "YOUR-TOKEN")
def get_changed_files(octokit, owner, repo, pull_number)
files_changed = []
begin
iterator = octokit.paginate.iterator("GET /repos/{owner}/{repo}/pulls/{pull_number}/files", owner: owner, repo: repo, pull_number: pull_number, per_page: 100)
iterator.each do | data |
files_changed.concat(data.map {
| file_data | file_data.filename
})
end
rescue Octokit::Error => error
if error.response
puts "Error! Status: #{error.response.status}. Message: #{error.response.data.message}"
end
puts error
end
files_changed
end
def comment_if_data_files_changed(octokit, owner, repo, pull_number)
changed_files = get_changed_files(octokit, owner, repo, pull_number)
if changed_files.any ? {
| file_name | /\/data\//i.match ? (file_name)
}
begin
comment = octokit.create_pull_request_review_comment(owner, repo, pull_number, "It looks like you changed a data file. These files are auto-generated. \n\nYou must revert any changes to data files before your pull request will be reviewed.")
comment.html_url
rescue Octokit::Error => error
if error.response
puts "Error! Status: #{error.response.status}. Message: #{error.response.data.message}"
end
puts error
end
end
end
# Example usage
owner = "github"
repo = "docs"
pull_number = 22809
comment_url = comment_if_data_files_changed(octokit, owner, repo, pull_number)
puts "A comment was added to the pull request: #{comment_url}"
Remarque
Il sâagit simplement dâun exemple de base. Dans la pratique, vous pouvez utiliser la gestion des erreurs et les examens conditionnelles pour gĂ©rer diffĂ©rents scĂ©narios.
Ătapes suivantes
Pour en savoir plus sur lâutilisation de lâAPI REST GitHub et Octokit.rb, explorez les ressources suivantes :
- Pour en savoir plus sur Octokit.rb, consultez la documentation Octokit.rb.
- Pour obtenir des informations dĂ©taillĂ©es sur les points de terminaison dâAPI REST disponibles de GitHub, y compris leurs structures de requĂȘte et de rĂ©ponse, consultez Documentation sur lâAPI REST GitHub.