Struktura repozytorium Mercurial z dużymi komunikacjami korporacyjnymi, zarządzanie konfiguracją i wymagania testowe

16

Jestem kolejnym użytkownikiem Subversion walczącym o ponowne nauczenie się w Tao rozproszonej kontroli wersji.

Korzystając z Subversion, byłem wielkim fanem niewielkich projektów i wraz z większością moich byłych pracodawców ustrukturyzowaliśmy nasze oddziały repozytoriów; tagi i bagażnik w następujący sposób:

branches-+
         +-personal-+
         |          +-alice-+
         |          |       +-shinyNewFeature
         |          |       +-AUTOMATED-+
         |          |                   +-shinyNewFeature
         |          +-bob-+
         |                +-AUTOMATED-+
         |                            +-bespokeCustomerProject
         +-project-+
                   +-shinyNewFeature
                   +-fixStinkyBug
tags-+
     +-m20110401_releaseCandidate_0_1
     +-m20110505_release_0_1
     +-m20110602_milestone
trunk

W obrębie samego drzewa źródłowego użylibyśmy (coś podobnego) następującej struktury:

  (src)-+
        +-developmentAutomation-+
        |                       +-testAutomation
        |                       +-deploymentAutomation
        |                       +-docGeneration
        |                       +-staticAnalysis
        |                       +-systemTest
        |                       +-performanceMeasurement
        |                       +-configurationManagement
        |                       +-utilities
        +-libraries-+
        |           +-log-+
        |           |     +-build
        |           |     +-doc
        |           |     +-test
        |           +-statistics-+
        |           |            +-build
        |           |            +-doc
        |           |            +-test
        |           +-charting-+
        |           |          +-build
        |           |          +-doc
        |           |          +-test
        |           +-distributedComputing-+
        |           |                      +-build
        |           |                      +-doc
        |           |                      +-test
        |           +-widgets-+
        |                     +-build
        |                     +-doc
        |                     +-test
        +-productLines-+
        |              +-flagshipProduct-+
        |              |                 +-coolFeature
        |              |                 +-anotherCoolFeature
        |              |                 +-build
        |              |                 +-doc
        |              |                 +-test
        |              +-coolNewProduct
        +-project-+
                  +-bigImportantCustomer-+
                  |                      +-bespokeProjectOne
                  |                      +-bespokeProjectTwo
                  +-anotherImportantCustomer-+
                                             +-anotherBespokeProject

Ideą było (i nadal jest) wykorzystanie struktury repozytorium, aby pomóc w strukturze komunikacji między zespołem inżynierów; część firmy zorientowana na klienta oraz inni interesariusze i eksperci w tej dziedzinie.

To znaczy: Dokumenty źródłowe znajdujące się w jednym z katalogów „projektu” są wykorzystywane (i zarabiają pieniądze) tylko raz. Dokumenty znajdujące się w jednym z katalogów „productLines” zarabiają tyle razy, ile razy produkt z danej linii zostaje sprzedany. Dokumenty znajdujące się w jednym z katalogów „bibliotek” zarabiają tyle razy, ile sprzedaje którykolwiek z ich produktów.

Wyjaśnia pojęcie amortyzacji kosztów i pomaga zbudować obsługę ponownego wykorzystania dokumentów źródłowych w całej firmie.

Oznacza to również, że istnieje wspólna struktura, nad którą mogą działać nasze narzędzia do automatyzacji kompilacji. (Nasze skrypty kompilacji chodzą po drzewie źródłowym w poszukiwaniu folderów „kompilacji”, w których znajdują pliki konfiguracyjne określające sposób budowania każdego komponentu; podobny proces ma miejsce w przypadku generowania i testowania dokumentacji).

Co znamienne, produkty, nad którymi pracuję, zwykle trwają DŁUGO, aby przeprowadzić testy pomiaru wydajności i charakterystyki; od 20 do 200 godzin; generowanie gdzieś między kilkoma GB a kilkoma TB przetworzonych wyników testów / danych pośrednich (które muszą być przechowywane i powiązane z określoną konfiguracją systemu, aby można było zmierzyć poprawę wydajności w czasie). Ten problem sprawia, że ​​zarządzanie konfiguracją jest ważnym czynnikiem, a także nakłada pewne wymagania dotyczące centralizacji, ponieważ zazwyczaj zasoby obliczeniowe potrzebne do uruchomienia pomiarów wydajności i testów charakteryzujących są ograniczone; (mały klaster złożony z 64-128 rdzeni).

