Evolusi CI dalam tim pengembangan seluler

Saat ini, sebagian besar produk perangkat lunak dikembangkan dalam tim. Kondisi keberhasilan pengembangan tim dapat direpresentasikan dalam bentuk diagram sederhana.

Evolusi CI dalam tim pengembangan seluler

Setelah Anda menulis kode, Anda perlu memastikannya:

  1. Π Π°Π±ΠΎΡ‚Π°Π΅Ρ‚.
  2. Itu tidak merusak apa pun, termasuk kode yang ditulis rekan Anda.

Jika kedua syarat tersebut terpenuhi, maka Anda berada di jalur menuju kesuksesan. Untuk memeriksa kondisi ini dengan mudah dan tidak menyimpang dari jalur yang menguntungkan, kami hadir dengan Integrasi Berkelanjutan.

CI adalah alur kerja tempat Anda mengintegrasikan kode Anda ke dalam keseluruhan kode produk sesering mungkin. Dan Anda tidak hanya mengintegrasikan, tetapi juga terus-menerus memeriksa apakah semuanya berfungsi. Karena Anda perlu sering memeriksa, ada baiknya memikirkan otomatisasi. Anda dapat memeriksa semuanya secara manual, tetapi Anda tidak boleh melakukannya, dan inilah alasannya.

  • Orang-orang terkasih. Satu jam kerja programmer mana pun lebih mahal daripada satu jam kerja server mana pun.
  • Orang-orang membuat kesalahan. Oleh karena itu, situasi mungkin timbul ketika pengujian dijalankan pada cabang yang salah atau komit yang salah dikompilasi untuk penguji.
  • Orang-orang malas. Dari waktu ke waktu, ketika saya menyelesaikan suatu tugas, muncul pemikiran: β€œApa yang perlu diperiksa? Saya menulis dua baris - semuanya berfungsi! Saya rasa beberapa dari Anda juga terkadang memiliki pemikiran seperti itu. Tapi Anda harus selalu memeriksanya.

Bagaimana Integrasi Berkelanjutan diimplementasikan dan dikembangkan di tim pengembangan seluler Avito, bagaimana mereka meningkatkan dari 0 menjadi 450 pembuatan per hari, dan bagaimana mesin pembuat dirakit 200 jam sehari, kata Nikolai Nesterov (nesterov) adalah peserta dalam semua perubahan evolusioner aplikasi CI/CD Android.

Cerita ini didasarkan pada contoh perintah Android, namun sebagian besar pendekatannya juga dapat diterapkan di iOS.


Suatu ketika, ada satu orang yang bekerja di tim Avito Android. Menurut definisinya, dia tidak memerlukan apa pun dari Integrasi Berkelanjutan: tidak ada orang yang bisa diajak berintegrasi.

Namun aplikasinya berkembang, semakin banyak tugas baru yang muncul, dan tim pun berkembang. Pada titik tertentu, inilah waktunya untuk menetapkan proses integrasi kode secara lebih formal. Diputuskan untuk menggunakan aliran Git.

Evolusi CI dalam tim pengembangan seluler

Konsep aliran Git sangat terkenal: sebuah proyek memiliki satu cabang pengembangan yang sama, dan untuk setiap fitur baru, pengembang memotong cabang terpisah, berkomitmen padanya, mendorong, dan ketika mereka ingin menggabungkan kode mereka ke dalam cabang pengembangan, buka a permintaan tarik. Untuk berbagi pengetahuan dan mendiskusikan pendekatan, kami memperkenalkan tinjauan kode, yaitu rekan kerja harus memeriksa dan mengonfirmasi kode satu sama lain.

Cek

Melihat kode dengan mata Anda memang keren, tetapi tidak cukup. Oleh karena itu, pemeriksaan otomatis diperkenalkan.

  • Pertama-tama, kami memeriksa perakitan BAHTERA.
  • Banyak sekali Tes Juni.
  • Kami mempertimbangkan cakupan kode, karena kami sedang menjalankan pengujian.

Untuk memahami bagaimana pemeriksaan ini harus dijalankan, mari kita lihat proses pengembangan di Avito.

