Migracja repozytorium SVN do git

Młodsi stażem programiści pewnie tego nie pamiętają, ale kiedyś synonimem systemu kontroli wersji był Subversion. W dziewięciu na dziesięć przypadków podczas rozmowy kwalifikacyjnej należało się wykazać znajomością SVN-a.

Z tamtych czasów zostało mi kilka projektów w SVN-ie. Większość z nich już od dawna nie funkcjonuje, ale kilka wciąż żyje, np. acp czy ta strona. Raz na ruski rok coś w nich poprawiam, choć ze wstydem przyznaję, że robiłem to wprost na serwerze, bo przy wymianie domowego serwerka parę lat temu już nawet nie stawiałem serwera Subversion. Teraz jednak postanowiłem zrobić z tym porządek i przenieść kod do gita.

Czytaj dalej Migracja repozytorium SVN do git

Ansible i cache buster

Pamięć podręczna przeglądarki może napsuć krwi programistom zajmującym się frontem. Wielokrotnie byłem świadkiem podobnych dialogów:
– Frontend developerze, mówiłeś, że to naprawiłeś, a nie działa.
– Naciśnij Control i F5.
– Aaa, OK.
Żeby wymusić na przeglądarce pobranie nowej wersji plików (JavaScriptów, arkuszy stylów) można do nazwy pliku dodać query string z wartością zmienianą razem plikami czyli tzw. cache buster, na przykład:

<link rel="stylesheet" type="text/css" href="compiled.css?v=1.23">

Mniej więcej tak robiliśmy w aplikacji, z którą obecnie dużo pracuję. Występujące w przykładzie 1.23 to była wersja aplikacji. Wartość ta była pobierana z pliku konfiguracyjnego. To rozwiązanie miało przynajmniej dwie wady:

  1. Na środowiska testowe często wydajemy wielokrotnie kolejne poprawki do tej samej wersji aplikacji, przez co cache buster nie działał.
  2. Zdarzało nam się zapomnieć podbić wersję w pliku konfiguracyjnym, przez co cache buster nie działał także na produkcji.

Ponieważ do wydań używamy ansible’a postanowiliśmy zautomatyzować generowanie wartości dla cache bustera.

W pierwszym podejściu chciałem użyć daty i czasu wykonania skryptu np. w formacie RRMMDDGGMMSS, ale to rozwiązanie nie za dobrze działa w wypadku, gdy wydanie jest robione dla podzbioru serwerów (tzw. rolling update). Czas uruchomienia skryptu jest inny dla każdej grupy serwerów.

Lepszym rozwiązaniem moim zdaniem jest użycie ID commitu z gita. Zalety tego podejścia to:

  1. Wartość będzie identyczna na każdym serwerze, niezależnie od tego w której serii serwer był aktualizowany.
  2. Wartość jest generowana automatycznie, więc odpada czynnik ludzko-zapominalski.
  3. Wartość jest inna po każdej zmianie kodu w repozytorium, więc będzie różna również dla każdej poprawki w ramach jednej wersji aplikacji.

W roli ansible’a można to ograć następująco:

- name: "Get git short commit ID"
  command: git rev-parse --short HEAD
  args:
    chdir: "/tmp/build-directory"
  register: git_revparse

W powyższym przykładzie wartość chdir to katalog, w którym mamy kod aplikacji z repozytorium git. Po wykonaniu tego kroku w
w zmiennej git_revparse.stdout będzie znajdować ID commitu, które można przekazać np. do szablonu pliku konfiguracyjnego.

Obecnie linki do skryptów i styli wyglądają mniej więcej tak:

<link rel="stylesheet" type="text/css" href="compiled.css?v=c109ad9">

Skype na Debianie

Swego czasu Skype na Linuksie (w tym na Debianie) był bardzo ułomny, dostępna była wyłącznie wersja 32-bitowa, a instalacja była upierdliwa. Między innymi dlatego starałem się unikać Skype’a.

Niedawno zostałem zmuszony do przeproszenia się ze Skype’em i ku memu zaskoczeniu okazało się, że Skype for Linux dojrzał. Ze strony można pobrać pakiet DEB, a instalacja sprowadza się do zwyczajowego:

dpkg -i /home/joe/Pobrane/skypeforlinux-64.deb

Instalator dodaje repozytorium do listy źródeł apta, ale nie pobiera klucza, więc po wykonaniu aptitude update pojawi się błąd:

W: Błąd GPG: https://repo.skype.com/deb stable InRelease: Następujące podpisy nie mogły zostać zweryfikowane z powodu braku klucza publicznego: NO_PUBKEY 1F3045A5DF7587C3
E: The repository 'https://repo.skype.com/deb stable InRelease' is not signed.
E: Failed to download some files
W: Nie udało się pobrać https://repo.skype.com/deb/dists/stable/InRelease: Następujące podpisy nie mogły zostać zweryfikowane z powodu braku klucza publicznego: NO_PUBKEY 1F3045A5DF7587C3
E: Nie udało się pobrać niektórych plików indeksu, zostały one zignorowane lub użyto ich starszej wersji.

Żeby go naprawić wystarczy pobrać klucz GPG:

curl https://repo.skype.com/data/SKYPE-GPG-KEY | apt-key add -

Sama aplikacja (w wersji 5.3.0.1) zdaje się działać poprawnie i stabilnie, niestety wciąż brakuje w niej niektórych funkcji, np. Udostępniania ekranu podczas rozmowy konferencyjnej.

Problem z aktualizacją widżetu Jakdojade

Jakdojade to jedna z najczęściej używanych przeze mnie aplikacji na telefonie. Na jednym ekranie mam dwa widżety z przystankami, z których najczęściej korzystam. Niestety po wymianie telefonu (Xiaomi Redmi 3S) godziny odjazdów w widżetach przestały być aktualizowane.

Czasami chwilowo pomagało otworzenie obserwowanych przystanków w aplikacji, ale po kilk lub kilkunastu godzinach dane znowu były nieaktualne. Myślałem, że to może być problem z uprawnieniami aplikacji do korzystania z transferu danych, jednak to był błędny trop.

Skuteczne rozwiązanie także było związane z uprawnieniami, ale do innej czynności, a mianowicie autostartu. Po dodaniu aplikacji Jakdojade do autostartu godziny odjazdów w widżetach są regularnie aktualizowane.

Konfigurowanie identyfikatora sesji w PHP

Rok temu pisałem o łączeniu staromodnej aplikacji z Laravelem. Po zmianach w konfiguracji środowiska PHP wypłynął nowy problem, a mianowicie Laravelowi nie podobały się identyfikatory sesji generowane przez starą aplikację, więc w ich miejsce generował nowe tym samym skutecznie niwecząc dzielenie sesji.

Identyfikatory generowane przez starą aplikację wyglądały np. tak: mh3e2fj757ed8nnlukqf5ag0f3. Tymczasem Laravel (konkretnie wersja 5.1) za jedynie słuszne uznaje identyfikatory składające się z 40 znaków (cyfry i litery od a do f). Takie zachowanie zdefiniowane jest w metodzie \Illuminate\Session\Store::isValid():

public function isValidId($id)
{
   return is_string($id) && preg_match('/^[a-f0-9]{40}$/', $id);
}

Rozwiązaniem tego problemu jest takie skonfigurowanie sesji PHP-owych, by identyfikatory spełniały wymagania Laravela 5.1:

ini_set('session.hash_function', 'sha1');
ini_set('session.hash_bits_per_character', '4');

Problem z repozytorium pakietów Opery

Przy próbie wykonania aktualizacji listy pakietów aptitude zakomunikował mi rzecz następującą:

E: The repository 'https://deb.opera.com/opera-stable stable Release' does no longer have a Release file.

Najwyraźniej w repozytorium Opery zaszły jakieś zmiany. Zajrzałem na adres repozytorium i faktycznie we wskazanym katalogu pliku Release nie było, ale za to w podkatalogu opera znajdowały się wszystkie niezbędne pliki. Wobec tego otworzyłem plik /etc/apt/sources.list.d/opera-stable.list i poniższą linijkę:

deb https://deb.opera.com/opera-stable/ stable non-free #Opera Browser (final releases)

zamieniłem na taką:

deb https://deb.opera.com/opera-stable/opera testing non-free

Po tym zabiegu aktualizacja przebiegła bez problemów.

Dodam, że korzystam z Debiana w wersji testing (obecnie stretch) i dlatego podałem taką dystrybucję.

PrestaShop 1.6 na PHP 7.0

Zgodnie z planem przenoszę kolejne strony na PHP 7.0. W ten weekend przyszła kolej na eduNagrody.pl, który to sklepik stoi na PrestaShop 1.6. Wedle autorów wersja 1.6.1.4 przynosi kompatybilność z PHP 7. Moja instalacja ma raptem kilka dodatkowych modułów, więc założyłem, że z przesiadką nie będzie problemów.

Podobnie jak w przypadku WordPressa, przyspieszenie generowania stron przez PrestaShop jest odczuwalne. Testy przez ab pokazują mniej więcej dwukrotnie lepsze wyniki.

PHP 5.6

Server Software:        nginx/1.8.1
Server Hostname:        edunagrody.pl
Server Port:            443
SSL/TLS Protocol:       TLSv1/SSLv3,ECDHE-RSA-AES256-GCM-SHA384,2048,256

Document Path:          /
Document Length:        76033 bytes

Concurrency Level:      4
Time taken for tests:   9.500 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      7664764 bytes
HTML transferred:       7603300 bytes
Requests per second:    10.53 [#/sec] (mean)
Time per request:       379.995 [ms] (mean)
Time per request:       94.999 [ms] (mean, across all concurrent requests)
Transfer rate:          787.92 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       38   63  15.1     64      93
Processing:   194  312  60.4    309     456
Waiting:      157  276  61.5    281     420
Total:        237  375  62.4    375     521

Percentage of the requests served within a certain time (ms)
  50%    375
  66%    402
  75%    415
  80%    428
  90%    457
  95%    500
  98%    520
  99%    521
 100%    521 (longest request)

PHP 7.0

Server Software:        nginx/1.8.1
Server Hostname:        edunagrody.pl
Server Port:            443
SSL/TLS Protocol:       TLSv1/SSLv3,ECDHE-RSA-AES256-GCM-SHA384,2048,256

Document Path:          /
Document Length:        76033 bytes

Concurrency Level:      4
Time taken for tests:   4.415 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      7664708 bytes
HTML transferred:       7603300 bytes
Requests per second:    22.65 [#/sec] (mean)
Time per request:       176.616 [ms] (mean)
Time per request:       44.154 [ms] (mean, across all concurrent requests)
Transfer rate:          1695.22 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       38   44   7.1     42      78
Processing:    79  130  36.0    129     278
Waiting:       54  100  37.2     98     253
Total:        119  175  36.1    171     328

Percentage of the requests served within a certain time (ms)
  50%    171
  66%    182
  75%    188
  80%    194
  90%    212
  95%    227
  98%    295
  99%    328
 100%    328 (longest request)

PHP 7.0 na blogasku

Postanowiłem dać szansę PHP 7.0 na serwerze produkcyjnym, a konkretnie na WordPressie napędzającym tegoż blogaska. W końcu od premiery minęło jakieś dwa i pół miesiąca, w tym czasie bolączki wieku dziecięcego chyba powinny zostać wyeliminowane. Co więcej, próby na moich testowych maszynach wirtualnych nie wykazały żadnych problemów, za to pokazały wyraźne przyspieszenie.

Przesiadka jest łatwa i w pełni odwracalna, PHP 7.0 z dotdeb może działać równolegle do PHP 5.6 od Debiana, zatem wystarczy w konfiguracji vhosta zmienić ścieżkę do interpretera. Różnica w prędkości generowania stron jest wyraźnie odczuwalna organoleptycznie, ale jeśli do kogoś bardziej przemawiają liczby to poniżej wrzucam szybki test wykonany ab.

PHP 5.6

Server Hostname:        michal.durys.pl
Server Port:            80

Document Path:          /
Document Length:        65737 bytes

Concurrency Level:      4
Time taken for tests:   16.142 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      6594000 bytes
HTML transferred:       6573700 bytes
Requests per second:    6.20 [#/sec] (mean)
Time per request:       645.680 [ms] (mean)
Time per request:       161.420 [ms] (mean, across all concurrent requests)
Transfer rate:          398.93 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       11   11   0.6     11      17
Processing:   281  630 143.4    627    1083
Waiting:      111  287  77.4    288     674
Total:        292  641 143.5    638    1094

Percentage of the requests served within a certain time (ms)
  50%    638
  66%    706
  75%    767
  80%    780
  90%    804
  95%    824
  98%    879
  99%   1094
 100%   1094 (longest request)

PHP 7.0

Server Software:        nginx/1.8.1
Server Hostname:        michal.durys.pl
Server Port:            80

Document Path:          /
Document Length:        65737 bytes

Concurrency Level:      4
Time taken for tests:   6.343 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      6594000 bytes
HTML transferred:       6573700 bytes
Requests per second:    15.77 [#/sec] (mean)
Time per request:       253.721 [ms] (mean)
Time per request:       63.430 [ms] (mean, across all concurrent requests)
Transfer rate:          1015.20 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       11   11   0.2     11      12
Processing:    63  241  92.4    219     557
Waiting:       34  123  45.1    109     206
Total:         74  252  92.3    230     568

Percentage of the requests served within a certain time (ms)
  50%    230
  66%    288
  75%    307
  80%    320
  90%    365
  95%    411
  98%    501
  99%    568
 100%    568 (longest request)

Tak na oko, przyspieszenie jest blisko dwu i półkrotne, wszystko działa, w logach czysto, więc wygląda to obiecująca. Jeśli w najbliższych dniach nie wystąpią problemy dam szansę siódemce z Piwikiem, PrestaShop i Roundcube’em.

Wyłączenie funkcji buforowania PrestaShop

PrestaShop posiada przydatny moduł pod tytułem „Aktualizacja 1 kliknięciem”. Aby takową aktualizację uruchomić należy spełnić kilka warunków.

Aktualizacja 1 kliknięciem

O ile przełączyć sklep w tryb konserwacji można z poziomu rzeczonej listy, o tyle sprawnie, że „Funkcje buforowania PrestaShop są wyłączone” nie jest takie oczywiste. PrestaShop ma kilka ustawień, które związane są z buforowaniem czy też pamięcią podręczną, a w polskim tłumaczeniu żadna z nich nie nazywa się „funkcje buforowania”. Żeby spełnić ten warunek należy udać się do menu Zaawansowane / Wydajność, a następnie zjechać na dół strony do panelu zatytułowanego Cache. Tam należy przestawić suwaczek „Użyj pamięci podręcznej” w pozycję „NIE”.

Użyj pamięci podręcznej

Android zacina się? Reset do ustawień fabrycznych może pomóc

Swego czasu jojczyłem na blogasku na ślamazarnie działającego Androida 5.0 na mojej Motorolce Moto G pierwszej generacji. Opisywane czyszczenie partycji cache działało doraźnie, Android 5.1 nic w tej kwestii nie zmienił, a moja irytacja rosła, aż w końcu urosła do tego stopnia, że pokonała moje lenistwo. Kilka dni temu zgrałem z telefonu wszystko, co uznałem za istotne i zaordynowałem reset do stanu fabrycznego. Efekt? Telefon działa tak jak po wyjęciu z pudełka, po kilku dniach działania nie wykazuje żadnych oznak zadyszki, a nawet nieco oszczędniej obchodzi się z baterią. Wygląda na to, że jednak nie będę miał sensownego powodu by wymieniać telefon w najbliższym czasie.

Operacja resetowania telefonu trwa krótko, ale ponowne konfigurowanie telefonu jest już dość czasochłonne. Na szczęście duża część może odbyć się automatycznie, o ile zezwolimy Googlowi na przechowywanie kopii zapasowych. Po zalogowaniu na konto Google’a telefon sam zainstalował wcześniej używane aplikacje, przywrócił tapetę i część ustawień.

Dla porządku zamieszczam skróconą instrukcję:

  1. Wyłączyć telefon.
  2. Przytrzymać przez kilka sekund jednocześnie przyciski zwiększania głośności, zmniejszania głośności i włącznik.
  3. Używając przycisku zmniejszania głośności w menu należy podświetlić opcję Recovery, a następnie zatwierdzić wybór przyciskiem zwiększania głośności.
  4. Gdy na ekranie pojawi się leżący na plecach android oraz napis „no command” należy przytrzymać włącznik i nacisnąć przycisk zwiększania głośności.
  5. Na ekranie objawi się menu trybu Recovery, z którego trzeba wybrać „Factory Reset”. Wybór zatwierdza się przyciskiem włączania telefonu.
  6. W kolejnym menu należy zatwierdzić operację w ten sam sposób.