Sobre o Octokit.rb
Se quiser escrever um script usando o Ruby para interagir com a API REST do GitHub, a GitHub recomenda o uso do SDK Octokit.rb. O Octokit.rb é mantido pela GitHub. O SDK implementa as melhores práticas e facilita a interação com a API REST por meio do Ruby. O Octokit.rb funciona com todos os navegadores modernos, Node.rb e Deno. Para obter mais informações sobre o Octokit.rb, confira o arquivo LEIAME do Octokit.rb.
Pré-requisitos
Este guia pressupõe que você esteja familiarizado com o Ruby e a API REST da GitHub. Para obter informações sobre a API REST, confira "Introdução à API REST".
Você deve instalar e importar a joia octokit
para usar a biblioteca Octokit.rb. Este guia usa instruções de importação de acordo com as convenções Ruby. Para obter mais informações sobre os diferentes métodos de instalação, consulte a seção Instalação do LEIAME do Octokit.rb.
Instanciação e autenticação
Aviso: trate suas credenciais de autenticação como uma senha.
Para manter suas credenciais seguras, você pode armazená-las como um segredo e executar seu script por meio de GitHub Actions. Para obter mais informações, confira "Usar segredos em ações do GitHub".
Se isso não for possível, considere usar outro serviço CLI para armazenar suas credenciais com segurança.
Autenticar com um personal access token
Se quiser usar a API REST do GitHub para uso pessoal, crie um personal access token. Para obter mais informações sobre como criar um personal access token, confira "Gerenciar seus tokens de acesso pessoal".
Primeiro, exija a biblioteca octokit
. Em seguida, crie uma instância de Octokit
, passando seu personal access token como a opção access_token
. No exemplo a seguir, substitua YOUR-TOKEN
pelo seu personal access token.
require 'octokit' octokit = Octokit::Client.new(access_token: 'YOUR-TOKEN')
require 'octokit'
octokit = Octokit::Client.new(access_token: 'YOUR-TOKEN')
Autenticação com um GitHub App
Se você quiser usar a API em nome de uma organização ou de outro usuário, a GitHub recomenda que você use um GitHub App. Se um ponto de extremidade estiver disponível para GitHub Apps, a documentação de referência da API REST para esse ponto de extremidade indicará que tipo de token do GitHub App é necessário. Para obter mais informações, confira "Registrar um Aplicativo GitHub" e "Sobre a autenticação com um GitHub App."
Em vez de exigir octokit
, crie uma instância de Octokit::Client
, passando as informações do GitHub App como opções. No exemplo a seguir, substitua APP_ID
pelo ID do seu aplicativo, PRIVATE_KEY
pela chave privada do seu aplicativo e INSTALLATION_ID
pelo ID da instalação do seu aplicativo em nome do qual você deseja autenticar. Você pode encontrar a ID do seu aplicativo e gerar uma chave privada na página de configurações do aplicativo. Para obter mais informações, confira "Como gerenciar chaves privadas para Aplicativos GitHub". Você pode obter uma ID de instalação com os pontos de extremidade GET /users/{username}/installation
, GET /repos/{owner}/{repo}/installation
ou GET /orgs/{org}/installation
. Para obter mais informações, consulte "Pontos de extremidade da API REST para o GitHub Apps." Substitua HOSTNAME
pelo nome de sua instância do GitHub Enterprise Server.
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)
Autenticação em GitHub Actions
Se você quiser usar a API em um fluxo de trabalho de GitHub Actions, a GitHub recomenda que você se autentique com o GITHUB_TOKEN
interno, em vez de criar um token. Você pode conceder permissões à GITHUB_TOKEN
com a chave permissions
. Para obter mais informações sobre GITHUB_TOKEN
, confira "Autenticação automática de token".
Se o fluxo de trabalho precisar acessar recursos fora do repositório dele, então você não poderá usar GITHUB_TOKEN
. Nesse caso, armazene suas credenciais como um segredo e substitua GITHUB_TOKEN
nos exemplos abaixo pelo nome do segredo. Para obter mais informações sobre segredos, confira "Usar segredos em ações do GitHub".
Se usar a palavra-chave run
para executar o script do Ruby em seus fluxos de trabalho de GitHub Actions, você poderá armazenar o valor de GITHUB_TOKEN
como uma variável de ambiente. Seu script pode acessar a variável de ambiente como ENV['VARIABLE_NAME']
.
Por exemplo, essa etapa do fluxo de trabalho armazena GITHUB_TOKEN
em uma variável de ambiente chamada TOKEN
:
- name: Run script
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ruby .github/actions-scripts/use-the-api.rb
O script que o fluxo de trabalho executa usa ENV['TOKEN']
para se autenticar:
require 'octokit' octokit = Octokit::Client.new(access_token: ENV['TOKEN'])
require 'octokit'
octokit = Octokit::Client.new(access_token: ENV['TOKEN'])
Instanciação sem autenticação
Você pode usar a API REST sem autenticação, embora isso forneça um limite de taxa mais baixo e não permita o uso de alguns pontos de extremidade. Para criar uma instância de Octokit
sem autenticação, não passe a opção access_token
.
require 'octokit' octokit = Octokit::Client.new
require 'octokit'
octokit = Octokit::Client.new
Como fazer solicitações
O Octokit dá suporte a várias maneiras de fazer solicitações. Você pode usar o método request
para fazer solicitações se souber o verbo HTTP e o caminho para o ponto de extremidade. Você pode usar o método rest
se quiser aproveitar o preenchimento automático em seu IDE e digitar. Para pontos de extremidade paginados, você pode usar o método paginate
para solicitar várias páginas de dados.
Como usar o método request
para fazer solicitações
Para usar o método request
a fim de fazer solicitações, passe o método HTTP e o caminho como o primeiro argumento. Passe qualquer parâmetro de corpo, consulta ou caminho em um hash como o segundo argumento. Por exemplo, para fazer uma solicitação GET
para /repos/{owner}/{repo}/issues
e passar os parâmetros owner
, repo
e 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)
O método request
passa automaticamente o cabeçalho Accept: application/vnd.github+json
. Para passar cabeçalhos adicionais ou um cabeçalho Accept
diferente, adicione uma opção headers
ao hash que é passado como um segundo argumento. O valor da opção headers
é um hash com os nomes de cabeçalho como chaves e os valores de cabeçalho como valores. Por exemplo, para enviar um cabeçalho content-type
com o valor 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" })
Como usar métodos de ponto de extremidade rest
para fazer solicitações
Cada ponto de extremidade da API REST tem um método de ponto de extremidade rest
associado no Octokit. Esses métodos geralmente são preenchidos automaticamente em seu IDE para conveniência. Você pode passar qualquer parâmetro como um hash para o método.
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)
Como fazer solicitações paginadas
Se o ponto de extremidade for paginado e você quiser buscar mais de uma página de resultados, poderá usar o método paginate
. paginate
buscará a próxima página de resultados até chegar à última página e retornará todos os resultados como uma matriz. Alguns pontos de extremidade retornam resultados paginados como matriz em um objeto, em vez de retornar os resultados paginados como uma matriz. paginate
sempre retorna uma matriz de itens, mesmo que o resultado bruto tenha sido um objeto .
Por exemplo, o caso a seguir obtém todos os problemas do repositório github/docs
. Embora solicite 100 solicitações por vez, a função não retornará até que a última página de dados seja atingida.
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)
O método paginate
aceita um bloco opcional, que você pode usar para processar cada página de resultados. Isso permite coletar apenas os dados desejados da resposta. Por exemplo, o caso a seguir segue buscando resultados até que seja retornado um problema com "teste" no título. Para as páginas de dados que foram retornadas, são armazenados apenas o título e o autor do problema.
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
Em vez de buscar todos os resultados de uma só vez, você pode usar octokit.paginate.iterator()
para percorrer uma só página de cada vez. Por exemplo, o caso a seguir busca uma página de resultados por vez e processa cada objeto da página atual antes de buscar a próxima. Uma vez encontrado um problema com "teste" no título, o script interrompe a iteração e retorna o título e o autor do problema de cada objeto que foi processado. O iterador é o método mais eficiente em termos de memória para buscar dados paginados.
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
Você também pode usar o método paginate
com os métodos de ponto de extremidade rest
. Passe o método de ponto de extremidade rest
como o primeiro argumento e quaisquer parâmetros como o segundo argumento.
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)
Para obter mais informações sobre paginação, confira "Como usar paginação na API REST".
Captura de erros
Capturando todos os erros
Às vezes, a API REST da GitHub retorna um erro. Por exemplo, um erro será exibido se o token de acesso tiver expirado ou se um parâmetro obrigatório for omitido. O Octokit.rb faz automaticamente novas tentativas de executar a solicitação quando obtém um erro diferente de 400 Bad Request
, 401 Unauthorized
, 403 Forbidden
, 404 Not Found
ou 422 Unprocessable Entity
. Se ocorrer um erro de API mesmo após novas tentativas, o Octokit.rb gera um erro que inclui o código de status HTTP da resposta (response.status
) e os cabeçalhos da resposta (response.headers
). Você deve tratar esses erros em seu código. Por exemplo, você pode usar um bloco try/catch para capturar erros:
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
Tratamento de códigos de erro previstos
Às vezes, a GitHub usa um código de status 4xx para indicar uma resposta sem erro. Se o ponto de extremidade que você está usando fizer isso, você poderá adicionar tratamentos adicionais para erros específicos. Por exemplo, o ponto de extremidade GET /user/starred/{owner}/{repo}
retornará 404
se o repositório não for estrelado. O exemplo a seguir usa a resposta 404
para indicar que o repositório não foi estrelado; todos os demais códigos de erros são tratados como erros.
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
Tratamento de erros de limite de taxa
Se você receber um erro de limite de taxa, talvez seja necessário repetir a solicitação após aguardar um tempo. Quando você tem taxa limitada, a GitHub responde com um erro 403 Forbidden
e o valor do cabeçalho de resposta x-ratelimit-remaining
será "0"
. Os cabeçalhos de resposta incluirão um cabeçalho x-ratelimit-reset
, que informa a hora em que a janela de limite de taxa atual é redefinida, em segundos UTC. Você pode repetir a solicitação após aguardar o tempo especificado por 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)
Como usar a resposta
O método request
retornará um objeto de resposta se a solicitação tiver sido bem-sucedida. O objeto de resposta contém data
(o corpo da resposta retornado pelo ponto de extremidade), status
(o código de resposta HTTP), url
(a URL da solicitação) e headers
(um hash que contém os cabeçalhos da resposta). A menos que especificado de outra forma, o corpo da resposta está no formato JSON. Alguns pontos de extremidade não retornam um corpo de resposta; nesses casos, a propriedade data
é omitida.
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']}"
Da mesma forma, o método paginate
retorna um objeto de resposta. Se o request
for bem-sucedido, o objeto response
conterá dados, status, URL e cabeçalhos.
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']}"
Script de exemplo
Aqui está um script de exemplo completo que usa o Octokit.rb. O script importa Octokit
e cria uma instância de Octokit
. Se você quiser se autenticar com um GitHub App em vez de um personal access token, importe e instancie App
em vez de Octokit
. Para obter mais informações, confira "Como se autenticar com um GitHub App", neste guia.
A função get_changed_files
obtém todos os arquivos alterados para uma solicitação de pull. A função comment_if_data_files_changed
chama a função get_changed_files
. Se qualquer um dos arquivos que a solicitação de pull alterou incluir /data/
no caminho, a função comentará sobre a solicitação de pull.
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}"
Observação: este é apenas um exemplo básico. Na prática, talvez você queira usar tratamento de erros e verificações condicionais para lidar com vários cenários.
Próximas etapas
Para saber mais sobre como trabalhar com a API REST da GitHub e o Octokit.rb, explore os seguintes recursos:
- Para saber mais sobre o Octokit.js, confira a documentação do Octokit.rb.
- Para obter informações detalhadas sobre os pontos de extremidade da API REST disponíveis da GitHub, incluindo suas estruturas de solicitação e resposta, consulte Documentação da API REST do GitHub.