Jako ostatnia uwaga; system ciągłej integracji wie, że musi uruchomić kompilację; analiza statyczna; test dymu i test jednostkowy uruchamiane przy każdej modyfikacji pnia, za każdym razem, gdy modyfikowana jest dowolna gałąź „tag”, oraz za każdym razem, gdy modyfikowana jest dowolna gałąź „AUTOMATED”. W ten sposób indywidualni programiści mogą korzystać z systemu CI ze swoimi osobistymi oddziałami, co jest ważną funkcją, IMHO.

Oto moje pytanie: Jak mogę powtórzyć wszystkie powyższe (i ulepszyć je, jeśli to możliwe), z Mercurial.

--edytować:

Mój obecny sposób myślenia polega na użyciu centralnego repozytorium Subversion, aby zdefiniować ogólną strukturę, ale pozwolić na użycie hg jako klienta, aby programiści mogli mieć repozytoria dostępne lokalnie.

William Payne
źródło
1
Łał. Myślę, że dobrą odpowiedzią na to będzie bardzo długi esej.
Ed James
Myślę, że kluczowym pytaniem jest, w jaki sposób i gdzie idą scalenia kodu, ponieważ to prawdopodobnie określi ścieżkę najmniejszego oporu. Jak więc scalić kod?
Wyatt Barnett
Zazwyczaj scalanie może pochodzić z gałęzi osobistej do gałęzi projektu lub operacji, a następnie do pnia. Nigdy nie doświadczyłem zbyt wielu trudności z scalaniem (korzystaliśmy z TortoiseSVN na Win32), chociaż nigdy nie działaliśmy zbyt długo (najwyżej jedna iteracja) bez ponownej integracji z bagażnikiem. Zresztą większość pracy wykonywaliśmy w bagażniku, choć celem było uproszczenie zarządzania ludźmi, a nie przepływu pracy programistycznej. (Jeden deweloper, wielu niezależnych programistów, więc posiadanie wszystkiego w bagażniku ułatwiło deweloperowi śledzenie tego, co się dzieje.)
William Payne
Jednym z kluczowych punktów było silne poleganie na testach prowadzonych przez system CI, szczególnie na poziomie testów systemu. Pomogło to zbudować pewność, że różni programiści nie przeszkadzają sobie nawzajem, oraz promować mentalność wielu małych iteracji. (Również obliczeniowe podnoszenie ciężarów wymagane do przeprowadzenia testów systemowych oznaczało mniejszą rywalizację o zasoby obliczeniowe, jeśli ludzie pracowali głównie na pniu).
William Payne

Odpowiedzi:

10

Odpowiedź Spoike jest doskonała, ale myślę, że warto dodać kilka rzeczy, które są zbyt duże, by można je było komentować.

Organizacja branżowa

Dzięki Mercurial możesz szczęśliwie zignorować cały swój pierwszy schemat organizacyjny. Jak mówi Spoke, każde repozytorium ma swój własny zestaw tagów, oddziałów (nazwanych i anonimowych) i można je organizować zgodnie z potrzebami biznesowymi.

Jeśli bespokeProjectTwopotrzebujesz specjalnej wersji chartingbiblioteki, możesz ją rozgałęzić charting, dodać nowe udogodnienia i użyć jej bespokeProjectTwo. Nowe obiekty (i ich błędy) nie byłyby wykorzystywane w innych projektach, które odwoływałyby się do standardowej chartingbiblioteki. Jeśli w chartingbibliotece głównej naprawiono błędy, można scalić te zmiany w gałęzi. Jeśli inne projekty również potrzebują tych ułatwień, możesz albo skłonić te projekty do korzystania ze specjalnej gałęzi, albo połączyć gałąź z linią główną i zamknąć gałąź.

Ponadto nic nie stoi na przeszkodzie, abyś posiadał politykę strukturyzowania nazw oddziałów w celu zapewnienia określonych udogodnień, takich jak oddziały AUTOMATION.

Organizacja katalogów

Nie ma powodu, dla którego nie można zachować katalogu źródłowego dokładnie tak, jak w przypadku Mercurial. Jedyna różnica polega na tym, że podczas gdy w Subversion masz jedno monolityczne (src)repozytorium, w Mercurial lepiej jest podzielić na repozytoria, które są logicznie pogrupowane. Z Twojej struktury drzewa źródłowego prawdopodobnie wyodrębniłbym każde z poniższych jako osobne repozytoria:

src-+
      +-(developmentAutomation)
      +-libraries-+
      |           +-(log)
      |           +-(statistics)
      |           +-(charting)
      |           +-(distributedComputing)
      |           +-(widgets)
      +-productLines-+
      |              +-(flagshipProduct)
      |              +-(coolNewProduct)
      +-project-+
                +-bigImportantCustomer-+
                |                      +-(bespokeProjectOne)
                |                      +-(bespokeProjectTwo)
                +-anotherImportantCustomer-+
                                           +-(anotherBespokeProject)

Umożliwia to dowolnemu produktowi lub projektowi na zamówienie korzystanie z dowolnej kombinacji bibliotek w dowolnej wersji. Przejrzyj repozytoria rtęciowe, aby w łatwy sposób zarządzać bibliotekami używanymi w dowolnej wersji produktu lub projektu.

Przepływ pracy

Alternatywą dla sugerowanego przepływu pracy Spoike (deweloper pobiera z błogosławionego repozytorium, działa lokalnie, wydaje żądanie ściągnięcia, a na koniec integrator pobiera te zmiany i łączy je) byłoby użycie systemu ciągłej integracji jako pośrednika.

Tak jak poprzednio, deweloper pobiera z pobłogosławionego repozytorium i działa lokalnie, ale po zakończeniu ponownie wyciąga z błogosławionego repozytorium i łączy się przed przejściem do nieskażonego repozytorium. Wszelkie zmiany niezwróconego repozytorium są następnie sprawdzane (ręcznie lub automatycznie) i przenoszone do błogosławionego repozytorium tylko wtedy, gdy zostaną zatwierdzone.

Oznacza to, że integrator zaakceptował lub odrzucił zmianę, a nie scalił. Z mojego doświadczenia wynika, że ​​prawie zawsze lepiej jest dla programisty, który napisał kod do wykonania scalenia, niż dla kogoś innego.

Jak zasugerowano w książce rtęci, haczyki można wykorzystać do automatyzacji tej procedury:

Gdy ktoś wypchnie zestaw zmian na serwer, z którego wszyscy pobierają, serwer przetestuje zestaw zmian, zanim zaakceptuje go jako stały, i odrzuci go, jeśli nie przejdzie pakietu testowego. Jeśli ludzie będą pobierać tylko zmiany z tego serwera filtrującego, zapewni to, że wszystkie zmiany, które te osoby pobiorą, zostaną automatycznie sprawdzone.

Inne sprawy

Problem dużych zestawów danych testowych można również rozwiązać, umieszczając te dane testowe w pod-repozytorium rtęciowym . Zapobiegnie to rozdęciu repozytorium kodu danymi testowymi, jednocześnie utrzymując dane testowe pod kontrolą wersji.

Mark Booth
źródło
Ponownie kolejna doskonała i pouczająca odpowiedź. Dziękuję Ci.
William Payne
RE: Organizacja oddziałów. Zgadzam się, że pierwszy schemat organizacyjny można zignorować. Zresztą nie komunikował on szczególnie dobrze przepływu pracy, a zatem nie zapewniał żadnej realnej użyteczności poza umocnieniem konwencji. Chciałbym jednak zastąpić to czymś, co silnie komunikuje (prosty jak to możliwe) przepływ pracy i zachęca do częstych zatwierdzeń. Być może zadzwonienie do głównej gałęzi „trunk / development” na co dzień?
William Payne
RE: Organizacja katalogów. Użyłem źródłowej organizacji katalogów jako podprogowego środka komunikacji; narzucenie niejawnej struktury organizacji kodu (i poprzez nią całej firmy). Zaczynam rozumieć, że Mercurial jest zwykle używany w bardzo bardzo elastyczny sposób; ale naprawdę chcę ograniczyć część tej elastyczności, aby narzucić strukturę na sposób, w jaki ludzie myślą o firmie, poprzez nałożenie struktury na sposób, w jaki ich dokumenty są zorganizowane na stacjach roboczych i w naszych obszarach pamięci sieciowej. (Więcej komunikacji korporacyjnej niż technicznej.)
William Payne,
RE: Workflow. Myślę, że najprostszym przepływem pracy byłoby wyciągnięcie z repozytorium „codziennego”, praca nad nim lokalnie, a następnie (często) powrót do repozytorium „codziennego”, rozpoczęcie analizy statycznej, testy dymu i testy regresji za pośrednictwem systemu CI. Cieszę się, że główne repozytorium zostało „zepsute”, o ile wiem o tym i o ile szybko zostanie naprawione ponownie. W rzeczywistości rozważam zaangażowanie się w repozytorium „codzienne” jako jedyny sposób kompilacji i kompilacji, aby zachęcić do częstych zatwierdzeń i dobrego pokrycia testowego. (Znacznie ważniejsze niż umiejętność pracy w izolacji, IMHO).
William Payne
@WilliamPayne - Dzięki. Chociaż Mercurial jest elastyczny, z odpowiednimi repozytoriami, gałęziami i zaczepami, możesz budować w dowolnych ograniczeniach, na poziomie organizacji lub repozytorium. Osobiście zacznę od kontroli organizacyjnych i kilku zaczepek CI i rozszerzę te kontrole w przyszłości, gdy ich potrzeba stanie się oczywista. Rozsądne użycie sub-repo może na przykład zachęcić ludzi do sprawdzania rzeczy lokalnie w tej samej strukturze, co na serwerze, na przykład poprzez posiadanie productLineslub bigImportantCustomerjako super-repo.
Mark Booth,
9