Secara skematis dapat direpresentasikan seperti ini:

  • Seorang pengembang menulis kode di laptopnya. Anda dapat menjalankan pemeriksaan integrasi di sini - baik dengan commit hook, atau cukup menjalankan pemeriksaan di latar belakang.
  • Setelah pengembang memasukkan kode, dia membuka permintaan penarikan. Agar kodenya dapat dimasukkan ke dalam cabang pengembangan, perlu melalui peninjauan kode dan mengumpulkan jumlah konfirmasi yang diperlukan. Anda dapat mengaktifkan pemeriksaan dan pembangunan di sini: sampai semua pembangunan berhasil, permintaan penarikan tidak dapat digabungkan.
  • Setelah permintaan tarik digabungkan dan kode disertakan dalam pengembangan, Anda dapat memilih waktu yang tepat: misalnya, pada malam hari, ketika semua server bebas, dan menjalankan pemeriksaan sebanyak yang Anda suka.

Tidak ada yang suka menjalankan pemindaian di laptop mereka. Ketika pengembang telah menyelesaikan suatu fitur, dia ingin segera mendorongnya dan membuka permintaan tarik. Jika pada saat ini beberapa pemeriksaan panjang diluncurkan, ini tidak hanya tidak terlalu menyenangkan, tetapi juga memperlambat pengembangan: saat laptop sedang memeriksa sesuatu, tidak mungkin untuk bekerja secara normal.

Kami sangat suka menjalankan pemeriksaan di malam hari, karena ada banyak waktu dan server, Anda dapat berkeliaran. Namun sayangnya, ketika kode fitur mulai dikembangkan, motivasi pengembang untuk memperbaiki kesalahan yang ditemukan CI berkurang. Saya secara berkala mendapati diri saya berpikir ketika saya melihat semua kesalahan yang ditemukan di laporan pagi bahwa saya akan memperbaikinya suatu hari nanti, karena sekarang ada tugas baru yang keren di Jira yang ingin saya mulai lakukan.

Jika pemeriksaan memblokir permintaan tarik, maka motivasinya cukup, karena sampai build berubah menjadi hijau, kode tidak akan masuk ke pengembangan, yang berarti tugas tidak akan selesai.

Hasilnya, kami memilih strategi berikut: kami menjalankan serangkaian pemeriksaan semaksimal mungkin di malam hari, dan meluncurkan pemeriksaan yang paling penting dan, yang terpenting, pemeriksaan tercepat berdasarkan permintaan tarik. Namun kami tidak berhenti di situβ€”secara paralel, kami mengoptimalkan kecepatan pemeriksaan untuk mentransfernya dari mode malam ke melakukan pemeriksaan permintaan.

Pada saat itu, semua build kami diselesaikan dengan cukup cepat, jadi kami cukup menyertakan build ARK, pengujian Junit, dan perhitungan cakupan kode sebagai pemblokir permintaan penarikan. Kami menyalakannya, memikirkannya, dan mengabaikan cakupan kode karena kami pikir kami tidak membutuhkannya.

Kami membutuhkan waktu dua hari untuk menyiapkan CI dasar sepenuhnya (selanjutnya perkiraan waktu adalah perkiraan, diperlukan untuk penskalaan).

Setelah itu, kami mulai berpikir lebih jauh - apakah kami memeriksanya dengan benar? Apakah kita menjalankan build berdasarkan permintaan tarik dengan benar?

Kami memulai pembangunan pada komit terakhir dari cabang tempat permintaan penarikan dibuka. Namun pengujian terhadap komit ini hanya dapat menunjukkan bahwa kode yang ditulis pengembang berfungsi. Tapi itu tidak membuktikan bahwa dia tidak merusak apapun. Faktanya, Anda perlu memeriksa status cabang pengembangan setelah suatu fitur digabungkan ke dalamnya.

Evolusi CI dalam tim pengembangan seluler

Untuk melakukan ini, kami menulis skrip bash sederhana premerge.sh:

#!/usr/bin/env bash

set -e

git fetch origin develop

git merge origin/develop

Di sini semua perubahan terbaru dari pengembangan ditarik dan digabungkan ke dalam cabang saat ini. Kami menambahkan skrip premerge.sh sebagai langkah pertama di semua build dan mulai memeriksa apa yang kami inginkan integrasi.

Butuh waktu tiga hari untuk melokalisasi masalah, menemukan solusi, dan menulis skrip ini.

