ほぼ無料で優秀なレビュアーを1人増やせるツール、Qodo Mergeを試す

2024/12/17に公開

TL;DR

  • コードレビューをAIに任せられる、Qodo Mergeという無料で簡単に使えるOSSツールがあるよ
  • 設定できる項目が多いから使いそうなものを抽出してみたよ
  • 筆者は、PRの要約やAIのレビューに対する質問ができる機能ががありがたいなと思ったよ

私たちの研究室

https://nisk.doshisha.ac.jp/

アドベントカレンダー 17日目🎄

https://nislab-advent-calendar-2024-12.vercel.app/

Qodo Mergeとは

Qodo Merge(旧 PR-Agent)とは、AIを用いてプルリクエスト(PR)のレビューを効率化するOSSツールです。
Qodo Mergeを使うことで「GitHubでPRを立てれば自動でAIがコードレビューをしてくれる」仕組みを簡単に構築することができます。
無料で使えるQodo Mergeと、月額課金で使えるQodo Merge Proの2種類があり、Pro版では無料版の機能に加えてカスタムプロンプトなどの機能が使えます。

今回は無料版のQodo Mergeを使ってみます。

使ってみる

必要なものはOpenAIのAPIキーのみです。(必要な料金もOpenAIのAPI利用料のみです)
作業自体はInstallationに書いてある通りでとても簡単です。

まず、リポジトリのGithub Actionsの設定のところでOPENAI_KEYの環境変数を設定します。

次に、リポジトリに.github/workflows/pr_agent.yamlのファイルを以下の内容で作成し、mainブランチにマージします。

pr_agent.yaml
on:
  pull_request:
    types: [opened, reopened, ready_for_review]
  issue_comment:
jobs:
  pr_agent_job:
    if: ${{ github.event.sender.type != 'Bot' }}
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
      contents: write
    name: Run pr agent on every pull request, respond to user comments
    steps:
      - name: PR Agent action step
        id: pragent
        uses: Codium-ai/pr-agent@main
        env:
          OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # opeanAIのモデル選択
          config.model: "o1-preview"
          # opeanAIのフォールバックモデル選択
          config.fallback_model: "o1-preview-2024-09-12"
          # pr_descriptionにおいてPRのタイトルも自動生成する
          pr_description.generate_ai_title: true 
          # pr_descriptionにおける追加プロンプト
          pr_description.extra_instructions: "回答は必ず全て日本語で記述してください。" 
          # pr_reviewにおける追加プロンプト
          pr_reviewer.extra_instructions: "回答は必ず全て日本語で記述してください。" 
          # pr_reviewにおけるPRにスコアを付ける機能を有効化
          pr_reviewer.require_score_review: true
          # pr_reviewにおけるPRが分割できるかどうかを判断してくれる機能を有効化
          pr_reviewer.require_can_be_split_review: true
          # pr_improveにおける追加のプロンプト
          pr_code_suggestions.extra_instructions: "回答は必ず全て日本語で記述してください。" 
          # pr_improveにおいて問題点にのみ焦点を当てるか(falseだと可読性や保守性等の指摘が入りやすくなる)
          pr_code_suggestions.focus_only_on_problems: false
          # pr_improveにおける各提案にGood/Badの評価をつける機能を有効化
          pr_code_suggestions.allow_thumbs_up_down: true
          # pr_update_changelogにおける追加のプロンプト
          pr_update_changelog.extra_instructions: "回答は必ず全て日本語で記述してください。" 

コメントが付いている部分の設定は自分なりに機能を有効化したり、日本語対応させたりするために追加したものです。複雑ですがこちらに設定できる全項目が並んでいます。

また、openAIのモデルはここの一覧から選ぶこともできます。
デフォルトのgpt-4oでは何をやっても日本語で返してくれなかったので、ここではo1-previewを選択してみました。

以上で設定は完了です。
あとはPRを立てれば自動でAIがレビューをしてくれるようになります。

今回はNext.jsのcreate-next-appで最初に作成されるpage.tsxを少し修正したPRを立ててみます。

修正後のコードはChatGPTに「以下を企業のランディングページみたいに少し書き換えて」と修正前のコードを添えて作ってもらいました。
ヘッダー要素がや仮の企業情報が追加され、その他CSSや文言も少し修正されたみたいです。

修正前のコード
page.tsx
import Image from "next/image";

