GitHubのPR file changesの中身をmarkdownのcode形式で抽出するコマンドを作った

2024/06/30に公開

GitHubのPR file changesの中身をmarkdownのcode形式で抽出するコマンドを作った

まあこんなことしなくても取得する方法ある気もしますが、作ってみました。

https://github.com/danimal141/ext-pr-diff

これは何か

ChatGPTやClaudeにコードのdiffを投げて、解説してもらいたいケースが多く、リポジトリとPR番号を指定したら、diffを出力してそれをコピペで生成AIにぶん投げるためのコマンド。

ほぼすべてClaudeに実装させたので、実装時間15分ぐらいです。

実装

main.go

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"

	"github.com/spf13/cobra"
)

type FileChange struct {
	Filename string `json:"filename"`
	Patch    string `json:"patch"`
}

var (
	prNumber  string
	repoOwner string
	repoName  string
)

var rootCmd = &cobra.Command{
	Use:   "ext_pr_diff",
	Short: "A tool to fetch and display GitHub PR diffs",
	Long:  `This tool fetches the diff for a specified GitHub Pull Request and displays it in a markdown-friendly format.`,
	Run: func(cmd *cobra.Command, args []string) {
		fetchAndDisplayDiff()
	},
}

func init() {
	rootCmd.Flags().StringVarP(&prNumber, "pr", "p", "", "Pull Request number (required)")
	rootCmd.Flags().StringVarP(&repoOwner, "owner", "o", "", "Repository owner (required)")
	rootCmd.Flags().StringVarP(&repoName, "repo", "r", "", "Repository name (required)")
	rootCmd.MarkFlagRequired("pr")
	rootCmd.MarkFlagRequired("owner")
	rootCmd.MarkFlagRequired("repo")
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func fetchAndDisplayDiff() {
	// auth token is needed.
	cmd := exec.Command("gh", "auth", "token")
	tokenBytes, err := cmd.Output()
	if err != nil {
		log.Fatalf("Error getting GitHub token: %v", err)
	}
	token := string(tokenBytes[:len(tokenBytes)-1]) // 改行を削除

	url := fmt.Sprintf("https://api.github.com/repos/%s/%s/pulls/%s/files", repoOwner, repoName, prNumber)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Fatalf("Error creating request: %v", err)
	}

	req.Header.Set("Authorization", "token "+token)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("Error sending request: %v", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Error reading response: %v", err)
	}

	var files []FileChange
	err = json.Unmarshal(body, &files)
	if err != nil {
		log.Fatalf("Error parsing JSON: %v", err)
	}

	for _, file := range files {
		fmt.Printf("```diff\n")
		fmt.Printf("# %s\n", file.Filename)
		fmt.Printf("%s\n", file.Patch)
		fmt.Printf("```\n\n")
	}
}

Claudeによる上記実装の解説

このコードは、GitHub のプルリクエスト(PR)の差分を取得し、マークダウン形式で表示するGoのコマンドラインツールです。主な機能は以下の通りです:

  1. コマンドライン引数の処理:

    • cobra ライブラリを使用して、PRナンバー、リポジトリ所有者、リポジトリ名を必須の引数として受け取ります。
  2. GitHub認証:

    • gh コマンドを使用してGitHubの認証トークンを取得します。
  3. GitHub APIへのリクエスト:

    • 指定されたPR、リポジトリ所有者、リポジトリ名を使用してGitHub APIのエンドポイントを構築します。
    • 取得した認証トークンを使用してAPIリクエストを行います。
  4. レスポンスの処理:

    • APIレスポンスをJSONとしてパースし、各ファイルの変更内容を取得します。
  5. 差分の表示:

    • 各ファイルの変更内容をマークダウンのコードブロック形式で出力します。
    • ファイル名とパッチ(変更内容)を表示します。

このツールを使用することで、指定したGitHubのプルリクエストの差分を簡単に取得し、マークダウン形式で表示できます。これは、PRのレビューや変更内容の確認に役立ちます。

コードの説明や詳細な解説が必要な場合は、お知らせください。

Example

こういうノリで活用できます。

https://github.com/danimal141/ext-pr-diff?tab=readme-ov-file#example

まとめ

結論、Claude3.5 Sonnetは最高です。

Discussion