Aplikasi berkembang, semakin banyak tugas yang muncul, tim bertambah, dan premerge.sh terkadang mulai mengecewakan kami. Mengembangkan memiliki perubahan yang bertentangan yang merusak pembangunan.

Contoh bagaimana hal ini terjadi:

Evolusi CI dalam tim pengembangan seluler

Dua pengembang secara bersamaan mulai mengerjakan fitur A dan B. Pengembang fitur A menemukan fitur yang tidak digunakan dalam proyek answer() dan, seperti pramuka yang baik, menghapusnya. Pada saat yang sama, pengembang fitur B menambahkan panggilan baru ke fungsi ini di cabangnya.

Pengembang menyelesaikan pekerjaannya dan membuka permintaan tarik pada saat yang bersamaan. Pembangunan diluncurkan, premerge.sh memeriksa kedua permintaan penarikan mengenai status pengembangan terbaru - semua pemeriksaan berwarna hijau. Setelah itu pull request fitur A digabungkan, pull request fitur B digabungkan... Boom! Jeda pengembangan karena kode pengembangan berisi panggilan ke fungsi yang tidak ada.

Evolusi CI dalam tim pengembangan seluler

Ketika itu tidak akan berkembang, itu akan terjadi bencana lokal. Seluruh tim tidak dapat mengumpulkan apa pun dan mengirimkannya untuk pengujian.

Kebetulan saya paling sering mengerjakan tugas infrastruktur: analitik, jaringan, database. Artinya, sayalah yang menulis fungsi dan kelas yang digunakan pengembang lain. Oleh karena itu, saya sering sekali mengalami situasi serupa. Aku bahkan menggantung gambar ini untuk sementara waktu.

Evolusi CI dalam tim pengembangan seluler

Karena hal ini tidak cocok bagi kami, kami mulai mencari cara untuk mencegah hal ini.

Bagaimana tidak putus berkembang

Opsi pertama: membangun kembali semua permintaan penarikan saat memperbarui pengembangan. Jika, dalam contoh kita, permintaan penarikan dengan fitur A adalah yang pertama disertakan dalam pengembangan, permintaan penarikan fitur B akan dibuat ulang, dan karenanya, pemeriksaan akan gagal karena kesalahan kompilasi.

Untuk memahami berapa lama waktu yang dibutuhkan, pertimbangkan contoh dengan dua PR. Kami membuka dua PR: dua build, dua proses pemeriksaan. Setelah PR pertama digabungkan menjadi pengembangan, PR kedua perlu dibangun kembali. Secara total, dua PR memerlukan tiga kali pemeriksaan: 2 + 1 = 3.

Pada prinsipnya, tidak apa-apa. Tapi kami melihat statistiknya, dan situasi umum di tim kami adalah 10 PR terbuka, dan jumlah pemeriksaan adalah jumlah perkembangannya: 10 + 9 +... + 1 = 55. Artinya, menerima 10 PR, Anda perlu membangun kembali 55 kali. Dan ini dalam situasi yang ideal, ketika semua pemeriksaan lolos pertama kali, ketika tidak ada yang membuka permintaan penarikan tambahan saat lusinan ini sedang diproses.

Bayangkan diri Anda sebagai seorang pengembang yang harus menjadi orang pertama yang mengklik tombol β€œgabung”, karena jika tetangga melakukan ini, maka Anda harus menunggu sampai semua pembangunan selesai lagi... Tidak, itu tidak akan berhasil , hal ini akan sangat memperlambat pembangunan.

Kemungkinan cara kedua: kumpulkan permintaan penarikan setelah peninjauan kode. Artinya, Anda membuka permintaan tarik, mengumpulkan jumlah persetujuan yang diperlukan dari rekan kerja, memperbaiki apa yang diperlukan, lalu meluncurkan pembangunan. Jika berhasil, permintaan tarik digabungkan menjadi pengembangan. Dalam hal ini, tidak ada restart tambahan, namun umpan baliknya sangat melambat. Sebagai seorang pengembang, ketika saya membuka permintaan tarik, saya langsung ingin melihat apakah itu akan berhasil. Misalnya, jika suatu pengujian gagal, Anda harus segera memperbaikinya. Dalam kasus pembangunan yang tertunda, umpan balik akan melambat, dan karenanya seluruh pengembangan akan melambat. Ini juga tidak cocok untuk kami.

