この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部の若槻です。
最近、Pull RequestのCloseの自動化のために、「最後にCommitがPushされた日時を取得」する処理を実装する必要がありました。
そこで今回は、GitHub API v3を使ってPull Requestの最終Push日時を取得する方法を確認してみました。
やってみた
アクセストークンの発行
まだの場合は下記から発行します。スコープはrepo
のrepo:status
を指定します。
Pull Requestの最終Push日時を取得する
まずPull Requestの情報を取得します。GitHub API v3の下記のエンドポイントを使用すれば取得可能です。
GET /repos/{owner}/{repo}/pulls/{pull_number}
GITHUB_ACCESS_AOKEN
には先ほどのアクセストークンを指定します。octocat/hello-world
はGitHubが公開しているRepositoryです。このRepositoryの番号997
のPull Requestを取得してみます。
% GITHUB_ACCESS_AOKEN=##GITHUB_ACCESS_AOKEN##
% result=$(curl \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${GITHUB_ACCESS_AOKEN}" \
https://api.github.com/repos/octocat/hello-world/pulls/997)
するとPull Requestの情報がJSONで出力されます。(長いため折りたたんでいます。)
クリックで展開
result.json
{
"url": "https://api.github.com/repos/octocat/Hello-World/pulls/997",
"id": 685581540,
"node_id": "MDExOlB1bGxSZXF1ZXN0Njg1NTgxNTQw",
"html_url": "https://github.com/octocat/Hello-World/pull/997",
"diff_url": "https://github.com/octocat/Hello-World/pull/997.diff",
"patch_url": "https://github.com/octocat/Hello-World/pull/997.patch",
"issue_url": "https://api.github.com/repos/octocat/Hello-World/issues/997",
"number": 997,
"state": "open",
"locked": false,
"title": "modify by logerror",
"user": {
"login": "logerrors",
"id": 31824303,
"node_id": "MDQ6VXNlcjMxODI0MzAz",
"avatar_url": "https://avatars.githubusercontent.com/u/31824303?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/logerrors",
"html_url": "https://github.com/logerrors",
"followers_url": "https://api.github.com/users/logerrors/followers",
"following_url": "https://api.github.com/users/logerrors/following{/other_user}",
"gists_url": "https://api.github.com/users/logerrors/gists{/gist_id}",
"starred_url": "https://api.github.com/users/logerrors/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/logerrors/subscriptions",
"organizations_url": "https://api.github.com/users/logerrors/orgs",
"repos_url": "https://api.github.com/users/logerrors/repos",
"events_url": "https://api.github.com/users/logerrors/events{/privacy}",
"received_events_url": "https://api.github.com/users/logerrors/received_events",
"type": "User",
"site_admin": false
},
"body": "",
"created_at": "2021-07-07T23:48:41Z",
"updated_at": "2021-07-07T23:51:57Z",
"closed_at": null,
"merged_at": null,
"merge_commit_sha": "7f951a24c1a3e83ce53230e80f243b7a5585ddb6",
"assignee": null,
"assignees": [
],
"requested_reviewers": [
],
"requested_teams": [
],
"labels": [
],
"milestone": null,
"draft": false,
"commits_url": "https://api.github.com/repos/octocat/Hello-World/pulls/997/commits",
"review_comments_url": "https://api.github.com/repos/octocat/Hello-World/pulls/997/comments",
"review_comment_url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}",
"comments_url": "https://api.github.com/repos/octocat/Hello-World/issues/997/comments",
"statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/9f81418f73fbebff5974c33bd705eaa8957f1dfc",
"head": {
"label": "logerrors:master",
"ref": "master",
"sha": "9f81418f73fbebff5974c33bd705eaa8957f1dfc",
"user": {
"login": "logerrors",
"id": 31824303,
"node_id": "MDQ6VXNlcjMxODI0MzAz",
"avatar_url": "https://avatars.githubusercontent.com/u/31824303?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/logerrors",
"html_url": "https://github.com/logerrors",
"followers_url": "https://api.github.com/users/logerrors/followers",
"following_url": "https://api.github.com/users/logerrors/following{/other_user}",
"gists_url": "https://api.github.com/users/logerrors/gists{/gist_id}",
"starred_url": "https://api.github.com/users/logerrors/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/logerrors/subscriptions",
"organizations_url": "https://api.github.com/users/logerrors/orgs",
"repos_url": "https://api.github.com/users/logerrors/repos",
"events_url": "https://api.github.com/users/logerrors/events{/privacy}",
"received_events_url": "https://api.github.com/users/logerrors/received_events",
"type": "User",
"site_admin": false
},
"repo": {
"id": 383948788,
"node_id": "MDEwOlJlcG9zaXRvcnkzODM5NDg3ODg=",
"name": "Hello-World",
"full_name": "logerrors/Hello-World",
"private": false,
"owner": {
"login": "logerrors",
"id": 31824303,
"node_id": "MDQ6VXNlcjMxODI0MzAz",
"avatar_url": "https://avatars.githubusercontent.com/u/31824303?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/logerrors",
"html_url": "https://github.com/logerrors",
"followers_url": "https://api.github.com/users/logerrors/followers",
"following_url": "https://api.github.com/users/logerrors/following{/other_user}",
"gists_url": "https://api.github.com/users/logerrors/gists{/gist_id}",
"starred_url": "https://api.github.com/users/logerrors/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/logerrors/subscriptions",
"organizations_url": "https://api.github.com/users/logerrors/orgs",
"repos_url": "https://api.github.com/users/logerrors/repos",
"events_url": "https://api.github.com/users/logerrors/events{/privacy}",
"received_events_url": "https://api.github.com/users/logerrors/received_events",
"type": "User",
"site_admin": false
},
"html_url": "https://github.com/logerrors/Hello-World",
"description": "My first repository on GitHub!",
"fork": true,
"url": "https://api.github.com/repos/logerrors/Hello-World",
"forks_url": "https://api.github.com/repos/logerrors/Hello-World/forks",
"keys_url": "https://api.github.com/repos/logerrors/Hello-World/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/logerrors/Hello-World/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/logerrors/Hello-World/teams",
"hooks_url": "https://api.github.com/repos/logerrors/Hello-World/hooks",
"issue_events_url": "https://api.github.com/repos/logerrors/Hello-World/issues/events{/number}",
"events_url": "https://api.github.com/repos/logerrors/Hello-World/events",
"assignees_url": "https://api.github.com/repos/logerrors/Hello-World/assignees{/user}",
"branches_url": "https://api.github.com/repos/logerrors/Hello-World/branches{/branch}",
"tags_url": "https://api.github.com/repos/logerrors/Hello-World/tags",
"blobs_url": "https://api.github.com/repos/logerrors/Hello-World/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/logerrors/Hello-World/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/logerrors/Hello-World/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/logerrors/Hello-World/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/logerrors/Hello-World/statuses/{sha}",
"languages_url": "https://api.github.com/repos/logerrors/Hello-World/languages",
"stargazers_url": "https://api.github.com/repos/logerrors/Hello-World/stargazers",
"contributors_url": "https://api.github.com/repos/logerrors/Hello-World/contributors",
"subscribers_url": "https://api.github.com/repos/logerrors/Hello-World/subscribers",
"subscription_url": "https://api.github.com/repos/logerrors/Hello-World/subscription",
"commits_url": "https://api.github.com/repos/logerrors/Hello-World/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/logerrors/Hello-World/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/logerrors/Hello-World/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/logerrors/Hello-World/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/logerrors/Hello-World/contents/{+path}",
"compare_url": "https://api.github.com/repos/logerrors/Hello-World/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/logerrors/Hello-World/merges",
"archive_url": "https://api.github.com/repos/logerrors/Hello-World/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/logerrors/Hello-World/downloads",
"issues_url": "https://api.github.com/repos/logerrors/Hello-World/issues{/number}",
"pulls_url": "https://api.github.com/repos/logerrors/Hello-World/pulls{/number}",
"milestones_url": "https://api.github.com/repos/logerrors/Hello-World/milestones{/number}",
"notifications_url": "https://api.github.com/repos/logerrors/Hello-World/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/logerrors/Hello-World/labels{/name}",
"releases_url": "https://api.github.com/repos/logerrors/Hello-World/releases{/id}",
"deployments_url": "https://api.github.com/repos/logerrors/Hello-World/deployments",
"created_at": "2021-07-07T23:32:53Z",
"updated_at": "2021-07-07T23:41:01Z",
"pushed_at": "2021-07-08T01:18:14Z",
"git_url": "git://github.com/logerrors/Hello-World.git",
"ssh_url": "git@github.com:logerrors/Hello-World.git",
"clone_url": "https://github.com/logerrors/Hello-World.git",
"svn_url": "https://github.com/logerrors/Hello-World",
"homepage": "",
"size": 1,
"stargazers_count": 0,
"watchers_count": 0,
"language": null,
"has_issues": false,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 0,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 0,
"license": null,
"forks": 0,
"open_issues": 0,
"watchers": 0,
"default_branch": "master"
}
},
"base": {
"label": "octocat:master",
"ref": "master",
"sha": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
"user": {
"login": "octocat",
"id": 583231,
"node_id": "MDQ6VXNlcjU4MzIzMQ==",
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"repo": {
"id": 1296269,
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
"name": "Hello-World",
"full_name": "octocat/Hello-World",
"private": false,
"owner": {
"login": "octocat",
"id": 583231,
"node_id": "MDQ6VXNlcjU4MzIzMQ==",
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"html_url": "https://github.com/octocat/Hello-World",
"description": "My first repository on GitHub!",
"fork": false,
"url": "https://api.github.com/repos/octocat/Hello-World",
"forks_url": "https://api.github.com/repos/octocat/Hello-World/forks",
"keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/octocat/Hello-World/teams",
"hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks",
"issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
"events_url": "https://api.github.com/repos/octocat/Hello-World/events",
"assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}",
"branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}",
"tags_url": "https://api.github.com/repos/octocat/Hello-World/tags",
"blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
"languages_url": "https://api.github.com/repos/octocat/Hello-World/languages",
"stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers",
"contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors",
"subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers",
"subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription",
"commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}",
"compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/octocat/Hello-World/merges",
"archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads",
"issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}",
"pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}",
"milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}",
"notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}",
"releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}",
"deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments",
"created_at": "2011-01-26T19:01:12Z",
"updated_at": "2021-07-09T02:07:41Z",
"pushed_at": "2021-07-07T23:48:42Z",
"git_url": "git://github.com/octocat/Hello-World.git",
"ssh_url": "git@github.com:octocat/Hello-World.git",
"clone_url": "https://github.com/octocat/Hello-World.git",
"svn_url": "https://github.com/octocat/Hello-World",
"homepage": "",
"size": 1,
"stargazers_count": 1670,
"watchers_count": 1670,
"language": null,
"has_issues": true,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 1577,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 534,
"license": null,
"forks": 1577,
"open_issues": 534,
"watchers": 1670,
"default_branch": "master"
}
},
"_links": {
"self": {
"href": "https://api.github.com/repos/octocat/Hello-World/pulls/997"
},
"html": {
"href": "https://github.com/octocat/Hello-World/pull/997"
},
"issue": {
"href": "https://api.github.com/repos/octocat/Hello-World/issues/997"
},
"comments": {
"href": "https://api.github.com/repos/octocat/Hello-World/issues/997/comments"
},
"review_comments": {
"href": "https://api.github.com/repos/octocat/Hello-World/pulls/997/comments"
},
"review_comment": {
"href": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}"
},
"commits": {
"href": "https://api.github.com/repos/octocat/Hello-World/pulls/997/commits"
},
"statuses": {
"href": "https://api.github.com/repos/octocat/Hello-World/statuses/9f81418f73fbebff5974c33bd705eaa8957f1dfc"
}
},
"author_association": "NONE",
"auto_merge": null,
"active_lock_reason": null,
"merged": false,
"mergeable": true,
"rebaseable": true,
"mergeable_state": "clean",
"merged_by": null,
"comments": 0,
"review_comments": 0,
"maintainer_can_modify": true,
"commits": 1,
"additions": 1,
"deletions": 0,
"changed_files": 1
}
このうちPull Requestの最終Push日時は.head.repo.pushed_at
の値となります。
% echo ${result} | jq -r .head.repo.pushed_at
2021-07-08T01:18:14Z
これでPull Requestの最終Push日時が取得できました。
別解
以前、前述のエンドポイントのバグ?で.head.repo
に同Pull Requestの.base.repo
と同じ値がなぜか入ってきて、うまく最終Push日時が取得できない時がありました。
その時には別解として以下のエンドポイントを代替で利用していました。
GET /repos/{owner}/{repo}/events
このエンドポイントを使用すると指定のRepositoryで発生したEventが取得できます。EventのType一覧は下記で確認できます。
しかしこのエンドポイントはRepository内の全てのEventが取得できてしまうため、その結果から指定のPull RequestのPushEvent
を下記のようなループ処理を実装するなどしてフィルターする必要があります。
page=1
pushed_at=$null
while [ -z "$pushed_at" ] && [ $page -le 5 ]; do
echo "page="$page
pushed_at=$(curl -sS -H "$ACCEPT_HEADER" -H "$AUTH_HEADER" "${GH_API_HOST}/events?per_page=100&page=${page}" | \
jq --arg ref $pull_branch_ref '.[] | select(.payload.ref == $ref and .type == "PushEvent")' | \
jq -rs '.[0].created_at' \
)
page=$((page+1))
done
しかし最初のGET /repos/{owner}/{repo}/pulls/{pull_number}
が正常に使えるようになったため、上記の冗長な方法を取らずに済んで良かったです。
以上