1import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'; 2 3const octokit = new Octokit({ 4 auth: process.env.GITHUB_TOKEN, 5}); 6 7// Predefine some params used across almost all requests. 8const owner = 'expo'; 9const repo = 'expo'; 10 11/** 12 * Returns public informations about the currently authenticated (by GitHub API token) user. 13 */ 14export async function getAuthenticatedUserAsync() { 15 const { data } = await octokit.users.getAuthenticated(); 16 return data; 17} 18 19/** 20 * Requests for the pull request object. 21 */ 22export async function getPullRequestAsync(pull_number: number): Promise<PullRequest> { 23 const { data } = await octokit.pulls.get({ 24 owner, 25 repo, 26 pull_number, 27 }); 28 return data; 29} 30 31/** 32 * Gets a list of reviews left in the pull request with given ID. 33 */ 34export async function listPullRequestReviewsAsync( 35 pull_number: number 36): Promise<PullRequestReview[]> { 37 const { data } = await octokit.pulls.listReviews({ 38 owner, 39 repo, 40 pull_number, 41 }); 42 return data; 43} 44 45/** 46 * Creates pull request review. By default the review is pending which needs to be submitted in order to be visible for other users. 47 * Provide `event` option to create and submit at once. 48 */ 49export async function createPullRequestReviewAsync<T>( 50 pull_number: number, 51 options?: T 52): Promise<PullRequestReview> { 53 const { data } = await octokit.pulls.createReview({ 54 owner, 55 repo, 56 pull_number, 57 ...options, 58 }); 59 return data; 60} 61 62/** 63 * Updates pull request review with a new main comment. 64 */ 65export async function updatePullRequestReviewAsync( 66 pull_number: number, 67 review_id: number, 68 body: string 69) { 70 const { data } = await octokit.pulls.updateReview({ 71 owner, 72 repo, 73 pull_number, 74 review_id, 75 body, 76 }); 77 return data; 78} 79 80/** 81 * Gets a list of comments in review. 82 */ 83export async function listPullRequestReviewCommentsAsync(pull_number: number, review_id: number) { 84 const { data } = await octokit.pulls.listReviewComments({ 85 owner, 86 repo, 87 pull_number, 88 review_id, 89 }); 90 return data; 91} 92 93/** 94 * Deletes a comment left under pull request review. 95 */ 96export async function deletePullRequestReviewCommentAsync(comment_id: number) { 97 const { data } = await octokit.pulls.deleteReviewComment({ 98 owner, 99 repo, 100 comment_id, 101 }); 102 return data; 103} 104 105/** 106 * Deletes all comments from given review. 107 */ 108export async function deleteAllPullRequestReviewCommentsAsync( 109 pull_number: number, 110 review_id: number 111) { 112 const comments = await listPullRequestReviewCommentsAsync(pull_number, review_id); 113 114 await Promise.all( 115 comments 116 .filter((comment) => comment.pull_request_review_id === review_id) 117 .map((comment) => deletePullRequestReviewCommentAsync(comment.id)) 118 ); 119} 120 121/** 122 * Requests given users to review the pull request. 123 * If the user already reviewed the PR, it resets his review state. 124 */ 125export async function requestPullRequestReviewersAsync(pull_number: number, reviewers: string[]) { 126 const { data } = await octokit.pulls.requestReviewers({ 127 owner, 128 repo, 129 pull_number, 130 reviewers, 131 }); 132 return data; 133} 134 135/** 136 * Returns an issue object with given issue number. 137 */ 138export async function getIssueAsync(issue_number: number) { 139 const { data } = await octokit.issues.get({ 140 owner, 141 repo, 142 issue_number, 143 }); 144 return data; 145} 146 147/** 148 * Creates an issue comment with given body. 149 */ 150export async function createCommentAsync(issue_number: number, body: string) { 151 const { data } = await octokit.issues.createComment({ 152 owner, 153 repo, 154 issue_number, 155 body, 156 }); 157 return data; 158} 159 160/** 161 * Lists commits in given issue. 162 */ 163export async function listCommentsAsync( 164 issue_number: number, 165 options: Partial<ListCommentsOptions> 166) { 167 const { data } = await octokit.issues.listComments({ 168 owner, 169 repo, 170 issue_number, 171 ...options, 172 }); 173 return data; 174} 175 176/** 177 * Returns a list of issue comments gathered from all pages. 178 */ 179export async function listAllCommentsAsync(issue_number: number) { 180 const issue = await getIssueAsync(issue_number); 181 const comments = [] as ListCommentsResponse['data']; 182 const pageSize = 100; 183 184 for (let page = 1, maxPage = Math.ceil(issue.comments / pageSize); page <= maxPage; page++) { 185 const commentsPage = await listCommentsAsync(issue_number, { 186 page, 187 per_page: pageSize, 188 }); 189 comments.push(...commentsPage); 190 } 191 return comments; 192} 193 194/** 195 * Deletes an issue comment with given identifier. 196 */ 197export async function deleteCommentAsync(comment_id: number) { 198 const { data } = await octokit.issues.deleteComment({ 199 owner, 200 repo, 201 comment_id, 202 }); 203 return data; 204} 205 206/** 207 * Adds labels to the issue. Throws an error when any of given labels doesn't exist. 208 */ 209export async function addIssueLabelsAsync(issue_number: number, labels: string[]) { 210 const { data } = await octokit.issues.addLabels({ 211 owner, 212 repo, 213 issue_number, 214 labels, 215 }); 216 return data; 217} 218 219/** 220 * Removes single label from the issue. 221 * Throws an error when given label doesn't exist and when the label isn't added. 222 */ 223export async function removeIssueLabelAsync(issue_number: number, name: string) { 224 const { data } = await octokit.issues.removeLabel({ 225 owner, 226 repo, 227 issue_number, 228 name, 229 }); 230 return data; 231} 232 233// Octokit's types are autogenerated and so inconvenient to use if you want to refer to them. 234// We re-export some of them to make it easier. 235export type PullRequestReviewEvent = 'COMMENT' | 'APPROVE' | 'REQUEST_CHANGES'; 236export type PullRequest = RestEndpointMethodTypes['pulls']['get']['response']['data']; 237export type PullRequestReview = RestEndpointMethodTypes['pulls']['getReview']['response']['data']; 238export type IssueComment = RestEndpointMethodTypes['issues']['getComment']['response']['data']; 239export type ListCommentsOptions = RestEndpointMethodTypes['issues']['listComments']['parameters']; 240export type ListCommentsResponse = RestEndpointMethodTypes['issues']['listComments']['response']; 241