Ebolusyon ng CI sa mobile development team

Ngayon, karamihan sa mga produkto ng software ay binuo sa mga koponan. Ang mga kondisyon para sa matagumpay na pagbuo ng koponan ay maaaring ilarawan sa anyo ng isang simpleng diagram.

Ebolusyon ng CI sa mobile development team

Kapag naisulat mo na ang iyong code, kailangan mong tiyakin na:

  1. Π Π°Π±ΠΎΡ‚Π°Π΅Ρ‚.
  2. Wala itong sinisira, kasama ang code na isinulat ng iyong mga kasamahan.

Kung ang parehong mga kondisyon ay natutugunan, kung gayon ikaw ay nasa landas sa tagumpay. Upang madaling suriin ang mga kundisyong ito at hindi lumihis mula sa kumikitang landas, kami ay gumawa ng Continuous Integration.

Ang CI ay isang daloy ng trabaho kung saan isinasama mo ang iyong code sa pangkalahatang code ng produkto nang madalas hangga't maaari. At hindi ka lamang nagsasama, ngunit patuloy ding suriin kung gumagana ang lahat. Dahil kailangan mong suriin nang marami at madalas, sulit na isipin ang tungkol sa automation. Maaari mong suriin ang lahat nang manu-mano, ngunit hindi mo dapat, at narito kung bakit.

  • Mahal na mga tao. Ang isang oras ng trabaho ng sinumang programmer ay mas mahal kaysa sa isang oras ng trabaho ng anumang server.
  • Ang mga tao ay nagkakamali. Samakatuwid, maaaring lumitaw ang mga sitwasyon kapag ang mga pagsubok ay pinatakbo sa maling sangay o ang maling commit ay naipon para sa mga tester.
  • Ang mga tao ay tamad. Paminsan-minsan, kapag natapos ko ang isang gawain, naiisip ko: β€œAno ang dapat suriin? Sumulat ako ng dalawang linya - lahat ay gumagana! I think some of you also sometimes have such thoughts. Ngunit dapat mong palaging suriin.

Paano ipinatupad at binuo ang Continuous Integration sa Avito mobile development team, kung paano sila napunta mula 0 hanggang 450 build bawat araw, at ang mga build machine ay nag-assemble ng 200 oras sa isang araw, sabi ni Nikolai Nesterov (nnesterov) ay isang kalahok sa lahat ng ebolusyonaryong pagbabago ng CI/CD Android application.

Ang kuwento ay batay sa halimbawa ng isang Android command, ngunit karamihan sa mga diskarte ay naaangkop din sa iOS.


Noong unang panahon, isang tao ang nagtrabaho sa Avito Android team. Sa pamamagitan ng kahulugan, hindi niya kailangan ng anuman mula sa Continuous Integration: walang sinumang makakasama.

Ngunit ang application ay lumago, parami nang parami ang mga bagong gawain na lumitaw, at ang koponan ay lumago nang naaayon. Sa ilang mga punto, oras na para mas pormal na magtatag ng proseso ng pagsasama ng code. Napagpasyahan na gamitin ang daloy ng Git.

Ebolusyon ng CI sa mobile development team

Ang konsepto ng daloy ng Git ay kilala: ang isang proyekto ay may isang karaniwang sangay ng pag-develop, at para sa bawat bagong tampok, ang mga developer ay pumutol ng isang hiwalay na sangay, nag-commit dito, itulak, at kapag gusto nilang pagsamahin ang kanilang code sa develop na sangay, magbukas ng isang hiling ng hilahin. Upang magbahagi ng kaalaman at talakayin ang mga diskarte, ipinakilala namin ang pagsusuri ng code, ibig sabihin, dapat suriin at kumpirmahin ng mga kasamahan ang code ng bawat isa.

Mga tseke

Ang makita ang code gamit ang iyong mga mata ay cool, ngunit hindi sapat. Samakatuwid, ipinakilala ang mga awtomatikong pagsusuri.

  • Una sa lahat, suriin namin ARK assembly.
  • Marami Mga pagsusulit ni Junit.
  • Isinasaalang-alang namin ang saklaw ng code, dahil nagpapatakbo kami ng mga pagsubok.

Upang maunawaan kung paano dapat patakbuhin ang mga pagsusuring ito, tingnan natin ang proseso ng pag-develop sa Avito.

Maaari itong ilarawan sa eskematiko tulad nito:

  • Isang developer ang nagsusulat ng code sa kanyang laptop. Maaari kang magpatakbo ng mga integration check dito mismo - alinman gamit ang commit hook, o simpleng patakbuhin ang mga tseke sa background.
  • Matapos itulak ng developer ang code, magbubukas siya ng pull request. Upang maisama ang code nito sa develop branch, kailangang dumaan sa pagsusuri ng code at kolektahin ang kinakailangang bilang ng mga kumpirmasyon. Maaari mong i-enable ang mga check at build dito: hanggang sa matagumpay ang lahat ng build, hindi maaaring pagsamahin ang pull request.
  • Pagkatapos pagsamahin ang kahilingan sa pag-pull at ang code ay kasama sa pag-develop, maaari kang pumili ng isang maginhawang oras: halimbawa, sa gabi, kapag ang lahat ng mga server ay libre, at magpatakbo ng maraming mga tseke hangga't gusto mo.

