성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Octokit을 이용한 GitHub Issue 검색</h1> <p> Octokit은 예전에도 한번 소개한 적이 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12601'>https://www.sysnet.pe.kr/2/0/12601</a> </pre> <br /> 그때는 Release를 이용해 가장 최신의 태그 이름을 가져왔었고, 이번에는 이슈와 관련된 정보를 가져오는 예제를 소개합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 사실, 제가 작성한 것은 아니고 ^^ 마침 .NET Conf 2023 동영상을 보다가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Build Intelligent Apps with .NET and Azure ; <a target='tab' href='https://youtu.be/xEFO1sQ2bUc?t=27773'>https://youtu.be/xEFO1sQ2bUc?t=27773</a> </pre> <br /> OpenAI를 다루면서 예제 데이터를 GitHub 프로젝트의 이슈로 다루길래 간단하게 베껴 봅니다. ^^<br /> <br /> 자, 그럼 제가 만들어 두었던 <a target='tab' href='https://github.com/stjeong/rasp_vusb'>rasp_vusb</a> repo를 대상으로 이슈를 가져올 텐데요, 동영상에서는 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12601#pat'>GitHub API 키</a>를 이용해야 하는 걸로 오해할 수 있지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string githubKey = "...<a target='tab' href='https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens'>GitHub API Key</a>..."; 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"); } </pre> <br /> 접근하려는 repository가 public이라면 굳이 PAT 키를 받지 않아도 됩니다. 실제로 이 글에서 예를 들게 될 rasp_vusb repo는 PAT 설정 없이 곧바로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var options = new ApiOptions(); var githubClient = new GitHubClient(new ProductHeaderValue("octokit_sample")); </pre> <br /> 사용할 수 있습니다. 이렇게 초기화한 githubClient 인스턴스로, 이제 프로젝트의 "Label" 목록을 다음과 같이 가져올 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var options = new ApiOptions(); var allLabels = await githubClient.Issue.Labels.GetAllForRepository(org, repoName, options); Console.WriteLine($"Labels: {allLabels.Count}"); // 출력 결과: Labels: 8 </pre> <br /> 이것은 "<a target='tab' href='https://github.com/stjeong/rasp_vusb/labels'>https://github.com/stjeong/rasp_vusb/labels</a>" 경로에서 확인할 수 있는, 등록된 라벨 이름을 가져오는데, 특별하게 설정하지 않았다면 8개 정도(bug, duplicate, enhancement, good first issue, help wanted, invalid, question, wontfix)를 반환할 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이후 동영상에서는 각각의 Label에 해당하는 이슈를 50개만 가져오는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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); } </pre> <br /> 굳이 Label로 그룹핑을 시킬 필요가 없다면 그냥 이렇게 모든 이슈를 가져올 수 있습니다. (너무 많으면 위의 예제에서 페이징 옵션만 가져와 추가하면 됩니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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()}"); </pre> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그렇게 해서 이슈를 모두 가져왔으면 적절하게 가공해 저장할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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); </pre> <br /> 저장한 issues.json 파일은 대충 다음과 같은 내용을 담고 있으니,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [ { "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" }, // ...[생략]... ] </pre> <br /> 향후 다시 쿼리를 하지 않고 재사용하시면 되겠습니다. ^^<br /> <br /> 이 정도면 대충 감이 오시죠. ^^<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2103&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, 위의 예제 코드로 private Repo에 접근하는 경우에는 다음과 같은 식으로 예외가 발생할 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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) </pre> <br /> 심지어 PAT 키를 설정했어도 예외가 여전히 발생할 수 있는데요, 해당 PAT의 권한에 ("Full control of private repositories"라는 이름에서 알 수 있듯이) "repo" 항목이,<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='octokit_issue_1.png' src='/SysWebRes/bbs/octokit_issue_1.png' /><br /> <br /> 설정되어 있는지 확인해 보셔야 합니다. ^^ (기본값은 off입니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8372
(왼쪽의 숫자를 입력해야 합니다.)