Automatyczne opisy pull requestów z OpenAI i Vercel

5 lutego 2025

W tym poradniku zbudujemy generator opisów pull requestów oparty na OpenAI Responses API i wdrożymy go na Vercel. Narzędzie przygotuje szkic opisu po otwarciu pull requesta lub oznaczeniu go jako gotowego do przeglądu. Wygenerowany tekst powinien zawsze zostać sprawdzony przez autora zmian.

Co robi narzędzie

Bot:

  • odbiera zdarzenia GitHub przez webhook,
  • pobiera listę zmienionych plików i ich różnice,
  • tworzy zwięzłe podsumowanie zmian,
  • aktualizuje opis pull requesta.

Założenia

Potrzebujesz kont GitHub, Vercel i OpenAI, tokenu GitHub, klucza API OpenAI oraz środowiska Node.js. Sekrety przechowuj w zmiennych środowiskowych, nigdy w repozytorium.

Dobry prompt

Instrukcja powinna wymagać informacji, które da się potwierdzić na podstawie różnicy w kodzie. Warto poprosić o:

  1. krótki opis celu zmian,
  2. listę najważniejszych modyfikacji,
  3. informację o zmianach niekompatybilnych wstecz,
  4. wskazanie obszarów, których nie da się ocenić bez dodatkowego kontekstu.

Unikaj polecenia „opisz wszystko”. Zachęca ono model do wypełniania braków domysłami.

Wywołanie OpenAI

Dla nowych integracji OpenAI rekomenduje Responses API. Model trzymamy w zmiennej środowiskowej, aby można było zmienić go bez modyfikowania kodu.

1const aiResponse = await openai.responses.create({
2 model: process.env.OPENAI_MODEL || "gpt-5.6-sol",
3 instructions: `
4 Przygotuj zwięzły i rzeczowy opis pull requesta.
5 Opieraj się wyłącznie na widocznych zmianach.
6 Nie wymyślaj zachowania, którego nie da się potwierdzić w diffie.
7 `,
8 input: fileDiffs,
9 max_output_tokens: 300,
10});
11
12const description = aiResponse.output_text;

Obsługa webhooka

Po odebraniu zdarzenia sprawdzamy jego typ, pobieramy zmienione pliki i przekazujemy ich zawartość do modelu.

1router.post('/webhook', async (req, res) => {
2 verifyWebhookSignature(req);
3
4 const { pull_request, repository, action } = req.body;
5 if (!pull_request || !['opened', 'ready_for_review'].includes(action)) {
6 return res.status(200).send('Event ignored');
7 }
8
9 const owner = repository.owner.login;
10 const repo = repository.name;
11 const pullNumber = pull_request.number;
12
13 const { data: files } = await octokit.pulls.listFiles({
14 owner,
15 repo,
16 pull_number: pullNumber,
17 });
18
19 const fileDiffs = files
20 .map((file) => `${file.filename}:\n${file.patch || '[binary or truncated]'}`)
21 .join('\n\n');
22
23 const aiResponse = await openai.responses.create({
24 model: process.env.OPENAI_MODEL || 'gpt-5.6-sol',
25 instructions: prompt,
26 input: fileDiffs,
27 max_output_tokens: 300,
28 });
29
30 await octokit.pulls.update({
31 owner,
32 repo,
33 pull_number: pullNumber,
34 body: aiResponse.output_text,
35 });
36
37 return res.status(200).send('Description updated');
38});

Bezpieczeństwo i ograniczenia

Podpis webhooka trzeba sprawdzać na podstawie surowego ciała żądania i porównywać w sposób odporny na analizę czasu wykonania. Warto też ograniczyć maksymalną wielkość wejścia, obsłużyć brak diffu dla plików binarnych oraz nie wysyłać do zewnętrznego API sekretów lub danych klienta.

Duży pull request może przekroczyć rozsądny limit kontekstu. W takim przypadku najpierw odfiltruj pliki generowane, lockfile i artefakty, a pozostałe zmiany podsumuj partiami.

Co dalej

Najbardziej praktyczne rozszerzenia to etykietowanie wygenerowanego opisu jako szkicu, dodanie sekcji z planem testów oraz zapisywanie informacji o tym, które pliki zostały pominięte. Dzięki temu automatyzacja oszczędza czas, ale nie ukrywa swoich ograniczeń.

Michał Winiarski

Michał Winiarski

Founder of Devbrains and senior software developer

Najnowsze artykuły