Skip to main content

Scripting con la API de REST y Ruby

Aprende cómo escribir un script mediante el SDK de Octokit.rb para interactuar con la API de REST.

Acerca de Octokit.rb

Si quieres escribir un script mediante Ruby para interactuar con la API de REST de GitHub, GitHub recomienda usar el SDK de Octokit.rb. GitHub mantiene Octokit.rb. El SDK implementa los procedimientos recomendados y facilita la interacción con la API de REST a través de Ruby. Octokit.rb funciona con todos los navegadores modernos, Node.js y Deno. Para más información sobre Octokit.rb, consulta el archivo README de Octokit.rb.

Requisitos previos

En esta guía se supone que sabes usar Ruby y la API de REST GitHub. Para obtener más información sobre la API de REST, consulta "Introducción a la API REST".

Debes instalar e importar la gema octokit para usar la biblioteca de Octokit.rb. En esta guía se usan instrucciones de importación de acuerdo con Ruby. Para obtener más información sobre los diferentes métodos de instalación, consulta la sección Instalación del archivo README de Octokit.rb.

Creación de instancias y autenticación

Advertencia: Trata tus credenciales de autenticación como una contraseña.

Para proteger tus credenciales, puedes almacenarlas como secreto y ejecutar el script a través de GitHub Actions. Para obtener más información, vea «Uso de secretos en Acciones de GitHub».

También puedes almacenar tus credenciales como un secreto de Codespaces y ejecutar el script en Codespaces. Para más información, consulta "Administración de secretos específicos de la cuenta para GitHub Codespaces".

Si estas opciones no son posibles, considera el uso de otro servicio de CLI para almacenar tus credenciales de forma segura.

Autenticación con un personal access token

Si deseas usar la API de REST de GitHub para uso personal, puedes crear un personal access token. Para obtener más información sobre la creación de un personal access token, consulte "Administración de tokens de acceso personal".

En primer lugar, debes requerir la biblioteca octokit. A continuación, pasa tu personal access token como la opción access_token al crear una instancia de Octokit. En el ejemplo siguiente, reemplaza YOUR-TOKEN por tu personal access token.

Ruby
require 'octokit'

octokit = Octokit::Client.new(access_token: 'YOUR-TOKEN')

Autenticación con una GitHub App

Si deseas usar la API en nombre de una organización u otro usuario, GitHub recomienda usar un GitHub App. Si un punto de conexión está disponible para GitHub Apps, en la documentación de referencia de REST para ese punto de conexión se indicará que tipo de token de GitHub App se requiere. Para obtener más información, vea «Registro de una instancia de GitHub App» y «Acerca de la autenticación con una aplicación de GitHub».

En lugar de requerir octokit, crea una instancia de Octokit::Client al transferir la información de tu GitHub App como opciones. En el ejemplo siguiente, reemplaza APP_ID por el identificador de la aplicación, PRIVATE_KEY por la clave privada de la aplicación y INSTALLATION_ID por el ID de instalación de la aplicación en nombre de la cual quieras autenticarte. Puedes encontrar el identificador de la aplicación y generar una clave privada en la página de configuración de la aplicación. Para obtener más información, vea «Administración de claves privadas para aplicaciones de GitHub». Puedes obtener un identificador de instalación con GET /users/{username}/installation, GET /repos/{owner}/{repo}/installation o los puntos de conexión de GET /orgs/{org}/installation. Para más información, consulta "Aplicaciones de GitHub" en la documentación de referencia de REST.

Ruby
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)

Autenticación en GitHub Actions

Si deseas usar la API en un flujo de trabajo de GitHub Actions, GitHub recomienda autenticarse con el GITHUB_TOKEN integrado en lugar de crear un token. Puedes conceder permisos a GITHUB_TOKEN con la clave permissions. Para obtener más información sobre GITHUB_TOKEN, consulta "Autenticación automática de tokens".

Si el flujo de trabajo necesita acceder a los recursos fuera del repositorio del flujo de trabajo, no podrás usar GITHUB_TOKEN. En ese caso, almacena tus credenciales como secreto y reemplaza GITHUB_TOKEN en el ejemplo siguiente por el nombre del secreto. Para obtener más información sobre secretos, consulte "Uso de secretos en Acciones de GitHub".

