Developer%27s environment setup - iiuni/projektzapisy GitHub Wiki

Wstęp

System Zapisów jest napisaną w Pythonie aplikacją serwerową. Na serwerze produkcyjnym (czyli w realu) uruchamiamy go za pomocą serwera Gunicorn ukrytego za Nginx'em. Stanem aplikacji zarządza serwer Postgresql.

Maszyna dla developera jest skonfigurowana tak, by nie różnić się zanadto od serwera produkcyjnego, ale jednocześnie być łatwą w użyciu i wygodną dla programisty. Nie wymaga od programisty żadnego konfigurowania ani wydawania wielu komend.

Uruchamianie

Instalacja oprogramowania

Na swoim komputerze musimy mieć zainstalowany Virtualbox, który pozwala nam uruchamiać maszyny wirtualne, oraz Vagrant, który potrafi tymi maszynami zarządzać i je łatwo konfigurować. W naszym projekcie mamy taką konfigurację, w pliku Vagrantfile. Oba programy da się zainstalować na każdym sensownym systemie operacyjnym — w Ubuntu można skorzystać z wersji w repozytorium:

ja@mojkomputer:~$ sudo apt install virtualbox vagrant

Analogicznie polecenia na Windowsie:

PS C:\Users\user> winget install vagrant
PS C:\Users\user> winget install virtualbox

Uruchamianie developerskiej wersji Systemu Zapisów

Klonujemy niniejsze repozytorium na swój komputer. Następnie otwieramy terminal w jego głównym katalogu (albo którymkolwiek podkatalogu) i wydajemy polecenie:

ja@mojkomputer:projektzapisy$ vagrant up

Vagrant ściągnie z internetu świeży obraz maszyny wirtualnej z systemem Ubuntu, a następnie uruchomi proces instalacji (prawie) wszystkiego, co jest potrzebne by System Zapisów działał. Ten proces nazywa się provisioning i został przez nas zaprogramowany w playbooku Ansible.

Gdy provisioning się zakończy i powyższe kroki zostaną wykonane, może konieczne być wykonanie polecenia:

ja@mojkomputer:projektzapisy$ vagrant reload

Gdy proces się zakończy, należy chwilkę odczekać, a następnie wejść (z komputera-hosta) na adres http://0.0.0.0:8000 (lub localhost:8000), gdzie nasłuchuje serwer testowy naszej aplikacji (uruchomiony w maszynie wirtualnej).

Ładowanie obrazu bazy