Akibatnya, hanya opsi ketiga yang tersisa – sepeda. Semua kode kami, semua sumber kami disimpan dalam repositori di server Bitbucket. Oleh karena itu, kami harus mengembangkan plugin untuk Bitbucket.

Evolusi CI dalam tim pengembangan seluler

Plugin ini mengesampingkan mekanisme penggabungan permintaan tarik. Permulaannya standar: PR terbuka, semua rakitan diluncurkan, peninjauan kode selesai. Namun setelah peninjauan kode selesai dan pengembang memutuskan untuk mengklik β€œgabung”, plugin akan memeriksa status pengembangan mana yang menjalankan pemeriksaan tersebut. Jika pengembangan telah diperbarui setelah pembangunan, plugin tidak akan mengizinkan permintaan penarikan tersebut untuk digabungkan ke dalam cabang utama. Ini hanya akan memulai kembali pembangunan dari pengembangan yang relatif baru.

Evolusi CI dalam tim pengembangan seluler

Dalam contoh kita dengan perubahan yang bertentangan, build tersebut akan gagal karena kesalahan kompilasi. Oleh karena itu, pengembang fitur B harus memperbaiki kode, memulai ulang pemeriksaan, lalu plugin akan secara otomatis menerapkan permintaan penarikan.

Sebelum mengimplementasikan plugin ini, kami rata-rata menjalankan 2,7 peninjauan per permintaan penarikan. Dengan plugin ada 3,6 peluncuran. Ini cocok untuk kami.

Perlu dicatat bahwa plugin ini memiliki kelemahan: plugin ini hanya memulai ulang build satu kali. Artinya, masih ada peluang kecil untuk terjadinya perubahan-perubahan yang saling bertentangan. Namun kemungkinannya kecil, dan kami melakukan trade-off antara jumlah permulaan dan kemungkinan kegagalan. Dalam dua tahun hanya ditembakkan sekali, jadi mungkin tidak sia-sia.

Kami membutuhkan waktu dua minggu untuk menulis versi pertama plugin Bitbucket.

Cek baru

Sementara itu, tim kami terus berkembang. Cek baru telah ditambahkan.

Kami berpikir: mengapa membuat kesalahan jika bisa dicegah? Dan itulah mengapa mereka menerapkannya analisis kode statis. Kami memulai dengan lint, yang disertakan dalam Android SDK. Namun saat itu dia sama sekali tidak tahu cara bekerja dengan kode Kotlin, dan kami sudah memiliki 75% aplikasi yang ditulis dalam Kotlin. Oleh karena itu, serat bawaan ditambahkan ke serat Pemeriksaan Android Studio.

Untuk melakukan ini, kami harus melakukan banyak penyelewengan: mengambil Android Studio, mengemasnya dalam Docker dan menjalankannya di CI dengan monitor virtual, sehingga ia mengira itu berjalan di laptop sungguhan. Tapi itu berhasil.

Pada saat inilah kami mulai banyak menulis tes instrumentasi dan diimplementasikan pengujian tangkapan layar. Ini adalah saat tangkapan layar referensi dihasilkan untuk tampilan kecil yang terpisah, dan pengujiannya terdiri dari mengambil tangkapan layar dari tampilan tersebut dan membandingkannya dengan standar secara langsung piksel demi piksel. Jika ada perbedaan, berarti ada yang salah dengan tata letaknya atau ada yang salah dengan gayanya.

Namun pengujian instrumentasi dan pengujian tangkapan layar perlu dijalankan di perangkat: di emulator atau di perangkat sebenarnya. Mengingat pengujiannya banyak dan sering dilakukan, maka diperlukan peternakan yang utuh. Memulai peternakan Anda sendiri terlalu memakan waktu, jadi kami menemukan opsi yang sudah jadi - Firebase Test Lab.

Lab Uji Firebase

Ini dipilih karena Firebase adalah produk Google, artinya Firebase dapat diandalkan dan tidak mungkin mati. Harganya masuk akal: $5 per jam pengoperasian perangkat nyata, 1 $ per jam pengoperasian emulator.

Diperlukan waktu sekitar tiga minggu untuk mengimplementasikan Firebase Test Lab ke dalam CI kami.