Walang nagustuhan ang pag-scan sa kanilang laptop. Kapag natapos na ng developer ang isang feature, gusto niyang mabilis itong itulak at magbukas ng pull request. Kung sa sandaling ito ay inilunsad ang ilang mahabang pagsusuri, hindi lamang ito kaaya-aya, ngunit pinapabagal din ang pag-unlad: habang sinusuri ng laptop ang isang bagay, imposibleng gumana nang normal dito.

Talagang nagustuhan namin ang pagpapatakbo ng mga tseke sa gabi, dahil maraming oras at mga server, maaari kang gumala sa paligid. Ngunit, sa kasamaang-palad, kapag na-develop ang feature code, mas mababa ang motibasyon ng developer na ayusin ang mga error na nakita ng CI. Paminsan-minsan ay nahuhuli ko ang aking sarili na nag-iisip kapag tinitingnan ko ang lahat ng mga error na natagpuan sa ulat sa umaga na aayusin ko ang mga ito balang araw, dahil ngayon ay may isang cool na bagong gawain sa Jira na gusto ko lang simulan ang paggawa.

Kung ang mga tseke ay humarang sa isang kahilingan sa paghila, kung gayon mayroong sapat na pagganyak, dahil hanggang sa maging berde ang mga build, ang code ay hindi mapapaunlad, na nangangahulugang ang gawain ay hindi makukumpleto.

Bilang resulta, pinili namin ang sumusunod na diskarte: pinapatakbo namin ang maximum na posibleng hanay ng mga tseke sa gabi, at inilulunsad namin ang pinaka-kritikal sa mga ito at, higit sa lahat, ang pinakamabilis sa isang pull request. Ngunit hindi kami tumitigil doonβ€”kasabay nito, ino-optimize namin ang bilis ng mga tseke para mailipat ang mga ito mula sa night mode para i-pull request checks.

Sa oras na iyon, ang lahat ng aming mga build ay nakumpleto nang napakabilis, kaya isinama lang namin ang ARK build, mga pagsusuri sa Junit at mga pagkalkula ng saklaw ng code bilang isang blocker para sa kahilingan sa paghila. In-on namin ito, pinag-isipan ito, at inabandona ang saklaw ng code dahil naisip namin na hindi namin ito kailangan.

Inabot kami ng dalawang araw upang ganap na mai-set up ang pangunahing CI (pagkatapos dito ang pagtatantya ng oras ay tinatayang, kailangan para sa sukat).

Pagkatapos nito, nagsimula kaming mag-isip nang higit pa - sinusuri ba namin nang tama? Gumaganap ba tayo ng mga build sa mga pull request nang tama?

Sinimulan namin ang build sa huling commit ng branch kung saan binuksan ang pull request. Ngunit maipapakita lamang ng mga pagsubok sa commit na ito na gumagana ang code na isinulat ng developer. Pero hindi nila napapatunayan na wala siyang sinira. Sa katunayan, kailangan mong suriin ang estado ng pag-develop ng sangay pagkatapos na pagsamahin ang isang tampok dito.

Ebolusyon ng CI sa mobile development team

Upang gawin ito, nagsulat kami ng isang simpleng script ng bash premerge.sh:

#!/usr/bin/env bash

set -e

git fetch origin develop

git merge origin/develop

Narito ang lahat ng pinakabagong mga pagbabago mula sa pag-develop ay hinila pataas at pinagsama sa kasalukuyang sangay. Idinagdag namin ang premerge.sh script bilang unang hakbang sa lahat ng mga build at nagsimulang suriin kung ano mismo ang gusto namin, iyon ay pagsasama.

Tumagal ng tatlong araw upang ma-localize ang problema, makahanap ng solusyon, at isulat ang script na ito.

Nabuo ang application, parami nang parami ang mga gawain na lumitaw, lumaki ang koponan, at kung minsan ay sinimulan kaming pabayaan ng premerge.sh. Nagkaroon ng magkasalungat na pagbabago ang Develop na sinira ang build.

Isang halimbawa kung paano ito nangyayari:

Ebolusyon ng CI sa mobile development team

Dalawang developer ang sabay na nagsimulang magtrabaho sa mga feature A at B. Natuklasan ng developer ng feature A ang isang hindi nagamit na feature sa proyekto answer() at, tulad ng isang mabuting boy scout, inaalis ito. Kasabay nito, nagdaragdag ang developer ng feature B ng bagong tawag sa function na ito sa kanyang branch.

