# REST API と Ruby を使用したスクリプト

Octokit.rb SDK を使用して REST API と対話するスクリプトを記述する方法を説明します。

## Octokit.rb について

Ruby を使用して GitHub REST API と対話するスクリプトを記述する場合 GitHub は、Octokit.rb SDK を使用することをお勧めします。 Octokit.rb は、 GitHubによって維持されます。 SDK によってベスト プラクティスが実装されており、Ruby を使用して REST API を簡単に操作できます。 Octokit.rb は、最新のあらゆるブラウザー、Node.rb、Deno で動作します。 Octokit.rb について詳しくは、[Octokit.rb の README](https://github.com/octokit/octokit.rb/#readme) を参照してください。

## 前提条件

このガイドでは、Ruby と GitHub REST API について理解していることを前提としています。 REST API の詳細については、「[REST API を使用した作業の開始](/ja/enterprise-server@3.22/rest/using-the-rest-api/getting-started-with-the-rest-api)」を参照してください。

Octokit.rb ライブラリを使うには、`octokit` gem をインストールしてインポートする必要があります。 このガイドでは、Ruby の規約に従って import ステートメントを使用します。 さまざまなインストールの方法について詳しくは、[Octokit.rb の README の「インストール」セクションを参照してください](https://github.com/octokit/octokit.rb/#installation)。

## インスタンス化と認証

> \[!WARNING]
> 認証の資格情報はパスワードと同じように扱ってください。
>
> 資格情報をセキュリティで保護するために、資格情報をシークレットとして格納し、 GitHub Actionsを使用してスクリプトを実行できます。 詳しくは、「[GitHub Actions でのシークレットの使用](/ja/enterprise-server@3.22/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets)」をご覧ください。

> これらのオプション これは不可能です資格情報を安全に格納するために別の CLI サービスを使用することを検討してください。

### personal access tokenを使用した認証の実行

個人用に GitHub REST API を使用する場合は、 personal access tokenを作成できます。
personal access tokenの作成の詳細については、「[個人用アクセス トークンを管理する](/ja/enterprise-server@3.22/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)」を参照してください。

まず、`octokit` ライブラリが必要です。 次に、`Octokit`を personal access token オプションとして渡して、`access_token`のインスタンスを作成します。 次の例では、 `YOUR-TOKEN` を personal access tokenに置き換えます。

```ruby copy
require 'octokit'

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

### GitHub Appを使用した認証の実行

組織や他のユーザーに代わって API を使用する場合は、GitHubがGitHub Appを使用することを推奨しています。 エンドポイントを GitHub Appsで使用できる場合は、そのエンドポイントの REST リファレンス ドキュメントに、必要な GitHub App トークンの種類が示されます。 詳細については、「[GitHub アプリの登録](/ja/enterprise-server@3.22/apps/creating-github-apps/registering-a-github-app/registering-a-github-app)」および「[GitHub アプリでの認証について](/ja/enterprise-server@3.22/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app)」を参照してください。

`octokit`を必要とするのではなく、`Octokit::Client`の情報をオプションとして渡して、GitHub Appのインスタンスを作成します。 次の例では、`APP_ID` をアプリ IDに、`PRIVATE_KEY` をアプリの秘密キーに、`INSTALLATION_ID` を代わりに認証するアプリのインストールの ID に置き換えます。 アプリの ID を見つけて、アプリの設定ページで秘密キーを生成できます。 詳しくは、「[GitHub アプリの秘密キーの管理](/ja/enterprise-server@3.22/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps)」をご覧ください。 インストール ID は `GET /users/{username}/installation`、`GET /repos/{owner}/{repo}/installation`、または `GET /orgs/{org}/installation` のエンドポイントで取得できます。 詳細については、 [AUTOTITLE を](/ja/enterprise-server@3.22/rest/apps/apps)参照してください。
`HOSTNAME`をお使いの GitHub Enterprise Server インスタンスの名前に置き換えます。

```ruby copy
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 での認証

GitHub Actions ワークフローで API を使用する場合GitHubは、トークンを作成するのではなく、組み込みの`GITHUB_TOKEN`で認証することをお勧めします。
`GITHUB_TOKEN` キーを使用して、`permissions` へのアクセス許可を付与できます。
`GITHUB_TOKEN` の詳細については、「[GITHUB\_TOKEN](/ja/enterprise-server@3.22/actions/concepts/security/github_token)」を参照してください。

ワークフローがワークフローのリポジトリの外部にあるリソースにアクセスする必要がある場合は、`GITHUB_TOKEN` を使用できません。 その場合は、資格情報をシークレットとして格納し、次の例で `GITHUB_TOKEN` を実際のシークレットの名前に置き換えます。 シークレットの詳細については、「[GitHub Actions でのシークレットの使用](/ja/enterprise-server@3.22/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets)」を参照してください。

`run` キーワードを使用して、GitHub Actions ワークフローで Ruby スクリプトを実行する場合は、`GITHUB_TOKEN`の値を環境変数として格納できます。 スクリプトは、`ENV['VARIABLE_NAME']` として環境変数にアクセスできます。

たとえば、このワークフロー ステップでは、`GITHUB_TOKEN` という環境変数に `TOKEN` が格納されます。

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

ワークフローが実行するスクリプトは、認証に `ENV['TOKEN']` を使用します。

```ruby copy
require 'octokit'

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

### 認証なしでのインスタンス化

REST API は認証なしで使用できますが、レート制限が低く、一部のエンドポイントを使用することができません。 認証を行わずにインスタンス `Octokit` を作成するには、この `access_token` オプションを渡さないでください。

```ruby copy
require 'octokit'

octokit = Octokit::Client.new
```

## リクエストの提出

Octokit では、要求を行う複数の方法がサポートされています。 エンドポイントの HTTP 動詞とパスがわかっている場合は、`request` メソッドを使用して要求を行うことができます。
`rest` メソッドを使用すると、IDE と入力でのオートコンプリートを利用できます。 ページ分割されたエンドポイントの場合は、`paginate` メソッドを使用して複数のページのデータを要求できます。

### `request` メソッドを使用して要求を行う

`request` メソッドを使用して要求を行うには、HTTP メソッドとパスを最初の引数として渡します。 ハッシュ内の本文、クエリ、またはパスのパラメーターを 2 番目の引数として渡します。 たとえば、`GET` 要求を `/repos/{owner}/{repo}/issues` に行い、`owner`、`repo`、`per_page` パラメーターを渡すには、次のようにします。

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

`request` メソッドでは `Accept: application/vnd.github+json` ヘッダーが自動的に渡されます。 追加のヘッダーまたは別の `Accept` ヘッダーを渡すには、2 番目の引数として渡されるハッシュに `headers` オプションを追加します。
`headers` オプションの値は、キーがヘッダー名で値がヘッダー値のハッシュです。 たとえば、値が `content-type` の `text/plain` ヘッダーを送信する場合は次のようになります。

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

### `rest` エンドポイント メソッドを使用して要求を行う

すべての REST API エンドポイントには、Octokit に関連付けられた `rest` エンドポイント メソッドがあります。 これらのメソッドは、通常、便宜上 IDE でオートコンプリートされます。 任意のパラメーターをハッシュとしてメソッドに渡すことができます。

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

### ページ分割された要求の作成

エンドポイントがページ分割されていて、複数のページの結果をフェッチする場合は、`paginate` メソッドを使用できます。
`paginate` は、最後のページに達するまで結果の次のページをフェッチし、すべての結果を配列として返します。 いくつかのエンドポイントは、ページ分割された結果を配列として返すのではなく、ページ分割された結果をオブジェクト内の配列として返します。 生の結果がオブジェクトであっても、`paginate` は常にアイテムの配列を返します。

たとえば、上記の例では `github/docs` リポジトリからすべての issue が取得されます。 一度に 100 の issue が要求されますが、データの最後のページに達するまで関数は返されません。

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

このメソッドは `paginate` オプションのブロックを受け入れます。これは、結果の各ページを処理するために使用できます。 これにより、応答から必要なデータのみを収集できます。 たとえば、次の例では、タイトルに "test" を含む issue が返されるまで、結果がフェッチされます。 返されたデータのページには、issue のタイトルと作成者のみが格納されます。

```ruby copy
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()` を使用して一度に 1 つのページを反復処理できます。 たとえば、次の例では、一度に 1 ページの結果をフェッチし、次のページをフェッチする前に、ページの各オブジェクトを処理します。 タイトルに "test" を含む issue に達すると、スクリプトは反復を停止し、処理された各オブジェクトの issue タイトルと issue 作成者を返します。 反復子は、ページ分割されたデータをフェッチするための最もメモリ効率の高いメソッドです。

```ruby copy
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` エンドポイント メソッドを最初の引数として、任意のパラメーターを 2 番目の引数として渡します。

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

ページネーションの詳細については、「[REST API でのページネーションの使用](/ja/enterprise-server@3.22/rest/using-the-rest-api/using-pagination-in-the-rest-api)」を参照してください。

## エラーのキャッチ

### すべてのエラーの捕捉

場合によっては、 GitHub REST API からエラーが返されることがあります。 たとえば、アクセス トークンの有効期限が切れている場合や、必要なパラメーターを省略した場合にエラーが発生します。 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 ブロックを使用してエラーをキャッチできます。

```ruby copy
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` 応答を使用して、リポジトリに星が付いていないことを示しています。その他のすべてのエラー コードはエラーとして扱われます。

```ruby copy
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"`されます。 応答ヘッダーには、現在のレート制限ウィンドウがリセットされる時刻を UTC エポック秒数で示す `x-ratelimit-reset` ヘッダーが含まれます。 要求は、`x-ratelimit-reset` で指定された時刻より後に再試行できます。

```ruby copy
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` プロパティは省略されます。

```ruby copy
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、ヘッダーが含まれます。

```ruby copy
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を使用した認証」](#authenticating-with-a-github-app)を参照してください。

`get_changed_files` 関数は、pull request で変更されたすべてのファイルを取得します。
`comment_if_data_files_changed` 関数は `get_changed_files` 関数を呼び出します。 pull request で変更されたいずれかのファイルのファイル パスに `/data/` が含まれている場合、この関数が pull request にコメントを付けます。

```ruby copy
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]
> これは基本的な例にすぎません。 実際には、エラー処理と条件付きチェックを使用して、さまざまなシナリオを処理する必要がある場合があります。

## 次のステップ

GitHub REST API と Octokit.rb の操作の詳細については、次のリソースを参照してください。

* Octokit.rb について詳しくは、[Octokit.rb のドキュメント](https://github.com/octokit/octokit.rb/#readme)を参照してください。
* 要求と応答の構造など、 GitHubの使用可能な REST API エンドポイントの詳細については、 [GitHub REST API のドキュメント](/ja/enterprise-server@3.22/rest) を参照してください。