Inginerie

De la conventional commits la un changelog

5 min de citit actualizat pe

Conventional commits dau gratuit unui changelog trei lucruri: tipul fiecărei schimbări, partea sistemului pe care a atins-o, și dacă rupe ceva. Nu dau nimic altceva. Formularea, gruparea și selecția, care sunt changelog-ul, rămân complet deschise, iar un pipeline care pretinde altfel livrează un jurnal git formatat.

feat(exports): add CSV column selection
fix(auth): reject expired refresh tokens
chore(deps): bump node-pg to 8.11

Trei commit-uri în formatul Conventional Commits. Din astea, o mașină vă poate spune că unul e o funcție, unul e o corecție, unul e curățenie, și ce parte a sistemului a atins fiecare. Asta e cu adevărat util, și e toată promisiunea convenției: un istoric de commit-uri care poate fi citit de altceva decât o persoană. Greșeala e să credeți că asta vă dă un changelog. Vă dă materia primă.

Ce specifică convenția?

Un tip, un scope opțional, și o descriere: type(scope): description. Tipurile sunt convențional feat, fix, chore, docs, refactor, test, perf, build, ci. Două lucruri marchează o schimbare care rupe compatibilitatea: un ! înainte de două puncte, sau un footer BREAKING CHANGE:. Uneltele se bazează pe feat și fix pentru creșteri de versiune minor și patch, și pe marcajul breaking pentru major.

Commit-ul vă dăChangelog-ul are nevoie deCine umple diferența
feat / fix / choreAdded / Fixed / internO mapare, automată
(scope)O grupare pe care cititorul o recunoașteO persoană, o dată pe scope
! sau BREAKING CHANGE:Cine se rupe, până când, și ce să facăO persoană, de fiecare dată
Descrierea, scrisă pentru o reviewerRezultatul, scris pentru o clientăO persoană, fiecare intrare
Un commitO schimbare, care poate fi mai multe commit-uriReguli de squash, sau o persoană

Marcajul îi spune uneltei; nu-i spune apelantului, care e subiectul cum se depreciază un API și ce e o schimbare care rupe compatibilitatea. E o specificație mică și merită urmată chiar dacă nu generați niciodată nimic din ea, pentru că forțează o decizie pe commit: e o schimbare pe care o văd utilizatorii, sau nu.

Unde se opresc conventional commits?

Se opresc la propoziție. Tot ce captează convenția sunt metadate despre o schimbare; schimbarea în sine e încă descrisă în vocabularul unei reviewer.

Mesajele de commit sunt scrise pentru reviewer. fix(auth): reject expired refresh tokens e corect și nu spune nimic unei cliente. Cititoarea unui changelog vrea “veți fi deconectată când o sesiune chiar a expirat, în loc să vedeți 401 intermitente”.

Scope-urile sunt interne. exports, auth, ingest sunt nume de module. Sunt stabile, ceea ce le face bune pentru grupare, și fără sens pentru oricine e în afara bazei de cod.

O schimbare e adesea mai multe commit-uri. O funcție integrată prin unsprezece commit-uri produce unsprezece intrări, zece din ele zgomot, iar strivirea lor pentru a ascunde asta pierde istoricul de review.

chore e un coș, nu o categorie. Actualizări de dependențe, schimbări CI și redenumiri ajung toate acolo, iar unele contează pentru utilizatori în timp ce majoritatea nu.

Deci: convenția vă dă tip, scope și status breaking gratis, și lasă formularea, gruparea și selecția complet deschise. Aceste trei sunt changelog-ul. Cine e de fapt responsabilă de o intrare de changelog acoperă cine ar trebui să se ocupe de acea formulare, grupare și selecție, din moment ce convenția în sine n-are nicio opinie în privința asta.

Cum se generează un changelog din conventional commits?

În două straturi, iar al doilea trebuie să fie obligatoriu.

Stratul unu, automat. La merge, derivați o intrare ciornă din commit: tip mapat la un tip de changelog (feat la Added, fix la Fixed, un marcaj breaking la Changed plus un flag), scope păstrat ca metadate în loc de text, link către PR. Puneți-l în secțiunea Unreleased pe care o cere Keep a Changelog.