Tinatapos ng mga developer ang kanilang trabaho at sabay na nagbukas ng pull request. Inilunsad ang mga build, sinusuri ng premerge.sh ang parehong mga pull request tungkol sa pinakabagong estado ng pag-develop - berde ang lahat ng mga tseke. Pagkatapos nito, ang pull request ng feature A ay pinagsama, ang pull request ng feature B ay pinagsama... Boom! Mga break sa pag-develop dahil naglalaman ang developer ng code ng isang tawag sa isang hindi umiiral na function.

Ebolusyon ng CI sa mobile development team

Kapag hindi ito bubuo, ito ay lokal na kalamidad. Ang buong koponan ay hindi maaaring mangolekta ng anuman at isumite ito para sa pagsubok.

Nagkataon na madalas akong nagtatrabaho sa mga gawain sa imprastraktura: analytics, network, mga database. Iyon ay, ako ang nagsulat ng mga function at klase na ginagamit ng ibang mga developer. Dahil dito, madalas akong nasa mga katulad na sitwasyon. Nakasabit pa ako ng larawang ito ng ilang sandali.

Ebolusyon ng CI sa mobile development team

Dahil hindi ito nababagay sa amin, nagsimula kaming mag-explore ng mga opsyon kung paano ito maiiwasan.

Paano hindi masira bumuo

Ang unang pagpipilian: muling buuin ang lahat ng pull request kapag nag-update ng develop. Kung, sa aming halimbawa, ang pull request na may feature A ang unang isasama sa develop, ang pull request ng feature B ay muling itatayo, at, nang naaayon, ang mga pagsusuri ay mabibigo dahil sa isang compilation error.

Upang maunawaan kung gaano ito katagal, isaalang-alang ang isang halimbawa na may dalawang PR. Binuksan namin ang dalawang PR: dalawang build, dalawang run of checks. Matapos ang unang PR ay pinagsama sa pagbuo, ang pangalawa ay kailangang itayo muli. Sa kabuuan, dalawang PR ang nangangailangan ng tatlong run of checks: 2 + 1 = 3.

Sa prinsipyo, ayos lang. Ngunit tiningnan namin ang mga istatistika, at ang karaniwang sitwasyon sa aming koponan ay 10 bukas na PR, at pagkatapos ay ang bilang ng mga tseke ay ang kabuuan ng pag-unlad: 10 + 9 +... + 1 = 55. Iyon ay, upang tanggapin ang 10 Mga PR, kailangan mong buuin muli ng 55 beses. At ito ay nasa perpektong sitwasyon, kapag ang lahat ng mga tseke ay pumasa sa unang pagkakataon, kapag walang nagbubukas ng karagdagang kahilingan sa paghila habang pinoproseso ang dosenang ito.

Isipin ang iyong sarili bilang isang developer na kailangang maging unang mag-click sa pindutan ng "pagsamahin", dahil kung gagawin ito ng isang kapitbahay, kailangan mong maghintay hanggang sa muling makumpleto ang lahat ng mga build... Hindi, hindi iyon gagana. , ito ay seryosong magpapabagal sa pag-unlad.

Pangalawang posibleng paraan: mangolekta ng mga kahilingan sa paghila pagkatapos ng pagsusuri ng code. Ibig sabihin, magbubukas ka ng pull request, mangolekta ng kinakailangang bilang ng mga pag-apruba mula sa mga kasamahan, iwasto kung ano ang kailangan, at pagkatapos ay ilunsad ang mga build. Kung sila ay matagumpay, ang kahilingan sa paghila ay pinagsama sa pagbuo. Sa kasong ito, walang mga karagdagang pag-restart, ngunit ang feedback ay lubhang pinabagal. Bilang developer, kapag nagbukas ako ng pull request, gusto kong makita kaagad kung gagana ito. Halimbawa, kung nabigo ang isang pagsubok, kailangan mong mabilis itong ayusin. Sa kaso ng isang naantalang build, bumabagal ang feedback, at samakatuwid ang buong pag-unlad. Hindi rin ito nababagay sa amin.

Bilang resulta, tanging ang pangatlong opsyon ang natitira - bisikleta. Ang lahat ng aming code, lahat ng aming mga mapagkukunan ay naka-imbak sa isang repositoryo sa Bitbucket server. Alinsunod dito, kailangan naming bumuo ng isang plugin para sa Bitbucket.

Ebolusyon ng CI sa mobile development team

