Salta ai contenuti

Conversazioni sulle PR

Una pull request è già il luogo in cui avviene la conversazione sul codice. Le conversazioni sulle PR la rendono una superficie su uno spazio di lavoro di Control Center, allo stesso modo in cui un ponte di chat rende tale Slack: @menziona il bot della GitHub App del server su una PR, rispondi all’interno dei suoi thread di revisione oppure aggiungi l’etichetta ai-review, e il lavoro avviene nello spazio di revisione della PR — con la stessa attribuzione e gli stessi guardrail di qualsiasi cosa digitata nell’app. Anche un commento è vincolato all’appartenenza; l’etichetta no, perché non ha un autore. Le risposte del bot tornano su GitHub, nel canale in cui è stata posta la domanda.

Tre decisioni danno forma alla funzionalità, e ciascuna è la stessa decisione che il ponte di chat ha preso per la chat, riformulata per una forge: in quale direzione scorre la connessione, cosa autorizza un commento che arriva dall’esterno e a chi è attribuito ciascun lato.

L’unico canale push di GitHub per un’app è un webhook, e un webhook richiede un URL in ingresso — l’unica cosa che questa superficie non richiede mai, di proposito. La scoperta è quindi una scansione: all’incirca ogni minuto, una ricerca con alias per ogni installazione dell’app chiede a GitHub le PR che menzionano il bot (o il suo slug breve) e le PR aperte che portano l’etichetta di revisione, le PR che hanno già uno spazio di revisione vedono leggere i propri thread di commenti, e tutto viene confrontato per id del commento con quanto visto alla scansione precedente.

Vale la pena dare un nome alle conseguenze progettuali:

  • La latenza è un intervallo di scansione, non un evento. La conferma 👀 arriva entro circa un minuto. Nulla di urgente vi dipende.
  • Esattamente una volta, per commento. Gli id dei commenti sono unici in tutto GitHub; ciascuno viene registrato nel momento in cui è classificato come idoneo, prima che qualsiasi cosa agisca su di esso. Un crash può perdere la gestione di un commento, ma non può mai attivarlo due volte.
  • Il tempo offline viene recuperato, non riprodotto. L’insieme degli elementi visti è persistito, quindi un riavvio consegna ciò che è arrivato mentre era inattivo. L’unica eccezione è la primissima scansione in assoluto: senza alcuna memoria con cui confrontare, tutto ciò che è in sospeso viene registrato in silenzio — un operatore che ha etichettato venti PR prima che questo server esistesse non deve ritrovarsi con venti revisioni in esecuzione.
  • L’etichetta è un insieme, non un evento. La ricerca segnala la presenza dell’etichetta, quindi una PR etichettata si attiva una volta sola, per sempre. Riaggiungere un’etichetta dopo averla rimossa è invisibile alla scansione; una menzione è la via per rieseguire.

La ricerca viene eseguita una volta per account di installazione sul client di quel proprietario, perché un token di installazione vede solo i repository della propria installazione — un’unica credenziale a livello di server mancherebbe silenziosamente ogni PR sotto le altre installazioni dell’app.

È l’appartenenza ad autorizzare, non il commento

Sezione intitolata “È l’appartenenza ad autorizzare, non il commento”

Un commento a una PR arriva portando poco più del login di un autore — un’identità esterna non autenticata finché non viene dimostrato il contrario. Il bot la risolve rispetto a un indice inverso dei membri dello spazio di lavoro e dei loro account GitHub collegati, ricostruito con un TTL perché le persone accedono e vengono invitate mentre il server è in esecuzione.

Negare in caso di dubbio somiglia a una scala, e ogni gradino risponde nel thread in cui ha rifiutato:

  • Un login sconosciuto — non collegato, o collegato da qualcuno che non è membro di questo spazio di lavoro — riceve l’indicazione di accedere e di assicurarsi di appartenervi
  • Un membro in sola lettura (viewer o guest) riceve l’indicazione che il suo ruolo non può avviare lavoro

I due casi di login sconosciuto condividono una sola risposta: l’indice inverso restituisce solo un membro di questo spazio di lavoro, quindi un account non collegato e un non membro collegato sono lo stesso mancato riscontro. Nessuno di essi procede. Un commento non viene mai attribuito a qualcuno che non lo ha inviato, e le parole di un estraneo non entrano mai in uno spazio di lavoro che non può vedere.

L’etichetta ai-review non ha un autore da risolvere, quindi non risale questa scala. Aggiungerla avvia la revisione nello spazio di lavoro che ha già collegato il repository.

Ciascun lato della conversazione porta un’identità diversa, e la differenza è la funzionalità:

  • Su GitHub, il bot è il server. Ogni commento, reazione e risposta pubblicati dal ponte viaggia sul token di installazione dell’app. Nessuno ha cliccato nulla in un client, quindi nulla prende in prestito l’account di una persona — la stessa regola che tiene i webhook e il polling lontani dalle credenziali umane.
  • Nello spazio, la domanda è tua. Il messaggio è attribuito al membro in cui l’autore si è risolto, e l’esecuzione dell’agente avviene per suo conto: i commit lo vedono come coautore, e il selettore dell’autonomia e i guardrail delle azioni dello spazio si applicano esattamente come a un messaggio digitato nell’app. Le scritture sulla forge provenienti dall’esecuzione escono comunque come l’app — la stessa regola del punto precedente — non sul token personale del membro.

Pubblicare i risultati della revisione di nuovo su GitHub resta una decisione di una persona, presa in Control Center. Una menzione può avviare una revisione; solo un essere umano la pubblica.

Tutto ciò che è stato creato da un account bot viene scartato prima che venga eseguito qualsiasi controllo — il nostro in particolare, e ogni suffisso [bot], così nemmeno il bot di revisione di un’altra app può svegliare il nostro. Senza questo, le risposte stesse del bot arriverebbero come nuovi commenti e lo riattiverebbero all’infinito.

Il suffisso [bot] è anche il motivo per cui esiste la forma breve. GitHub fissa il login di un account app a <slug>[bot], non offre alcun completamento automatico per esso e non risolve mai @slug nell’app — quindi accettare lo slug nudo è una decisione del nostro matcher, e scoprirlo richiede una corsia di ricerca sul commento in testo grezzo anziché l’indice delle menzioni di GitHub. Il costo viene dichiarato invece che nascosto: un utente umano che condivide lo slug dell’app si rivolge al bot con ogni menzione in forma breve di se stesso. Nomi di app distintivi rendono la collisione ipotetica.

  • Nessuna pubblicazione automatica. I risultati arrivano nello spazio di revisione; pubblicare su GitHub è un’azione separata, umana
  • Nessuna identità approssimativa. L’autore si risolve in esattamente un membro oppure viene rifiutato — non esiste un’attribuzione basata su congetture
  • Nessun webhook della GitHub App. Questa superficie non ne registra mai uno, quindi non c’è alcun URL in ingresso da esporre per i commenti delle PR. Gli altri webhook del prodotto (pipeline, sincronizzazione dei ticket) sono una configurazione separata e opzionale.