Stratul doi, uman, și necesar. Înainte ca o versiune să iasă, fiecare intrare ciornă fie primește o rescriere de o linie în vocabularul utilizatorului, fie e marcată internă și scoasă din vizualizarea publică. Ăsta e pasul pe care oamenii încearcă să-l sară, iar sărirea lui produce changelog-uri care se citesc ca un diff.

Detaliul important de design e că stratul doi nu e opțional în pipeline. Dacă o versiune poate fi tăiată cu ciorne needitate, se va întâmpla, în săptămâna în care toată lumea e ocupată. Ce pași aparțin mașinii și care persoanei e tot conținutul automatizării changelog-ului.

Tăierea lansării e și momentul în care un tag git, o lansare și această intrare de changelog fie se potrivesc, fie încep să se desincronizeze; tag-uri git, lansări și changelog-ul tău acoperă cum ții cele trei sincronizate.

Trei capcane

Squash merge-urile mănâncă footer-ele. Dacă platforma voastră strivește cu titlul PR ca mesaj, footer-ul BREAKING CHANGE: al unui commit din interiorul acelei ramuri dispare, iar uneltele voastre încetează silențios să vadă schimbarea care rupe compatibilitatea. Verificați ce păstrează cu adevărat șablonul vostru de squash.

Commit-urile de revert produc intrări fantomă. Un fix care e revertit a doua zi generează o intrare pentru ceva care nu a fost niciodată lansat, decât dacă derivarea reconciliază revert-urile. Majoritatea uneltelor nu fac asta.

Creșterea versiunii și changelog-ul ies din sincronizare. Dacă versiunea e calculată din commit-uri și changelog-ul e scris manual după aceea, se despart în cam două versiuni. Calculați ambele în aceeași trecere sau acceptați că unul dintre ele e greșit.

Dacă vreți partea mecanică fără un pipeline

Generatorul nostru de changelog face pasul de derivare în browser: lipiți commit-uri, obțineți intrări grupate și tipizate. E deliberat determinist și complet client-side, deci commit-urile pe care le lipiți nu-și părăsesc niciodată mașina, ceea ce contează când mesajele provin dintr-un repository privat. Face jumătatea de colectare cinstit și nu încearcă stratul doi, pentru că stratul doi e o judecată, iar o unealtă care o simulează produce exact changelog-ul împotriva căruia argumentează acest articol.

Pentru versiunea de pipeline, unelte pentru changelog acoperă ce există.

Rezumatul

Conventional commits răspund “ce tip de schimbare e asta” în mod fiabil și ieftin. Nu răspund “ce ar trebui să le spunem oamenilor”, și nicio cantitate de unelte peste mesajul de commit nu o va face, pentru că informația n-a fost niciodată în mesajul de commit. Bugetați pentru rescriere.

FAQ

Generează conventional commits automat un changelog? Generează automat o ciornă: intrări tipizate, cu scope, legate. Formularea pentru o clientă, gruparea și decizia despre ce să omiteți încă au nevoie de o persoană, iar un pipeline care sare peste acel pas publică mesaje de commit.

Ce tipuri de conventional commit apar într-un changelog? feat și fix mereu, ca Added și Fixed. perf de obicei, ca Changed. chore, docs, refactor, test, build și ci sunt interne implicit și apar doar dacă o persoană promovează unul.

Cum marchează conventional commits o schimbare care rupe compatibilitatea? Un ! după tip sau scope (feat(api)!: ...), sau un footer BREAKING CHANGE: în corpul commit-ului. Ambele se pierd dacă un squash merge păstrează doar titlul PR.

Aveți nevoie de conventional commits pentru a automatiza un changelog? Nu. Etichetele PR, șabloanele PR și legăturile către issue-uri poartă aceleași metadate pentru echipele care fac merge prin pull request. Conventional commits sunt opțiunea cea mai ieftină când unitatea de schimbare e commit-ul.


Afirmațiile tehnice din acest articol nu au fost verificate independent. Dacă ceva nu e corect, spune-ne și vom corecta.

Pe changeloop: Generator de changelog, Comparație instrumente changelog

changeloop
Echipa care construiește un changelog care închide bucla. Utilizatorii cer ceva, echipa ta livrează, cel care a cerut află.