Git2Doc / GitDog

Git Dog
MIT License
0 stars 0 forks source link

Github API Research #3

Open BEMELON opened 1 year ago

BEMELON commented 1 year ago
sungxsoo commented 1 year ago

Personal Access Token

Github API에서는 personal access token 이라는 인증 방식을 사용

사용자가 발급한 개인 토큰을 이용하여 인증을 수행

Github personal access token의 권한은 생성 시에 부여되는 스코프(scope)에 따라 달라짐

Scope 정리 표

Scope | 권한 -- | -- read:user | 로그인한 사용자의 프로필 정보, 이메일 등을 읽을 수 있습니다. user:email | 로그인한 사용자의 이메일 주소를 읽을 수 있습니다. user:follow | 다른 사용자를 팔로우할 수 있습니다. repo | 사용자의 public 및 private 레포지토리에 대한 읽기 및 쓰기 권한이 있습니다. repo:status | 사용자의 레포지토리 상태를 읽을 수 있습니다. delete_repo | 사용자의 레포지토리를 삭제할 수 있습니다. gist | 사용자의 gist를 읽고 쓸 수 있습니다. notifications | 사용자의 알림을 읽고 쓸 수 있습니다. admin:org | 조직을 관리할 수 있는 권한이 있습니다. write:discussion | 댓글을 작성하고 수정할 수 있습니다. workflow | 워크플로우를 읽고 쓸 수 있습니다. read:packages | 패키지를 읽을 수 있습니다. write:packages | 패키지를 읽고 쓸 수 있습니다. delete:packages | 패키지를 삭제할 수 있습니다. read:org | 조직 정보를 읽을 수 있습니다. write:org | 조직 정보를 쓸 수 있습니다. admin:public_key | 사용자의 SSH 공개 키를 관리할 수 있습니다. admin:repo_hook | 사용자의 레포지토리 후크를 관리할 수 있습니다. admin:org_hook | 조직의 후크를 관리할 수 있습니다. gist:enterprise | Github Enterprise Server의 gist를 읽고 쓸 수 있습니다.
sungxsoo commented 1 year ago

그래서 다른 유저의 정보를 어디까지 가져올 수 있는가?

Github personal access token이 모든 scope를 가진 경우, 해당 토큰으로 다른 사용자의 정보를 가져올 수 있는 범위는 다음과 같음

가능한 범위 | 불가능한 범위 -- | -- 다른 사용자의 public 프로필 정보를 가져올 수 있습니다. | 다른 사용자의 private 프로필 정보를 가져올 수 없습니다. 다른 사용자의 public 레포지토리를 가져올 수 있습니다. | 다른 사용자의 private 레포지토리를 가져올 수 없습니다. 다른 사용자의 public gist를 가져올 수 있습니다. | 다른 사용자의 private gist를 가져올 수 없습니다. 다른 사용자의 public 이벤트 목록을 가져올 수 있습니다. | 다른 사용자의 private 이벤트 목록을 가져올 수 없습니다. 다른 사용자의 public 리포지토리의 PR 및 커밋 이력을 가져올 수 있습니다. | 다른 사용자의 private 리포지토리의 PR 및 커밋 이력을 가져올 수 없습니다. 다른 사용자를 팔로우할 수 있습니다. | 다른 사용자의 팔로워 목록을 가져올 수 없습니다. 다른 사용자가 팔로우하는 사용자를 팔로우할 수 있습니다. | 다른 사용자가 팔로우하는 사용자 목록을 가져올 수 없습니다. 다른 사용자의 public 이슈를 가져올 수 있습니다. | 다른 사용자의 private 이슈를 가져올 수 없습니다. 다른 사용자의 public 프로젝트를 가져올 수 있습니다. | 다른 사용자의 private 프로젝트를 가져올 수 없습니다. 다른 사용자의 public 리포지토리의 웹훅을 관리할 수 있습니다. | 다른 사용자의 private 리포지토리의 웹훅을 관리할 수 없습니다. 다른 사용자의 public 리포지토리를 복제할 수 있습니다. | 다른 사용자의 private 리포지토리를 복제할 수 없습니다.
sungxsoo commented 1 year ago

사용 예정 GITHUB API와 스팩

