6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

概要

下記の記事のように、GitLabのAPIを実行してIssueを起票するツールをJavaで実装して運用していました。ところがいろいろあってGitLabからGitHubに移行しなければならなくなりました。

同じようなことができるライブラリを探していたところ、hub4j/github-apiを見つけ、これを利用すれば同じことが実現できました。ここではhub4j/github-apiを利用した実装について簡単にまとめます。

参考ドキュメント

下記のGitHubのリポジトリとリポジトリからリンクされている公式ドキュメントを参考にしました。

事前準備

gradleプロジェクトの準備

下記の環境にて開発を行いました。

gradle : 8.10
java   : 21

build.gradleは下記になります。利用するライブラリは本記事執筆時点での最新のものになります。

apply plugin: "java"

repositories {
  mavenCentral()
}

java {
  sourceCompatibility = JavaVersion.VERSION_21
  targetCompatibility = JavaVersion.VERSION_21
}

compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'

test {
  useJUnitPlatform()
}

dependencies {
  // hub4j/github-api
  implementation "org.kohsuke:github-api:2.0.0-alpha-2"

  // for unit test
  testImplementation "org.junit.jupiter:junit-jupiter:5.11.3"
  testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.3"
  testImplementation "org.hamcrest:hamcrest-core:3.0-rc1"
  testImplementation "org.mockito:mockito-core:5.14.2"
  testImplementation "org.mockito:mockito-inline:5.2.0"
  testImplementation "org.mockito:mockito-junit-jupiter:5.14.2"
  testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.11.3"
  testRuntimeOnly "org.junit.platform:junit-platform-launcher:1.9.3"
}

PATの準備

下記を参考に、Personal Access Token(PAT)を発行します。トークンにはFine-grainedclassicがありますが、ここではclassicを利用しました。

実装

ここからJavaのコードを実装していきます。ここから先に実装するGitHubのAPIを実行する処理はすべてorg.kohsuke.github.GitHubクラスを介して行います。

今回はこれをラップするGitHubClientクラスを実装して進めます。

認証

まずはじめに、PATを使ってGitHubへの認証部分を実装します。

public class GitHubClient {

    private GitHub gitHub;

    // tokenは事前に発行しておいたPAT
    public GitHubClient(String token) throws IOException {
        super();
        gitHub = new GitHubBuilder().withOAuthToken(token).build();
    }
}

Repositoryオブジェクトの取得

ここでは特定のリポジトリに対しての操作を行います。そのためにはまずGitHuubクラスを介してリポジトリオブジェクトを取得する必要があります。

private GHRepository getRepository(String repositoryName) throws IOException {
    return gitHub.getRepository(repositoryName);
}

引数のrepositoryNameはGitHubRepositoryのowner/nameの文字列を渡します。
例えばhttps://github.com/qiita/sampleというリポジトリの場合はqiita/sampleを指定します。

Issueの一覧の取得

指定のGitHubのリポジトリに作成されたIssueの一覧を取得するAPIを実装します。

public List<GHIssue> getIssues(String repositoryName) throws IOException {
    GHRepository repository = getRepository(repositoryName);
    List<GHIssue> result = repository.getIssues(GHIssueState.ALL);
    result.stream().forEach(issue -> {
        System.out.println(issue.getNumber());// issueの番号
        System.out.println(issue.getTitle());// issueのタイトル
        System.out.println(issue.getBody());// issueの本文
        issue.getLabels().forEach(e -> {
            System.out.println(e.getName());// issueのラベル
        });
    });
    return result;
}

引数にGHIssueState.ALLを指定しているので、すべてのIssueを取得しています。

Issueの作成

指定のGitHubのリポジトリにIssueを起票します。ここでは下記の要素を指定します。

  • タイトル
  • 説明
  • ラベル