Namun tim terus berkembang, dan sayangnya Firebase mulai mengecewakan kami. Saat itu, dia belum memiliki SLA apa pun. Terkadang Firebase membuat kami menunggu hingga jumlah perangkat yang diperlukan tersedia untuk pengujian, dan tidak segera menjalankannya, seperti yang kami inginkan. Mengantri memakan waktu hingga setengah jam, yang merupakan waktu yang sangat lama. Uji instrumentasi dijalankan pada setiap PR, penundaan benar-benar memperlambat pengembangan, dan kemudian tagihan bulanan datang dengan jumlah yang besar. Secara umum, diputuskan untuk meninggalkan Firebase dan bekerja sendiri, karena tim sudah cukup berkembang.

Docker + Python + pesta

Kami menggunakan Docker, memasukkan emulator ke dalamnya, menulis program sederhana dengan Python, yang pada saat yang tepat menampilkan jumlah emulator yang diperlukan dalam versi yang diperlukan dan menghentikannya bila diperlukan. Dan, tentu saja, beberapa skrip bash - bagaimana jadinya kita tanpa skrip tersebut?

Butuh waktu lima minggu untuk membuat lingkungan pengujian kami sendiri.

Akibatnya, untuk setiap permintaan penarikan, terdapat daftar pemeriksaan pemblokiran gabungan yang ekstensif:

  • perakitan BAHTERA;
  • tes bulan Juni;
  • serat;
  • pemeriksaan Android Studio;
  • Tes instrumentasi;
  • Tes tangkapan layar.

Hal ini mencegah banyak kemungkinan kerusakan. Secara teknis semuanya berfungsi, namun pengembang mengeluh karena penantian hasilnya terlalu lama.

Seberapa lamakah sangat lama itu? Kami mengunggah data dari Bitbucket dan TeamCity ke dalam sistem analisis dan menyadarinya waktu tunggu rata-rata 45 menit. Artinya, seorang pengembang, ketika membuka pull request, menunggu rata-rata 45 menit untuk melihat hasil build. Menurut saya, ini banyak, dan Anda tidak bisa bekerja seperti itu.

Tentu saja, kami memutuskan untuk mempercepat semua pembangunan kami.

Mari kita mempercepat

Melihat build sering kali mengantri, hal pertama yang kami lakukan adalah membeli lebih banyak perangkat keras β€” pengembangan ekstensif adalah yang paling sederhana. Build berhenti mengantri, namun waktu tunggu hanya berkurang sedikit, karena beberapa pemeriksaan sendiri memakan waktu yang sangat lama.

Menghapus cek yang memakan waktu terlalu lama

Integrasi Berkelanjutan kami dapat mendeteksi kesalahan dan masalah seperti ini.

  • Tidak akan. CI dapat menangkap kesalahan kompilasi ketika sesuatu tidak dapat dibuat karena perubahan yang bertentangan. Seperti yang sudah saya katakan, tidak ada yang bisa merakit apa pun, pengembangan terhenti, dan semua orang menjadi gugup.
  • Bug dalam perilaku. Misalnya ketika aplikasi dibangun, namun mengalami crash saat tombol ditekan, atau tombol tidak ditekan sama sekali. Ini buruk karena bug seperti itu dapat menjangkau pengguna.
  • Bug dalam tata letak. Misalnya sebuah tombol diklik, namun telah berpindah 10 piksel ke kiri.
  • Peningkatan utang teknis.

Setelah melihat daftar ini, kami menyadari bahwa hanya dua poin pertama yang penting. Kami ingin mengatasi masalah seperti itu terlebih dahulu. Bug dalam tata letak ditemukan pada tahap peninjauan desain dan kemudian dapat diperbaiki dengan mudah. Menangani utang teknis memerlukan proses dan perencanaan terpisah, jadi kami memutuskan untuk tidak mengujinya berdasarkan permintaan tarik.

Berdasarkan klasifikasi ini, kami menggoyang seluruh daftar cek. Dicoret Lint dan menunda peluncurannya dalam semalam: hanya agar dapat menghasilkan laporan tentang berapa banyak masalah yang ada dalam proyek tersebut. Kami sepakat untuk bekerja secara terpisah dengan utang teknis, dan Pemeriksaan Android Studio sepenuhnya ditinggalkan. Android Studio di Docker untuk menjalankan inspeksi terdengar menarik, tetapi menyebabkan banyak masalah dalam dukungan. Setiap pembaruan pada versi Android Studio berarti perjuangan melawan bug yang tidak dapat dipahami. Sulit juga untuk mendukung pengujian tangkapan layar, karena perpustakaannya tidak terlalu stabil dan terdapat hasil positif palsu. Tes tangkapan layar telah dihapus dari daftar periksa.