Ino-override ng plugin na ito ang mekanismo ng pagsasama ng pull request. Ang simula ay pamantayan: ang PR ay bubukas, ang lahat ng mga pagtitipon ay inilunsad, ang pagsusuri ng code ay nakumpleto. Ngunit pagkatapos makumpleto ang pagsusuri ng code at nagpasya ang developer na mag-click sa "pagsamahin", sinusuri ng plugin kung aling estado ng pag-develop ang pinatakbo ang mga pagsusuri. Kung na-update ang pag-develop pagkatapos ng mga build, hindi papayagan ng plugin ang naturang pull request na isama sa pangunahing sangay. Ire-restart lang nito ang mga build ng isang medyo kamakailang pag-develop.

Ebolusyon ng CI sa mobile development team

Sa aming halimbawa na may magkasalungat na pagbabago, ang mga naturang build ay mabibigo dahil sa isang error sa compilation. Alinsunod dito, kailangang itama ng developer ng feature B ang code, i-restart ang mga pagsusuri, pagkatapos ay awtomatikong ilalapat ng plugin ang pull request.

Bago ipatupad ang plugin na ito, nag-average kami ng 2,7 review run bawat pull request. Sa plugin mayroong 3,6 na paglulunsad. Ito ay nababagay sa amin.

Kapansin-pansin na ang plugin na ito ay may disbentaha: isang beses lang nitong i-restart ang build. Ibig sabihin, mayroon pa ring maliit na window kung saan maaaring mabuo ang magkasalungat na pagbabago. Ngunit ang posibilidad na ito ay mababa, at ginawa namin ang trade-off na ito sa pagitan ng bilang ng mga pagsisimula at ang posibilidad ng pagkabigo. Sa loob ng dalawang taon, isang beses lang ito nagpaputok, kaya malamang na hindi ito nawalan ng saysay.

Inabot kami ng dalawang linggo upang isulat ang unang bersyon ng Bitbucket plugin.

Mga bagong tseke

Samantala, ang aming koponan ay patuloy na lumago. Ang mga bagong tseke ay naidagdag.

Naisip namin: bakit magkakamali kung mapipigilan? At iyon ang dahilan kung bakit sila nagpatupad static na pagsusuri ng code. Nagsimula kami sa lint, na kasama sa Android SDK. Ngunit sa oras na iyon ay hindi niya alam kung paano magtrabaho sa Kotlin code, at mayroon na kaming 75% ng application na nakasulat sa Kotlin. Samakatuwid, ang mga built-in ay idinagdag sa lint Sinusuri ng Android Studio.

Upang gawin ito, kailangan naming gumawa ng maraming perverting: kumuha ng Android Studio, i-package ito sa Docker at patakbuhin ito sa CI gamit ang isang virtual na monitor, upang isipin na ito ay tumatakbo sa isang tunay na laptop. Ngunit ito ay gumana.

Sa panahong ito din kami nagsimulang magsulat ng marami mga pagsubok sa instrumentasyon at ipinatupad pagsubok ng screenshot. Ito ay kapag ang isang reference na screenshot ay nabuo para sa isang hiwalay na maliit na view, at ang pagsubok ay binubuo ng pagkuha ng isang screenshot mula sa view at paghahambing nito sa karaniwang direktang pixel bawat pixel. Kung mayroong pagkakaiba, nangangahulugan ito na nagkamali ang layout sa isang lugar o may mali sa mga istilo.

Ngunit kailangang patakbuhin ang mga instrumentation test at screenshot test sa mga device: sa mga emulator o sa mga totoong device. Isinasaalang-alang na mayroong maraming mga pagsubok at sila ay tumatakbo nang madalas, isang buong sakahan ang kailangan. Masyadong labor-intensive ang pagsisimula ng sarili mong farm, kaya nakakita kami ng handa na opsyon - Firebase Test Lab.

Firebase Test Lab

Napili ito dahil ang Firebase ay isang produkto ng Google, ibig sabihin ay dapat itong mapagkakatiwalaan at malamang na hindi mamatay. Ang mga presyo ay makatwiran: $5 bawat oras ng pagpapatakbo ng isang tunay na device, 1 $ bawat oras ng pagpapatakbo ng isang emulator.

Tumagal ng humigit-kumulang tatlong linggo upang ipatupad ang Firebase Test Lab sa aming CI.

Ngunit nagpatuloy ang paglaki ng team, at ang Firebase, sa kasamaang-palad, ay nagsimulang pabayaan kami. Sa oras na iyon, wala siyang anumang SLA. Minsan pinahintay kami ng Firebase hanggang sa ang kinakailangang bilang ng mga device ay libre para sa mga pagsubok, at hindi agad nagsimulang isagawa ang mga ito, ayon sa gusto namin. Umabot ng kalahating oras ang paghihintay sa pila, na napakatagal. Ang mga pagsubok sa instrumentasyon ay pinapatakbo sa bawat PR, ang mga pagkaantala ay talagang nagpabagal sa pag-unlad, at pagkatapos ay ang buwanang bayarin ay may kasamang bilog na kabuuan. Sa pangkalahatan, napagpasyahan na iwanan ang Firebase at magtrabaho sa loob ng bahay, dahil sapat na ang paglaki ng team.

