Microsoft MVP성태의 닷넷 이야기
닷넷: 2164. C# - Octokit을 이용한 GitHub Issue 검색 [링크 복사], [링크+제목 복사]
조회: 2363
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 2개 있습니다.)
.NET Framework: 1040. C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근
; https://www.sysnet.pe.kr/2/0/12601

닷넷: 2164. C# - Octokit을 이용한 GitHub Issue 검색
; https://www.sysnet.pe.kr/2/0/13450




C# - Octokit을 이용한 GitHub Issue 검색

Octokit은 예전에도 한번 소개한 적이 있는데요,

C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근
; https://www.sysnet.pe.kr/2/0/12601

그때는 Release를 이용해 가장 최신의 태그 이름을 가져왔었고, 이번에는 이슈와 관련된 정보를 가져오는 예제를 소개합니다.




사실, 제가 작성한 것은 아니고 ^^ 마침 .NET Conf 2023 동영상을 보다가,

Build Intelligent Apps with .NET and Azure
; https://youtu.be/xEFO1sQ2bUc?t=27773

OpenAI를 다루면서 예제 데이터를 GitHub 프로젝트의 이슈로 다루길래 간단하게 베껴 봅니다. ^^

자, 그럼 제가 만들어 두었던 rasp_vusb repo를 대상으로 이슈를 가져올 텐데요, 동영상에서는 GitHub API 키를 이용해야 하는 걸로 오해할 수 있지만,

string githubKey = "...GitHub API Key...";

var githubClient = new GitHubClient(new ProductHeaderValue("octokit_sample"));

if (!string.IsNullOrEmpty(githubKey))
{
    Console.WriteLine("Using GitHub API Token");
    var tokenAuth = new Credentials(githubKey);
    githubClient.Credentials = tokenAuth;
}
else
{
    Console.WriteLine("Using anonymous GitHub API");
}

접근하려는 repository가 public이라면 굳이 PAT 키를 받지 않아도 됩니다. 실제로 이 글에서 예를 들게 될 rasp_vusb repo는 PAT 설정 없이 곧바로,

var options = new ApiOptions();
var githubClient = new GitHubClient(new ProductHeaderValue("octokit_sample"));

사용할 수 있습니다. 이렇게 초기화한 githubClient 인스턴스로, 이제 프로젝트의 "Label" 목록을 다음과 같이 가져올 수 있습니다.

var options = new ApiOptions();
var allLabels = await githubClient.Issue.Labels.GetAllForRepository(org, repoName, options);
Console.WriteLine($"Labels: {allLabels.Count}"); // 출력 결과: Labels: 8

이것은 "https://github.com/stjeong/rasp_vusb/labels" 경로에서 확인할 수 있는, 등록된 라벨 이름을 가져오는데, 특별하게 설정하지 않았다면 8개 정도(bug, duplicate, enhancement, good first issue, help wanted, invalid, question, wontfix)를 반환할 것입니다.




이후 동영상에서는 각각의 Label에 해당하는 이슈를 50개만 가져오는데요,

var repoName = "rasp_vusb"; // repo 이름
var org = "stjeong";        // 소유자 이름

var allIssues = new List<Issue>();

foreach (var label in allLabels)
{
    var request = new RepositoryIssueRequest()
    {
        Filter = IssueFilter.All,
        State = ItemStateFilter.All, // Open, Closed, All (기본값: Open)
    };

    request.Labels.Add(label.Name);

    var apiOptions = new ApiOptions()
    {
        PageSize = 50,
        PageCount = 1,
    };

    var issues = await githubClient.Issue.GetAllForRepository(org, repoName, request, apiOptions);
    allIssues.AddRange(issues);
}

굳이 Label로 그룹핑을 시킬 필요가 없다면 그냥 이렇게 모든 이슈를 가져올 수 있습니다. (너무 많으면 위의 예제에서 페이징 옵션만 가져와 추가하면 됩니다.)

var allIssues = new List<Issue>();

var request = new RepositoryIssueRequest()
{
    Filter = IssueFilter.All,
    State = ItemStateFilter.All,
};

var issues = await githubClient.Issue.GetAllForRepository(org, repoName, request);

allIssues.AddRange(issues);
Console.WriteLine($"All issues: {allIssues.Count()}");




자, 그렇게 해서 이슈를 모두 가져왔으면 적절하게 가공해 저장할 수 있습니다.

var dataCollection = allIssues
        .Select(issue => new GitHubIssue(
                                issue.Title,
                                issue.Body,
                                issue.HtmlUrl)
                            );            

await SaveIssuesToFileAsync(dataCollection, "issues.json");

public static async Task SaveIssuesToFileAsync(IEnumerable<GitHubIssue> data, string fileName)
{
    var filePath = Path.Combine("..", "..", "..", fileName);
    var issuesJson = JsonSerializer.Serialize(data, new JsonSerializerOptions(
        JsonSerializerOptions.Default) { WriteIndented = true });
    await File.WriteAllTextAsync(filePath, issuesJson);
} 

public record GitHubIssue(string Title, string Text, string Url);

저장한 issues.json 파일은 대충 다음과 같은 내용을 담고 있으니,

[
  {
    "Title": "Increase hold of left click",
    "Text": "Hello, thank you for making this project open source. I ran succefully in a raspberry pi zero w. However, I need to hold the left click for around 3-4 seconds. Could you please give a general instruction on how I can achieve this?\r\n\r\nI was trying to add a sleep at the end of MouseDevice::SendRelative function inside the rasp_vusb_server but I\u0027m having some trouble building this project. Could you please inform If I\u0027m in the right path.",
    "Url": "https://github.com/stjeong/rasp_vusb/issues/16"
  },

  // ...[생략]...
]