Akibatnya, kami memiliki:

  • perakitan BAHTERA;
  • tes bulan Juni;
  • Tes instrumentasi.

Cache jarak jauh Gradle

Tanpa pemeriksaan berat, segalanya menjadi lebih baik. Namun tidak ada batasan untuk kesempurnaan!

Aplikasi kami telah dipecah menjadi sekitar 150 modul gradle. Cache jarak jauh Gradle biasanya berfungsi dengan baik dalam kasus ini, jadi kami memutuskan untuk mencobanya.

Cache jarak jauh Gradle adalah layanan yang dapat menyimpan artefak build untuk tugas individual dalam modul individual. Gradle, alih-alih mengkompilasi kode, menggunakan HTTP untuk mengetuk cache jarak jauh dan menanyakan apakah seseorang telah melakukan tugas ini. Jika ya, cukup unduh hasilnya.

Menjalankan cache jarak jauh Gradle mudah dilakukan karena Gradle menyediakan image Docker. Kami berhasil melakukan ini dalam tiga jam.

Yang harus Anda lakukan hanyalah meluncurkan Docker dan menulis satu baris di proyek tersebut. Namun meski bisa diluncurkan dengan cepat, namun butuh waktu yang cukup lama agar semuanya bisa berjalan dengan baik.

Di bawah ini adalah grafik cache yang hilang.

Evolusi CI dalam tim pengembangan seluler

Pada awalnya, persentase cache yang hilang adalah sekitar 65. Setelah tiga minggu, kami berhasil meningkatkan nilai ini hingga 20%. Ternyata tugas yang dikumpulkan aplikasi Android memiliki ketergantungan transitif yang aneh, yang menyebabkan Gradle melewatkan cache.

Dengan menghubungkan cache, kami mempercepat pembangunan. Namun selain perakitan, ada juga uji instrumentasi yang memakan waktu lama. Mungkin tidak semua pengujian perlu dijalankan untuk setiap permintaan penarikan. Untuk mengetahuinya, kami menggunakan analisis dampak.

Analisis dampak

Berdasarkan permintaan tarik, kami mengumpulkan git diff dan menemukan modul Gradle yang dimodifikasi.

Evolusi CI dalam tim pengembangan seluler

Masuk akal untuk hanya menjalankan pengujian instrumentasi yang memeriksa modul yang diubah dan semua modul yang bergantung padanya. Tidak ada gunanya menjalankan pengujian untuk modul tetangga: kode di sana tidak berubah dan tidak ada yang dapat rusak.

Tes instrumentasi tidak sesederhana itu, karena tes tersebut harus ditempatkan di modul Aplikasi tingkat atas. Kami menggunakan heuristik dengan analisis bytecode untuk memahami modul mana yang dimiliki setiap pengujian.

Meningkatkan pengoperasian pengujian instrumentasi sehingga hanya menguji modul yang terlibat memerlukan waktu sekitar delapan minggu.

Langkah-langkah untuk mempercepat inspeksi telah berhasil. Dari 45 menit kami naik menjadi sekitar 15 menit. Menunggu seperempat jam untuk membangun sudah menjadi hal yang normal.

Namun kini para pengembang mulai mengeluh karena mereka tidak memahami build mana yang diluncurkan, di mana melihat lognya, mengapa build tersebut berwarna merah, pengujian mana yang gagal, dll.

Evolusi CI dalam tim pengembangan seluler

Masalah dengan umpan balik memperlambat pengembangan, jadi kami mencoba memberikan informasi sejelas dan sedetail mungkin tentang setiap PR dan membangunnya. Kami memulai dengan komentar di Bitbucket kepada PR, menunjukkan build mana yang gagal dan alasannya, dan menulis pesan yang ditargetkan di Slack. Pada akhirnya, kami membuat dasbor PR untuk halaman tersebut dengan daftar semua build yang sedang berjalan dan statusnya: antri, berjalan, mogok, atau selesai. Anda dapat mengklik build dan membuka lognya.