Przed uruchomieniem maszyny warto ściągnąć obraz bazy danych (link można znaleźć na Slacku, na kanale #db_backups), rozpakować go i umieścić (plik ii_zapisy_dump_dev.sql) w głównym katalogu projektzapisy/. Provisioner instalujący oprogramowanie wychwyci ten plik i załaduje go do maszyny wirtualnej.

Obraz bazy jest zanonimizowany. Zamazane są w nim nazwiska, adresy email i inne „wrażliwe” dane. Hasła wszystkich użytkowników są zamienione na pass. Użytkownikami z uprawnieniami administratora są np. mbiernacka, asm.

Wyłączanie maszyny, ponowna instalacja

Maszynę wirtualną można wyłączyć poleceniem vagrant halt lub nawet zniszczyć, poleceniem vagrant destroy. Odpalenie polecenia vagrant provision gdy maszyna jest uruchomiona spowoduje powtórzenie procesu instalacji — można to zrobić, gdy nasz playbook uległ zmianie, albo gdy chcemy załadować od nowa bazę danych.

Co jest w tej konfiguracji?

W konfiguracji Vagranta katalog projektu (czyli projektzapisy/) na naszym komputerze jest zmapowany do /vagrant/ w maszynie wirtualnej. W maszynie wirtualnej uruchomione są dwie istotne dla nas usługi systemowe:

  1. Serwer testowy naszej aplikacji zapisy, uruchomiony za pomocą polecenia python manage.py runserver z katalogu /vagrant/zapisy/. Serwer ten jest zdecydowanie mniej wydajny niż ten produkcyjny, ale za to przeładowuje aplikację zawsze, gdy zmieni się jej kod. Dzięki temu możemy programować (na maszynie hosta) i na żywo widzieć efekty.
  2. Kompilator assetów (Javascript/Typescript/Vue/style) obserwujący zmiany w plikach. Uruchomiony za pomocą polecenia yarnpkg dev:watch. Dzięki temu możemy programować we front-endzie i na żywo widzieć efekty (więcej informacji: Praca nad front-endem).

Inne porady

  • Live-kompilator assetów nie poradzi sobie, gdy dodajemy nowy bundle (w pliku asset-defs.js) lub jakieś zależności (do pliku package.json). Musimy wtedy zrestartować maszynę wirtualną (poleceniem vagrant reload).

  • Gdy dodajemy jakąś zależność Pythonową (do plików requirements.*.txt), musimy przeprowadzić jeszcze raz provisioning.

  • Jeśli chcemy, by Django wygenerowało nam migracje po zmianach w strukturze bazy danych, musimy zalogować się do maszyny wirtualnej:

    ja@mojkomputer:projektzapisy$ vagrant ssh 

    wewnątrz maszyny wirtualnej wchodzimy do katalogu /vagrant/zapisy i odpalamy polecenie:

    (env3) vagrant@ubuntu-focal:/vagrant/zapisy$ python manage.py makemigrations
  • Aby przetestować nasze zmiany na backendzie, należy skorzystać z unit testów. Aby je wywołać, należy wewnątrz maszyny wirtualnej wejść do katalogu /vagrant/zapisy i odpalić polecenie:

    (env3) vagrant@ubuntu-focal:/vagrant/zapisy$ python manage.py test

    Komenda ta odpala wszystkie testy i informuje nas o ich wyniku. Jeżeli zmieniamy lub dodajemy nową logikę należy zmodyfikować/dodać nowe testy pokrywające wszystkie przypadki. Więcej o unit testach w Django i w Pythonie.

  • Jeśli maszyna wirtualna zacina się podczas provisioningu (na przykład na kroku Gathering facts), warto go przerwać (Ctrl+C) i uruchomić od nowa (vagrant provision).

  • Łączenie się z deweloperską bazą danych zostało opisane na osobnej stronie wiki: Łączenie się z serwerem bazy danych środowiska deweloperskiego

  • Użytkownicy Windowsa napotykają często na bardzo trudne do zdiagnozowania kłopoty związane z kodowaniem końca linii. W Windowsie koniec linii koduje się za pomocą \r\n (CRLF — carriage return, line feeed), zaś w Uniksie/Linuksie tylko za pomocą \n (LF). Nasz kod jest uruchamiany na Linuksie, więc w naszym projekcie trzymamy się standardu uniksowego. Git na Windowsie próbuje być jednak mądrzejszy i przy klonowaniu repozytorium zamienia \n na \r\n — co prowadzi do problemów z maszyną wirtualną. By upewnić się, że:

    • Klonujemy pliki bez konwertowania, oraz;
    • Nie wrzucamy do repozytorium plików z windowsowymi końcami linii;

    Należy ustawienie Git-a core.autocrlf ustawić na input.

  • Użytkownicy Windows 11 mogą napotkać na problemy związane z wydajnością maszyn wirtualnych uruchamianych w VirtualBoxie, w szczególności naszej deweloperskiej wersji systemu zapisów. Jeśli maszyna uruchamia się podejrzanie długo, a w jej podglądzie w VB widzimy ikonkę żółwika w prawym dolnym rogu, to należy się upewnić, że wyłączona jest opcja "Integralność pamięci" pod "Ustawienia / Prywatność i zabezpieczenia / Zabezpieczenia Windows / Zabezpieczenia urządzenia / Izolacja rdzenia", która może uniemożliwić VirtualBoxowi korzystanie z odpowiedniej akceleracji. Ponadato w "Funkcjach systemu Windows" ("Panel Sterowania / Programy / Włącz lub wyłącz funkcje systemu Windows") należy wyłączyć trzy opcje: "Platforma maszyny wirtualnej", "Podsystem Windows dla systemu Linux" oraz "Hyper-V", a następnie zrestartować komputer. Wtedy zamiast ikonki żółwika powinna się pojawić biała literka "V" na niebieskim tle (czwarta ikonka od prawej w panelu VB).

  • Jeśli nie korzystamy z IDE (np. VS Code) z odpowiednio skonfigurowanymi wtyczkami lintera, warto osobno sprawdzić formatowanie kodu przed spushowaniem zmian (w przeciwnym wypadku ryzykujemy, że niezgodności zostaną wykryte dopiero przez GitHub Actions, dostaniemy o tym niepokojącego maila, i będziemy trzeba zrobić kolejny push). Najłatwiej zrobić to łącząc się z maszyną wirtualną (vagrant ssh) i wykonując w jej katalogu /vagrant/zapisy polecenia

    $ python3 -m flake8 --statistics .
    $ yarnpkg prettier --check apps/ 

    które skontrolują odpowiednio źródła Pythona oraz JS itp. (w tym drugim przypadku tylko w podkatalogu apps/; inne rzeczy, m.in. automatycznie wygenerowane, nas nie interesują). Ew. błędy w Pythonie należy skorygować ręcznie; dla JS trzeba (!) użyć polecenia

    $ yarnpkg prettier --write apps/

    (i oczywiście scommitować zmiany). Analogiczne polecenia można wykonać na maszynie macierzystej, choć mogą wkraść się różnice m.in. w konfiguracji linterów czy nawet nazwach poleceń (w innych niż Ubuntu dystrybucjach yarnpkg może nazywać się yarn).

  • Tam, gdzie "normalnie" użylibyśmy funkcji print do debugowania Pythonowych skryptów, warto zamiast tego użyć mechanizmu logów zapewnionego przez moduł logging ze standardowej biblioteki (używanego już zresztą bezpośrednio przez Django i dodatkowo w kilku miejscach jawnie w kodzie SZ), na przykład tak:

    import logging
    logger = logging.getLogger(__name__)
    logger.info('Hello, world!')

    Informacja trafi do pliku wskazanego w konfiguracji w settings.py (słownik LOGGING), czyli, o ile nic tam się nie zmieni, do zapisy/logs/djangoproject.log.

    To, co skrypty piszą na stderr (ale z jakiegoś powodu już nie na stdout), dostępne jest przez journalctl zgodnie z działaniem systemd, jako którego usługa uruchamiany jest serwer Django. Możemy więc zamiast powyższego faktycznie użyć funkcji print (z kwargumentem file=sys.stderr), a potem na maszynie wirtualnej (vagrant ssh) wykonać sudo journalctl -u runserver.service.

Podziękowania

Pierwszy set-up z użyciem Vagranta wprowadził do naszego projektu @florczakraf. Obecne rozwiązanie z użyciem Ansible (i nie wymagające logowania się do maszyny wirtualnej) stworzyli @barnij i @receek (#888).

⚠️ **GitHub.com Fallback** ⚠️