Docker + Python + bash

Kinuha namin ang Docker, pinalamanan ang mga emulator dito, nagsulat ng isang simpleng programa sa Python, na sa tamang oras ay magsisimula ng kinakailangang bilang ng mga emulator sa tamang bersyon at ihihinto ang mga ito kapag kinakailangan. At, siyempre, isang pares ng mga script ng bash - saan tayo kung wala ang mga ito?

Tumagal ng limang linggo upang lumikha ng sarili naming kapaligiran sa pagsubok.

Bilang resulta, para sa bawat kahilingan sa paghila ay mayroong malawak na listahan ng mga pagsusuri sa pagharang sa pagsasanib:

  • ARK assembly;
  • Mga pagsubok sa Junit;
  • Lint;
  • Mga pagsusuri sa Android Studio;
  • Mga pagsubok sa instrumentasyon;
  • Mga pagsubok sa screenshot.

Napigilan nito ang maraming posibleng pagkasira. Sa teknikal na paraan, nagtrabaho ang lahat, ngunit nagreklamo ang mga developer na ang paghihintay para sa mga resulta ay masyadong mahaba.

Gaano katagal ang sobrang tagal? Nag-upload kami ng data mula sa Bitbucket at TeamCity sa sistema ng pagsusuri at napagtanto namin iyon average na oras ng paghihintay 45 minuto. Ibig sabihin, ang isang developer, kapag nagbubukas ng pull request, ay naghihintay sa average na 45 minuto para sa mga resulta ng build. Sa aking opinyon, ito ay marami, at hindi ka maaaring magtrabaho nang ganoon.

Siyempre, nagpasya kaming pabilisin ang lahat ng aming mga build.

Bilisan natin

Nakikita na ang mga build ay madalas na nakatayo sa isang pila, ang unang bagay na ginagawa namin ay bumili pa ng hardware β€” ang malawak na pag-unlad ay ang pinakasimple. Huminto sa pagpila ang mga Build, ngunit bahagyang nabawasan ang oras ng paghihintay, dahil ang ilang mga pagsusuri mismo ay tumagal ng napakatagal.

Pag-alis ng mga tseke na masyadong matagal

Maaaring mahuli ng aming Patuloy na Pagsasama ang mga ganitong uri ng mga error at problema.

  • Hindi pupunta. Maaaring mahuli ng CI ang isang error sa compilation kapag may hindi nabuo dahil sa magkasalungat na pagbabago. Tulad ng sinabi ko na, kung gayon walang sinuman ang maaaring mag-ipon ng anuman, huminto ang pag-unlad, at lahat ay kinakabahan.
  • Bug sa pag-uugali. Halimbawa, kapag ang application ay binuo, ngunit nag-crash kapag pinindot mo ang isang pindutan, o ang pindutan ay hindi pinindot. Masama ito dahil maaabot ng naturang bug ang user.
  • Bug sa layout. Halimbawa, na-click ang isang button, ngunit inilipat ang 10 pixels sa kaliwa.
  • Pagtaas ng teknikal na utang.

Matapos tingnan ang listahang ito, napagtanto namin na ang unang dalawang puntos lamang ang kritikal. Gusto muna nating mahuli ang mga ganitong problema. Ang mga bug sa layout ay natuklasan sa yugto ng pagsusuri sa disenyo at madaling maitama pagkatapos. Ang pagharap sa teknikal na utang ay nangangailangan ng hiwalay na proseso at pagpaplano, kaya nagpasya kaming huwag itong subukan sa isang pull request.

Batay sa pag-uuri na ito, pinagpag namin ang buong listahan ng mga tseke. Tinawid si Lint at ipinagpaliban ang paglulunsad nito ng magdamag: para lang makagawa ito ng ulat kung gaano karaming mga problema ang nagkaroon sa proyekto. Sumang-ayon kaming magtrabaho nang hiwalay sa teknikal na utang, at Ang mga pagsusuri sa Android Studio ay ganap na inabandona. Ang Android Studio sa Docker para sa pagpapatakbo ng mga inspeksyon ay mukhang kawili-wili, ngunit nagdudulot ng maraming problema sa suporta. Ang anumang pag-update sa mga bersyon ng Android Studio ay nangangahulugan ng pakikibaka sa mga hindi maintindihang bug. Mahirap ding suportahan ang mga screenshot test, dahil hindi masyadong stable ang library at may mga false positive. Ang mga pagsusuri sa screenshot ay inalis sa check list.

Bilang resulta, naiwan sa amin ang:

  • ARK assembly;
  • Mga pagsubok sa Junit;
  • Mga pagsubok sa instrumentasyon.

Gradle remote cache

Nang walang mabibigat na pagsusuri, ang lahat ay naging mas mahusay. Ngunit walang limitasyon sa pagiging perpekto!

Ang aming aplikasyon ay nahati na sa humigit-kumulang 150 gradle modules. Karaniwang gumagana nang maayos ang Gradle remote cache sa kasong ito, kaya nagpasya kaming subukan ito.

Ang Gradle remote cache ay isang serbisyo na maaaring mag-cache ng mga artifact para sa mga indibidwal na gawain sa mga indibidwal na module. Si Gradle, sa halip na aktwal na i-compile ang code, ay gumagamit ng HTTP upang kumatok sa remote na cache at magtanong kung may nakagawa na sa gawaing ito. Kung oo, dina-download lang nito ang resulta.

Ang pagpapatakbo ng Gradle remote cache ay madali dahil ang Gradle ay nagbibigay ng Docker image. Nagawa namin ito sa loob ng tatlong oras.

Ang kailangan mo lang gawin ay ilunsad ang Docker at magsulat ng isang linya sa proyekto. Ngunit bagama't mabilis itong mailunsad, aabutin ng maraming oras para gumana nang maayos ang lahat.

Nasa ibaba ang cache misses graph.

Ebolusyon ng CI sa mobile development team

Sa simula pa lang, ang porsyento ng mga nakaligtaan sa cache ay humigit-kumulang 65. Pagkatapos ng tatlong linggo, nagawa naming taasan ang halagang ito sa 20%. Ito ay lumabas na ang mga gawain na kinokolekta ng Android application ay may kakaibang transitive dependencies, dahil sa kung saan hindi nakuha ni Gradle ang cache.

Sa pamamagitan ng pagkonekta sa cache, lubos naming pinabilis ang pagbuo. Ngunit bilang karagdagan sa pagpupulong, mayroon ding mga pagsubok sa instrumento, at tumatagal sila ng mahabang panahon. Marahil hindi lahat ng pagsubok ay kailangang isagawa para sa bawat kahilingan sa paghila. Upang malaman, ginagamit namin ang pagsusuri sa epekto.

Pagsusuri ng epekto

Sa isang pull request, kinokolekta namin ang git diff at hinahanap ang binagong mga module ng Gradle.

Ebolusyon ng CI sa mobile development team

Makatuwiran na magpatakbo lamang ng mga pagsubok sa instrumentasyon na sumusuri sa mga binagong module at lahat ng mga module na nakadepende sa kanila. Walang punto sa pagpapatakbo ng mga pagsubok para sa mga kalapit na module: ang code doon ay hindi nagbago at walang maaaring masira.

Ang mga pagsubok sa instrumentasyon ay hindi gaanong simple, dahil ang mga ito ay dapat na matatagpuan sa pinakamataas na antas na module ng Application. Gumamit kami ng heuristics na may bytecode analysis upang maunawaan kung saang module nabibilang ang bawat pagsubok.

Ang pag-upgrade sa operasyon ng mga pagsubok sa instrumentasyon upang masubukan lamang nila ang mga module na kasangkot ay tumagal ng humigit-kumulang walong linggo.

Ang mga hakbang upang mapabilis ang mga inspeksyon ay matagumpay na gumana. Mula sa 45 minuto umakyat kami sa humigit-kumulang 15. Normal na maghintay ng quarter ng isang oras para sa isang build.

Ngunit ngayon ang mga developer ay nagsimulang magreklamo na hindi nila naiintindihan kung aling mga build ang inilunsad, kung saan makikita ang log, kung bakit ang build ay pula, kung aling pagsubok ang nabigo, atbp.

Ebolusyon ng CI sa mobile development team

Ang mga problema sa feedback ay nagpapabagal sa pag-unlad, kaya sinubukan naming magbigay ng malinaw at detalyadong impormasyon tungkol sa bawat PR at build hangga't maaari. Nagsimula kami sa mga komento sa Bitbucket sa PR, na nagsasaad kung aling build ang nabigo at bakit, at nagsulat ng mga naka-target na mensahe sa Slack. Sa huli, gumawa kami ng PR dashboard para sa page na may listahan ng lahat ng mga build na kasalukuyang tumatakbo at ang kanilang status: nakapila, tumatakbo, nag-crash o natapos. Maaari kang mag-click sa build at makarating sa log nito.

Ebolusyon ng CI sa mobile development team

Anim na linggo ang ginugol sa detalyadong feedback.

Mga Plano

Lumipat tayo sa kamakailang kasaysayan. Nang malutas ang isyu ng feedback, naabot namin ang isang bagong antas - nagpasya kaming bumuo ng aming sariling emulator farm. Kapag maraming pagsubok at emulator, mahirap pangasiwaan ang mga ito. Bilang resulta, lahat ng aming mga emulator ay lumipat sa k8s cluster na may flexible na pamamahala ng mapagkukunan.

