AI Agents · Ders

Otomatik PR İnceleme Yorumları

PR farklarını okuma ve API üzerinden satır içi inceleme yorumları gönderme.

3. ders / 413 adım

Otomatik PR İnceleme Yorumları, CoddyKit'te ücretsiz bir AI Agents dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Agents öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Agents kursu toplamda 4 dersten oluşur.

Otomatik PR İncelemesi Nedir?

Otomatik PR inceleme aracısı, değiştirilen dosyaları okur, farkları analiz eder ve hedefe yönelik inceleme yorumları gönderir; tıpkı bir insan inceleyici gibi, ancak daha hızlı ve tutarlı şekilde. Kullanım alanları: güvenlik taraması, stil kurallarını uygulama, yaygın hataları tespit etme, sınama kapsamını kontrol etme veya bir PR'ın ne yaptığını özetleme.

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}')

Açık Çekme İsteklerini Listeleme

PR'ları listelemek için repo.get_pulls() kullanın. Duruma (open, closed), temel dala veya sıralama düzenine göre filtreleyin. Bir inceleme botu için genellikle henüz incelenmemiş open PR'ları işlersiniz.

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}')

Değiştirilen Dosyaları Alma

pr.get_files(), her biri PR'da değiştirilen bir dosyayı temsil eden File nesnelerinin listesini döndürür. Önemli özellikler: filename, status (eklendi/değiştirildi/kaldırıldı), additions, deletions, patch (birleştirilmiş fark metni).

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'
]

Dosya Yamasını (Farkını) Okuma

file.patch, birleştirilmiş fark dizesidir. + ile başlayan satırlar eklendi, - ile başlayan satırlar kaldırıldı. @@ başlığı, yama parçasının hangi satır numarasında başladığını gösterir. Yeni koddaki sorunların tam olarak nerede bulunduğunu belirlemek için bunu ayrıştırın.

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)

PR İşleme Kayıtlarını Alma

pr.get_commits(), PR'daki işleme kayıtlarını döndürür. Her işleme kaydında bir ileti, yazar ve değiştirilen dosyaların listesi bulunur. İşleme kaydı iletilerini analiz etmek, inceleme botunun PR'ın ne yaptığını özetlemesine ve düzenli işleme kaydı uygulamalarını denetlemesine yardımcı olur.

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')

PR İncelemesi Gönderme

Resmî bir inceleme göndermek için pr.create_review() kullanın. event parametresi inceleme türünü belirler: 'APPROVE', 'REQUEST_CHANGES' veya 'COMMENT'. Bir inceleme, genel bir gövde ve belirli satırlara ilişkin satır içi yorumlar listesi içerebilir.

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'
)

Satır İçi İnceleme Yorumları Gönderme

Satır içi yorumlar, fark içindeki belirli satırların doğrudan üzerinde görünür. Her yorumda path (dosya yolu), line (yeni dosyadaki satır numarası) ve body belirtilir. Yorum listesini pr.create_review(comments=[...]) işlevine iletin.

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')

LLM Destekli Kod Analizi

Dosya farkını, güvenlik ve kalite incelemesi istemiyle bir LLM'ye verin. Sorunları dosya yolu, satır numarası, önem derecesi ve açıklamayla listeleyen yapılandırılmış bir JSON yanıtı isteyin. Satır içi yorumlar listesini oluşturmak için bunu kullanın.

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 []

Eksiksiz PR İnceleme Aracısı Akışı

Tüm parçaları eksiksiz bir PR inceleme akışında birleştirin: dosyaları getirin, incelenebilir olanlara filtre uygulayın, her birini LLM ile analiz edin, yorumları toplayın ve incelemeyi tek bir API çağrısıyla gönderin. LLM maliyetlerini denetlemek için değiştirilen ilk 10 dosyayla sınırlayın.

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')

Mevcut Aracı İncelemelerini Denetleme

Bir inceleme göndermeden önce aracı botunuzun bu PR'ı mevcut durumda daha önce inceleyip incelemediğini kontrol edin. Botunuzun kullanıcı adı için mevcut incelemeler listesini denetleyerek yinelenen incelemelerden kaçının.

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'))

PR İncelemesi için Güvenlik Tarama Deseni

Yaygın bir otomatik inceleme deseni, eklenen satırlarda güvenlik açısından kötü örüntüleri taramaktır: sabit kodlanmış gizli bilgiler, kullanımdan kaldırılmış işlevlerin kullanımı, SQL dizelerinin birleştirilmesi veya eval() çağrıları. Eklenen her satırı bir dizi örüntüyle karşılaştırın ve bulguları satır içi yorumlar olarak işaretleyin.

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)

Hızlı Kontrol: İnceleme Olayları

PR inceleme seçeneklerini ne kadar anladığınızı sınayın.

Otomatik PR İncelemesi Özeti

Aracınız artık çekme isteklerini programatik olarak inceleyebilir:

  • repo.get_pull(number) — üst verileri içeren bir PR nesnesi alın
  • pr.get_files() — filename, patch, additions içeren değişmiş dosyaları listeleyin
  • pr.get_commits() — işleme iletilerini ve istatistiklerini inceleyin
  • pr.create_review(body, event, comments) — isteğe bağlı satır içi yorumlarla inceleme gönderin
  • Satır içi yorum biçimi: {'path': filename, 'line': N, 'body': markdown}
  • Gözlemler için 'COMMENT', engelleyici sorunlar için 'REQUEST_CHANGES' kullanın
  • Yinelenen incelemeleri önlemek için yeniden inceleme yapmadan önce mevcut incelemeleri kontrol edin
  • API sınırları içinde kalmak için dosya analizini 10 dosyayla, satır içi yorumları 25 yorumla sınırlayın
Başlamak ücretsiz

Yapay zeka eğitmeniyle AI Agents öğren — ücretsiz

Tarayıcında gerçek kod yaz ve çalıştır, 7/24 yapay zeka eğitmeninden anında yardım al; web'de ya da uygulamada kaldığın yerden devam et.

Kurslar
60
Dersler
239

Sıkça Sorulan Sorular

“Otomatik PR İnceleme Yorumları” dersi ücretsiz mi?

Evet — “Otomatik PR İnceleme Yorumları” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Agents kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Agents kursu toplamda 4 dersten oluşur.

“Otomatik PR İnceleme Yorumları” dersinde ne öğreneceğim?

PR farklarını okuma ve API üzerinden satır içi inceleme yorumları gönderme. AI Agents ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

AI Agents öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te AI Agents, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.

“Otomatik PR İnceleme Yorumları” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu AI Agents dersinde kod yazıp çalıştırabilir miyim?

Evet. Her AI Agents dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. GitHub REST API'ye Genel Bakış
  2. Sorunları Listeleme ve Yönetme
  3. Otomatik PR İnceleme Yorumları
  4. İşleme Geçmişi ve Fark Analizi
← AI Agents Sayfasına Dön