Dobra, próbuję odpowiedzieć na to po prostu.

Co musisz wiedzieć

Pierwszą rzeczą, którą musisz wiedzieć: Mercurial jest rozproszoną kontrolą wersji i ma pewne właściwości, o których powinieneś wiedzieć poniżej.

  • Źródło pochodzi z jednego repozytorium, w którym to repozytorium można sklonować. Wszystkie sklonowane repozytoria mogą współdzielić kod między sobą poprzez synchronizację (za pomocą poleceń pull i push, które można ograniczyć dostęp).
  • Każdy użytkownik, który ma kopię kodu, ma klon repozytorium. Jeśli chcą się rozgałęzić, mogą to zrobić w swoim lokalnym klonie. Oznacza to, że nie musisz organizować sposobu rozgałęziania każdego użytkownika. Mogą to zrobić dla siebie.
  • Znaczniki są tworzone w mercurial przez zatwierdzenie (to samo co twarde znaczniki w git). Oznacza to, że nie potrzebujesz katalogu wewnątrz struktury repozytorium dla tagów.
  • Typowym modelem, z którym ludzie pracują w DVCS (który jest wykorzystywany w github i bitbucket) jest robienie tego częściowo scentralizowanego.

    Każdy użytkownik ma repozytorium publiczne (w niektórych udziałach lub na bezpiecznym serwerze) i repozytorium prywatne (na własnych stacjach roboczych). Oba są klonami „błogosławionego” repozytorium integratora. Ilekroć czują, że są gotowi opublikować swój kod, mogą przekazać zmiany z repozytorium publicznego. Integrator może następnie wybrać, którzy użytkownicy mają pobrać kod do „błogosławionego” repozytorium.

    Jeśli integrator nie może łatwo scalić kodu użytkownika, zmiany są odrzucane i to właśnie ten użytkownik musi zaktualizować swoje repozytorium i samodzielnie naprawić scalenie. Zwykle nie jest to trudne, jeśli często się scalasz (ponieważ jest mniej kodu, który trzeba scalić) i zwykle ten użytkownik powinien wiedzieć, co poszło nie tak podczas scalania.

Konfiguracja repozytoriów na projekt

Tak więc zwykle konfiguruje się, że dla każdego projektu są następujące:

  • Publiczne repozytorium tylko do odczytu, za które odpowiedzialny jest integrator. Jest „błogosławiony”.

    Tj. Wszyscy użytkownicy mogą pobierać / pobierać treści, ale nie mają dostępu do ich przesyłania.

  • Każdy użytkownik może mieć własny publiczny klon repozytorium.

    Najłatwiejsza konfiguracja w postaci dysku współużytkowanego (choć możesz rozważyć hosting taki jak bitbucket). Integrator odbiera żądania ściągania od użytkowników i próbuje pobrać nowy kod z tych repozytoriów. Gdy scalenia są wykonywane bez problemów, jest ono umieszczane w repozytorium tylko do odczytu. Jeśli nie, użytkownicy proszeni są o naprawienie konfliktów scalania, które powstają poprzez lokalną aktualizację i scalenie.

  • Każdy użytkownik może mieć własne prywatne klony repozytorium.

    Dobrą praktyką jest wyciąganie ze swojego publicznego klonu, ale nie ma znaczenia, czy wyciągają ze swojego publicznego czy integratora. Wszystkie zatwierdzenia są jednoznacznie identyfikowalne, więc scalanie zatwierdzeń, które zapomniałeś pobrać w publicznym, jest względnie łatwe do naprawienia (poprzez przeniesienie zmian z prywatnego na publiczne, automatycznie pobiera również zmiany integratora).