Bilang karagdagan, may iba pang mga plano.

  • Ibalik ang Lint (at iba pang static na pagsusuri). Nagtatrabaho na kami sa direksyong ito.
  • Patakbuhin ang lahat sa isang PR blocker end-to-end na mga pagsubok sa lahat ng bersyon ng SDK.

Kaya, nasubaybayan namin ang kasaysayan ng pag-unlad ng Continuous Integration sa Avito. Ngayon gusto kong magbigay ng ilang payo mula sa isang karanasan na pananaw.

Π‘ΠΎΠ²Π΅Ρ‚Ρ‹

Kung maaari lang akong magbigay ng isang piraso ng payo ay ito:

Mangyaring mag-ingat sa mga script ng shell!

Ang Bash ay isang napaka-flexible at makapangyarihang tool, ito ay napaka-maginhawa at mabilis na magsulat ng mga script. Ngunit maaari kang mahulog sa isang bitag kasama nito, at, sa kasamaang-palad, nahulog kami dito.

Nagsimula ang lahat sa mga simpleng script na tumatakbo sa aming mga build machine:

#!/usr/bin/env bash
./gradlew assembleDebug

Ngunit, tulad ng alam mo, ang lahat ay bubuo at nagiging mas kumplikado sa paglipas ng panahon - patakbuhin natin ang isang script mula sa isa pa, ipasa natin ang ilang mga parameter doon - sa huli kailangan nating magsulat ng isang function na tumutukoy sa kung anong antas ng bash nesting tayo ngayon sa pagkakasunud-sunod upang ipasok ang mga kinakailangang panipi, para makapagsimula ang lahat.

Ebolusyon ng CI sa mobile development team

Maaari mong isipin ang mga gastos sa paggawa para sa pagbuo ng naturang mga script. Ipinapayo ko sa iyo na huwag mahulog sa bitag na ito.

Ano ang maaaring palitan?

  • Anumang scripting language. Sumulat sa Python o Kotlin Script mas maginhawa dahil ito ay programming, hindi script.
  • O ilarawan ang lahat ng build logic sa form Mga custom na gradle na gawain para sa iyong proyekto.

Napagpasyahan naming piliin ang pangalawang opsyon, at ngayon ay sistematikong tinatanggal namin ang lahat ng mga script ng bash at nagsusulat ng maraming custom na gawain sa gradle.

Tip #2: Mag-imbak ng imprastraktura sa code.

Maginhawa kapag ang setting ng Continuous Integration ay hindi naka-imbak sa interface ng UI ng Jenkins o TeamCity, atbp., ngunit sa anyo ng mga text file nang direkta sa repository ng proyekto. Nagbibigay ito ng versionability. Hindi magiging mahirap na i-rollback o buuin ang code sa isa pang branch.

Maaaring iimbak ang mga script sa isang proyekto. Ano ang gagawin sa kapaligiran?

Tip #3: Makakatulong ang Docker sa kapaligiran.

Tiyak na makakatulong ito sa mga developer ng Android; sa kasamaang-palad, wala pang isa ang iOS.

Ito ay isang halimbawa ng isang simpleng docker file na naglalaman ng jdk at android-sdk:

FROM openjdk:8

ENV SDK_URL="https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip" 
    ANDROID_HOME="/usr/local/android-sdk" 
    ANDROID_VERSION=26 
    ANDROID_BUILD_TOOLS_VERSION=26.0.2

# Download Android SDK
RUN mkdir "$ANDROID_HOME" .android 
    && cd "$ANDROID_HOME" 
    && curl -o sdk.zip $SDK_URL 
    && unzip sdk.zip 
    && rm sdk.zip 
    && yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses

# Install Android Build Tool and Libraries
RUN $ANDROID_HOME/tools/bin/sdkmanager --update
RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" 
    "platforms;android-${ANDROID_VERSION}" 
    "platform-tools"

RUN mkdir /application
WORKDIR /application

Ang pagkakaroon ng nakasulat na Docker file na ito (Sasabihin ko sa iyo ang isang lihim, hindi mo kailangang isulat ito, ngunit hilahin lamang ito na handa mula sa GitHub) at tipunin ang imahe, makakakuha ka ng isang virtual machine kung saan maaari kang bumuo ng application at magpatakbo ng mga pagsubok sa Junit.

Ang dalawang pangunahing dahilan kung bakit ito ay may katuturan ay scalability at repeatability. Gamit ang docker, mabilis kang makakapagtaas ng isang dosenang build agent na magkakaroon ng eksaktong kaparehong kapaligiran gaya ng nauna. Pinapadali nito ang buhay ng mga inhinyero ng CI. Napakadaling itulak ang android-sdk sa docker, ngunit sa mga emulator ay medyo mas mahirap: kailangan mong magtrabaho nang kaunti pa (o i-download muli ang natapos mula sa GitHub).

