
Continuous Integration – po co mi to?
Gdzie projekt w którym jest więcej niż jeden programista, tam Continuous Integration jest nieodzowne. Zaryzykuję nawet stwierdzenie, że nawet jak jest jeden programista – to jest potrzebne. Jeśli nie zastosujemy automatycznego budowania projektu, to trudno będzie utrzymać dyscyplinę – ktoś w końcu zapomnij odpalić testy czy trzymać się standardu kodowania.
Utrzymywanie swojego serwera CI nie zawsze się opłaca – zwłaszcza, jeśli zespół jest niewielki. Z pomocą przychodzą wtedy usługi chmurowe. Nie musimy opłacać admina – jak w przypadku swojego serwera, bo wykupujemy (czasem nawet dostajemy za darmo) usługę, która jest dla nas utrzymywana.
Bitbucket Pipelines
Jak to działa?
Tak właśnie jest w jednym z projektów w którym uczestniczę. Repozytorium jest już w chmurze – na bitbucket.org, więc postanowiłem dać szansę ich CI – Bitbucket Pipelines. 50 minut do wykorzystania dostajemy co miesiąc za darmo.
Całe środowisko oparte jest o kontenery Dockera, co daje elastyczność, jeśli chodzi o konfigurację. Całość tejże została sprowadzone do jednego pliku yml trzymanego w głównym katalogu repozytorium – „bitbucket-pipelines.yml”
W moim projekcie używam docker-compose do stawiania środowiska. Chciałem, więc, żeby identyczne środowisko uruchamiało się podczas budowania projektu. Pipelines nie mają z pudełka obsługi docker-compose – można bezpośrednio definiować dodatkowe serwisy takie jak MySQL, ale to by oznaczało utrzymanie zduplikowanej konfiguracji: jednej dla docker-compose, drugiej dla Bitbucket Pipelines. Mało praktyczne.
Docker-compose z Bitbucket Pipelines
Można użyć „docker-in-docker”, czyli obrazu Dockera, który sam w sobie jest Dockerem (musimy dodać tylko docker-compose). Opisał to autor tego artykułu.
Przykładowy plik z konfiguracją może wyglądać tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
image: raphdocker007/docker-compose:latest options: docker: true pipelines: custom: full-tests: - step: caches: - docker script: - chmod +x ci/prepare_env.sh - ci/prepare_env.sh - cd docker - docker-compose exec -T project-php1 bash -c "cd /src && ./vendor/bin/phpunit ./tests/TestCase/Unit --log-junit ./test-reports/phpunit_unit.xml" - docker-compose exec -T project-php1 bash -c "cd /src && ./vendor/bin/phpunit ./tests/TestCase/Integration --log-junit ./test-reports/phpunit_integration.xml" - docker-compose exec -T project-php1 bash -c "cd /src && mkdir -p ./test-reports && vendor/bin/phpcs --standard=psr2 --report=junit --report-file=./test-reports/phpcs.xml src" artifacts: - src/vendor/** pull-requests: '**': - step: name: Run tests without integration tests caches: - docker script: - chmod +x ci/prepare_env.sh - ci/prepare_env.sh - cd docker - docker-compose exec -T project-php1 bash -c "cd /src && ./vendor/bin/phpunit ./tests/TestCase/Unit --log-junit ./test-reports/phpunit_unit.xml" - docker-compose exec -T project-php1 bash -c "cd /src && mkdir -p ./test-reports && vendor/bin/phpcs --standard=psr2 --report=junit --report-file=./test-reports/phpcs.xml src" artifacts: - src/vendor/** branches: master: - step: name: Run full tests caches: - docker script: - chmod +x ci/prepare_env.sh - ci/prepare_env.sh - cd docker - docker-compose exec -T project-php1 bash -c "cd /src && ./vendor/bin/phpunit ./tests/TestCase/Unit --log-junit ./test-reports/phpunit_unit.xml" - docker-compose exec -T project-php1 bash -c "cd /src && ./vendor/bin/phpunit ./tests/TestCase/Integration --log-junit ./test-reports/phpunit_integration.xml" - docker-compose exec -T project-php1 bash -c "cd /src && mkdir -p ./test-reports && vendor/bin/phpcs --standard=psr2 --report=junit --report-file=./test-reports/phpcs.xml src" artifacts: - src/vendor/** |
Nawet bez czytania przejrzystej dokumentacji Pipelines – łatwo się domyśleć, że mamy zdefiniowane w powyższym przykładzie 3 pipeline’y w tym jeden odpalany przy pull-requestach.
Niektóre funkcje warte wspomnienia
Warto wspomnieć, że narzędzie potrafi parsować pliki xml w formacie junit i wyświetlać informacje o wynikach naszych testów.
Pipelines umożliwia zrównoleglanie kroków (co zaoszczędza zużywany czas) – niestety przy korzystaniu z docker-compose nie udało mi się przyspieszyć żadnego z moich pipeline’ów.
Żeby nie tracić niepotrzebnie cennych minut autorzy udostępniają nam cache – np. Composera.
Jakieś problemy muszą być
Poza brakiem natywnej obsługi docker-compose, scheduler do uruchamiania pipeline’ów nie rozpoznaje prawidłowo branchy z „/” w nazwie (zgłoszony i jeszcze nie naprawiony błąd z API Bitbucket).
Czy warto zostać hydraulikiem?
W opisywanym przeze mnie przypadku – jak najbardziej warto. Nawet, jeśli te darmowe 50 minut to za mało, abonament zaczyna się od 10$ za 500 minut, a tu już spokojnie wystarczy na mały, a może i średni projekt.