0Pricing
AI Agents · Pelajaran

Komentar Tinjauan PR Otomatis

Baca perbedaan PR dan kirim komentar tinjauan sebaris melalui API.

Komentar Tinjauan PR Otomatis adalah pelajaran AI Agents gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Agents, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Agents mencakup 4 pelajaran total.

Apa Itu Tinjauan PR Otomatis

Agen tinjauan PR otomatis membaca berkas yang diubah, menganalisis perbedaan, dan mengirim komentar tinjauan yang terarah — seperti peninjau manusia, tetapi lebih cepat dan konsisten. Kasus penggunaannya meliputi pemindaian keamanan, penegakan gaya, pendeteksian bug umum, pemeriksaan cakupan pengujian, atau peringkasan fungsi suatu PR.

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')

# Get a specific pull request by number
pr = repo.get_pull(42)
print(f'PR #{pr.number}: {pr.title}')
print(f'Author: {pr.user.login}')
print(f'Base branch: {pr.base.ref} <- Head: {pr.head.ref}')
print(f'State: {pr.state}')
print(f'Changed files: {pr.changed_files}')
print(f'Additions: +{pr.additions}')
print(f'Deletions: -{pr.deletions}')

Mencantumkan PR Terbuka

Gunakan repo.get_pulls() untuk mencantumkan PR. Saring berdasarkan status (open, closed), cabang dasar, atau urutan pengurutan. Untuk bot peninjau, biasanya Anda memproses PR open yang belum ditinjau.

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')

# All open PRs targeting main
open_prs = repo.get_pulls(
    state='open',
    base='main',
    sort='created',
    direction='desc'
)

for pr in open_prs:
    # Check if agent has already reviewed it
    reviews = list(pr.get_reviews())
    agent_reviewed = any(
        r.user.login == 'my-agent-bot[bot]'
        for r in reviews
    )
    if not agent_reviewed:
        print(f'Needs review: PR #{pr.number} - {pr.title}')

Mendapatkan Berkas yang Diubah

pr.get_files() mengembalikan daftar objek File — masing-masing merepresentasikan satu berkas yang diubah dalam PR. Properti utamanya adalah: filename, status (ditambahkan/diubah/dihapus), additions, deletions, dan patch (teks perbedaan terpadu).

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)

changed_files = list(pr.get_files())
print(f'Changed files: {len(changed_files)}')

for file in changed_files:
    print(f'\n{file.status.upper()}: {file.filename}')
    print(f'  +{file.additions} -{file.deletions}')

    # The patch is the unified diff
    if file.patch:
        print(f'  Patch preview: {file.patch[:200]}')

# Filter to only Python files
python_files = [
    f for f in changed_files
    if f.filename.endswith('.py') and f.status != 'removed'
]

Membaca Patch Berkas (Perbedaan)

file.patch adalah string perbedaan terpadu. Baris yang dimulai dengan + ditambahkan, sedangkan baris yang dimulai dengan - dihapus. Tajuk @@ menunjukkan nomor baris tempat bagian tersebut dimulai. Uraikan informasi ini untuk mengidentifikasi dengan tepat lokasi masalah dalam kode baru.

def parse_patch_hunks(patch):
    if not patch:
        return []

    hunks = []
    current_hunk = None
    new_line_num = 0

    for line in patch.split('\n'):
        if line.startswith('@@'):
            # Parse: @@ -old_start,old_count +new_start,new_count @@
            import re
            match = re.search(r'\+([0-9]+)', line)
            if match:
                new_line_num = int(match.group(1))
            current_hunk = {'header': line, 'lines': [], 'start_line': new_line_num}
            hunks.append(current_hunk)
        elif current_hunk is not None:
            if line.startswith('+'):
                current_hunk['lines'].append({'type': 'add', 'content': line[1:], 'line': new_line_num})
                new_line_num += 1
            elif line.startswith('-'):
                current_hunk['lines'].append({'type': 'remove', 'content': line[1:], 'line': None})
            else:
                new_line_num += 1

    return hunks

# --- demo ---
patch = (
    '@@ -10,3 +10,4 @@\n'
    ' def add(a, b):\n'
    '-    return a - b\n'
    '+    return a + b\n'
    '+    # fixed subtraction bug\n'
)
for hunk in parse_patch_hunks(patch):
    print(hunk['header'])
    for line in hunk['lines']:
        print(' ', line)

Mendapatkan Komit PR

pr.get_commits() mengembalikan komit dalam PR. Setiap komit memiliki pesan, penulis, dan daftar berkas yang diubah. Menganalisis pesan komit membantu bot peninjau meringkas fungsi PR dan memeriksa praktik komit yang baik.

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)

commits = list(pr.get_commits())
print(f'Commits in PR: {len(commits)}')

for commit in commits:
    print(f'\nCommit: {commit.sha[:8]}')
    print(f'Author: {commit.commit.author.name}')
    print(f'Message: {commit.commit.message[:80]}')
    print(f'Changes: +{commit.stats.additions} -{commit.stats.deletions}')

# Check commit message quality
poor_messages = [
    c for c in commits
    if len(c.commit.message.split()[0] if c.commit.message else '') < 3
    or c.commit.message.lower().startswith(('wip', 'fix', 'test'))
]
if poor_messages:
    print(f'{len(poor_messages)} commits have poor message quality')

Memublikasikan Tinjauan PR

Gunakan pr.create_review() untuk mengirimkan tinjauan formal. Parameter event mengontrol jenis tinjauan: 'APPROVE', 'REQUEST_CHANGES', atau 'COMMENT'. Tinjauan dapat mencakup isi umum dan daftar komentar sebaris pada baris tertentu.

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)

# Submit a review with a general comment (no inline comments)
pr.create_review(
    body=(
        '**Automated Code Review**\n\n'
        'I analyzed this PR and found the following:\n'
        '- No security issues detected\n'
        '- Style conforms to project guidelines\n'
        '- Test coverage looks adequate\n\n'
        '_This review was generated automatically._'
    ),
    event='COMMENT'  # 'APPROVE', 'REQUEST_CHANGES', or 'COMMENT'
)

Memublikasikan Komentar Tinjauan Sebaris

Komentar sebaris muncul langsung pada baris tertentu dalam perbedaan. Setiap komentar menentukan path (jalur berkas), line (nomor baris dalam berkas baru), dan body. Teruskan daftar komentar ke pr.create_review(comments=[...]).

from github import Github
import os

g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)

# Build inline comments from analysis results
inline_comments = [
    {
        'path': 'src/auth.py',
        'line': 47,
        'body': (
            ':warning: **Security Issue:** Using `md5` for password hashing.\n'
            'Use `bcrypt` or `argon2` instead.\n'
            'Example: `import bcrypt; bcrypt.hashpw(password.encode(), bcrypt.gensalt())`'
        )
    },
    {
        'path': 'src/utils.py',
        'line': 23,
        'body': (
            ':bulb: Consider using `pathlib.Path` instead of `os.path.join` '
            'for better cross-platform compatibility.'
        )
    }
]

pr.create_review(
    body='Security and style review complete. See inline comments.',
    event='REQUEST_CHANGES',
    comments=inline_comments
)
print(f'Review submitted with {len(inline_comments)} inline comments')

Analisis Kode Berbasis LLM

Masukkan perbedaan berkas ke LLM dengan perintah tinjauan keamanan/kualitas. Minta respons JSON terstruktur yang mencantumkan masalah beserta jalur berkas, nomor baris, tingkat keparahan, dan deskripsi. Gunakan hasil ini untuk membuat daftar komentar sebaris.

import anthropic
import json
import os

client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])