public GHIssue createIssue(String repositoryName, String title, String body, String label) throws IOException {
    GHRepository repository = getRepository(repositoryName);
    GHIssue createdIssue = repository.createIssue(title).body(body).label(label).create();
    System.out.println(createdIssue.getNumber());// issueの番号
    System.out.println(createdIssue.getTitle());// issueのタイトル
    System.out.println(createdIssue.getBody());// issueの本文
    createdIssue.getLabels().forEach(e -> {
        System.out.println(e.getName());// issueのラベル
    });
    return createdIssue;
}

このAPIはIssueの起票に成功すると、起票したIssueのオブジェクトを返してくれます。

Issueの説明は、下記のようにMarkdown形式の文字列を指定すれば、その通りに起票してくれます。

String getBody() {
    StringBuilder sb = new StringBuilder();
    sb.append("# Overview");
    sb.append(System.lineSeparator());
    sb.append(System.lineSeparator());
    sb.append("|COLUMN1|COLUMN2|COLUMN3|");
    sb.append(System.lineSeparator());
    sb.append("|---|---|---|");
    sb.append(System.lineSeparator());
    sb.append("|hoge|fuga|piyo|");
    sb.append(System.lineSeparator());
    sb.append("|foo|bar|baz|");
    return sb.toString();
}

Branchの一覧を取得する

指定のGitHubのリポジトリに存在するBranchの一覧を取得してみます。

public Map<String, GHBranch> getBranches(String repositoryName) throws IOException {
    GHRepository repository = getRepository(repositoryName);
    Map<String, GHBranch> result = repository.getBranches();
    repository.getBranches().forEach((branchName, branchObj) -> {
        System.out.println(branchName);// ブランチ名
    });
    return result;
}

このAPIはBranch名をキー、BranchオブジェクトをバリューとしたMapを返します。

UNITテストを使って簡単に動作確認しながら開発する

GitHubにIssueを起票するAPIを実行するというのは、多くの場合ツールの一部に組み込まれているものと思います。私の場合は下記のような処理をする社内ツールの一部で利用しています。

  1. 定期的にS3にアップロードされるCSVファイルをダウンロードする
  2. CSVを解析し、起票すべき情報を抽出する
  3. GitHubリポジトリから起票済みのIssueの一覧を取得し、重複チェックする
  4. 起票すべきIssueを起票する

GitHubのAPIを実行するのは処理の後半です。動作確認をするたびに処理を最初から実行するのは効率が悪いです。GitHubのAPI実行部分を単独で実行しながら開発したいです。

こういった場合に思いつくのがUnitテストですが、ここで問題が発生します。PATはシークレットな情報なので、ハードコーディングすることができません。PATをリポジトリにコミットしてしまうことは情報漏洩につながります。

NGの例

下記のコードはNGの例です。

@Test
void getIssuesTest() throws Exception {
    String repositoryName = "qiita/sample";
    // PATをハードコーディングするのはNG
    GitHubClient gitHubClient = new GitHubClient("my-personal-access-token");
    gitHubClient.getIssues(repositoryName);
}

各開発者がローカルで動作確認するたびにテストコードを書き換え、PATのコーディング部分をコミットしないように気を付けるのもなしではないですが、もう少しスマートにやりたいです。

解決策の一例

私は下記のように、PATを環境変数にセットし、それを利用する形がこの問題の解決策のひとつではないかと考えています。

@Test
@EnabledIfEnvironmentVariable(named = "GITHUB_PAT", matches = "ghp_.*")
void getIssuesTest() throws Exception {
    String repositoryName = "qiita/sample";
    GitHubClient gitHubClient = new GitHubClient(System.getenv("GITHUB_PAT"));
    gitHubClient.getIssues(repositoryName);
}

環境変数は、Unitテストの実行構成の環境変数にセットします。こうすれば、PATをハードコーディングすることなく、各開発者が自分のPATを利用して動作確認することができます。

image.png

EnabledIfEnvironmentVariableは、環境変数が指定されていなければテストを実行しないようにするアノテーションです。

GitHubの公式より、classicのPATはghp_をプレフィックスとする文字列なので、環境変数の条件としてghp_から始まるものという条件を付けています。

これにより、各開発者はPATを一切ハードコーディングすることなく、かつ自分のPATを利用して動作確認しながら開発することができます。

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?