Organizacja kodu źródłowego

Tak jak w przypadku aranżacji samego źródła projektu jest coś, co trzeba przemyśleć. Jeśli artefakt wymaga kontroli źródła, włącz kontrolę źródła. Osobiście nie podoba mi się pomysł sprawdzania artefaktów tworzonych przez kompilację lub środowisko wykonawcze (ze względu na wysokie ryzyko konfliktów scalania na tego rodzaju artefaktach), takich jak pliki binarne lub pliki dziennika.

Możesz także sprawdzić konfigurację, o ile ułatwiają one programistom rozpoczęcie pracy i nie psują konfiguracji wydań lub środowiska na żywo / produkcyjnego (takich jak ustawienia aplikacji / serwera WWW). Prowadzi to do wniosku, że jeśli konfiguracja, którą masz poważnie utrudnia programistom rozpoczęcie pracy w ciągu pięciu minut po sprawdzeniu kodu, należy go zrefaktoryzować. Kolejnym wymaganiem jest to, że programiści powinni mieć trudności z zepsuciem wydania lub środowiska na żywo / produkcyjnego.

Wspominasz, że masz dane testowe, które muszą być powiązane z jakąś wersją kodu. Teraz jest to nieco trudniejsze, ponieważ systemy DVCS, takie jak Mercurial i Git, mają tendencję do spowalniania podczas sprawdzania danych, które są OGROMNE. Z mojego doświadczenia wynika, że ​​staje się on nie do zniesienia po 5 GB plików binarnych (twój przebieg może się różnić, więc powinieneś sprawdzić, jak to działa). Zalecałbym jednak umieszczenie wygenerowanych danych we własnym repozytorium i sprawdzenie, czy system testowy odpowiednio je oznaczył (i / lub utworzyć pliki tekstowe dla tych samych celów metadanych).

Mam nadzieję, że to wszystko ma sens. Proszę o komentarz poniżej, jeśli pominąłem jakiś szczegół lub jeśli coś wymaga dalszego wyjaśnienia, a ja spróbuję edytować.

Łup
źródło
+1 za bardzo miłą odpowiedź z kilkoma bardzo przydatnymi punktami. W odpowiedzi na pierwszą część odpowiedzi nie zrozumiałem znaczenia, jakie ma każdy użytkownik posiadający własne publiczne repozytorium. Być może muszę się zastanowić, jak można zorganizować przepływy pracy między użytkownikami.
William Payne
W odpowiedzi na drugą część twojej odpowiedzi, cały sens (moim zdaniem) posiadania jednego repozytorium dla całej organizacji to stworzenie wspólnego mentalnego obrazu struktury pracy i ułatwienie znalezienia komponentów, które mogą być ponownie wykorzystane. (Bardziej katedra niż bazar, ale to środowisko, w którym pracuję). Naprawdę chciałbym wiedzieć, jak osiągnąć takie samo poczucie zorganizowanej organizacji (systemu archiwizacji) za pomocą DCVS.
William Payne
W odpowiedzi na trzecią część twojej odpowiedzi: Z całego serca zgadzam się, że system kontroli źródła dotyczy dokumentów źródłowych, a artefakty pochodne tam nie należą. Zgadzam się również, że niepraktyczne jest przechowywanie dużych plików binarnych dowolnego opisu w VCS. Uważam jednak, że można przechowywać duże pliki binarne w uzgodnionej lokalizacji sieciowej o określonej nazwie i odwoływać się do nich z poziomu VCS. Na przykład środowisko (środowiska) kompilacji można przechowywać jako nazwane obrazy dysków maszyn wirtualnych i przywoływać je z różnych skryptów kompilacji. (np .: build me on build_env_A). To samo dotyczy danych testowych.
William Payne
W przeszłości korzystałem z hierarchii katalogów na dysku sieciowym, gdzie nazwy katalogów pochodzą z numeru wersji subversion + skrótu lokalizacji gałęzi, aby powiązać pliki pośrednie i wyniki testów z poszczególnymi wersjami. Oznacza to, że mamy możliwość śledzenia bez konieczności przechowywania plików pochodnych w kontroli wersji.
William Payne