Sari la conținut

Conversații PR

Un pull request este deja locul unde se petrece conversația despre cod. Conversațiile PR îl transformă într-o suprafață a unui spațiu de lucru Control Center, la fel cum o punte de chat transformă Slack într-una: menționează cu @ botul GitHub App al serverului pe un PR, răspunde în firele lui de revizuire sau adaugă eticheta ai-review, iar munca se desfășoară în spațiul de revizuire al PR-ului — sub aceeași atribuire și aceleași guardrail-uri ca orice text tastat în aplicație. Un comentariu este și el condiționat de apartenență; eticheta nu este, pentru că nu are autor. Răspunsurile botului se întorc pe GitHub, în firul în care a fost pusă întrebarea.

Trei decizii modelează funcționalitatea și fiecare este aceeași decizie pe care puntea de chat a luat-o pentru chat, reformulată pentru un host de cod: în ce direcție rulează conexiunea, ce autorizează un comentariu care sosește din exterior și cui este atribuită fiecare parte.

Singurul canal push al GitHub pentru o aplicație este un webhook, iar un webhook are nevoie de un URL de intrare — exact lucrul de care această suprafață nu are niciodată nevoie, în mod deliberat. Așa că descoperirea este o scanare: la aproximativ un minut, câte o căutare aliasată per instalare a aplicației cere de la GitHub PR-urile care menționează botul (sau slug-ul lui scurt) și PR-urile deschise care poartă eticheta de revizuire, PR-urilor care au deja un spațiu de revizuire li se citesc firele de comentarii, iar totul este comparat după id-ul comentariului cu ce s-a văzut la scanarea anterioară.

Consecințele de design merită numite:

  • Latența este un interval de scanare, nu un eveniment. Confirmarea 👀 apare în circa un minut. Nimic urgent nu depinde de ea.
  • Exact o dată, per comentariu. Id-urile comentariilor sunt unice în tot GitHub; fiecare este înregistrat în momentul în care este clasificat ca eligibil, înainte ca ceva să acționeze asupra lui. O cădere poate pierde procesarea unui comentariu, dar nu îl poate declanșa de două ori.
  • Timpul offline este recuperat, nu reluat. Mulțimea celor văzute este persistată, așa că o repornire livrează ce a sosit cât timp serverul a fost oprit. Singura excepție este prima scanare din toate timpurile: fără memorie cu care să compare, tot ce este în așteptare este înregistrat în silență — un operator care a etichetat douăzeci de PR-uri înainte ca acest server să existe nu trebuie să se întoarcă la douăzeci de revizuiri în rulare.
  • Eticheta este o mulțime, nu un eveniment. Căutarea raportează prezența etichetei, deci un PR etichetat se declanșează o singură dată, pentru totdeauna. Re-adăugarea unei etichete după eliminarea ei este invizibilă pentru scanare; o mențiune este calea de re-rulare.

Căutarea rulează o dată per cont de instalare, pe clientul propriu al acelui proprietar, pentru că un token de instalare vede doar repository-urile propriei instalări — o singură acreditare la nivel de server ar rata în silențiu fiecare PR aflat sub celelalte instalări ale aplicației.

Un comentariu pe un PR sosește purtând nu mult mai mult decât loginul unui autor — o identitate externă neautentificată până la proba contrarie. Botul îl rezolvă printr-un index invers al membrilor spațiului de lucru și al conturilor GitHub conectate ale acestora, reconstruit pe baza unui TTL, pentru că oamenii se autentifică și sunt invitați cât timp serverul rulează.

Eșecul închis arată ca o scară, iar fiecare treaptă răspunde în firul în care a refuzat:

  • Un login necunoscut — nelegat sau legat de cineva care nu este membru al acestui spațiu de lucru — este îndemnat să se autentifice și să se asigure că aparține spațiului
  • Un membru cu acces doar în citire (viewer sau guest) este informat că rolul lui nu poate porni muncă

Cele două cazuri de login necunoscut împart un singur răspuns: indexul invers returnează doar un membru al acestui spațiu de lucru, așa că un cont nelegat și un non-membru legat sunt aceeași ratare. Niciunul nu merge mai departe. Un comentariu nu este niciodată atribuit cuiva care nu l-a trimis, iar vorbele unui străin nu intră niciodată într-un spațiu de lucru pe care nu îl poate vedea.

Eticheta ai-review nu are niciun autor de rezolvat, așa că nu urcă această scară. Adăugarea ei pornește revizuirea în spațiul de lucru care are deja repository-ul legat.

Fiecare parte a conversației poartă o identitate diferită, iar diferența este chiar funcționalitatea:

  • Pe GitHub, botul este serverul. Fiecare comentariu, reacție și răspuns publicat de punte călărește tokenul de instalare al aplicației. Nimeni nu a dat clic pe nimic într-un client, deci nimic nu împrumută contul unei persoane — aceeași regulă care ține webhook-urile și interogarea periodică departe de acreditările umane.
  • În spațiu, întrebarea este a ta. Mesajul este atribuit membrului în care s-a rezolvat autorul, iar rularea agentului se execută în numele lui: commit-urile îl trec ca coautor, iar setarea de autonomie a spațiului și guardrail-urile de acțiune se aplică exact cum se aplică unui mesaj tastat în aplicație. Scrierile către host-ul de cod făcute de rulare ies tot sub identitatea aplicației — aceeași regulă ca la punctul de mai sus — nu pe tokenul personal al membrului.

Publicarea constatărilor de revizuire înapoi pe GitHub rămâne decizia unei persoane, luată în Control Center. O mențiune poate începe o revizuire; doar un om o publică.

Tot ce este scris de un cont de bot este eliminat înainte ca vreo poartă să ruleze — în mod specific al nostru și orice sufix [bot], așa că nici botul de revizuire al unei alte aplicații nu îl poate trezi pe al nostru. Fără asta, propriile răspunsuri ale botului ar sosi ca comentarii noi și l-ar declanșa la nesfârșit.

Sufixul [bot] este și motivul pentru care există forma scurtă. GitHub fixează loginul unui cont de aplicație la <slug>[bot], nu oferă autocomplete pentru el și nu rezolvă niciodată @slug către aplicație — așa că acceptarea slug-ului simplu este decizia potrivitorului nostru, iar descoperirea lui are nevoie de o cale de căutare în textul brut al comentariilor, nu de indexul de mențiuni al GitHub. Costul este enunțat, nu netezit: un utilizator uman care împărtășește slug-ul aplicației adresează botul la fiecare mențiune în formă scurtă a sa. Numele distinctive de aplicații fac coliziunea ipotetică.

  • Fără publicare automată. Constatările ajung în spațiul de revizuire; publicarea pe GitHub este o acțiune separată, umană
  • Fără identitate aproximativă. Autorul se rezolvă la exact un membru sau este refuzat — nu există atribuire bazată pe presupuneri
  • Fără webhook de GitHub App. Această suprafață nu înregistrează niciodată unul, așa că nu există niciun URL de intrare de expus pentru comentariile de PR. Alte webhook-uri de produs (pipeline-uri, sincronizare de tichete) sunt o configurare separată, opțională.