Tip No. 4: huwag kalimutan na ang mga inspeksyon ay hindi ginagawa para sa kapakanan ng mga inspeksyon, ngunit para sa mga tao.

Ang mabilis at, pinakamahalaga, ang malinaw na feedback ay napakahalaga para sa mga developer: ano ang nasira, anong pagsubok ang nabigo, saan ko makikita ang buildlog.

Tip #5: Maging pragmatic sa pagbuo ng Continuous Integration.

Malinaw na maunawaan kung anong mga uri ng mga error ang gusto mong pigilan, kung gaano karaming mga mapagkukunan, oras, at oras ng computer ang handa mong gastusin. Ang mga tseke na masyadong mahaba, halimbawa, ay maaaring ipagpaliban ng magdamag. At ang mga nakakakuha ng hindi napakahalagang mga pagkakamali ay dapat na ganap na iwanan.

Tip #6: Gumamit ng mga nakahandang kasangkapan.

Maraming kumpanya ngayon ang nagbibigay ng cloud CI.

Ebolusyon ng CI sa mobile development team

Ito ay isang magandang solusyon para sa maliliit na koponan. Hindi mo kailangang suportahan ang anuman, magbayad lang ng kaunting pera, buuin ang iyong aplikasyon at magpatakbo pa ng mga pagsubok sa instrumentasyon.

Tip #7: Sa isang malaking team, ang mga in-house na solusyon ay mas kumikita.

Ngunit sa lalong madaling panahon, habang lumalaki ang koponan, ang mga in-house na solusyon ay magiging mas kumikita. May isang problema sa mga desisyong ito. Mayroong batas ng lumiliit na kita sa ekonomiya: sa anumang proyekto, ang bawat kasunod na pagpapabuti ay higit na mahirap at nangangailangan ng higit at higit na pamumuhunan.

Inilalarawan ng ekonomiks ang ating buong buhay, kabilang ang Patuloy na Pagsasama. Gumawa ako ng iskedyul ng mga gastos sa paggawa para sa bawat yugto ng pag-unlad ng ating Patuloy na Pagsasama.

Ebolusyon ng CI sa mobile development team

Malinaw na ang anumang pagpapabuti ay nagiging mas mahirap. Sa pagtingin sa graph na ito, mauunawaan mo na ang Patuloy na Pagsasama ay kailangang mabuo alinsunod sa paglaki ng laki ng koponan. Para sa isang pangkat ng dalawang tao, ang paggugol ng 50 araw sa pagbuo ng isang panloob na emulator farm ay isang pangkaraniwang ideya. Ngunit sa parehong oras, para sa isang malaking koponan, ang hindi paggawa ng Patuloy na Pagsasama ay isang masamang ideya din, dahil ang mga problema sa pagsasama, pag-aayos ng komunikasyon, atbp. aabutin pa ito ng mas maraming oras.

Nagsimula kami sa ideya na kailangan ang automation dahil mahal ang mga tao, nagkakamali at tamad. Ngunit ang mga tao ay nag-automate din. Samakatuwid, ang lahat ng parehong mga problema ay nalalapat sa automation.

  • Mahal ang automation. Tandaan ang iskedyul ng paggawa.
  • Pagdating sa automation, nagkakamali ang mga tao.
  • Minsan napakatamad na mag-automate, dahil lahat ay gumagana sa ganoong paraan. Bakit pagbutihin ang anumang bagay, bakit ang lahat ng ito ay Patuloy na Pagsasama?

Ngunit mayroon akong mga istatistika: ang mga error ay nakuha sa 20% ng mga pagtitipon. At hindi ito dahil hindi maganda ang pagsulat ng code ng aming mga developer. Ito ay dahil kumpiyansa ang mga developer na kung magkamali sila, hindi ito mauuwi sa develop, mahuhuli ito ng mga automated na pagsusuri. Alinsunod dito, ang mga developer ay maaaring gumugol ng mas maraming oras sa pagsulat ng code at mga kawili-wiling bagay, sa halip na patakbuhin at subukan ang isang bagay nang lokal.

Magsanay ng Patuloy na Pagsasama. Ngunit sa katamtaman.

Sa pamamagitan ng paraan, si Nikolai Nesterov ay hindi lamang nagbibigay ng magagandang ulat sa kanyang sarili, ngunit miyembro din ng komite ng programa AppsConf at tumutulong sa iba na maghanda ng mga makabuluhang talumpati para sa iyo. Ang pagkakumpleto at pagiging kapaki-pakinabang ng susunod na programa ng kumperensya ay maaaring masuri ng mga paksa sa iskedyul. At para sa mga detalye, pumunta sa Infospace sa Abril 22-23.

Pinagmulan: www.habr.com

Magdagdag ng komento