def analyze_file_with_llm(filename, patch):
    prompt = (
        f'Review this Python code diff for issues.\n'
        f'File: {filename}\n\n'
        f'Return JSON array: [{{"line": N, "severity": "error|warning|info", '
        f'"message": "..."}}]\n'
        f'Focus on: security vulnerabilities, bugs, and anti-patterns.\n'
        f'Diff:\n{patch[:3000]}'
    )

    response = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=1000,
        messages=[{'role': 'user', 'content': prompt}]
    )

    try:
        issues = json.loads(response.content[0].text)
        return [
            {'path': filename, 'line': i.get('line', 1),
             'body': f'[{i.get("severity", "info").upper()}] {i.get("message", "")}'}
            for i in issues if isinstance(i, dict)
        ]
    except json.JSONDecodeError:
        return []

Alur Lengkap Agen Tinjauan PR

Hubungkan semua bagian menjadi alur tinjauan PR lengkap: ambil berkas, saring berkas yang dapat ditinjau, analisis masing-masing dengan LLM, kumpulkan komentar, dan kirimkan tinjauan dalam satu pemanggilan API. Batasi hingga 10 berkas yang diubah pertama untuk mengendalikan biaya LLM.

def run_pr_review(g, repo_name, pr_number):
    repo = g.get_repo(repo_name)
    pr = repo.get_pull(pr_number)

    print(f'Reviewing PR #{pr_number}: {pr.title}')

    # Get changed files (limit to 10)
    files = [
        f for f in list(pr.get_files())[:10]
        if f.filename.endswith(('.py', '.js', '.ts'))
        and f.patch
    ]

    all_comments = []
    for file in files:
        print(f'  Analyzing: {file.filename}')
        comments = analyze_file_with_llm(file.filename, file.patch)
        all_comments.extend(comments)

    # Determine review event based on findings
    critical = [c for c in all_comments if '[ERROR]' in c['body']]
    event = 'REQUEST_CHANGES' if critical else 'COMMENT'

    summary = (
        f'Analyzed {len(files)} files. '
        f'Found {len(all_comments)} issues '
        f'({len(critical)} critical).'
    )

    pr.create_review(body=summary, event=event, comments=all_comments[:25])
    print(f'Review submitted: {event} with {len(all_comments)} comments')

Memeriksa Tinjauan Agen yang Sudah Ada

Sebelum memublikasikan tinjauan, periksa apakah bot agen Anda sudah meninjau PR ini dalam status saat ini. Hindari tinjauan duplikat dengan memeriksa daftar tinjauan yang ada untuk nama pengguna bot Anda.

def should_review_pr(pr, bot_username):
    reviews = list(pr.get_reviews())

    # Check if bot already reviewed this PR
    bot_reviews = [
        r for r in reviews
        if r.user and r.user.login == bot_username
    ]

    if not bot_reviews:
        return True  # No previous review, proceed

    # Check if new commits were pushed since last review
    last_review_time = max(r.submitted_at for r in bot_reviews)
    commits = list(pr.get_commits())
    latest_commit_time = commits[-1].commit.author.date if commits else None

    if latest_commit_time and latest_commit_time > last_review_time:
        print(f'New commits since last review, re-reviewing...')
        return True

    print(f'PR #{pr.number} already reviewed by {bot_username}')
    return False

# --- demo: minimal stand-ins for PyGithub-style PR/review/commit objects ---
import datetime
from types import SimpleNamespace

class _FakeCommit:
    def __init__(self, date):
        self.commit = SimpleNamespace(author=SimpleNamespace(date=date))

class _FakeReview:
    def __init__(self, login, submitted_at):
        self.user = SimpleNamespace(login=login)
        self.submitted_at = submitted_at

class _FakePR:
    def __init__(self, number, reviews, commits):
        self.number = number
        self._reviews = reviews
        self._commits = commits
    def get_reviews(self):
        return self._reviews
    def get_commits(self):
        return self._commits

pr_never_reviewed = _FakePR(101, [], [_FakeCommit(datetime.datetime(2026, 8, 1))])
print('PR #101 needs review:', should_review_pr(pr_never_reviewed, 'coddy-bot'))

review_time = datetime.datetime(2026, 8, 1, 10, 0)
pr_up_to_date = _FakePR(102, [_FakeReview('coddy-bot', review_time)],
                        [_FakeCommit(review_time - datetime.timedelta(hours=1))])