Evolusi CI dalam tim pengembangan seluler

Enam minggu dihabiskan untuk umpan balik yang terperinci.

Rencana

Mari beralih ke sejarah terkini. Setelah menyelesaikan masalah umpan balik, kami mencapai level baru - kami memutuskan untuk membangun peternakan emulator kami sendiri. Ketika ada banyak tes dan emulator, maka sulit untuk dikelola. Hasilnya, semua emulator kami dipindahkan ke cluster k8s dengan manajemen sumber daya yang fleksibel.

Selain itu, ada rencana lain.

  • Kembalikan Lint (dan analisis statis lainnya). Kami sudah berupaya ke arah ini.
  • Jalankan semuanya di pemblokir PR tes ujung ke ujung pada semua versi SDK.

Nah, kita sudah menelusuri sejarah perkembangan Continuous Integration di Avito. Sekarang saya ingin memberikan beberapa saran dari sudut pandang yang berpengalaman.

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

Jika saya bisa memberikan satu nasihat saja, maka ini adalah:

Harap berhati-hati dengan skrip shell!

Bash adalah alat yang sangat fleksibel dan kuat, sangat nyaman dan cepat untuk menulis skrip. Namun Anda bisa saja jatuh ke dalam perangkapnya, dan sayangnya, kita pun terjatuh ke dalamnya.

Semuanya dimulai dengan skrip sederhana yang berjalan di mesin build kami:

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

Namun, seperti yang Anda ketahui, semuanya berkembang dan menjadi lebih rumit seiring berjalannya waktu - mari kita jalankan satu skrip dari skrip lainnya, mari kita berikan beberapa parameter di sana - pada akhirnya kita harus menulis sebuah fungsi yang menentukan pada level berapa bash nesting yang kita miliki sekarang. untuk memasukkan kutipan yang diperlukan, untuk memulai semuanya.

Evolusi CI dalam tim pengembangan seluler

Anda dapat membayangkan biaya tenaga kerja untuk pengembangan skrip tersebut. Saya menyarankan Anda untuk tidak jatuh ke dalam perangkap ini.

Apa yang bisa diganti?

  • Bahasa skrip apa pun. Menulis ke Skrip Python atau Kotlin lebih nyaman karena ini pemrograman, bukan skrip.
  • Atau jelaskan semua logika build dalam formulir Tugas tingkatan khusus untuk proyek Anda.

Kami memutuskan untuk memilih opsi kedua, dan sekarang kami secara sistematis menghapus semua skrip bash dan menulis banyak tugas gradle khusus.

Tip #2: Simpan infrastruktur dalam kode.

Akan lebih mudah jika pengaturan Integrasi Berkelanjutan disimpan bukan di antarmuka UI Jenkins atau TeamCity, dll., tetapi dalam bentuk file teks langsung di repositori proyek. Ini memberikan kemampuan untuk membuat versi. Tidak akan sulit untuk melakukan rollback atau membuat kode di cabang lain.

Skrip dapat disimpan dalam sebuah proyek. Apa yang harus dilakukan dengan lingkungan?

Tip #3: Docker dapat membantu lingkungan.

Ini pasti akan membantu pengembang Android; sayangnya iOS belum memilikinya.

Ini adalah contoh file buruh pelabuhan sederhana yang berisi jdk dan 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

Setelah menulis file Docker ini (saya akan memberi tahu Anda sebuah rahasia, Anda tidak perlu menulisnya, tetapi cukup menariknya yang sudah jadi dari GitHub) dan merakit gambarnya, Anda mendapatkan mesin virtual tempat Anda dapat membangun aplikasi dan jalankan tes Junit.

Dua alasan utama mengapa hal ini masuk akal adalah skalabilitas dan pengulangan. Dengan menggunakan buruh pelabuhan, Anda dapat dengan cepat membangun selusin agen pembangunan yang akan memiliki lingkungan yang persis sama dengan yang sebelumnya. Hal ini membuat kehidupan para insinyur CI jauh lebih mudah. Cukup mudah untuk memasukkan Android-sdk ke buruh pelabuhan, tetapi dengan emulator itu sedikit lebih sulit: Anda harus bekerja sedikit lebih keras (atau mengunduh lagi yang sudah selesai dari GitHub).