export default function Home() {
  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
        <Image
          className="dark:invert"
          src="/next.svg"
          alt="Next.js logo"
          width={180}
          height={38}
          priority
        />
        <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
          <li className="mb-2">
            Get started by editing{" "}
            <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
              src/app/page.tsx
            </code>
            .
          </li>
          <li>Save and see your changes instantly.</li>
        </ol>

        <div className="flex gap-4 items-center flex-col sm:flex-row">
          <a
            className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            <Image
              className="dark:invert"
              src="/vercel.svg"
              alt="Vercel logomark"
              width={20}
              height={20}
            />
            Deploy now
          </a>
          <a
            className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
            href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            Read our docs
          </a>
        </div>
      </main>
      <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/file.svg"
            alt="File icon"
            width={16}
            height={16}
          />
          Learn
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/window.svg"
            alt="Window icon"
            width={16}
            height={16}
          />
          Examples
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/globe.svg"
            alt="Globe icon"
            width={16}
            height={16}
          />
          Go to nextjs.org →
        </a>
      </footer>
    </div>
  );
}
修正後のコード
page.tsx
import Image from "next/image";

export default function Home() {
  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <header className="flex flex-col items-center gap-4">
        <Image
          className="dark:invert"
          src="/next.svg"
          alt="Company logo"
          width={200}
          height={50}
          priority
        />
        <h1 className="text-3xl font-bold text-center sm:text-left">Innovate with Confidence</h1>
        <p className="text-center sm:text-left text-gray-600 dark:text-gray-400 max-w-2xl">
          Empowering businesses with cutting-edge solutions and seamless deployment. Discover the tools to bring your ideas to life.
        </p>
      </header>

      <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
        <section className="flex flex-col items-center gap-8 sm:items-start">
          <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
            <li className="mb-2">
              Start by exploring our resources and tools to accelerate your journey.
            </li>
            <li>Join a global community of innovators and creators.</li>
          </ol>

          <div className="flex gap-4 items-center flex-col sm:flex-row">
            <a
              className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-blue-600 text-white hover:bg-blue-700 text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
              href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
              target="_blank"
              rel="noopener noreferrer"
            >
              <Image
                className="dark:invert"
                src="/vercel.svg"
                alt="Deploy Icon"
                width={20}
                height={20}
              />
              Get Started
            </a>
            <a
              className="rounded-full border border-solid border-gray-300 dark:border-gray-600 transition-colors flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
              href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
              target="_blank"
              rel="noopener noreferrer"
            >
              Learn More
            </a>
          </div>
        </section>

        {/* Company Info Section */}
        <section className="flex flex-col gap-4 max-w-2xl">
          <h2 className="text-2xl font-bold">About Our Company</h2>
          <p className="text-gray-600 dark:text-gray-400">
            We are a technology company dedicated to empowering businesses worldwide with innovative solutions. Our mission is to bridge the gap between vision and execution through cutting-edge tools and platforms.
          </p>
          <ul className="text-sm text-gray-600 dark:text-gray-400">
            <li><strong>Founded:</strong> 2015</li>
            <li><strong>Headquarters:</strong> San Francisco, CA</li>
            <li><strong>Our Mission:</strong> Innovating to create a seamless digital future for businesses.</li>
          </ul>
        </section>
      </main>

      <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/file.svg"
            alt="File icon"
            width={16}
            height={16}
          />
          Tutorials
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/window.svg"
            alt="Window icon"
            width={16}
            height={16}
          />
          Templates
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/globe.svg"
            alt="Globe icon"
            width={16}
            height={16}
          />
          Visit Next.js →
        </a>
      </footer>
    </div>
  );
}

PRを立てると、GitHub Actionsが動き出します。
1,2分経つとPRがどんどん華やかに更新されていきます。
以下に詳細をまとめます。

Describe

DescribeはPRを要約してくれる機能です。
以下のようなことをやってくれました。

  • PRタイトルの自動付与
  • PR種別の自動判別
  • 差分の要約

Review

ReviewはPRのレビューを実際にやってくれるというよりは、レビューをするための考慮点などをまとめてくれる機能です。
以下のようなことをやってくれました。

  • レビューに掛かる労力の見積もり
  • PRに対する点数を付ける(100点満点っぽい?)
  • セキュリティ上の懸念があるかの判断
  • どこにフォーカスを当ててレビューすべきかの助言

Improve

ImproveはPRのレビューを実際にやってくれる機能です。
人間の目では見落とす可能性がある細かい点まで指摘してくれていますね。

Ask

AskはAIが行ったPRのレビューに関して質問できる機能です。
/ask "[質問内容]"とコメントすると質問に回答してくれます。

Update Changelog

Update Changelogはchangelogを作成してくれる機能です。
/update_changelogとコメントするとchangelogを作成してくれます。リリース時に便利ですね。

感想

思った以上にできることが多くてビックリしました。カスタマイズ性も高く、そして何より簡単で使いやすい。本当に開発人員が1人増えたみたいです。

普段はPRを立てるときは自分で伝わりやすくするように要約していたので、要約してくれる機能は大変有難いと思いました。(PRテンプレートを使わなくなるかも)

また、PRコメントの意図がどうしても分からず質問したくなることもありますが、この点もAskという機能で考慮されているところも素晴らしいと思います。

NISLab 小板研究室

Discussion