API GET /users/{username}/repos
용도 해당 Github 사용자의 public 레포지토리 목록을 가져옵니다.
{username} Github 사용자의 이름입니다.
응답 포맷 JSON
응답 필드
name 레포지토리의 이름
description 레포지토리의 설명
url 레포지토리의 URL
created_at 레포지토리의 생성일시
updated_at 레포지토리의 업데이트 일시
pushed_at 레포지토리의 push 일시
language 레포지토리의 주 언어
default_branch 레포지토리의 기본 브랜치 이름
API GET /repos/{owner}/{repo}/pulls
용도 해당 레포지토리에서 생성된 PR 목록을 가져옵니다.
{owner} 레포지토리의 소유자의 이름입니다.
{repo} 레포지토리의 이름입니다.
응답 포맷 JSON
응답 필드
title PR의 제목
user.login PR 작성자의 Github username
API GET /repos/{owner}/{repo}/commits
용도 해당 레포지토리에서 생성된 Commit 목록을 가져옵니다.
{owner} 레포지토리의 소유자의 이름입니다.
{repo} 레포지토리의 이름입니다.
응답 포맷 JSON
응답 필드
commit.message Commit의 메시지
commit.author.name Commit 작성자의 이름
sungxsoo commented 1 year ago

API 테스트

특정 유저의 public 레포지토리들을 가져온 후, 각각의 레포지토리에 대해 commit 내역과 PR 내역을 출력하는 코드입니다. 황규도군의 public 코드들을 대상으로 테스트 진행했습니다.

import requests

headers = {
    'Authorization': 'token {personal_access_key}',
    'Accept': 'application/vnd.github.v3+json'
}

username = "BEMELON"

# 해당 사용자의 public 레포지토리 리스트 가져오기
response = requests.get(f'https://api.github.com/users/{username}/repos', headers=headers)

if response.status_code == 200:
    repositories = response.json()
    for repo in repositories:
        print(f"Repository name: {repo['name']}\n")

        # PR 내역 가져오기
        response = requests.get(f"https://api.github.com/repos/{username}/{repo['name']}/pulls", headers=headers)
        if response.status_code == 200:
            pulls = response.json()
            # PR 제목과 작성자 출력
            print("=== Pull Requests ===")
            for pull in pulls:
                print(f"- {pull['title']} by {pull['user']['login']}")
        else:
            print(f"Failed to retrieve pull requests for {repo['name']}.")

        # Commit 내역 가져오기
        response = requests.get(f"https://api.github.com/repos/{username}/{repo['name']}/commits", headers=headers)
        if response.status_code == 200:
            commits = response.json()
            # Commit 제목과 작성자 출력
            print("\n=== Commits ===")
            for commit in commits:
                print(f"- {commit['commit']['message']} by {commit['commit']['author']['name']}")
        else:
            print(f"Failed to retrieve commits for {repo['name']}.")
        print("\n")
else:
    print(f"Failed to retrieve repositories for {username}.")

테스트 결과

스크린샷 2023-04-03 오후 4 29 51
sungxsoo commented 1 year ago

API 호출 제한

초당 API 요청 횟수: 초당 60회 시간당 API 요청 횟수: 시간당 5,000회 대용량 API 요청 횟수: 대용량 API 요청의 경우, API 응답에 X-RateLimit-Remaining 및 X-RateLimit-Reset 헤더를 포함하며, 이를 통해 사용 가능한 API 요청 수와 다음 API 요청까지 대기해야 하는 시간을 확인할 수 있습니다.

piedroconti commented 1 year ago
카테고리 요청 횟수 기타 정보
Reviewers 1
Labels 0
Commits 1
Conversations 1
Checks 1
File changes 1
Code changes 파일 하나당 1번 요청 per_page 설정에 따라 요청 횟수가 달라짐


100개의 PR이 있고 각 PR에 평균 30개의 File changed가 있는 경우 각 파일(100개의 PR * 30개 파일)에 대한 Code changed를 가져오기 위해 3000번의 요청을 해야 한다. 또한 Reviewers, Commits, Conversations, Checks, File changed 같은 기타 정보에 대해 500개의 추가 요청을 수행한다. (PR 100개 * 고정 요청 5개) 따라서 총 3500번의 요청이 수행된다.

piedroconti commented 1 year ago

Pagination

한 페이지에 표시할 수 있는 데이터의 양을 제한하고 나머지 데이터를 다른 페이지에 분산시키는 방법입니다.

깃허브 API를 사용하여 데이터를 추출할 때 페이지네이션의 크기를 기준으로 요청할 데이터 양을 정할 수 있습니다. 깃허브 API는 요청 시 pageper_page 매개변수를 사용하여 페이지네이션을 지원합니다.

https://api.github.com/users/{username}/repos?page=1&per_page=100


100개의 PR이 있고 각 PR에 30개의 File changed에 대한 Code changed 정보를 가져올 경우 per_page가 100으로 설정되어 있으므로 30개의 File changed 정보를 1번의 요청으로 가져올 수 있다. 따라서 Code changed 정보를 추출하기 위해 3000번의 요청이 아닌 100번의 요청이 발생한다.

PR 조회 요청(1회) + File changed에 대한 Code 조회 요청(100회) + 기타 요청(500회) $\therefore$ 3500번의 요청이 아닌 601번의 요청이 수행된다.