Si usas la palabra clave run para ejecutar el script de Ruby en tus flujos de trabajo de GitHub Actions, puedes almacenar el valor de GITHUB_TOKEN como una variable de entorno. El script puede acceder a la variable de entorno como ENV['VARIABLE_NAME'].

Por ejemplo, este paso de flujo de trabajo almacena GITHUB_TOKEN en una variable de entorno denominada TOKEN:

- name: Run script
  env:
    TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    ruby .github/actions-scripts/use-the-api.rb

El script que el flujo de trabajo ejecuta usa ENV['TOKEN'] para autenticarse:

Ruby
require 'octokit'

octokit = Octokit::Client.new(access_token: ENV['TOKEN'])

Creación de instancias sin autenticación

Puedes usar la API de REST sin autenticación, aunque tendrás un límite de velocidad inferior y no podrás usar algunos puntos de conexión. Para crear una instancia de Octokit sin autenticar, no pases la opción access_token.

Ruby
require 'octokit'

octokit = Octokit::Client.new

Realización de solicitudes

Octokit admite varias formas de realizar solicitudes. Puedes usar el método request para realizar solicitudes si conoces el verbo HTTP y la ruta de acceso del punto de conexión. Puedes usar el método rest si quieres aprovechar la función de autocompletar en el IDE y escribir. En el caso de los puntos de conexión paginados, puedes usar el método paginate para solicitar varias páginas de datos.

Uso del método request para realizar solicitudes

Para usar el método request para realizar solicitudes, pasa el método HTTP y la ruta de acceso como primer argumento. Pasa cualquier parámetro de cuerpo, consulta o ruta en un hash como segundo argumento. Por ejemplo, para realizar una solicitud GET a /repos/{owner}/{repo}/issues y pasar los parámetros owner, repo y per_page:

