Kendi VPS Sunucunuzda AI PR İnceleme Aracı Çalıştırma
Kendi VPS sunucunuzda AI tabanlı PR inceleme botu kurarak kod güvenliğini sağlayın. Diff kapsamlı istemler, dosya filtreleri ve satır içi yorum özelliklerini yapılandırın.
Self-hosted PR inceleme aracının işlevi
Kendi sunucunuzda barındırılan PR inceleme agent’ı, size ait bir sunucuda çalışan küçük bir programdır. Bir pull request’in (PR) diff çıktısını okur ve yalnızca değiştirilen satırları bir modele gönderir. Dönen sonuç, satır içi inceleme yorumları olarak yayınlanır. Branch’inizi hiçbir zaman checkout etmez ve pull request’in dokunmadığı hiçbir dosyayı okumaz. Elinde yalnızca bir model API (application programming interface) key’i ve yorum yazma dışında hiçbir işlem yapamayan bir token bulunur. Bu, agent için dar bir tanımdır. Agent, otonom bir sistemden çok, önünde filtreler bulunan bir prompt’a daha yakındır. Bu agent’ı oluşturmadan önce daha kapsamlı bir çerçeveye ihtiyaç duyuluyorsa, kavramlardan kendi yazacağınız bir döngüye uzanan aşamalı yol bunun temelini açıklar.
Bir model diff verisini okuyabilir. Bu kısım çözülmüştür. Önemli olan, diff verisinin nereye gittiği ve anahtarı kimin tuttuğudur. Hosted bir inceleme botu kullanmak, tüm özel depolarınızdaki diff verilerinin ağınızdan çıkması, üçüncü tarafın günlüklerine düşmesi ve onların saklama politikalarına tabi olması anlamına gelir. Kendi VPS (virtual private server) sunucunuzda ise diff verisi GitHub'dan sizin sunucunuza, oradan da model API'sine gider; ayrıca neyin gönderileceğine karar veren kırk satırlık kodu kendiniz inceleyebilirsiniz.
Başlamadan önce gerekenler
- Halihazırda depoya kayıtlı self-hosted GitHub Actions runner içeren Ubuntu 24.04 yüklü bir VPS. Kayıt işlemi sırasında
pr-reviewetiketini ekleyin; aşağıdaki iş akışı bu etiketi temel alarak seçim yapacaktır. - Claude Console üzerinden alınmış bir Anthropic API anahtarı.
- Kimlerin pull request açabileceğini kontrol edebildiğiniz bir depo. Özel (private) depolar en kolay senaryodur. Aşağıdaki fork bölümü herkese açık (public) depoları ele almaktadır, ancak oradaki çözüm daha zahmetlidir.
Reviewer yazılımının VPS üzerine kurulması
Runner servisi, ./svc.sh install komutunu çalıştırdığınızda oluşturduğunuz yetkisiz hesap altında çalışır. İşlerin sudo yetkisi gerektirmeden yürütülebilmesi için reviewer yazılımını aynı hesap altında kurun. Aşağıdaki runner ifadesini kendi hesap adınızla değiştirin.
sudo apt update && sudo apt install -y gh python3-venv
sudo install -d -m 755 -o runner -g runner /opt/pr-review
sudo -u runner python3 -m venv /opt/pr-review/venv
sudo -u runner /opt/pr-review/venv/bin/pip install anthropic
gh --versiongh --version komutu, Ağustos 2026 itibarıyla Ubuntu 24.04 üzerinde gh version 2.45.0 çıktısını verir. 2.20 ve sonraki tüm sürümler, aşağıda kullanılan --input bayrağını destekler. Bir Command 'gh' not found mesajı, universe bileşeninin etkinleştirilmediği anlamına gelir; bu durumda sudo add-apt-repository universe komutunu çalıştırın ve tekrar deneyin.
Anahtar ve belirtecin konumu
İki gizli veri, iki farklı yaşam döngüsü. İkisi de depoya (repository) girmez.
ANTHROPIC_API_KEY bir depo gizli verisidir; Settings, ardından Secrets and variables ve sonrasında Actions altında ayarlanır. GitHub bunu şifreler ve çalışma zamanında adımın ortamına enjekte eder. Bu veri hiçbir zaman diskte bir dosya olarak bulunmaz ve git geçmişine girmez.
GITHUB_TOKEN farklı çalışır. Actions, her iş için yeni bir belirteç oluşturur ve iş bittiğinde bu belirteci yok eder. Bu belirtecin ne yapabileceği, iş akışındaki permissions: bloğu ile belirlenir; bu nedenle en az yetki prensibi (least privilege) tam olarak burada uygulanır:
permissions:
contents: read
pull-requests: writeBu belirteç bir inceleme (review) gönderebilir. Commit gönderemez, dal birleştiremez, iş akışı dosyasını düzenleyemez veya başka bir depoya dokunamaz. Yorum yapabilen bir aracı, bir inceleyicidir. Commit gönderebilen bir aracı ise bir committer'dır ve kimse buna onay vermemiştir. Model anahtarına da aynı özeni gösterin, çünkü bu anahtar hesabınız üzerinden harcama yapar. Bu tür sorunların yapısı hakkında daha fazla bilgi için gizli verileri bir yapay zeka aracısının erişiminden uzak tutmak bölümüne bakabilirsiniz.
Actions, iş günlüklerinde tam gizli veri dizisini *** ile değiştirir. Sadece tam eşleşen dizileri maskeler; bu nedenle base64 ile kodladığınız, iki satıra böldüğünüz veya karakter karakter yazdırdığınız bir anahtar açık metin olarak görünür. Ortamı dökümleyen bir hata ayıklama (debug) adımı eklemeyin.
Bir fork üzerinden gelen pull request neden API anahtarınızı görmez
GitHub'ın kuralı kısadır: GITHUB_TOKEN istisnası haricinde, bir iş akışı (workflow) fork edilmiş bir depodan tetiklendiğinde gizli veriler (secrets) çalıştırıcıya (runner) aktarılmaz. Bu nedenle, bir fork üzerinden başlatılan pull_request, betiğinizi herhangi bir ANTHROPIC_API_KEY olmadan başlatır ve ilk API çağrısı invalid x-api-key hatasıyla başarısız olur.
Cazip görünen çözüm, tetikleyiciyi pull_request_target olarak değiştirmektir; bu tetikleyici ana depo bağlamında çalışır ve gizli verilere erişebilir. Bunu burada yapmayın. GitHub'ın kendi güvenlik kılavuzu, bu tür iş akışlarının "ayrıcalıklı olduğunu, yani ana dalın önbelleğini diğer ayrıcalıklı iş akışı tetikleyicileriyle paylaştığını, depo yazma erişimine ve referans verilen gizli verilere erişim yetkisine sahip olabileceğini" ve sonucun "depoyu ele geçirmek için kullanılabileceğini" belirtmektedir.
Aynı kılavuz, çalıştırıcı konusunda oldukça nettir: "Self-hosted runner'lar, GitHub üzerindeki herkese açık depolar için neredeyse hiçbir zaman kullanılmamalıdır; çünkü herhangi bir kullanıcı depoya pull request açabilir ve ortamı tehlikeye atabilir."
Bu durum iki tasarım tercihini zorunlu kılar. İş, yalnızca kendi deponuza gönderilen dallarda çalışmasını sağlayan bir korumaya sahiptir. Ayrıca iş akışında hiçbir actions/checkout adımı bulunmaz. Aracı (agent), dalı hiçbir zaman disk üzerinde tutmaz; bu nedenle kötü niyetli bir pull request, yalnızca bir modele gönderilen metinden ibarettir. VPS'niz üzerinde bir derleme betiği çalıştıramaz, çünkü VPS'niz üzerinde hiçbir şey bunu çalıştırmaz. Yine de metin, zararsız olduğu anlamına gelmez: yabancı biri tarafından yazılan bir diff, bir modele ulaşan güvenilmeyen bir girdidir; bu, bir araca web araması yaptırdığınızda karşılaştığınız güven sınırı ile aynıdır ve burada onu sınırlayan tek şey, bu aracın bir yorum göndermekten başka hiçbir şey yapamamasıdır.
Deponun tamamını değil, farkı (diff) çekin
Tek bir istek, tüm farkı düz metin olarak getirir.
export GH_TOKEN=your_token # in the workflow this comes from secrets.GITHUB_TOKEN
gh api /repos/OWNER/REPO/pulls/42 -H "Accept: application/vnd.github.diff"Accept: application/vnd.github.diff medya türü, yanıtı pull request'i tanımlayan bir JSON nesnesinden birleşik farkın (unified diff) kendisine dönüştürür ve gh api gövdeyi değiştirmeden yazdırır. Gördüğünüz ilk satır diff --git a/ ile başlamalıdır. gh: Not Found (HTTP 404) hatası, token'ın depoyu göremediği anlamına gelir; bu durum, kapsamlı (fine-grained) kişisel token'larda neredeyse her zaman Pull requests izninin verilmediği anlamına gelir.
Token harcamadan önce filtreleme yapın
Bu bölüm, insanların okuduğu bir bot ile insanların sessize aldığı bir bot arasındaki farkı belirler. Aşağıdaki her bir filtre, model tek bir bayt bile görmeden önce çalışır.
- Yol filtreleri. Kilit dosyalarını, vendored dizinlerini, küçültülmüş (minified) paketleri ve oluşturulmuş kodları hariç tutun.
package-lock.jsonüzerindeki bir model yorumu tamamen gürültüdür ve bu dosyalar genellikle bir diff içindeki baytların çoğunluğunu oluşturur. - Boyut sınırı. Sınır aşıldığında incelemeyi atlayın ve başarılı (green) olarak çıkış yapın. 4.000 satırlık bir refactor işlemi için altmış tane tahmini yorum yerine, dosyanın otomatik inceleme için çok büyük olduğunu belirten tek bir dürüst satır yazın.
- Önem derecesi eşiği ve yorum sınırı. Yüksek ve orta seviyeli bulguları, en yüksek önem derecesinden başlayarak en fazla on taneye kadar raporlayın. On birinci yorumu kimse okumaz.
Betik
Bunu /opt/pr-review/review.py olarak kaydedin. Yapılandırmasını ortam değişkenlerinden okur, böylece iş akışı kod değişikliği gerektirmeden modelleri değiştirebilir.
#!/usr/bin/env python3
"""Review only the changed lines of one pull request."""
import json
import os
import subprocess
import sys
import anthropic
REPO = os.environ["GITHUB_REPOSITORY"]
PR = os.environ["PR_NUMBER"]
MODEL = os.environ.get("REVIEW_MODEL", "claude-haiku-4-5-20251001")
MAX_DIFF_BYTES = int(os.environ.get("MAX_DIFF_BYTES", "120000"))
MIN_SEVERITY = os.environ.get("MIN_SEVERITY", "medium")
MAX_COMMENTS = 10
RANK = {"low": 0, "medium": 1, "high": 2}
SKIP = ("package-lock.json", "poetry.lock", "/vendor/", "/node_modules/", ".min.js")
raw_diff = subprocess.run(
["gh", "api", f"/repos/{REPO}/pulls/{PR}",
"-H", "Accept: application/vnd.github.diff"],
check=True, capture_output=True, text=True,
).stdoutFarkın dosya bazında ayrılması, yol filtrelemeyi mümkün kılar. Her satırın numaralandırılması ise inceleme yorumlarının doğru yere yerleşmesini sağlar. GitHub, satır içi yorumları yalnızca diff'in parçası olan satırlara kabul eder; bu nedenle modelin gerçek bir satır numarası belirtmesi gerekir. Numaraların kendisine verilmesi, modelin uydurmak yerine mevcut olanı kopyalamasını sağlar.
def per_file(diff_text):
"""Split a unified diff into one string per file."""
sections, current = [], []
for line in diff_text.splitlines():
if line.startswith("diff --git ") and current:
sections.append("\n".join(current))
current = []
current.append(line)
if current:
sections.append("\n".join(current))
return sections
def annotate(section):
"""Prefix every line that exists in the new file with its line number."""
out, n, in_hunk = [], 0, False
for line in section.splitlines():
if line.startswith("@@"):
n = int(line.split("+")[1].split(",")[0].split(" ")[0])
in_hunk = True
out.append(line)
elif not in_hunk or line.startswith(("-", "\\")):
out.append(line)
else:
out.append(f"{n}\t{line}")
n += 1
return "\n".join(out)
kept = [s for s in per_file(raw_diff)
if not any(p in s.split("\n", 1)[0] for p in SKIP)]
payload = "\n".join(annotate(s) for s in kept)
if not payload.strip():
print("every changed file was filtered out")
raise SystemExit(0)
if len(payload) > MAX_DIFF_BYTES:
print(f"diff is {len(payload)} bytes, over the {MAX_DIFF_BYTES} cap")
raise SystemExit(0)Hunk başlığı numaralandırmayı taşır. @@ -12,7 +12,9 @@, yeni dosyanın hunk'ının 12. satırdan başladığını belirtir; bu yüzden sayaç oradan başlar ve yalnızca eklenen veya değişmeyen satırlarda ilerler. Silinen satırlar numaralandırılmaz, çünkü yeni dosyada mevcut değillerdir. Ters eğik çizgi ile başlayan satırlardaki koruma, git'in dosya sonunda yazdığı ve aksi takdirde sonraki tüm numaraları bir kaydıracak olan "no-newline" işaretçisini atlar.
Her iki çıkış da 1 yerine 0 durum kodunu kullanır. Filtrelenmiş veya boyutu aşılmış bir pull request, yeşil bir onay işareti göstermelidir. İnsanın müdahale edemeyeceği kırmızı bir onay işareti görmezden gelinir; bir kez görmezden gelindiğinde ise hepsi aynı akıbete uğrar.
SYSTEM = (
"You review one pull request diff. Every line that exists in the new file is "
"prefixed with its line number and a tab character. "
"Report only defects you can see in the lines shown: a crash, a resource leak, "
"a security mistake, a wrong boundary condition, a broken contract with code "
"that is visible in this diff. Do not comment on style, naming or formatting. "
"Do not guess about code you cannot see. Leave out anything you are not "
"certain about. An empty findings list is a normal and common answer. "
'Reply with JSON only, in this shape: {"findings": [{"path": "src/app.py", '
'"line": 42, "severity": "high", "comment": "what is wrong, then why"}]} '
"Every line number must be one you can see in the left column of that file."
)
client = anthropic.Anthropic()
message = client.messages.create(
model=MODEL,
max_tokens=2000,
system=SYSTEM,
messages=[{"role": "user", "content": payload}],
)
print(f"stop={message.stop_reason} in={message.usage.input_tokens} "
f"out={message.usage.output_tokens}", file=sys.stderr)
text = message.content[0].text
findings = json.loads(text[text.find("{"):text.rfind("}") + 1])["findings"]
findings = [f for f in findings if RANK.get(f["severity"], 0) >= RANK[MIN_SEVERITY]]
findings.sort(key=lambda f: -RANK.get(f["severity"], 0))
del findings[MAX_COMMENTS:]
if not findings:
print("nothing above the severity threshold; posting no comment")
raise SystemExit(0)
review = {
"event": "COMMENT",
"body": f"Automated review of the changed lines. {len(findings)} finding(s).",
"comments": [
{"path": f["path"].removeprefix("b/"), "line": f["line"], "side": "RIGHT",
"body": f"**{f['severity']}** {f['comment']}"}
for f in findings
],
}
subprocess.run(
["gh", "api", "-X", "POST", f"/repos/{REPO}/pulls/{PR}/reviews", "--input", "-"],
input=json.dumps(review), text=True, check=True,
)Bu bloktaki üç detay kritik öneme sahiptir. JSON, ilk { ile son } arasında dilimlenir; çünkü model bazen cevabını bir kod bloğu içine alır ve json.loads bu blokla çalışamaz. path, başındaki b/ ifadesinden arındırılır; çünkü bu önek diff başlığından gelir ve GitHub depo ile ilişkili bir yol bekler. Ayrıca --input -, tüm incelemeyi tek bir API çağrısı olarak gönderir; böylece on bulgu, on ayrı bildirim yerine tek bir bildirim olarak ulaşır.
Raporlanacak bir şey olmadığında betik hiçbir şey göndermez. Her pull request'e "sorun bulunamadı" yazan bir bot, insanların onu göz ardı etmesine neden olur; bu durum, önemli olan bildirimin de gözden kaçırılmasına yol açar.
İş akışına entegre etme
Bunu .github/workflows/pr-review.yml olarak kaydedin:
name: pr-review
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
pull-requests: write
concurrency:
group: pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
if: github.event.pull_request.head.repo.full_name == github.repository && !contains(github.event.pull_request.labels.*.name, 'no-ai-review')
runs-on: [self-hosted, linux, pr-review]
steps:
- name: Review the changed lines
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REVIEW_MODEL: claude-haiku-4-5-20251001
MIN_SEVERITY: medium
run: /opt/pr-review/venv/bin/python /opt/pr-review/review.pyGITHUB_REPOSITORY, env: bloğunda yer almaz çünkü Actions bunu her iş için zaten ayarlar. concurrency grubu faturalandırma açısından önemlidir: bu grup olmadan, bir dala yapılan üç hızlı düzeltme üç tam incelemeyi tetikler ve üçü için de ödeme yaparsınız; bu grupla ise yalnızca sonuncusu işleme alınır.
if: satırı iki işlevi yerine getirir. İlk kısım, zaten anahtar eksikliği nedeniyle başarısız olacak olan fork'lardan gelen pull request'leri atlar. İkinci kısım ise ekibinize bir kapatma anahtarı sunar: bir pull request'e no-ai-review etiketini eklediğinizde iş çalışmaz.
Bir pull request açın ve süreci izleyin:
gh run list --workflow=pr-review.yml --limit 3
gh run view --log
gh pr view 42 --commentsLog dosyasında nothing above the severity threshold; posting no comment ile birkaç saniye içinde tamamlanan bir çalışma, doğru şekilde işlediğini gösterir. Küçük ve temiz bir pull request üzerinde beklenen sonuç budur.
Otomatik bir pull request incelemesinin maliyeti nedir?
Diff, girdinin neredeyse tamamını oluşturduğundan, fiyatı diff boyutu belirler. Aşağıda, tahmin edilmek yerine token sayma uç noktası ile hesaplanmış, sistem istemi dahil 500 satırlık bir diff örneği yer almaktadır.
The data behind this chart
[
{
"label": "Haiku 4.5",
"input_tokens": "8,000",
"output_tokens": "1,200"
},
{
"label": "Sonnet 5",
"input_tokens": "10,400",
"output_tokens": "1,560"
},
{
"label": "Opus 5",
"input_tokens": "10,400",
"output_tokens": "1,560"
}
]Bu diff, Haiku 4.5 üzerinde 8,000 girdi token'ına, Sonnet 5 üzerinde ise 10,400 girdi token'ına karşılık gelmektedir. Metin aynı, ancak sayı farklıdır. 4.7 sürümünden itibaren Claude modelleri, aynı girdi için yaklaşık %30 daha fazla token üreten daha yeni bir tokenizer kullanmaktadır; Anthropic bunu fiyatlandırma sayfasında belgelemektedir. Daha yeni bir modeli daha eski bir modelle yalnızca milyon token başına fiyat üzerinden karşılaştırırken bunu dikkate alın.
Ağustos 2026 itibarıyla liste fiyatları şöyledir: Haiku 4.5, milyon girdi token'ı başına 1 dolar ve milyon çıktı token'ı başına 5 dolardır. Sonnet 5, 31 Ağustos 2026 tarihine kadar geçerli olan tanıtım fiyatlandırması kapsamında 2 dolar ve 10 dolar, bu tarihten sonra ise 3 dolar ve 15 dolardır. Opus 5 ise 5 dolar ve 25 dolardır.
The data behind this chart
[
{
"label": "Haiku 4.5",
"cost_per_pr_cents": 1.4,
"cost_200_prs_usd": "2.80"
},
{
"label": "Sonnet 5",
"cost_per_pr_cents": 3.64,
"cost_200_prs_usd": "7.28"
},
{
"label": "Opus 5",
"cost_per_pr_cents": 9.1,
"cost_200_prs_usd": "18.20"
}
]Bu, Haiku 4.5 üzerinde pull request başına 1.4 sent, Opus 5 üzerinde ise 9.1 sent maliyet anlamına gelir. Ayda 200 pull request birleştiren bir ekip, Haiku 4.5 üzerinde yaklaşık $2.80, Sonnet 5 üzerinde $7.28 veya Opus 5 üzerinde $18.20 öder. 1 Eylül 2026 tarihinden itibaren Sonnet 5 satırını 1.5 ile çarpın.
İki durum, gerçek faturayı bu tahminin üzerine çıkarır. synchronize her push işleminde incelemeleri tetikler; bu nedenle sekiz push içeren aktif bir dal sekiz inceleme maliyeti doğurur ve eşzamanlılık kuralı yalnızca push işlemleri birbirine yakın gerçekleştiğinde yardımcı olur. Rakamlar ayrıca yol filtrelerinin çalıştığını varsayar: filtrelenmemiş tek bir lock dosyası, girdi miktarını tek başına iki katına çıkarabilir.
Prompt caching burada yardımcı olmaz. Önbelleğe alınan önekin çağrılar arasında bayt düzeyinde aynı olması gerekir, ancak diff her seferinde farklıdır. Sistem istemi tek sabit kısımdır ve minimum önbelleğe alınabilir uzunluğun çok altında kalır. Genel kural için prompt caching ne zaman kendini amorti eder kısmına, yukarıdaki üç model arasında seçim yapmak için ise hangi iş için hangi Claude modeli kullanılmalı kısmına bakın.
Özelliği açmadan önce kendi diff'lerinizi ölçün
payload oluşturulduktan sonra bu satırı ekleyin, ardından betiği geçen ayın birkaç pull request'i üzerinde manuel olarak çalıştırın:
print(client.messages.count_tokens(
model=MODEL, system=SYSTEM, messages=[{"role": "user", "content": payload}]
).input_tokens)Sayma uç noktası modeli çalıştırmaz, bu nedenle girdi veya çıktı token'ı tüketmez ve belirttiğiniz modele ait tokenizer'ı kullanır. Bunu kendi deponuzdaki on gerçek pull request üzerinde çalıştırın ve ortalama yerine medyan değerini alın; böylece devasa bir taşıma işlemi tahmininizi saptırmaz.
İnceleme botlarının neden sessize alındığı ve bunun nasıl önleneceği
İki davranış bu botlara duyulan güveni yok eder ve her ikisinin de yukarıdaki kodda bir çözümü vardır.
Her şeyi aynı anda incelemek. Kırk yorum bırakan bir botun yorumlarının hiçbiri okunmaz. Önem derecesi eşiği ve on yorum sınırı nezaket kuralı değildir; gerçek bulguların görünür kalmasını sağlayan unsurlardır. Kırpmadan önce önem derecesine göre sıralama yapmak, sınırın rastgele on bulgu yerine en az önemli olanları elemesini sağlar.
Kontrol edemediği bir konuda eminmiş gibi yorum yapmak. Mühendislerin botu tamamen kapatmasına neden olan davranış budur. 40.000 satırlık bir kod tabanının 200 satırını gören bir model, yine de hiç görmediği bir dosya hakkında "bu, redis_client.py içindeki önbellek geçersiz kılma işlemini bozuyor" şeklinde yorum yazacaktır. Sistem istemi, bunu açık bir dille engeller: yalnızca gösterilen satırlarda görülebilen kusurları raporlayın ve emin olmadığınız hiçbir şeyi dahil etmeyin. Hatayı doğrudan isimlendirmek, genel olarak doğruluk istemekten daha iyi sonuç verir; modele boş bir sonucun normal olduğunu söylemek, iki satırlık bir değişiklik hakkında söyleyecek bir şey uydurmasını engeller.
İncelemeyi COMMENT olarak gönderin, asla REQUEST_CHANGES olarak göndermeyin. Bir modelin görüşü bir birleştirmeyi (merge) engelleyememelidir; engelleyebildiği anda, teslim tarihi olan biri botla tartışmak yerine tüm iş akışını kaldıracaktır.
Hata modları ve karşılaşacağınız dizeler
İnceleme gönderilirken HTTP 422 hatası. gh, gh: Unprocessable Entity (HTTP 422) çıktısını verir ve yanıt gövdesinde ilgili alan belirtilir: Pull request review thread line must be part of the diff. GitHub bu yorumu sabitleyemez. Yaygın nedenler; modelin uydurduğu bir satır numarası, hala b/ öneki taşıyan bir path veya kaldırılmış bir satıra yapılan yorumdur; bu durumda side değerinin RIGHT yerine LEFT olarak ayarlanması gerekir. Göndermeden önce inceleme JSON'unu yazdırın ve bir yorumu diff ile manuel olarak karşılaştırın.
Model API'sinden invalid x-api-key hatası. Adım, ilk messages.create çağrısında başarısız olur. Ya ANTHROPIC_API_KEY gizli anahtarı depoda ayarlanmamıştır ya da çekme isteği (pull request) bir fork üzerinden geldiği için Actions hiçbir gizli anahtarı iletmemiştir. if: satırındaki fork koruması bunu atlamış olmalıdır, bu yüzden önce o satırı kontrol edin.
gh: Resource not accessible by integration (HTTP 403). İş belirteci (job token) çekme isteklerine yazamaz. permissions: bloğuna pull-requests: write ekleyin. Eğer zaten ekliyse, Settings, ardından Actions ve General kısmına bakın; burada bir organizasyon politikası, herhangi bir iş akışı belirtecinin talep edebileceği izinleri kısıtlıyor olabilir.
json.decoder.JSONDecodeError. Model ayrıştırılabilir bir JSON döndürmedi. Yaygın neden, yanıtın belirteç sınırına (token ceiling) ulaşması ve nesnenin ortasında kesilmesidir. Günlük satırı tam olarak bunun için stop_reason çıktısını verir: max_tokens değeri, max_tokens değerini yükseltmeniz veya MAX_COMMENTS değerini düşürmeniz gerektiği anlamına gelir.
İş akışı hiç çalışmıyor. gh run list, çekme isteği için hiçbir şey göstermiyor. paths-ignore değerinin değişen tüm dosyaları filtrelemediğinden emin olun; ardından fork korumasını, etiket korumasını ve VPS üzerinde sudo systemctl status 'actions.runner.*' ile çalıştırıcının (runner) aktif olup olmadığını kontrol edin. Çevrimdışı bir çalıştırıcı, işi kuyrukta bırakır ve çekme isteğinde herhangi bir hata mesajı göstermez.
Her inceleme boş dönüyor. Bir çalıştırma için MIN_SEVERITY değerini low olarak ayarlayın. Eğer bulgular görünüyorsa, eşik değeri (threshold) görevini yapıyordur. Eğer hiçbir şey görünmüyorsa, payload değerini yazdırın ve filtrelerin tüm diff'i kaldırmadığını doğrulayın.
Diğer ajanlarınızla birlikte çalıştırma
Reviewer küçük bir araç olduğundan, onu halihazırda her şeyi çalıştıran sunucuya kurmak cazip gelebilir. Ancak deponun güvenliği önemliyse, bu servisi ayrı tutun. Bu süreç, kodunuza yorum yapabilen bir token ve harcama yetkisi olan bir anahtar barındırır; self-hosted runner, doğası gereği iş akışı kodlarının yürütüldüğü bir yerdir. sudo yetkisi olmayan, özel bir kısıtlı hesap ve başka hiçbir şeyin çalışmadığı bir sunucu, temel güvenlik standardıdır. Eğer kod indiren interaktif ajanlar da çalıştırıyorsanız, her ajan için tek kullanımlık bir VM kullanmak en sağlam yöntemdir; bir VPS üzerinde kodlama ajanı çalıştırmak ise genel kurulumu açıklar. Anthropic API sizin için yeniyse, bir VPS üzerinde ilk Claude API uygulaması bu işe başlamak için daha uygun bir başlangıç noktasıdır.
FAQ
Bir yapay zeka PR inceleme aracının depoma yazma erişimine ihtiyacı var mı?
Hayır. İnceleme göndermek için pull-requests: write ve diff verisini çekmek için contents: read yetkisine ihtiyaç duyar. Listenin tamamı budur ve bu yetkileri, iş bazlı GITHUB_TOKEN yetkilerini sınırlayan permissions: bloğunda tanımlarsınız. Bu iki satır ile araç, bir pull request üzerine yorum yapabilir ancak commit gönderemez veya branch birleştiremez. Birleştirmeyi engellemesini önlemek için incelemeleri REQUEST_CHANGES yerine event: COMMENT ile gönderin.
İnceleme yorumum neden HTTP 422 hatası veriyor?
GitHub, satır içi inceleme yorumlarını yalnızca pull request diff verisinin bir parçası olan satırlarda kabul eder; aksi durumda Pull request review thread line must be part of the diff hatası döner. path yolunun, diff başlığındaki b/ öneki olmaksızın depo bazlı olduğunu ve satır numarasının ilgili dosyanın bir parçası (hunk) içinde yer aldığını doğrulayın. side, eklenen veya değişmeyen satırlar için RIGHT, silinen satırlar için ise LEFT olmalıdır. Modele göndermeden önce diff verisindeki her satırın önüne yeni dosya satır numarasını eklemek, modelin kendi kendine satır numarası uydurmasını engeller.
Bunu, fork'lardan gelen pull request'lerin olduğu herkese açık bir depoda çalıştırabilir miyim?
Bu tasarımla hayır. GitHub, fork üzerinden tetiklenen bir iş akışına secret bilgilerini aktarmaz; bu nedenle model anahtarı eksik kalır ve işlem başarısız olur. Ayrıca GitHub, "self-hosted" çalıştırıcıların "herkese açık depolarda neredeyse hiçbir zaman kullanılmaması gerektiğini" belirtir; çünkü herhangi biri, sizin makinenizde kod çalıştırılmasına neden olacak bir pull request açabilir. Herkese açık bir proje için inceleme aracını yalnızca doğrudan depoya gönderilen branch'lerle kısıtlayın (ki if: koruması bunu yapar) ya da inceleme adımını GitHub tarafından barındırılan bir çalıştırıcıya taşıyın ve diff verisinin altyapınızdan çıkmasını kabul edin.
Pull request incelemesi için hangi modeli kullanmalıyım?
Haiku 4.5 ile başlayın. Sınırlı bir diff verisini sabit bir hata türleri listesine göre okumak karmaşık bir mantık yürütme gerektirmez ve en ucuz model, aylık faturayı kimsenin itiraz etmeyeceği bir seviyede tutar. Eğer kullandığınız dilde veya framework'te gerçek hataları gözden kaçırdığını fark ederseniz Sonnet 5 modeline geçin; bunu varsayımlarla değil, ölçüm yaparak belirleyin. Opus 5, pull request başına üç model arasında açık ara en pahalı olanıdır; bu maliyeti her özellik branch'ine yapılan her push işleminde değil, yalnızca release branch'lerinde gerekçelendirmek daha kolaydır.