Tip No. 4: jangan lupa bahwa inspeksi dilakukan bukan untuk kepentingan inspeksi, tetapi untuk manusia.

Umpan balik yang cepat dan, yang terpenting, umpan balik yang jelas sangat penting bagi pengembang: apa yang rusak, pengujian apa yang gagal, di mana saya dapat melihat buildlognya.

Tip #5: Bersikaplah pragmatis saat mengembangkan Integrasi Berkelanjutan.

Pahami dengan jelas jenis kesalahan apa yang ingin Anda cegah, berapa banyak sumber daya, waktu, dan waktu komputer yang ingin Anda keluarkan. Pemeriksaan yang memakan waktu terlalu lama bisa, misalnya, ditunda semalaman. Dan mereka yang menemukan kesalahan yang tidak terlalu penting harus ditinggalkan sepenuhnya.

Tip #6: Gunakan alat yang sudah jadi.

Saat ini sudah banyak perusahaan yang menyediakan cloud CI.

Evolusi CI dalam tim pengembangan seluler

Ini adalah solusi yang bagus untuk tim kecil. Anda tidak perlu mendukung apa pun, cukup membayar sedikit uang, membangun aplikasi Anda, dan bahkan menjalankan pengujian instrumentasi.

Tip #7: Dalam tim besar, solusi internal lebih menguntungkan.

Namun cepat atau lambat, seiring pertumbuhan tim, solusi internal akan menjadi lebih menguntungkan. Ada satu masalah dengan keputusan ini. Ada hukum keuntungan yang semakin berkurang dalam perekonomian: dalam proyek apa pun, setiap perbaikan selanjutnya akan semakin sulit dan membutuhkan lebih banyak investasi.

Ekonomi menggambarkan seluruh kehidupan kita, termasuk Integrasi Berkelanjutan. Saya membuat jadwal biaya tenaga kerja untuk setiap tahap pengembangan Integrasi Berkelanjutan kami.

Evolusi CI dalam tim pengembangan seluler

Jelas bahwa perbaikan apa pun menjadi semakin sulit. Melihat grafik ini, Anda dapat memahami bahwa Integrasi Berkelanjutan perlu dikembangkan sesuai dengan pertumbuhan ukuran tim. Untuk tim yang terdiri dari dua orang, menghabiskan 50 hari mengembangkan peternakan emulator internal adalah ide yang biasa-biasa saja. Namun di saat yang sama, untuk tim yang besar, tidak melakukan Continuous Integration sama sekali juga merupakan ide yang buruk, karena masalah integrasi, perbaikan komunikasi, dan lain-lain. itu akan memakan waktu lebih lama lagi.

Kami memulai dengan gagasan bahwa otomatisasi diperlukan karena manusia itu mahal, mereka membuat kesalahan, dan malas. Namun manusia juga melakukan otomatisasi. Oleh karena itu, semua masalah yang sama juga berlaku untuk otomatisasi.

  • Otomatisasi itu mahal. Ingat jadwal persalinan.
  • Dalam hal otomatisasi, orang melakukan kesalahan.
  • Terkadang sangat malas untuk mengotomatisasi, karena semuanya berjalan seperti itu. Mengapa perlu memperbaiki hal lain, mengapa harus melakukan Integrasi Berkelanjutan?

Tapi saya punya statistik: kesalahan terjadi di 20% majelis. Dan ini bukan karena pengembang kami menulis kode dengan buruk. Hal ini karena pengembang yakin bahwa jika mereka melakukan kesalahan, kesalahan tersebut tidak akan berakhir di pengembangan, melainkan akan ditangkap oleh pemeriksaan otomatis. Oleh karena itu, pengembang dapat menghabiskan lebih banyak waktu untuk menulis kode dan hal-hal menarik, daripada menjalankan dan menguji sesuatu secara lokal.

Praktekkan Integrasi Berkelanjutan. Tapi dalam jumlah sedang.

Omong-omong, Nikolai Nesterov tidak hanya memberikan laporan yang bagus, tetapi juga anggota komite program AplikasiConf dan membantu orang lain mempersiapkan pidato yang bermakna untuk Anda. Kelengkapan dan kegunaan program konferensi selanjutnya dapat dinilai berdasarkan topik di jadwal. Dan untuk lebih jelasnya kunjungi Infospace pada 22-23 April.

Sumber: www.habr.com

Tambah komentar