print('PR #102 needs review:', should_review_pr(pr_up_to_date, 'coddy-bot'))

Pola Pemindaian Keamanan dalam Peninjauan PR

Pola peninjauan otomatis yang umum adalah memindai anti-pola keamanan pada baris yang ditambahkan: rahasia yang ditulis langsung, penggunaan fungsi yang sudah tidak digunakan, penggabungan string SQL, atau pemanggilan eval(). Periksa setiap baris yang ditambahkan terhadap sekumpulan pola dan tandai temuan sebagai komentar sebaris.

import re

SECURITY_PATTERNS = [
    (r'eval\s*\(', 'Dangerous eval() call — code injection risk'),
    (r'os\.system\s*\(', 'os.system() is unsafe; use subprocess with a list'),
    (r'pickle\.loads?\s*\(', 'pickle.load is unsafe with untrusted data'),
    (r'execute\(.*%\s*', 'Possible SQL injection — use parameterized queries'),
    (r'md5\s*\(|hashlib\.md5', 'MD5 is cryptographically broken for passwords; use bcrypt'),
]

def scan_patch_for_security(filename, patch):
    if not patch:
        return []
    comments = []
    new_line = 0
    for line in patch.split('\n'):
        if line.startswith('@@'):
            import re as _re
            m = _re.search(r'\+([0-9]+)', line)
            if m:
                new_line = int(m.group(1))
        elif line.startswith('+') and not line.startswith('+++'):
            content = line[1:]
            for pattern, message in SECURITY_PATTERNS:
                if re.search(pattern, content):
                    comments.append({'path': filename, 'line': new_line,
                                     'body': f':rotating_light: **Security:** {message}'})
            new_line += 1
        elif not line.startswith('-'):
            new_line += 1
    return comments

# --- demo ---
patch = (
    '@@ -1,2 +1,3 @@\n'
    ' def run(cmd):\n'
    '+    os.system(cmd)\n'
    '+    hashlib.md5(cmd.encode())\n'
)
for c in scan_patch_for_security('run.py', patch):
    print(c)

Pemeriksaan Singkat: Peristiwa Peninjauan

Uji pemahaman Anda tentang opsi peninjauan PR.

Ringkasan Peninjauan PR Otomatis

Agen Anda sekarang dapat meninjau pull request secara terprogram:

  • repo.get_pull(number) — mendapatkan objek PR beserta metadatanya
  • pr.get_files() — mencantumkan berkas yang diubah dengan filename, patch, additions
  • pr.get_commits() — memeriksa pesan komit dan statistiknya
  • pr.create_review(body, event, comments) — mengirimkan peninjauan dengan komentar sebaris opsional
  • Format komentar sebaris: {'path': filename, 'line': N, 'body': markdown}
  • Gunakan 'COMMENT' untuk pengamatan, dan 'REQUEST_CHANGES' untuk masalah yang menghalangi
  • Periksa peninjauan yang sudah ada sebelum melakukan peninjauan ulang agar tidak terjadi duplikasi
  • Batasi analisis berkas hingga 10 berkas dan 25 komentar sebaris agar tetap berada dalam batas API

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Komentar Tinjauan PR Otomatis” gratis?

Ya — teks lengkap “Komentar Tinjauan PR Otomatis” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Agents, upgrade ke CoddyKit PRO. Kursus AI Agents mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Komentar Tinjauan PR Otomatis”?

Baca perbedaan PR dan kirim komentar tinjauan sebaris melalui API. Kamu berlatih AI Agents dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai AI Agents?

Tidak diperlukan pengalaman sebelumnya. AI Agents di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Komentar Tinjauan PR Otomatis” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Agents ini?

Ya. Setiap pelajaran AI Agents menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Ikhtisar GitHub REST API
  2. Mencantumkan dan Mengelola Isu
  3. Komentar Tinjauan PR Otomatis
  4. Riwayat Commit dan Analisis Perbedaan
← Kembali ke AI Agents