Github Actions をワークフロー内で使う方法についてすぐに見返すための備忘録
Github Action を作るためのメモはこちら => Github Action の作り方メモ
ドキュメント
- GitHub Actions について - GitHub Docs
- Expressions - GitHub Docs
- Contexts - GitHub Docs
- 使用制限、支払い、管理 - GitHub Docs
- Environment variables - GitHub Docs
ワークフローの定義
リポジトリに次のディレクトリを作成し、その中にYAML形式で定義する。
.github/workflows/
YAMLファイルは名前は自由。複数OK。
ワークフローの構造
ワークフローを定義するYAMLファイルは次のような階層構造を持っている。
ワークフロー(YAMLファイル)
└ jobs:
└ ジョブ(名前は任意)
└ steps:
└ アクション
ワークフロー
- .github/workflows/ ディレクトリ以下に配置されるYAMLファイルが個別のワークフローとなる。
- 複数のワークフローを設置してもOK。
- ワークフローは並列で実行される。
ジョブ
- 各ジョブは仮想環境の新しいインスタンスで実行される。
- したがって、ジョブ間で環境変数やファイル、セットアップ処理の結果などは共有されない。
- ジョブ間の依存を定義して待ち合わせることができる。
- データの受け渡しが必要ならアーティファクト経由で。
Step
- Jobが実行する処理の集合。
- 同じJobのStepは同じ仮想環境で実行されるのでファイルやセットアップ処理は共有できる。
- しかし各ステップは別プロセスなのでステップ内で定義した環境変数は共有できない。
-
jobs.<job_id>.env
で定義した環境変数は全Step
で利用できる
-
アクション
- ワークフローの最小構成要素。
- 単に
run
でコマンドを実行することもできるし、Githubやサードパーティの公開アクションを利用(use
)することもできる。 - コンテキストを利用して秘密情報を受け取ったりできる
仮想環境
Jobが実行される仮想環境のスペックは次の通り。
そんなにスペックは高くない。
- 2コアCPU
- 7 GBのRAMメモリー
- 14 GBのSSDディスク容量
Publicリポジトリなら無料。
Privateだと Linux仮想環境で $0.008/min かかる。
$0.008/min = $0.48/hour = 約52円/hour($1=108.4円)
10分かかるビルドを実行すると約9円かかる。
Freeアカウントで2,000分/月 無料。
Permission
リポジトリごとにどの Github Action を利用できるのか? あるいは、Workflow中でリポジトリの読み書きを許可するかを設定できる。
設定はリポジトリの Settings/Actions にある。
Actions permissions
リポジトリで Github Action を実行できるかどうかを設定する。
次の選択肢から選ぶ。
- Allow all actions: すべての Action の実行を許可
- Disable Actions: すべての Action の実行禁止
- Allow local actions only: リポジトリ内で定義した Action のみ実行を許可
-
Allow select actions: 実行できる Action を選択する
- Allow actions created by GitHub: Github製 Action のみ実行を許可
- Allow Marketplace actions by verified creators: 認証された作者の Action のみ実行を許可
- Allow specified actions: 設定した任意の Action のみ実行を許可
Fork pull request workflows
プライベートリポジトリのみで設定できる項目。フォークされたプルリクエストからのワークフローの権限を設定でできる。
Run workflows from fork pull requests を有効にすると次の2項目を設定できる。
-
Send write tokens to workflows from fork pull requests: 書き込める
GITHUB_TOKEN
を利用を許可する - Send secrets to workflows from fork pull requests: すべてのシークレットを利用可能にする
Workflow permissions
Workflow 内で実行される Action がリポジトリを操作することを許可するかどうかを設定する。
次の2つの選択肢から選び、その選択が GITHUB_TOKEN
に与えられるデフォルトの権限となる。
-
Read and write permissions: 読み書きを許可する。
- ただし、フォークされたリポジトリからのPRでは読み込みのみに制限される
- Read repository contents permission: 読み込みのみ許可する
ワークフローの permissions 設定
Read repository contents permission を設定した場合、リポジトリのコンテンツ(コミットやIssue, PR)に対して書き込むような Action は実行時に権限エラーが発生する。
例えば、Lint系の Action ならば、検査結果でプルリクエストやコミットの状態を更新したり、プルリクエストににコメントを追加したりするものがあるが、それらができなくなる。
このような処理を許可するために Workflow 定義に permissions
を設定することで許可してやることができる。
設定できる権限の種類は次の通り。
permissions:
actions: read|write|none
checks: read|write|none
contents: read|write|none
deployments: read|write|none
issues: read|write|none
packages: read|write|none
pull-requests: read|write|none
repository-projects: read|write|none
security-events: read|write|none
statuses: read|write|none
上記のLint系 Action では pull-requests
と statuses
の write
の許可が必要となるであろう。
Read and write permissionsを選択した場合にはこれらの項目に一律write
権限が与えられるということ。
コンカレンシー
同時に複数のジョブを実行されないように制限することもできる。
例えば、 Push すると自動テストを実行するワークフローを実行中に次の Push を行うと、実行中のワークフローを中断して、後続の Push に対するワークフロー実行を実施することができる(つまり、実行の意味を失った先行 Push のテストをやめてワークフロー実行に対するコストを節約できる)。
コンカレンシーはワークフローレベルまたはジョブレベルで設定できる。
次の例はプルリクエストに Push した際に実行するワークフローで、同じブランチに対してすでにワークフローが実行中であれば、それをキャンセルして、新たなワークフローを実行する例。
name: concurrency sample
on:
pull_request:
types: [opened, synchronize]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
コンテナ内でのジョブ実行
ジョブを指定したコンテナ内で実行することもできる。
次の例は ruby:3.1.2
コンテナでジョブを実行する例。
通常は actions/setup-ruby
で Ruby の実行環境を用意するが、ruby:3.1.2
コンテナで実行するのでそれが不要となっている。
name: Container job sample
on:
push:
jobs:
container-test-job:
runs-on: ubuntu-latest
container:
image: ruby:3.1.2
steps:
- name: show ruby version
run: ruby -v
このワークフローのログを確認すると show ruby version
の結果は次の様に出力されている。
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
ジョブを実効するコンテナに対して volumes
をマウントしたり、 env
で環境変数を設定したりできる。
また、 credentials
を設定できるのでプライベートレジストリからイメージを Pull することもできる。
see コンテナ内でのジョブの実行 - GitHub Docs
サービスコンテナ
ワークフローの処理の中で、Dockerコンテナを起動することができる。
例えば、DB を利用するアプリケーションのテストを実行する場合に MySQL や PostgreSQL をコンテナで建てることができる。
サービスコンテナを立ち上げるには Job 内で service:
を利用する。
次の例は PostgreSQL を利用する Rails アプリケーションの Rspec を実行する例。
name: Rspec for Rails with PostgreSQL
on: push
jobs:
# Label of the container job
rspec:
# Containers must run in Linux based operating systems
runs-on: ubuntu-latest
services: # サービスコンテナの定義
postgres:
image: postgres:15.0-alpine3.16
ports:
- 5432:5432
env:
POSTGRES_PASSWORD: postgres
options: >- # 起動後のヘルスチェック
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Check out repository code
uses: actions/checkout@v3
- actions/setup-ruby@v1
with:
ruby-version: 3.1
- name: Rspec
env:
# テスト対象の Rails アプリケーションは次の環境変数で DB 接続先を設定できる想定
- DB_HOST: localhost
- DB_PORT: 5432
run: |
bundle install
bundle install --jobs 4 --retry 3
bundle exec rails db:create
bundle exec rails db:migrate
bundle exec rspec
上記の例ではコンテナを使わずに Rspec を実行しているが、コンテナでジョブを実行する場合、 DB_HOST: postgres
の様にサービスの名前で接続先を指定できる。
また、 volumes
をマウントしたり、 credentials
を設定することもできる。
see サービスコンテナについて - GitHub Docs
ファイルシステム
- Dockerコンテナで実行されるアクションには、 /githubパスの下に静的なディレクトリがある。
- Dockerコンテナで実行されないアクションでは3つのディレクトリが作成される。これらのディレクトリパスは動的に生成されるので一定ではない。各ディレクトリの位置は対応する環境変数で取得する。
-
home
(HOME
): ユーザ認証情報などのユーザ関連データが書き込まれる -
workspace
(GITHUB_WORKSPACE
):アクションが実行されるワークディレクトリ -
workflow
:workflow/event.json(GITHUB_EVENT_PATH
)が書き込まれるディレクトリ
公開アクション
Github自身が作成しているActionがリポジトリで公開されている。
サードパーティが作ったものはマーケットプレイスで探せる。
- GitHub Actions:Github謹製のアクション
- GitHub Marketplace: actions to improve your workflow:サードパーティ製のアクションのレジストリ
環境変数
環境変数を定義して、ワークフロー内で参照できる。
設定できる環境変数は次のスコープを取ることができる。
- Workflow (トップレベルの
env:
で定義) - Job (
jobs.<job_id>.env
で定義) - Step (
jobs.<job_id>.steps[*].env
で定義)
下へ行くほどスコープは狭くなるが、上位のスコープで設定された値を上書きできる。
env:
SOME_VAR: 'workflow-scope' # Workflowスコープの環境変数
jobs:
one_job:
env:
SOME_VAR: 'job-scope' # Job スコープの環境変数。このJob内ではworkflowスコープの値を上書きする
runs-on: ubuntu-latest
steps:
env:
SOME_VAR: 'job-scope' # Stepスコープの環境変数。他のスコープで設定された値を上書きする
- run: echo ${{ env.SOME_VAR }} # 環境変数の参照
環境変数を動的に追加する
例えば、アーティファクトのファイル名にモジュールのバージョン番号などを入れたい場合などに動的に環境変数を設定したい場合がある。
$GITHUB_ENV
に追記することで環境変数を設定できる。
環境変数 GITHUB_ENV はステップ内で実行するシェルスクリプトなどの中でも有効。
jobs:
one_job:
runs-on: ubuntu-latest
steps:
- run: echo "DYN_VAR='value'" >> $GITHUB_ENV # 環境変数 DYN_VAR を追加している
- run: echo ${{ env.DYN_VAR }} # 環境変数の参照
- uses: actions/upload-artifact@v3
with:
name: artifact-${{ env.DYN_VAR }} # 環境変数の参照し、アーティファクトのファイル名の一部として利用
path: artifacts/*
Tips
複数のコマンドを run:
したい
通常のYAMLの文法に従ってマルチラインのテキストとして例えば次の様に書けば良い
steps:
- name: run multi command
run: | # <- ここがミソ
git config --global user.email "someone@sample.com"
git config --global user.name "github workflow"
git add .
git commit -m 'modify manifests'
git status
プルリクエストの内容で実行するかしないかを決める
コンテキストgithub
にワークフローに関する情報が色々入っている。
例えば、プルリクエストイベントでトリガーするワークフローだとgithub.event.pull_request
にGithub REST APIのpull_request
相当のプルリクの情報が格納されている。
例えば次のようにするとWIPラベルの付いているときだけ実行するjobとなる。
jobs:
<job_name>:
runs-on: ubuntu-latest
if: "contains(join(github.event.pull_request.labels.*.name), 'WIP')"
steps:
- run: <COMMAND
…以下略
条件式の書き方など詳しくはこちら -> GitHub Actions のコンテキストおよび式の構文
チェックアウトするブランチを指定する
Githubが公開するアクション actions/checkoutを利用すれば、ソースコードをチェックアウトできる。
では、チェックアウトしたソースのgit的な状態はどうなっているか?
steps:
- uses: actions/checkout@v3
- run: git status
- run: git branch
上記のようなコマンドをワークフロー内で実行した結果は次の通り。
Run git status
nothing to commit, working tree clean
Run git status
HEAD detached at pull/21/merge
nothing to commit, working tree clean
Run git branch
* (HEAD detached at pull/21/merge)
見ての通り、プルリクエストのheadブランチとは異なるブランチとしてチェックアウトされている。
プルリクエストのheadブランチをチェックアウトしたいなら次ように指定する
steps:
- uses: actions/checkout@v3
with:
ref: ${{github.head_ref}}
${{github.head_ref}}
はワークフローを実行するブランチを示す。
${{github.head_ref}}
はプルリクエストイベント(pull_request
または pull_request_target
)でトリガーするワークフローでないと 使えないので注意。
ワークフロー内でファイルを変更 commit
→ push
する
actions/checkout を使って取得したファイルを書き換えてGitコミットすることができる。
コミットをするということは、ワークフローが実行されるブランチに変更を加えたいということであり、そのためには Push する必要もある。
前述したように actions/checkout でデフォルトでチェックアウトされるブランチはプルリクエストのブランチとは異なる。そのため、上のようにチェックアウトするブランチを指定する必要がある。
プルリクエストをマージする
次のGithub製の公開Actionで、Github APIを叩けば、プルリクエストをマージすることもできる。
actions/github-script は Node.js版のoctokitを使うためのアクション。
このアクションは script
というパラメータを取り、その中にJSのコードを記述すれば実行される。
script
内ではgithub
という変数に認証済みのoctakitインスタンスが格納されており、ワークフローの情報はcontext
オブジェクトに格納されている。
次のワークフロー設定はREADME.mdをちょっと変更して、それをコミットしてpushし、マージする例
name: Test
on:
pull_request:
types:
- opened
- synchronize
jobs:
merge:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{github.head_ref}}
- run: |
date >> README.md
git add .
- name: check diff
shell: bash
id: changes
run: |
echo "::set-output name=count::$(git diff --staged --name-only . |wc -l)"
- name: git commit and push
shell: bash
if: steps.changes.outputs.count > 0
run: |
git config --global user.name ${GITHUB_ACTOR}
git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com"
git commit -m "[WF] ${{inputs.operation}} article(s)"
git push
- name: merge pull request
if: steps.changes.outputs.count > 0
uses: actions/github-script@0.4.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.pulls.merge({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number
})
コミット対象のファイルに変更差分が発生しない場合、commitが失敗する。それを避けるためにステップ name: check diff で git diff
を使って変更されているファイルの数をワークフローコマンド set-output
を利用して steps.changes.outputs.count
に保存している。後続のステップはこの値が 1 以上であれば実行するようにしている。
ステップ name: merge pull request 以下が actions/github-script を使ってプルリクエストをマージしているところ。
ちなみにプルリクエストイベントでトリガーされるワークフローでのcontext
はこんな感じ。
`context`の中身を開く
{
"context": {
"payload": {
"action": "synchronize",
"after": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"before": "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
"number": 65,
"pull_request": {
"_links": {
"comments": {
"href": "https://api.github.com/repos///issues/65/comments"
},
"commits": {
"href": "https://api.github.com/repos///pulls/65/commits"
},
"html": {
"href": "https://github.com///pull/65"
},
"issue": {
"href": "https://api.github.com/repos///issues/65"
},
"review_comment": {
"href": "https://api.github.com/repos///pulls/comments{/number}"
},
"review_comments": {
"href": "https://api.github.com/repos///pulls/65/comments"
},
"self": {
"href": "https://api.github.com/repos///pulls/65"
},
"statuses": {
"href": "https://api.github.com/repos///statuses/79dae33c3eb3dee6990846ed9136649454935bfb"
}
},
"additions": 45,
"assignee": null,
"assignees": [],
"author_association": "OWNER",
"base": {
"label": ":master",
"ref": "master",
"repo": {
"archive_url": "https://api.github.com/repos///{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos///assignees{/user}",
"blobs_url": "https://api.github.com/repos///git/blobs{/sha}",
"branches_url": "https://api.github.com/repos///branches{/branch}",
"clone_url": "https://github.com//.git",
"collaborators_url": "https://api.github.com/repos///collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos///comments{/number}",
"commits_url": "https://api.github.com/repos///commits{/sha}",
"compare_url": "https://api.github.com/repos///compare/{base}...{head}",
"contents_url": "https://api.github.com/repos///contents/{+path}",
"contributors_url": "https://api.github.com/repos///contributors",
"created_at": "2019-12-04T09:14:10Z",
"default_branch": "master",
"deployments_url": "https://api.github.com/repos///deployments",
"description": null,
"disabled": false,
"downloads_url": "https://api.github.com/repos///downloads",
"events_url": "https://api.github.com/repos///events",
"fork": false,
"forks": 0,
"forks_count": 0,
"forks_url": "https://api.github.com/repos///forks",
"full_name": "/",
"git_commits_url": "https://api.github.com/repos///git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos///git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos///git/tags{/sha}",
"git_url": "git://github.com//.git",
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": true,
"has_wiki": true,
"homepage": null,
"hooks_url": "https://api.github.com/repos///hooks",
"html_url": "https://github.com//",
"id": 225825747,
"issue_comment_url": "https://api.github.com/repos///issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos///issues/events{/number}",
"issues_url": "https://api.github.com/repos///issues{/number}",
"keys_url": "https://api.github.com/repos///keys{/key_id}",
"labels_url": "https://api.github.com/repos///labels{/name}",
"language": "HTML",
"languages_url": "https://api.github.com/repos///languages",
"license": null,
"merges_url": "https://api.github.com/repos///merges",
"milestones_url": "https://api.github.com/repos///milestones{/number}",
"mirror_url": null,
"name": "",
"node_id": "MDEwOlJlcG9zaXRvcnkyMjU4MjU3NDc=",
"notifications_url": "https://api.github.com/repos///notifications{?since,all,participating}",
"open_issues": 60,
"open_issues_count": 60,
"owner": {
"avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
"events_url": "https://api.github.com/users//events{/privacy}",
"followers_url": "https://api.github.com/users//followers",
"following_url": "https://api.github.com/users//following{/other_user}",
"gists_url": "https://api.github.com/users//gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/",
"id": 26864070,
"login": "",
"node_id": "MDQ6VXNlcjI2ODY0MDcw",
"organizations_url": "https://api.github.com/users//orgs",
"received_events_url": "https://api.github.com/users//received_events",
"repos_url": "https://api.github.com/users//repos",
"site_admin": false,
"starred_url": "https://api.github.com/users//starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users//subscriptions",
"type": "User",
"url": "https://api.github.com/users/"
},
"private": true,
"pulls_url": "https://api.github.com/repos///pulls{/number}",
"pushed_at": "2020-01-24T04:41:03Z",
"releases_url": "https://api.github.com/repos///releases{/id}",
"size": 16,
"ssh_url": "git@github.com:/.git",
"stargazers_count": 0,
"stargazers_url": "https://api.github.com/repos///stargazers",
"statuses_url": "https://api.github.com/repos///statuses/{sha}",
"subscribers_url": "https://api.github.com/repos///subscribers",
"subscription_url": "https://api.github.com/repos///subscription",
"svn_url": "https://github.com//",
"tags_url": "https://api.github.com/repos///tags",
"teams_url": "https://api.github.com/repos///teams",
"trees_url": "https://api.github.com/repos///git/trees{/sha}",
"updated_at": "2020-01-22T03:25:23Z",
"url": "https://api.github.com/repos//",
"watchers": 0,
"watchers_count": 0
},
"sha": "26a7c543d54cd86669592979a5ae35210f05bf35",
"user": {
"avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
"events_url": "https://api.github.com/users//events{/privacy}",
"followers_url": "https://api.github.com/users//followers",
"following_url": "https://api.github.com/users//following{/other_user}",
"gists_url": "https://api.github.com/users//gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/",
"id": 26864070,
"login": "",
"node_id": "MDQ6VXNlcjI2ODY0MDcw",
"organizations_url": "https://api.github.com/users//orgs",
"received_events_url": "https://api.github.com/users//received_events",
"repos_url": "https://api.github.com/users//repos",
"site_admin": false,
"starred_url": "https://api.github.com/users//starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users//subscriptions",
"type": "User",
"url": "https://api.github.com/users/"
}
},
"body": "\nSource PR: [Can merge]()\n",
"changed_files": 3,
"closed_at": null,
"comments": 0,
"comments_url": "https://api.github.com/repos///issues/65/comments",
"commits": 6,
"commits_url": "https://api.github.com/repos///pulls/65/commits",
"created_at": "2020-01-23T07:07:24Z",
"deletions": 2,
"diff_url": "https://github.com///pull/65.diff",
"draft": false,
"head": {
"label": ":can_merge",
"ref": "can_merge",
"repo": {
"archive_url": "https://api.github.com/repos///{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos///assignees{/user}",
"blobs_url": "https://api.github.com/repos///git/blobs{/sha}",
"branches_url": "https://api.github.com/repos///branches{/branch}",
"clone_url": "https://github.com//.git",
"collaborators_url": "https://api.github.com/repos///collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos///comments{/number}",
"commits_url": "https://api.github.com/repos///commits{/sha}",
"compare_url": "https://api.github.com/repos///compare/{base}...{head}",
"contents_url": "https://api.github.com/repos///contents/{+path}",
"contributors_url": "https://api.github.com/repos///contributors",
"created_at": "2019-12-04T09:14:10Z",
"default_branch": "master",
"deployments_url": "https://api.github.com/repos///deployments",
"description": null,
"disabled": false,
"downloads_url": "https://api.github.com/repos///downloads",
"events_url": "https://api.github.com/repos///events",
"fork": false,
"forks": 0,
"forks_count": 0,
"forks_url": "https://api.github.com/repos///forks",
"full_name": "/",
"git_commits_url": "https://api.github.com/repos///git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos///git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos///git/tags{/sha}",
"git_url": "git://github.com//.git",
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": true,
"has_wiki": true,
"homepage": null,
"hooks_url": "https://api.github.com/repos///hooks",
"html_url": "https://github.com//",
"id": 225825747,
"issue_comment_url": "https://api.github.com/repos///issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos///issues/events{/number}",
"issues_url": "https://api.github.com/repos///issues{/number}",
"keys_url": "https://api.github.com/repos///keys{/key_id}",
"labels_url": "https://api.github.com/repos///labels{/name}",
"language": "HTML",
"languages_url": "https://api.github.com/repos///languages",
"license": null,
"merges_url": "https://api.github.com/repos///merges",
"milestones_url": "https://api.github.com/repos///milestones{/number}",
"mirror_url": null,
"name": "",
"node_id": "MDEwOlJlcG9zaXRvcnkyMjU4MjU3NDc=",
"notifications_url": "https://api.github.com/repos///notifications{?since,all,participating}",
"open_issues": 60,
"open_issues_count": 60,
"owner": {
"avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
"events_url": "https://api.github.com/users//events{/privacy}",
"followers_url": "https://api.github.com/users//followers",
"following_url": "https://api.github.com/users//following{/other_user}",
"gists_url": "https://api.github.com/users//gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/",
"id": 26864070,
"login": "",
"node_id": "MDQ6VXNlcjI2ODY0MDcw",
"organizations_url": "https://api.github.com/users//orgs",
"received_events_url": "https://api.github.com/users//received_events",
"repos_url": "https://api.github.com/users//repos",
"site_admin": false,
"starred_url": "https://api.github.com/users//starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users//subscriptions",
"type": "User",
"url": "https://api.github.com/users/"
},
"private": true,
"pulls_url": "https://api.github.com/repos///pulls{/number}",
"pushed_at": "2020-01-24T04:41:03Z",
"releases_url": "https://api.github.com/repos///releases{/id}",
"size": 16,
"ssh_url": "git@github.com:/.git",
"stargazers_count": 0,
"stargazers_url": "https://api.github.com/repos///stargazers",
"statuses_url": "https://api.github.com/repos///statuses/{sha}",
"subscribers_url": "https://api.github.com/repos///subscribers",
"subscription_url": "https://api.github.com/repos///subscription",
"svn_url": "https://github.com//",
"tags_url": "https://api.github.com/repos///tags",
"teams_url": "https://api.github.com/repos///teams",
"trees_url": "https://api.github.com/repos///git/trees{/sha}",
"updated_at": "2020-01-22T03:25:23Z",
"url": "https://api.github.com/repos//",
"watchers": 0,
"watchers_count": 0
},
"sha": "79dae33c3eb3dee6990846ed9136649454935bfb",
"user": {
"avatar_url": "https://avatars0.githubusercontent.com/u/26864070?v=4",
"events_url": "https://api.github.com/users//events{/privacy}",
"followers_url": "https://api.github.com/users//followers",
"following_url": "https://api.github.com/users//following{/other_user}",
"gists_url": "https://api.github.com/users//gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/",
"id": 26864070,
"login": "",
"node_id": "MDQ6VXNlcjI2ODY0MDcw",
"organizations_url": "https://api.github.com/users//orgs",
"received_events_url": "https://api.github.com/users//received_events",
"repos_url": "https://api.github.com/users//repos",
"site_admin": false,
"starred_url": "https://api.github.com/users//starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users//subscriptions",
"type": "User",
"url": "https://api.github.com/users/"
}
},
"html_url": "https://github.com///pull/65",
"id": 366212162,
"issue_url": "https://api.github.com/repos///issues/65",
"labels": [],
"locked": false,
"maintainer_can_modify": false,
"merge_commit_sha": "9999999999999999999999999999999999999999",
"mergeable": null,
"mergeable_state": "unknown",
"merged": false,
"merged_at": null,
"merged_by": null,
"milestone": null,
"node_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"number": 65,
"patch_url": "https://github.com///pull/65.patch",
"rebaseable": null,
"requested_reviewers": [],
"requested_teams": [],
"review_comment_url": "https://api.github.com/repos/