О Octokit.rb
Если вы хотите написать скрипт с помощью Ruby для взаимодействия с REST API GitHub, GitHub рекомендует использовать пакет SDK octokit.rb. Octokit.rb поддерживается GitHub. Пакет SDK реализует рекомендации и упрощает взаимодействие с REST API через Ruby. Octokit.rb работает со всеми современными браузерами, Node.rb и Deno. Дополнительные сведения о Octokit.rb см . в разделе Octokit.rb README.
Необходимые компоненты
В этом руководстве предполагается, что вы знакомы с Ruby и REST API GitHub. Дополнительные сведения о REST API см. в разделе Начало работы с REST API.
Чтобы использовать библиотеку Octokit.rb, необходимо установить и импортировать драгоценный octokit
камень. В этом руководстве используются инструкции импорта в соответствии с соглашениями Ruby. Дополнительные сведения о различных методах установки см . в разделе установки Octokit.rb README.
Создание экземпляров и проверка подлинности
Warning
Обработайте учетные данные проверки подлинности как пароль.
Чтобы обеспечить безопасность учетных данных, вы можете хранить свои учетные данные в виде секрета и запускать скрипт с помощью GitHub Actions. Дополнительные сведения см. в разделе Использование секретов в GitHub Actions.
Вы также можете хранить свои учетные данные в виде секрета Codespaces и запустить скрипт в Codespaces. Дополнительные сведения см. в разделе Управление секретами конкретной учетной записи для GitHub Codespaces.
Если это невозможно, попробуйте использовать другую службу CLI для безопасного хранения учетных данных.
Проверка подлинности с помощью personal access token
Если вы хотите использовать REST API GitHub для личного использования, можно создать personal access token. Дополнительные сведения о создании personal access tokenсм. в разделе Управление личными маркерами доступа.
Сначала требуется octokit
библиотека. Затем создайте экземпляр Octokit
, передав personal access token в качестве access_token
параметра. В следующем примере замените YOUR-TOKEN
данные personal access token.
require 'octokit' octokit = Octokit::Client.new(access_token: 'YOUR-TOKEN')
require 'octokit'
octokit = Octokit::Client.new(access_token: 'YOUR-TOKEN')
Проверка подлинности с помощью GitHub App
Если вы хотите использовать API от имени организации или другого пользователя, GitHub рекомендует использовать GitHub App. Если конечная точка доступна для GitHub Apps, справочная документация REST для этой конечной точки будет указывать тип маркера GitHub App. Дополнительные сведения см. в разделе [AUTOTITLE и Регистрация приложения GitHub](/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app).
Вместо необходимости octokit
создайте экземпляр Octokit::Client
, передав данные GitHub Appв качестве параметров. В следующем примере замените APP_ID
идентификатором приложения, PRIVATE_KEY
закрытым ключом приложения и INSTALLATION_ID
идентификатором установки приложения, от имени которого требуется пройти проверку подлинности. Идентификатор приложения можно найти и создать закрытый ключ на странице параметров приложения. Дополнительные сведения см. в разделе Управление закрытыми ключами для приложений GitHub. Идентификатор установки можно получить с GET /users/{username}/installation
помощью конечных точек или GET /orgs/{org}/installation
конечных GET /repos/{owner}/{repo}/installation
точек. Дополнительные сведения см. в разделе Конечные точки REST API для GitHub Apps.{ %ifversion ghes %} Замените HOSTNAME
именем GitHub.com.{ % endif %}
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)
Проверка подлинности в GitHub Actions
Если вы хотите использовать API в рабочем процессе GitHub Actions, GitHub рекомендует выполнять проверку подлинности с помощью встроенного GITHUB_TOKEN
вместо создания маркера. Вы можете предоставить разрешения для GITHUB_TOKEN
с помощью ключа permissions
. Дополнительные сведения см. в GITHUB_TOKEN
разделе Автоматическая проверка подлинности токенов.
Если рабочий процесс должен получить доступ к ресурсам за пределами репозитория рабочего процесса, вы не сможете использовать GITHUB_TOKEN
. В этом случае сохраните свои учетные данные в виде секрета и замените GITHUB_TOKEN
в приведенных ниже примерах именем секрета. Дополнительные сведения о секретах см. в разделе Использование секретов в GitHub Actions.
Если вы используете ключевое run
слово для выполнения скрипта Ruby в рабочих процессах GitHub Actions, можно сохранить значение GITHUB_TOKEN
в виде переменной среды. Скрипт может получить доступ к переменной среды как ENV['VARIABLE_NAME']
.
Например, этот шаг рабочего процесса сохраняется GITHUB_TOKEN
в переменной среды:TOKEN
- name: Run script
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ruby .github/actions-scripts/use-the-api.rb
Скрипт, который ENV['TOKEN']
выполняется рабочим процессом для проверки подлинности:
require 'octokit' octokit = Octokit::Client.new(access_token: ENV['TOKEN'])
require 'octokit'
octokit = Octokit::Client.new(access_token: ENV['TOKEN'])
Создание экземпляров без проверки подлинности
REST API можно использовать без проверки подлинности, хотя у вас будет более низкий предел скорости и не удастся использовать некоторые конечные точки. Чтобы создать экземпляр без проверки подлинности Octokit
, не передайте access_token
этот параметр.
require 'octokit' octokit = Octokit::Client.new
require 'octokit'
octokit = Octokit::Client.new
Выполнение запросов
Octokit поддерживает несколько способов выполнения запросов. Метод можно использовать request
для выполнения запросов, если вы знаете HTTP-команду и путь к конечной точке. Этот метод можно использовать, если вы хотите воспользоваться rest
преимуществами автозаполнения в интегрированной среде разработки и вводе. Для конечных точек с разбивкой на страницы можно использовать paginate
метод для запроса нескольких страниц данных.
request
Использование метода для выполнения запросов
Чтобы использовать метод для выполнения запросов, передайте request
метод HTTP и путь в качестве первого аргумента. Передайте все параметры текста, запроса или пути в хэш в качестве второго аргумента. Например, чтобы выполнить GET
запрос к /repos/{owner}/{repo}/issues
и передать параметрыrepo``owner
, и 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)
Метод request
автоматически передает Accept: application/vnd.github+json
заголовок. Чтобы передать дополнительные заголовки или другой Accept
заголовок, добавьте headers
параметр в хэш, передаваемый в качестве второго аргумента. Значение headers
параметра — хэш с именами заголовков в качестве ключей и значений заголовков в качестве значений. Например, чтобы отправить заголовок content-type
со значением 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" })
Использование rest
методов конечной точки для выполнения запросов
Каждая конечная точка REST API имеет связанный rest
метод конечной точки в Octokit. Эти методы обычно автоматически заполняются в интегрированной среде разработки для удобства. В метод можно передать любые параметры в виде хэша.
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)
Выполнение запросов с разбивкой на страницы
Если конечная точка разбина на страницы и вы хотите получить несколько страниц результатов, можно использовать paginate
этот метод. paginate
Возвращает следующую страницу результатов, пока не достигнет последней страницы, а затем возвращает все результаты в виде массива. Несколько конечных точек возвращают результаты с разбивкой на страницы в виде массива в объекте, а не возвращать результаты с разбивкой на страницы в виде массива. paginate
всегда возвращает массив элементов, даже если необработанный результат был объектом.
Например, следующий пример получает все проблемы из github/docs
репозитория. Хотя она запрашивает 100 проблем за раз, функция не возвращается до достижения последней страницы данных.
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)
Метод paginate
принимает необязательный блок, который можно использовать для обработки каждой страницы результатов. Это позволяет собирать только нужные данные из ответа. Например, следующий пример продолжает получение результатов до тех пор, пока не будет возвращена проблема, содержащая "test" в заголовке. Для страниц возвращаемых данных хранятся только название проблемы и автор.
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
Вместо одновременного получения всех результатов можно использовать octokit.paginate.iterator()
для итерации по одной странице за раз. Например, следующий пример извлекает одну страницу результатов за раз и обрабатывает каждый объект из страницы перед получением следующей страницы. После достижения проблемы, включающей "test" в заголовок, скрипт останавливает итерацию и возвращает заголовок проблемы и автор проблемы каждого обработанного объекта. Итератор — это наиболее эффективный метод для получения данных с разбивкой на страницы.
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
Этот paginate
метод также можно использовать с методами конечной rest
точки. Передайте метод конечной точки в качестве первого аргумента rest
и любые параметры в качестве второго аргумента.
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)
Дополнительные сведения о разбиении на страницы см. в разделе Использование разбиения на страницы в REST API.
выявления ошибок;
Перехват всех ошибок
Иногда REST API GitHub возвращает ошибку. Например, вы получите ошибку, если срок действия маркера доступа истек или если не указан обязательный параметр. Octokit.rb автоматически повторяет запрос при получении ошибки, отличной от 400 Bad Request
, 401 Unauthorized
, и 403 Forbidden``404 Not Found
.422 Unprocessable Entity
Если ошибка API возникает даже после повторных попыток, Octokit.rb выдает ошибку, содержащую код состояния HTTP ответа (response.status
) и заголовки ответа (response.headers
). Эти ошибки следует обрабатывать в коде. Например, для перехвата ошибок можно использовать блок try/catch:
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
Обработка предполагаемых кодов ошибок
Иногда GitHub использует код состояния 4xx для указания ответа без ошибок. Если используется эта конечная точка, можно добавить дополнительную обработку для определенных ошибок. Например, конечная GET /user/starred/{owner}/{repo}
точка вернет объект, 404
если репозиторий не указан. В следующем примере используется ответ, указывающий, что репозиторий 404
не был указан в главной роли. Все остальные коды ошибок рассматриваются как ошибки.
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
Обработка ошибок ограничения скорости
Если вы получаете ошибку ограничения скорости, вы можете повторить запрос после ожидания. Если скорость ограничена, GitHub отвечает ошибкой403 Forbidden
, а x-ratelimit-remaining
значение заголовка ответа будет."0"
Заголовки ответа будут содержать x-ratelimit-reset
заголовок, который указывает время сброса текущего ограничения скорости в секундах эпохи UTC. После указанного 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)
Использование ответа
Метод request
возвращает объект ответа, если запрос выполнен успешно. Объект ответа содержит data
(текст ответа, возвращаемый конечной точкой), status
(код ответа HTTP), url
(URL-адрес запроса) и headers
(хэш, содержащий заголовки ответа). Если не указано иное, текст ответа имеет формат JSON. Некоторые конечные точки не возвращают текст ответа; В этих случаях data
свойство опущено.
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']}"
Аналогичным образом paginate
метод возвращает объект ответа. В случае успешного request
выполнения response
объект содержит данные, состояние, URL-адрес и заголовки.
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']}"
Пример сценария
Ниже приведен полный пример скрипта, использующего Octokit.rb. Скрипт импортирует Octokit
и создает новый экземпляр Octokit
. Если вы хотите выполнить проверку подлинности с помощью GitHub App, а не personal access token, вы импортируете и создайте экземпляр App
вместо Octokit
него. Дополнительные сведения см. в руководстве по проверке подлинности с помощью GitHub App в этом руководстве.
Функция get_changed_files
получает все файлы, измененные для запроса на вытягивание. Функция comment_if_data_files_changed
вызывает функцию get_changed_files
. Если любой из файлов, измененных запросом на вытягивание, включен /data/
в путь к файлу, функция будет комментировать запрос на вытягивание.
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}"
Note
Это просто базовый пример. На практике может потребоваться использовать обработку ошибок и условные проверки для обработки различных сценариев.
Следующие шаги
Дополнительные сведения о работе с API REST API GitHub и Octokit.rb см. в следующих ресурсах:
- Дополнительные сведения о Octokit.rb см . в документации по Octokit.rb.
- Подробные сведения о доступных конечных точках REST API % variables.product.company_short %}, включая структуры запросов и ответов, см. в разделе AUTOTITLE.