향후 다시 쿼리를 하지 않고 재사용하시면 되겠습니다. ^^

이 정도면 대충 감이 오시죠. ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




참고로, 위의 예제 코드로 private Repo에 접근하는 경우에는 다음과 같은 식으로 예외가 발생할 것입니다.

Unhandled exception. Octokit.NotFoundException: repos/...[org].../...[repo_name]..../labels was not found.
   at Octokit.ApiPagination.GetAllPages[T](Func`1 getFirstPage, Uri uri) in /_/Octokit/Clients/ApiPagination.cs:line 34
   at octokit_sample.Program.Main(String[] args) in C:\c:\temp\octokit_sample\Program.cs:line 49
   at octokit_sample.Program.<Main>(String[] args)

심지어 PAT 키를 설정했어도 예외가 여전히 발생할 수 있는데요, 해당 PAT의 권한에 ("Full control of private repositories"라는 이름에서 알 수 있듯이) "repo" 항목이,

octokit_issue_1.png

설정되어 있는지 확인해 보셔야 합니다. ^^ (기본값은 off입니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/21/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  5  [6]  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13483정성태12/14/20232315닷넷: 2184. C# - 하나의 resource 파일을 여러 프로그램에서 (AOT 시에도) 사용하는 방법파일 다운로드1
13482정성태12/13/20232908닷넷: 2183. C# - eFriend Expert OCX 예제를 .NET Core/5+ Console App에서 사용하는 방법 [2]파일 다운로드1
13481정성태12/13/20232287개발 환경 구성: 693. msbuild - .NET Core/5+ 프로젝트에서 resgen을 이용한 리소스 파일 생성 방법파일 다운로드1
13480정성태12/12/20232660개발 환경 구성: 692. Windows WSL 2 + Chrome 웹 브라우저 설치
13479정성태12/11/20232340개발 환경 구성: 691. WSL 2 (Ubuntu) + nginx 환경 설정
13477정성태12/8/20232532닷넷: 2182. C# - .NET 7부터 추가된 Int128, UInt128 [1]파일 다운로드1
13476정성태12/8/20232269닷넷: 2181. C# - .NET 8 JsonStringEnumConverter의 AOT를 위한 개선파일 다운로드1
13475정성태12/7/20232335닷넷: 2180. .NET 8 - 함수 포인터에 대한 Reflection 정보 조회파일 다운로드1
13474정성태12/6/20232181개발 환경 구성: 690. 닷넷 코어/5+ 버전의 ilasm/ildasm 실행 파일 구하는 방법 - 두 번째 이야기
13473정성태12/5/20232391닷넷: 2179. C# - 값 형식(Blittable)을 메모리 복사를 이용해 바이트 배열로 직렬화/역직렬화파일 다운로드1
13472정성태12/4/20232198C/C++: 164. Visual C++ - InterlockedCompareExchange128 사용 방법
13471정성태12/4/20232277Copilot - To enable GitHub Copilot, authorize this extension using GitHub's device flow
13470정성태12/2/20232571닷넷: 2178. C# - .NET 8부터 COM Interop에 대한 자동 소스 코드 생성 도입파일 다운로드1
13469정성태12/1/20232297닷넷: 2177. C# - (Interop DLL 없이) CoClass를 이용한 COM 개체 생성 방법파일 다운로드1
13468정성태12/1/20232232닷넷: 2176. C# - .NET Core/5+부터 달라진 RCW(Runtime Callable Wrapper) 대응 방식파일 다운로드1
13467정성태11/30/20232330오류 유형: 882. C# - Unhandled exception. System.Runtime.InteropServices.COMException (0x800080A5)파일 다운로드1
13466정성태11/29/20232499닷넷: 2175. C# - DllImport 메서드의 AOT 지원을 위한 LibraryImport 옵션
13465정성태11/28/20232246개발 환경 구성: 689. MSBuild - CopyToOutputDirectory가 "dotnet publish" 시에는 적용되지 않는 문제파일 다운로드1
13464정성태11/28/20232390닷넷: 2174. C# - .NET 7부터 UnmanagedCallersOnly 함수 export 기능을 AOT 빌드에 통합파일 다운로드1
13463정성태11/27/20232302오류 유형: 881. Visual Studio - NU1605: Warning As Error: Detected package downgrade
13462정성태11/27/20232344오류 유형: 880. Visual Studio - error CS0246: The type or namespace name '...' could not be found
13461정성태11/26/20232380닷넷: 2173. .NET Core 3/5+ 기반의 COM Server를 registry 등록 없이 사용하는 방법파일 다운로드1
13460정성태11/26/20232332닷넷: 2172. .NET 6+ 기반의 COM Server 내에 Type Library를 내장하는 방법파일 다운로드1
13459정성태11/26/20232314닷넷: 2171. .NET Core 3/5+ 기반의 COM Server를 기존의 regasm처럼 등록하는 방법파일 다운로드1
13458정성태11/26/20232335닷넷: 2170. .NET Core/5+ 기반의 COM Server를 tlb 파일을 생성하는 방법(tlbexp)
13457정성태11/25/20232275VS.NET IDE: 187. Visual Studio - 16.9 버전부터 추가된 "Display inline type hints" 옵션
1  2  3  4  5  [6]  7  8  9  10  11  12  13  14  15  ...