Ruby
octokit.request("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 2)

El método request pasa automáticamente el encabezado Accept: application/vnd.github+json. Para pasar encabezados adicionales o un encabezado Accept diferente, añade una opción headers al hash que se pasa como segundo argumento. El valor de la opción headers es un hash con los nombres de encabezado como claves y valores de encabezado como valores. Por ejemplo, para enviar un encabezado content-type con un valor de text/plain:

Ruby
octokit.request("POST /markdown/raw", text: "Hello **world**", headers: { "content-type" => "text/plain" })

Uso de métodos de punto de conexión rest para realizar solicitudes

Cada punto de conexión de la API de REST tiene un método de punto de conexión asociado rest en Octokit. Por lo general, estos métodos se autocompletan en el IDE para mayor comodidad. Puedes pasar cualquier parámetro como un hash al método.

Ruby
octokit.rest.issues.list_for_repo(owner: "github", repo: "docs", per_page: 2)

Realización de solicitudes paginadas

Si el punto de conexión está paginado y quieres capturar más de una página de resultados, puedes usar el método paginate. paginate capturará la siguiente página de resultados hasta llegar a la última página y, a continuación, devolverá todos los resultados como una matriz. Algunos puntos de conexión devuelven resultados paginados como una matriz en un objeto, en lugar de devolverlos como una matriz. paginate siempre devuelve una matriz de elementos, aun cuando el resultado sin procesar sea un objeto.

Por ejemplo, el siguiente ejemplo obtiene todas las incidencias del repositorio github/docs. Aunque se solicitan 100 incidencias a la vez, la función no regresará hasta que se alcance la última página de datos.

Ruby
issue_data = octokit.paginate("GET /repos/{owner}/{repo}/issues", owner: "github", repo: "docs", per_page: 100)

El método paginate acepta un bloque opcional, que puedes usar para procesar cada página de resultados. Esto te permite recopilar únicamente los datos que deseas de la respuesta. Por ejemplo, el ejemplo siguiente continúa capturando resultados hasta que se devuelve un problema que incluye "test" en el título. Para las páginas de datos que se han devuelto, solo se almacenan el título del problema y el autor.

Ruby
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

En lugar de capturar todos los resultados a la vez, puedes usar octokit.paginate.iterator() para recorrer en iteración una sola página a la vez. Por ejemplo, en el ejemplo siguiente se captura una página de resultados a la vez y se procesa cada objeto de la página antes de capturar la página siguiente. Una vez que se alcanza un problema que incluye "prueba" en el título, el script detiene la iteración y devuelve el título del problema y el autor del problema de cada objeto que se procesó. El iterador es el método más eficiente en memoria para obtener datos paginados.

Ruby
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

También puedes usar el método paginate con los métodos de punto de conexión rest. Pasa el método de punto de conexión rest como primer argumento y cualquier parámetro como segundo argumento.

Ruby
iterator = octokit.paginate.iterator(octokit.rest.issues.list_for_repo, owner: "github", repo: "docs", per_page: 100)

Para obtener más información sobre la paginación, consulta "Uso de la paginación en la API de REST".

Almacenamiento en caché de los errores

Detección de todos los errores

A veces, la API de REST de GitHub devolverá un error. Por ejemplo, recibirás un error si el token de acceso ha expirado o si has omitido un parámetro necesario. Octokit.rb reintenta automáticamente la solicitud cuando obtiene un error distinto de 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found y 422 Unprocessable Entity. Si se produce un error de API incluso después de varios reintentos, Octokit.rb genera un error que incluye el código de estado HTTP de la respuesta (response.status) y los encabezados de respuesta (response.headers). Debes controlar estos errores en el código. Por ejemplo, puedes usar un bloque try/catch para detectar errores:

Ruby
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

Control de los códigos de error previstos

A veces, los datos GitHub usan un código de estado 4xx para indicar una respuesta que no es de error. Si el punto de conexión que usa lo hace, puedes agregar control adicional para errores específicos. Por ejemplo, el punto de conexión GET /user/starred/{owner}/{repo} devolverá un valor 404 si el repositorio no está destacado. En el ejemplo siguiente se usa la respuesta 404 para indicar que el repositorio no se ha destacado; todos los demás códigos de error se tratan como errores.

Ruby
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

Control de errores de límite de frecuencia

Si recibes un error de límite de frecuencia, es posible que quieras volver a realizar la solicitud después de esperar. Cuando se limita la velocidad, GitHub responde con un error 403 Forbidden y el valor del encabezado de respuesta x-ratelimit-remaining será "0". Los encabezados de respuesta incluirán un encabezado x-ratelimit-reset, que indica la hora a la que se restablece la ventana de límite de velocidad actual, en segundos de época UTC. Puedes volver a intentar realizar tu solicitud después del tiempo especificado por x-ratelimit-reset.

Ruby
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)

Análisis de la respuesta

El método request devuelve un objeto de respuesta si la solicitud se ha realizado correctamente. El objeto de respuesta contiene data (el cuerpo de la respuesta devuelto por el punto de conexión), status (el código de respuesta HTTP), url (la dirección URL de la solicitud) y headers (un hash que contiene los encabezados de respuesta). A menos que se especifique lo contrario, el cuerpo de la respuesta está en formato JSON. Algunos puntos de conexión no devuelven un cuerpo de respuesta; en esos casos, se omite la propiedad data.

Ruby
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']}"

Del mismo modo, el método paginate devuelve un objeto de respuesta. Si la request se realizó correctamente, el objeto response contendrá datos, estado, dirección URL y encabezados.

Ruby
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 ejemplo

Este es un script de ejemplo completo que usa Octokit.rb. El script importa Octokit y crea una nueva instancia de Octokit. Si quieres autenticarte con una GitHub App en lugar de con un personal access token, tendrás que crear e importar instancias de App en lugar de Octokit. Para más información, consulta «Autenticación con una GitHub App» en esta guía.

La función get_changed_files obtiene todos los archivos modificados para una solicitud de incorporación de cambios. La función comment_if_data_files_changed llama a la función get_changed_files. Si alguno de los archivos que cambió la solicitud de incorporación de cambios incluye /data/ en la ruta de acceso del archivo, la función comentará la solicitud de incorporación de cambios.

Ruby
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}"

Nota: Este es solo un ejemplo básico. En la práctica, es posible que desees usar el control de errores y las comprobaciones condicionales para controlar varios escenarios.

Pasos siguientes

Para obtener más información sobre cómo trabajar con la API de REST de GitHub y Octokit.rb, consulta los siguientes recursos: