Projekt Bilet #4 — Przygotowania do implementacji

By 15 October 2018 Projekt Bilet

Mamy już pod­sta­wowe ele­men­ty aplikacji — połącze­nie z bazą danych, kon­fig­u­rac­je testową, pod­sta­wowy mod­el uwierzytel­ni­a­nia i auto­ryza­cji, kole­jnym log­icznym krok­iem jest więc imple­men­tac­ja funkcjon­al­noś­ci. Zan­im do tego prze­jdziemy, warto przy­go­tować pro­jekt pod inten­sy­wną pracę — ustaw­ić reguły check­style, środowisko CI itp — i tym zajmiemy się dzisiaj.

Każdy pro­jekt opro­gramowa­nia moż­na rozbić na dwa ele­men­ty — funkcjon­al­ność, czyli wartość jaką pro­jekt dostar­cza swoim użytkown­ikom, oraz potenc­jał roz­wo­ju (znacznie lep­iej pasu­je tutaj ang­iel­skie słówko — main­tan­abil­i­ty), który określa jak łat­wo jest nasz pro­dukt utrzymy­wać oraz rozwi­jać. Oczy­wiś­cie funkcjon­al­ność, wartość dla użytkown­ików jest najważniejsza — z tym nikt nie dysku­tu­je. Jed­nak fakt, który dość łat­wo przeoczyć, to wpływ jakoś­ci kodu na zdol­ność dostar­cza­nia funkcjon­al­noś­ci w przyszłoś­ci — pro­jekt poprawnie zbu­dowany od początku wyma­ga zde­cy­dowanie mniej cza­su na wszys­tkie czyn­noś­ci ‘dookoła’ takie jak utrzy­manie, aktu­al­iza­c­je, popraw­ki błędów, wdrażanie nowej wer­sji itp. Dzię­ki temu pro­gramiś­ci mają więcej cza­su, aby skupić się na potrze­bach użytkown­i­ka i fak­ty­cznie rozwi­jać aplikację, a nie tylko gasić pożary. To będzie tem­atem tej lekcji — przy­go­towanie aplikacji do dal­szej efek­ty­wnej pracy.

Podstawowe testy aplikacji

Testowanie na tym etapie pro­jek­tu może wydawać się prze­sadą — w końcu nasz pro­jekt właś­ci­wie nic nie robi. Niem­niej jest kil­ka testów, które mają sens — np. taki, który sprawdza, czy aplikac­ja się uruchamia. Nie tylko cza­sem może­my coś przeoczyć (literówka, brak adno­tacji, niespełniona zależność itp), ale też testy ist­niejące od samego początku życia aplikacji wyz­nacza­ją pewien stan­dard i konwencje.

Zaczni­jmy od wspom­ni­anego tes­tu, czy aplikac­ja się uruchamia. Z pewnoś­cią nie jest to test jed­nos­tkowy — w końcu nie tes­tu­je­my poje­dynczego kom­po­nen­tu, a całą aplikację. Do tego służą testy inte­gra­cyjne — ich celem jest sprawdze­nie, jak aplikac­ja wyglą­da ‘z zewnątrz’ (z punk­tu widzenia kogoś, kto się z nią inte­gru­je — stąd nazwa).

Maven do uruchami­a­nia testów wyko­rzys­tu­je plug­iny o nazwie Sure­fire oraz Fail­safe, w opisie kon­fig­u­racji zna­jdziemy domyśl­ną kon­wencję jeśli chodzi o testy jed­nos­tkowe oraz inte­gra­cyjne. Niekoniecznie jest to najbardziej czytel­na doku­men­tac­ja świa­ta, ale dla nas istotne są dwie rzeczy:

  • Aby test uru­chomił się jako test jed­nos­tkowy, nazwa musi speł­ni­ać jeden z kryteriów: 
    • Zaczy­nać się od Test (np. Test­My­Func­tion­al­i­ty)
    • Kończyć się na Test lub Tests (np. MyClassTest, MyMod­uleTests)
    • Kończyć się na Test­Case (np. User­Ac­tion­Test­Case)
  • Aby test uru­chomił się jako test inte­gra­cyjny, nazwa musi speł­ni­ać jeden z kryteriów: 
    • Zaczy­nać się od IT (np. ITMy­Func­tion­al­i­ty)
    • Kończyć się na IT (np. MyAp­pli­ca­tion­IT)
    • Kończyć się na ITCase (np. User­Ac­tion­IT­Case)

Oczy­wiś­cie powyższe odnosi się jedynie do domyśl­nych ustaw­ień, i może­my je zmienić w zależnoś­ci od wyma­gań pro­jek­tu. Co ważne, wielkość liter ma znacze­nie, a jeżeli nasza klasa z tes­ta­mi nie speł­nia tych kry­ter­iów, nie będzie ona uruchami­ana przez Maven w trak­cie budowa­nia pro­jek­tu! Może to doprowadz­ić do sytu­acji, w której test będzie zwracał błędy, ale nie będzie częś­cią automaty­cznego pro­ce­su budowa­nia więc będą one przeoczone!

Mając na uwadze powyższe, stwórzmy nasz pier­wszy test inte­gra­cyjny, który sprawdzi, czy aplikac­ja się uruchamia. Spring Boot posi­a­da bard­zo dobre wspar­cie do testowa­nia opisane w doku­men­tacji (uwa­ga na nazewnict­wo klas!). Najprost­szy test pole­ga na zainicjowa­niu kon­tek­stu oraz założe­niu, że jakikol­wiek prob­lem spowodu­je wyjątek i w efek­cie błąd w teście:

@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest
public class WebappApplicationIT {
  @Test
  public void contextLoads() {
  }
}

Jeśli do wygen­erowa­nia pro­jek­tu użyliśmy Spring Ini­tial­izr, to podob­ny test będzie już zaim­ple­men­towany (aby uruchami­ał się jako inte­gra­tion test, wystar­czy zmienić jego nazwę wg powyższych wyty­cznych; dodal­iśmy też akty­wny pro­fil test). Opiera się on jed­nak o założe­nie, że aplikac­ja rzu­ci wyjątek w przy­pad­ku prob­le­mu — w prak­tyce jest wiele sytu­acji, w której wyjąt­ki mogą nie zostać rzu­cone lub zostaną przech­wycone w między­cza­sie. Dlat­ego warto rozsz­erzyć nasz test tak, aby sprawdzał czy aplikac­ja fak­ty­cznie się uruchomiła.

Aby testy uruchami­ały się automaty­cznie jako część pro­ce­su budowa­nia, wystar­czy dodać stosowną kon­fig­u­rację do pliku pom.xml:

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-failsafe-plugin</artifactId>
</plugin>

To najprost­szy sposób na ‘bazowy’ test inte­gra­cyjny — pozwala nam sprawdz­ić, czy środowisko jest w stanie się uru­chomić i w przy­pad­ku prob­lemów zapo­biec wdroże­niu takiej wer­sji na środowiska pro­duk­cyjne. Oczy­wiś­cie to nie powinien być jedyny test inte­gra­cyjny w aplikacji — inny­mi zajmiemy się jed­nak wraz z imple­men­tacją funkcjonalności.

Spring Boot Actuator

Powyższą metodę moż­na trochę ulep­szyć — moglibyśmy np. sprawdzać określony end­point naszej aplikacji. Wtedy jed­nak nasz test sta­je się wrażli­wy na wszelkie zmi­any w danym end­poincie, nie jest to więc sytu­ac­ja ide­al­na (biorąc pod uwagę, że w tym teś­cie chce­my przetestować całą aplikację, a nie tylko wybrany end­point). Na szczęś­cie Spring przy­chodzi z pomocą ofer­u­jąc wbu­dowane end­pointy, które może­my wyko­rzys­tać. W naszym teś­cie na ten moment wyko­rzys­tamy jedynie end­point ‘/health’, w prak­tyce ofer­u­ją nam one dużo więk­sze możli­woś­ci (np. wery­fikac­je zaap­likowanych migracji bazy danych). Są one częś­cią Spring Boot Actu­a­tor — paki­etu narzędzi przy­dat­nych do automatyza­cji i zarządza­nia aplikacją dzi­ała­jącą na pro­dukcji. Po bardziej szczegółowy opis odsyłamy do doku­men­tacji.

W naszej aplikacji wyłączymy więc wszys­tkie domyślne end­pointy, a pozostaw­imy jedynie /health. Przy­da nam się on nie tylko do testów, ale też do zewnętrznej wery­fikacji, czy aplikac­ja dzi­ała. Przede wszys­tkim musimy dodać zależność do naszego pliku pom.xml:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Następ­nie do naszego pliku appli­ca­tion-prop­er­ties doda­je­my lin­ij­ki (wyłącza­my wszys­tkie end­pointy oraz akty­wu­je­my tylko end­point /health):

endpoints.enabled=false
endpoints.health.enabled=true

Pamię­taj także aby zak­tu­al­i­zować ustaw­ienia Spring Secu­ri­ty tak, aby każdy bez auto­ryza­cji miał dostęp do ścież­ki /health, w tym celu mody­fiku­je­my naszą metodę con­fig­ure() doda­jąc jed­ną linijkę:

@Override
protected void configure(HttpSecurity http) throws Exception {
  http
    .authorizeRequests()
      .antMatchers("/health").permitAll()
      .anyRequest().denyAll()
      .and()
    .formLogin()
      .disable();
}

Modyfikujemy test

Dzię­ki powyższym zabiegom może­my użyć tego end­pointu w naszym teś­cie inte­gra­cyjnym. Jed­nocześnie zmody­fiku­je­my nieco sam test, aby wyko­rzysty­wał losowy port, dostęp­ny w sys­temie (dzię­ki temu unikniemy potenc­jal­nych prob­lemów pod­czas jed­noczes­nego testowa­nia i uruchami­a­nia aplikacji). W tym celu sko­rzys­tamy z gotowego przykładu z doku­men­tacji, nasz test ostate­cznie wyglą­da następująco:

@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class WebappApplicationIT {

  @Autowired
  private TestRestTemplate restTemplate;

  @Test
  public void contextLoads() {
    ResponseEntity<ObjectNode> response = this.restTemplate.getForEntity("/health", ObjectNode.class);
    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);

    ObjectNode body = response.getBody();
    assertThat(body).isNotNull().isNotEmpty();

    JsonNode healthStatus = body.get("status");
    assertThat(healthStatus).isInstanceOf(TextNode.class);
    assertThat(healthStatus.asText()).isEqualTo("UP");
  }
}

Zwróć uwagę, że do naszego tes­tu wstrzyku­je­my TestRe­stRem­plate — ta klasa to nic innego jak wari­ant Rest­Tem­plate, które­mu nie musimy podawać adresu naszej aplikacji (np. localhost:8080). Uży­wamy też klasy JsonN­ode — to jed­na z klas bib­liote­ki Jack­son, która reprezen­tu­je dowol­ną struk­turę w doku­men­cie JSON (dziedz­iczą po niej klasy ObjectN­ode, ArrayN­ode, TextN­ode itp).

Czytelność ponad wszystko

Czytel­ność kodu jest jed­nym z ważniejszych ele­men­tów pra­cy zespołowej nad aplikac­ja­mi — dzię­ki temu łatwiej zlokali­zować prob­lem czy zrozumieć/zauważyć sposób jego pow­stawa­nia. I o ile zasady clean code są (mamy nadzieję) Ci dobrze znane, o tyle czytel­ność w przy­pad­ku zależnoś­ci pro­jek­tu nieste­ty nie jest częs­to spo­tykaną prak­tyką. W tym wypad­ku dobrym stan­dar­d­em powin­ny być trzy proste zasady:

  • zależnoś­ci w pliku pom.xml są ułożone alfa­bety­cznie — pozwala to szy­bko sprawdz­ić, czy określona zależność jest dodana do pro­jek­tu czy nie oraz w jakiej wersji
  • w miarę możli­woś­ci pro­jekt korzys­ta z depen­den­cy­Man­age­ment lub co najm­niej z uży­wa­nia prop­er­ties do określa­nia wer­sji — dzię­ki temu unikniemy sytu­acji, w których zależnoś­ci z tej samej grupy (np. mod­uły Springa) są impor­towane w różnych, potenc­jal­nie niez­god­nych, wersjach
  • wszys­tkie uży­wane zależnoś­ci powin­ny być deklarowane, a te, które nie są już uży­wane powin­ny być usuwane z pliku POM — to najm­niej oczy­wista zasa­da z powyższych — pole­ga ona na tym, że korzys­ta­jąc z klasy pochodzącej z bib­liote­ki lub zależnoś­ci, musimy wskazać tą zależność jawnie w pliku pom.xml (inny­mi słowy, nie może­my uży­wać zależnoś­ci, które ist­nieją w pro­jek­cie tylko poprzez inne zależnoś­ci; jest to dobra prak­ty­ka z 2 powodów — przede wszys­tkim pozwala kon­trolować wer­sje zależnoś­ci, których uży­wamy, a także zabez­piecza nas przed zmi­ana­mi w drzewie zależnoś­ci które mogą spowodować prob­le­my z budowaniem się projektu

Może­my w tym celu wyko­rzys­tać kil­ka plug­inów. Pier­wszy z nich to maven-depen­den­cy-plu­g­in oraz jego cel ana­lyze. Plu­g­in ten anal­izu­je zależnoś­ci, jakie mamy zadeklarowane w pro­jek­cie oraz te, w których są klasy bezpośred­nio przez nas uży­wane. W przy­pad­ku roz­bieżnoś­ci infor­mu­je o tym w postaci warn­ingów. Może­my (i w naszym przy­pad­ku tak zro­bimy) skon­fig­urować go tak, że każdy znaleziony prob­lem skutku­je błę­dem w budowa­niu. Do naszego pliku pom.xml doda­jmy następu­ją­cy kod:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <configuration>
    <skip>${skip.dependency.analysis}</skip>
    <outputXML>true</outputXML>
    <failOnWarning>true</failOnWarning>
    <ignoreNonCompile>true</ignoreNonCompile>
  </configuration>
  <executions>
    <execution>
      <id>analyze</id>
      <goals>
        <goal>analyze-only</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Dodanie tej kon­fig­u­racji przy pier­wszym buildzie zwró­ci najpraw­dopodob­niej sporo prob­lemów i uwag. Warto jed­nak poświę­cić chwilę na jego kon­fig­u­rację, aby pil­nował nas w przyszłości ;)

Kole­jny z przy­dat­nych plug­inów to sort­pom, który moż­na znaleźć na GitHu­bie.

Plu­g­in ten pozwala na uporząd­kowanie pliku POM, sor­tu­jąc jego ele­men­ty, wyrównu­jąc wcię­cia itp. Aby automaty­cznie sfor­ma­tować nasz plik pom.xml wystar­czy wywołać polecenie:

mvn com.github.ekryd.sortpom:sortpom-maven-plugin:sort

Warto wykon­ać to polece­nie i jego wynik dodać do repozy­to­ri­um jako osob­ny com­mit — licz­ba zmi­an najpraw­dopodob­niej będzie spo­ra, dlat­ego dodanie ich razem z inny­mi zmi­ana­mi mogło­by je ‘schować’ pod­czas PR. Po posor­towa­niu pliku pom.xml może­my dodać kon­fig­u­rację, która będzie sprawdza­ła czy plik ten nadal jest posor­towany i poprawnie sfor­ma­towany, w tym celu do sekcji <build> doda­je­my kole­jną kon­fig­u­rację pluginu:

<plugin>
  <groupId>com.github.ekryd.sortpom</groupId>
  <artifactId>sortpom-maven-plugin</artifactId>
  <version>2.8.0</version>
  <executions>
    <execution>
      <id>sortpom</id>
      <goals>
        <goal>verify</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <keepBlankLines>true</keepBlankLines>
    <verifyFail>Stop</verifyFail>
  </configuration>
</plugin>

Oczy­wiś­cie możli­woś­ci kon­fig­u­racji jest więcej — ich opis zna­jdziesz w doku­men­tacji plug­inu. Możli­we jest także automaty­czne sor­towanie zami­ast wery­fikacji w cza­sie budowa­nia — w tym celu wystar­czy usunąć lin­ijkę <verifyFail>Stop</verifyFail>. Może to jed­nak prowadz­ić do for­ma­towa­nia niez­god­nego z naszy­mi oczeki­wa­ni­a­mi (np. usunię­cie linii, zmi­ana względ­nej lokaliza­cji komen­tarzy itp) — dlat­ego bez­pieczniej jest man­u­al­nie zwery­fikować wyni­ki automaty­cznego poprawiania.

Dodatkowe sprawdzenia

Cza­sem zdarza się, że pro­jekt ma spec­jalne wyma­gania — np. konkret­nej wer­sji JDK, sys­te­mu oper­a­cyjnego lub innych czyn­ników. W takim przy­pad­ku pomoc­ny może być Maven enforcer plu­g­in — pozwala sprawdzać para­me­try środowiska, zależnoś­ci pod kątem niedoz­wolonych wersji/artefaktów, jak też zas­tosować własne reguły. W tym pro­jek­cie nie będziemy z niego korzys­tać, ale warto wiedzieć o jego istnieniu.

Listę ele­men­tów możli­wych do sprawdzenia ‘od ręki’ zna­jdziesz oczy­wiś­cie w doku­men­tacji.

Automatyzujemy kontrolę jakości — Checkstyle, PMD, SpotBugs (FindBugs)

Kole­jnym krok­iem jest sprawdze­nie (oraz wymusze­nie) sty­lu naszego kodu. W tej kat­e­gorii na szczegól­ną uwagę zasługu­ją trzy narzędzia:

  • Check­style — pozwala sprawdz­ić nasz kod pod wzglę­dem zgod­noś­ci z przyję­ty­mi zasada­mi sty­lu. O ile w Two­jej orga­ni­za­cji / pro­jek­cie nie ist­nieją specy­ficzne zasady, warto sko­rzys­tać z wcześniej przy­go­towanych (np. Google style guide). O tym, czym jest Check­style oraz jak go skon­fig­urować, pisal­iśmy w osob­nym poś­cie.
  • Spot­Bugs (kon­tynu­ac­ja Find­Bugs, w przy­pad­ku którego zaprzes­tano roz­wo­ju) — sprawdza kod pod kątem potenc­jal­nych błędów (niezainicjowane zmi­enne, niezamknięte zaso­by itp). O Find­Bugs (który jest bazą dla Spot­Bugs i póki co jest w więk­szoś­ci kom­paty­bil­ny pod kątem kon­fig­u­racji i funkcji) także pisal­iśmy w osob­nym wpisie.
  • PMD/CPD — PMD jest narzędziem podob­nym do Find­Bugs (sku­pia się na staty­cznej anal­izie kodu w celu znalezienia potenc­jal­nych prob­lemów), poszuki­wane prob­le­my różnią się jed­nak, więc warto korzys­tać z obu. CPD z kolei jest ciekawym narzędziem, ponieważ wyszuku­je frag­men­ty kodu sko­pi­owane w różnych miejs­cach. Dzię­ki temu wymusza wyciąg­nię­cie wspól­nego kodu do osob­nych metod, co znacznie poma­ga w zwięk­sze­niu czytel­noś­ci kodu

Wszys­tkie te narzędzia kon­fig­u­ru­je­my w pliku pom.xml jako plug­iny. Bazowa kon­fig­u­rac­ja dla naszego pro­jek­tu wyglą­dać będzie następująco:

<plugins>
  ...
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>2.17</version>
    <executions>
      <execution>
        <id>validate</id>
        <phase>validate</phase>
        <goals>
          <goal>check</goal>
        </goals>
        <configuration>
          <configLocation>google_checks.xml</configLocation><!-- używamy domyślnych reguł wg Google -->
          <encoding>UTF-8</encoding>
          <consoleOutput>true</consoleOutput>
          <failsOnError>true</failsOnError>
          <failOnViolation>true</failOnViolation>
          <violationSeverity>warning</violationSeverity>
          <linkXRef>false</linkXRef>
        </configuration>
      </execution>
    </executions>
  </plugin>
  <plugin>
    <groupId>com.github.spotbugs</groupId>
    <artifactId>spotbugs-maven-plugin</artifactId>
    <version>3.1.3</version>
    <configuration>
      <effort>Max</effort>
      <threshold>Low</threshold>
      <xmlOutput>true</xmlOutput>
    </configuration>
    <executions>
      <execution>
        <goals>
          <goal>check</goal>
        </goals>
      </execution>
    </executions>
  </plugin>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-pmd-plugin</artifactId>
    <version>3.8</version>
    <executions>
      <execution>
        <id>pmd-pmd</id>
        <goals>
          <goal>check</goal>
        </goals>
        <configuration>
          <excludeFromFailureFile>src/main/resources/exclude-pmd.properties</excludeFromFailureFile>
        </configuration>
      </execution>
      <execution>
        <id>pmd-cpd</id>
        <goals>
          <goal>cpd-check</goal>
        </goals>
        <configuration>
          <excludeFromFailureFile>src/main/resources/exclude-cpd.properties</excludeFromFailureFile>
        </configuration>
      </execution>
    </executions>
  </plugin>
</plugins>

Konfigurujemy środowisko CI

Uwa­ga! W tej sekcji omaw­iamy kon­fig­u­rację na przykładzie Cir­cle CI, ponieważ narzędzie to jest intu­icyjne w uży­ciu i w pakiecie bezpłat­nym moż­na budować pry­watne pro­jek­ty. Jeśli z jakiegoś powodu nie jest to narzędzie dla Ciebie, lub po pros­tu chcesz spróbować innych narzędzi, na końcu tej sekcji zna­jdziesz kil­ka alter­natyw — więk­szość narzędzi typu SaaS jest bezpłat­na dla pro­jek­tów Open Source.

W przy­pad­ku środowiska CI mamy do wyboru dwie ścież­ki — insta­lac­je na włas­nym serwerze/infrastrukturze lub sko­rzys­tanie z narzędzia dostęp­nego w chmurze. W naszym wypad­ku wybierze­my opcję drugą — jest to zde­cy­dowanie wygod­niejsze dla małych pro­jek­tów oraz także tańsze (dzię­ki bezpłat­nym opcjom ofer­owanym przez różnych dostaw­ców). W naszym pro­jek­cie sko­rzys­tamy z narzędzia CircleCI.

Pier­wszym krok­iem jest oczy­wiś­cie rejes­trac­ja — może­my utworzyć nowe kon­to za pomocą loginu i hasła lub korzys­ta­jąc z inte­gracji z Google czy GitHubem. Pole­camy szczegól­nie inte­grację z GitHubem, ponieważ poz­woli to nam skon­fig­urować cały pro­ces niemalże automatycznie.

Rejes­trac­ja za pomocą kon­ta GitHub

Inte­gru­jąc się z Twoim kon­tem GitHub zosta­niesz popros­zona o akcep­tac­je uprawnień:

GitHub — akcep­tac­ja uprawnień dla Cir­cle­CI pod­czas rejestracji

Jakkol­wiek byśmy nie utworzyli kon­ta, po zal­o­gowa­niu się sys­tem prze­niesie nas na główny ekran:

Główny ekran po pier­wszym logowa­niu do CircleCI

Widz­imy tutaj listę orga­ni­za­cji, do których należymy. Najczęś­ciej będzie to tylko jed­na pozy­c­ja — Two­ja nazwa użytkown­i­ka. W naszym przy­pad­ku mamy zarówno nazwę użytkown­i­ka (kobi­ety­doko­du) jak i orga­ni­za­cję (kobi­ety­dokodu­pl). Przy opcji, która nas intere­su­je wybier­amy ‘Add projects’ (jeśli później będziesz chci­ała skon­fig­urować dodatkowe repozy­to­ria oczy­wiś­cie jest to możli­we). Powin­naś zobaczyć listę dostęp­nych repozytoriów:

List repozy­toriów w ramach organizacji

Tutaj wybier­amy już konkretne repozy­to­ri­um, które chce­my skon­fig­urować i klikamy w ‘Set­up project’ w odpowied­nim wier­szu. Aplikac­ja przenosi nas do ustaw­ień projektu:

Ekran kon­fig­u­racji projektu

Jedynym ustaw­ie­niem, które musimy zmienić w naszym przy­pad­ku to przełącze­nie typu pro­jek­tu (sekc­ja ‘Lan­guage’) na ‘Maven (Java)’ (domyśl­nie wybrany jest Gra­dle). Pozostałe ustaw­ienia może­my pozostaw­ić bez zmian.

Zwróć uwagę na list­ing na samym dole — zaw­iera on pod­sta­wową kon­fig­u­rację którą należy sko­pi­ować i umieś­cić w pliku .circleci/config.yaml (tą samą kon­fig­u­rację moż­na znaleźć pod adresem https://circleci.com/docs/2.0/language-java/#sample-configuration). Bez niej nasza aplikac­ja także zostanie zbu­dowana, ale ten plik poz­woli nam na dokład­niejsze dopa­sowanie pro­ce­su do naszych potrzeb w przyszłoś­ci. Na koniec klikamy ‘Start build­ing’ aby rozpocząć pier­wszy build projektu.

Aplikac­ja przekieru­je nas na ekran buil­da — będziemy mogli na bieżą­co śledz­ić postęp a także prze­jrzeć infor­ma­c­je o błę­dach, jeśli takie się pojaw­ią. Zachę­camy do ‘przek­lika­nia’ się przez inter­fe­js — teo­re­ty­cznie po skon­fig­urowa­niu pro­jek­tu nie będziemy wracać do tego narzędzia za częs­to, w prak­tyce warto mieć choć­by min­i­malne oby­cie z inter­fe­jsem i dostęp­ny­mi opc­ja­mi na wszel­ki wypadek ;)

Uwa­ga! Jako że domyślne środowisko dla pro­jek­tu wyko­rzys­tu­je sys­tem Lin­ux do budowa­nia pro­jek­tu, za pier­wszym razem nasz build zwró­ci błąd. Aby go rozwiązać, musimy dodać do naszego pliku pom.xml wer­sję testowej bazy danych dla sys­te­mu linux:

<dependency>
<groupId>ch.vorburger.mariaDB4j</groupId>
<artifactId>mariaDB4j-db-linux64</artifactId>
<version>10.1.13</version>
<scope>provided</scope>
</dependency>

Konfigurujemy status badge

Jed­nym z dodatków, które ofer­u­je Cir­cle­CI jest możli­wość wstaw­ia­nia ‘badge’ ze sta­tusem buil­du. O ile w przy­pad­ku pro­jek­tu, który rozwi­jamy samodziel­nie (lub w małym zes­pole) ma to ogranic­zoną przy­dat­ność (w przy­pad­ku prob­lemów z buil­dem i tak otrzy­mamy powiadomie­nie mailowe), o tyle w przy­pad­ku pro­jek­tów pub­licznych pozwala to innym osobom szy­bko zori­en­tować się czy pro­jekt jest w stanie nada­ją­cym się do sklonowania/wykorzystania/rozwoju czy nie. Dodanie badge jest bard­zo proste, przede wszys­tkim musimy wygen­erować token API dla projektu.

Pod­sumowanie tokenów API dla projektu

W tym celu w widoku ‘API Per­mis­sions’ wybier­amy opcję ‘Cre­ate token’:

Widok dodawa­nia tokenu API

A następ­nie wybier­amy nazwę dla naszego tokenu (nazwa może być dowol­na i służy tylko dla nas do łatwiejszej iden­ty­fikacji tokenu w przyszłoś­ci). W polu ‘scope’ wybier­amy opcję ‘Sta­tus’ i klikamy ‘Add token’.

Następ­nie prze­chodz­imy na zakład­kę ‘Sta­tus badge’, wybier­amy branch ‘mas­ter’ oraz nasz nowo utwor­zony token:

Gen­erowanie badge ze sta­tusem projektu

W okienku na dole pojawi się kod mark­down, który może­my następ­nie sko­pi­ować do naszego pliku README.md .

Alternatywne narzędzia

Oczy­wiś­cie poza omaw­iany­mi powyżej narzędzi­a­mi ist­nieje wiele innych, które speł­ni­a­ją podob­ne funkc­je. Częs­to różnią się głównie inter­fe­jsem użytkown­i­ka, ceną i sposobem kon­fig­u­racji — funkcjon­al­nie więk­szość narzędzi pozwala na mniej więcej to samo. Pokazane przez nas narzędzia wcale nie muszą być najlep­sze czy naje­fek­ty­wniejsze dla Two­jego pro­jek­tu — zachę­camy do ekspery­men­towa­nia z inny­mi plat­for­ma­mi i wybra­nia takiej, która jest dla Ciebie najlep­sza. Poniżej bard­zo skró­towa lista narzędzi, które znamy — jeśli znasz i pole­casz inne, zostaw lin­ka w komentarzu!

Host­ing projektu:

CI / build system:

Jak sama widzisz, opcji jest mnóst­wo (a to z pewnoś­cią nie wszys­tkie dostęp­ne możli­woś­ci) — nieste­ty nie jesteśmy w stanie opisać kon­fig­u­racji każdej z nich, ale najczęś­ciej doku­men­tac­ja tych narzędzi jest bard­zo dokład­na i samodziel­na kon­fig­u­rac­ja nie powin­na stanow­ić prob­le­mu. Wiele z nich ma także akty­wne społecznoś­ci, które chęt­nie pomogą z potenc­jal­ny­mi problemami.

Jeśli zas­tanaw­iasz się, która z nich jest najlep­sza, to nieste­ty nie ma jed­nej ‘najlep­szej’ plat­formy. Kon­cen­tru­ją się one na różnych rzeczach, więc w zależnoś­ci od pro­jek­tu, umiejęt­noś­ci i kom­pe­tencji zespołu oraz budże­tu pro­jek­tu rozwiąza­nia te będą w innym stop­niu dopa­sowane lub nie.

Podsumowanie

Mam nadzieję, że przekon­al­iśmy Cię, że czas poświę­cony na kon­fig­u­rac­je i automatyza­cję pro­ce­su nie jest cza­sem stra­conym :) Choć cza­sem może być trud­no przekon­ać oso­by zarządza­jące na spędze­nie kilku dni bez dostar­cza­nia dodatkowej funkcjon­al­noś­ci, włożony wysiłek z pewnoś­cią zwró­ci się bard­zo szybko.

Kod źródłowy

Przeglądaj kodPobierz ZIP

Kody źródłowe są dostęp­ne w ser­wisie GitHub — użyj przy­cisków po prawej aby pobrać lub prze­jrzeć kod do tego mod­ułu. Jeśli masz wąt­pli­woś­ci, jak posługi­wać się Git’em, instrukc­je i lin­ki zna­jdziesz w naszym wpisie na tem­at Git’a.

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzięki!