تکامل CI در تیم توسعه موبایل

امروزه اکثر محصولات نرم افزاری به صورت تیمی توسعه می یابند. شرایط توسعه تیم موفق را می توان در قالب یک نمودار ساده نشان داد.

تکامل CI در تیم توسعه موبایل

هنگامی که کد خود را نوشتید، باید از آن مطمئن شوید:

  1. کار می کند
  2. هیچ چیز را خراب نمی کند، از جمله کدهایی که همکاران شما نوشته اند.

اگر هر دو شرط رعایت شود، در مسیر موفقیت قرار گرفته اید. برای بررسی آسان این شرایط و عدم انحراف از مسیر سودآور، به Continuous Integration رسیدیم.

CI یک گردش کاری است که در آن کد خود را تا حد امکان در کد محصول کلی ادغام می کنید. و شما نه تنها ادغام می کنید، بلکه دائماً بررسی می کنید که همه چیز کار می کند. از آنجایی که باید زیاد و اغلب بررسی کنید، ارزش دارد به اتوماسیون فکر کنید. شما می توانید همه چیز را به صورت دستی بررسی کنید، اما نباید این کار را انجام دهید، و دلیل آن اینجاست.

  • مردم عزیز. یک ساعت کار هر برنامه نویسی از یک ساعت کار هر سروری گران تر است.
  • مردم اشتباه می کنند. بنابراین، موقعیت‌هایی ممکن است به وجود بیاید که آزمایش‌ها در شعبه اشتباهی اجرا شوند یا commit اشتباهی برای آزمایش‌کنندگان کامپایل شده باشد.
  • مردم تنبل هستند. هر از گاهی وقتی کاری را تمام می کنم، این فکر به ذهنم خطور می کند: «چه چیزی برای بررسی وجود دارد؟ من دو خط نوشتم - همه چیز کار می کند! من فکر می کنم برخی از شما نیز گاهی اوقات چنین افکاری دارید. اما همیشه باید بررسی کنید.

نیکولای نستروف می‌گوید: چگونه Continuous Integration در تیم توسعه موبایل Avito پیاده‌سازی و توسعه داده شد، چگونه آنها از 0 به 450 ساخت در روز رسیدند، و ماشین‌های ساخت 200 ساعت در روز مونتاژ می‌شوند.nnesterov) در تمام تغییرات تکاملی برنامه اندروید CI/CD شرکت می کند.

داستان بر اساس مثالی از دستور اندروید است، اما بیشتر رویکردها در iOS نیز قابل اجرا هستند.


روزی روزگاری یک نفر در تیم اندروید آویتو کار می کرد. طبق تعریف، او به چیزی از یکپارچگی مستمر نیاز نداشت: کسی نبود که با او ادغام شود.

اما برنامه رشد کرد، کارهای جدید بیشتر و بیشتری ظاهر شد و تیم بر این اساس رشد کرد. در برخی موارد، زمان آن فرا رسیده است که به طور رسمی فرآیند یکپارچه سازی کد را ایجاد کنیم. تصمیم گرفته شد از Git flow استفاده شود.

تکامل CI در تیم توسعه موبایل

مفهوم جریان Git به خوبی شناخته شده است: یک پروژه دارای یک شاخه توسعه مشترک است و برای هر ویژگی جدید، توسعه دهندگان یک شاخه جداگانه را قطع می کنند، به آن متعهد می شوند، فشار می دهند و زمانی که می خواهند کد خود را در شاخه توسعه ادغام کنند، یک شاخه را باز می کنند. درخواست کشش برای به اشتراک گذاشتن دانش و بحث در مورد رویکردها، مرور کد را معرفی کردیم، یعنی همکاران باید کد یکدیگر را بررسی و تایید کنند.

چک کردن

دیدن کد با چشم جالب است، اما کافی نیست. بنابراین چک های خودکار در حال معرفی هستند.

  • اول از همه بررسی می کنیم مونتاژ ARK.
  • خیلی زیاد تست های جونیت.
  • ما پوشش کد را در نظر می گیریم، از آنجایی که در حال اجرای آزمایش هستیم.

برای درک نحوه اجرای این بررسی ها، اجازه دهید به روند توسعه در Avito نگاهی بیندازیم.

می توان آن را به صورت شماتیک به صورت زیر نشان داد:

  • یک توسعه دهنده روی لپ تاپ خود کد می نویسد. می‌توانید چک‌های یکپارچه‌سازی را دقیقاً در اینجا اجرا کنید - یا با یک قلاب commit یا به سادگی بررسی‌ها را در پس‌زمینه اجرا کنید.
  • پس از اینکه توسعه دهنده کد را فشار داد، یک درخواست کشش را باز می کند. برای اینکه کد آن در شاخه توسعه قرار گیرد، باید یک بررسی کد انجام شود و تعداد تاییدیه های مورد نیاز جمع آوری شود. می‌توانید چک‌ها و ساخت‌ها را در اینجا فعال کنید: تا زمانی که همه ساخت‌ها موفق نباشند، درخواست کشش نمی‌تواند ادغام شود.
  • پس از اینکه درخواست کشش ادغام شد و کد در توسعه گنجانده شد، می توانید زمان مناسبی را انتخاب کنید: به عنوان مثال، در شب، زمانی که همه سرورها آزاد هستند، و هر تعداد که دوست دارید بررسی کنید.

هیچ کس اجرای اسکن روی لپ تاپ خود را دوست نداشت. وقتی یک توسعه‌دهنده یک ویژگی را تمام کرد، می‌خواهد به سرعت آن را فشار دهد و یک درخواست کشش را باز کند. اگر در این لحظه برخی از بررسی های طولانی راه اندازی شوند، این نه تنها بسیار خوشایند نیست، بلکه باعث کاهش سرعت توسعه نیز می شود: در حالی که لپ تاپ در حال بررسی چیزی است، غیرممکن است که به طور معمول روی آن کار کنید.

ما خیلی دوست داشتیم در شب چک کنیم، زیرا زمان و سرورهای زیادی وجود دارد، می توانید در اطراف پرسه بزنید. اما، متأسفانه، وقتی کد ویژگی وارد توسعه می‌شود، توسعه‌دهنده انگیزه کمتری برای رفع خطاهایی که CI پیدا کرده است، دارد. زمانی که به تمام خطاهای موجود در گزارش صبحگاهی نگاه می‌کردم، به طور دوره‌ای فکر می‌کردم که روزی بعد آنها را برطرف خواهم کرد، زیرا اکنون یک کار جالب جدید در Jira وجود دارد که می‌خواهم آن را شروع کنم.

اگر چک‌ها یک درخواست کشش را مسدود کنند، انگیزه کافی وجود دارد، زیرا تا زمانی که بیلدها سبز شوند، کد وارد توسعه نمی‌شود، به این معنی که کار تکمیل نخواهد شد.

در نتیجه، استراتژی زیر را انتخاب کردیم: ما حداکثر مجموعه‌ای از چک‌های ممکن را در شب اجرا می‌کنیم، و مهم‌ترین آنها و مهم‌تر از همه، سریع‌ترین‌ها را در صورت درخواست کشش راه‌اندازی می‌کنیم. اما ما در اینجا متوقف نمی‌شویم - به موازات آن، سرعت چک‌ها را بهینه می‌کنیم تا آنها را از حالت شب به چک‌های درخواستی منتقل کنیم.

در آن زمان، تمام ساخت‌های ما خیلی سریع تکمیل شدند، بنابراین ما به سادگی ساخت ARK، تست‌های Junit و محاسبات پوشش کد را به عنوان مسدودکننده‌ای برای درخواست کشش اضافه کردیم. ما آن را روشن کردیم، به آن فکر کردیم و پوشش کد را کنار گذاشتیم زیرا فکر می‌کردیم به آن نیاز نداریم.

دو روز طول کشید تا CI اصلی را به طور کامل تنظیم کنیم (از این پس تخمین زمان تقریبی است و برای مقیاس مورد نیاز است).

پس از آن، ما شروع به فکر کردن بیشتر کردیم - آیا حتی به درستی بررسی می کنیم؟ آیا ما ساخت‌های مربوط به درخواست‌های کشش را به درستی اجرا می‌کنیم؟

ما ساخت را بر روی آخرین commit شعبه ای که درخواست pull از آن باز شد شروع کردیم. اما تست های این commit فقط می تواند نشان دهد که کدی که توسعه دهنده نوشته است کار می کند. اما آنها ثابت نمی کنند که او چیزی را نشکند. در واقع، شما باید وضعیت شاخه توسعه را پس از ادغام یک ویژگی در آن بررسی کنید.

تکامل CI در تیم توسعه موبایل

برای این کار یک اسکریپت bash ساده نوشتیم premerge.sh:

#!/usr/bin/env bash

set -e

git fetch origin develop

git merge origin/develop

در اینجا همه آخرین تغییرات از توسعه به سادگی کشیده شده و در شاخه فعلی ادغام می شوند. ما اسکریپت premerge.sh را به عنوان اولین مرحله در تمام ساخت‌ها اضافه کردیم و شروع کردیم به بررسی دقیق آنچه می‌خواهیم، ​​یعنی ادغام.

بومی سازی مشکل، یافتن راه حل و نوشتن این اسکریپت سه روز طول کشید.

برنامه توسعه یافت، وظایف بیشتر و بیشتری ظاهر شد، تیم بزرگ شد و premerge.sh گاهی اوقات ما را ناامید می کرد. Develop تغییرات متناقضی داشت که بیلد را شکست.

نمونه ای از چگونگی این اتفاق:

تکامل CI در تیم توسعه موبایل

دو توسعه دهنده به طور همزمان شروع به کار بر روی ویژگی های A و B می کنند. توسعه دهنده ویژگی A یک ویژگی استفاده نشده را در پروژه کشف می کند. answer() و مانند یک پیشاهنگ خوب، آن را حذف می کند. در همان زمان، توسعه دهنده ویژگی B یک فراخوانی جدید به این تابع در شعبه خود اضافه می کند.

توسعه‌دهندگان کار خود را به پایان می‌رسانند و همزمان یک درخواست کشش را باز می‌کنند. ساخت‌ها راه‌اندازی می‌شوند، premerge.sh هر دو درخواست pull را در مورد آخرین وضعیت توسعه بررسی می‌کند - همه چک‌ها سبز هستند. پس از آن درخواست کشش ویژگی A ادغام می شود، درخواست کشش ویژگی B ادغام می شود... بوم! توسعه وقفه به این دلیل است که کد توسعه شامل فراخوانی به یک تابع غیر موجود است.

تکامل CI در تیم توسعه موبایل

وقتی قرار نیست توسعه پیدا کند، همینطور است فاجعه محلی. کل تیم نمی تواند چیزی را جمع آوری کند و برای آزمایش ارسال کند.

این اتفاق افتاد که من اغلب بر روی وظایف زیرساخت کار می کردم: تجزیه و تحلیل، شبکه، پایگاه داده. یعنی این من بودم که توابع و کلاس هایی را نوشتم که توسعه دهندگان دیگر استفاده می کنند. به همین دلیل، اغلب خود را در موقعیت های مشابه می دیدم. من حتی این عکس را برای مدتی آویزان بودم.

تکامل CI در تیم توسعه موبایل

از آنجایی که این برای ما مناسب نبود، شروع به بررسی گزینه هایی در مورد نحوه جلوگیری از این کردیم.

چگونه به شکستن توسعه

گزینه اول: بازسازی تمام درخواست های کشش هنگام به روز رسانی توسعه. اگر در مثال ما، درخواست کشش با ویژگی A اولین موردی باشد که در توسعه گنجانده شده است، درخواست کشش ویژگی B دوباره ساخته می‌شود و بر این اساس، بررسی‌ها به دلیل خطای کامپایل با شکست مواجه می‌شوند.

برای درک اینکه چقدر طول می کشد، یک مثال با دو PR در نظر بگیرید. ما دو PR را باز می کنیم: دو ساخت، دو اجرای چک. پس از ادغام PR اول در توسعه، دومی نیاز به بازسازی دارد. در مجموع، دو PR نیاز به سه بار بررسی دارند: 2 + 1 = 3.

در اصل خوب است. اما ما به آمار نگاه کردیم و وضعیت معمولی در تیم ما 10 PR باز بود و سپس تعداد چک ها حاصل جمع پیشرفت است: 10 + 9 +... + 1 = 55. یعنی قبول کردن 10 روابط عمومی، شما باید 55 بار بازسازی کنید. و این در یک وضعیت ایده آل است، زمانی که همه چک ها برای اولین بار تصویب می شوند، زمانی که هیچ کس درخواست کشش اضافی را در حالی که این ده ها در حال پردازش هستند باز نمی کند.

خود را به عنوان یک توسعه دهنده تصور کنید که باید اولین کسی باشید که روی دکمه "ادغام" کلیک می کند، زیرا اگر یکی از همسایگان این کار را انجام دهد، باید منتظر بمانید تا همه بیلدها دوباره انجام شوند... نه، این کار نمی کند. ، به طور جدی توسعه را کند می کند.

راه ممکن دوم: پس از بررسی کد، درخواست های کشش را جمع آوری کنید. یعنی شما یک درخواست کشش را باز می کنید، تعداد تاییدیه های لازم را از همکاران جمع آوری می کنید، موارد مورد نیاز را تصحیح می کنید و سپس بیلدها را راه اندازی می کنید. اگر آنها موفق باشند، درخواست کشش در توسعه ادغام می شود. در این مورد، راه اندازی مجدد اضافی وجود ندارد، اما بازخورد تا حد زیادی کند می شود. به عنوان یک توسعه‌دهنده، وقتی یک درخواست کشش را باز می‌کنم، بلافاصله می‌خواهم ببینم آیا کار می‌کند یا خیر. به عنوان مثال، اگر آزمایشی با شکست مواجه شد، باید به سرعت آن را برطرف کنید. در مورد ساخت تاخیری، بازخورد کند می شود و در نتیجه کل توسعه را کاهش می دهد. این به ما هم نمی خورد.

در نتیجه، تنها گزینه سوم باقی ماند - دوچرخه سواري. تمام کدهای ما، همه منابع ما در یک مخزن در سرور Bitbucket ذخیره می شوند. بر این اساس، مجبور شدیم یک افزونه برای Bitbucket توسعه دهیم.

تکامل CI در تیم توسعه موبایل

این افزونه مکانیسم ادغام درخواست کشش را لغو می کند. شروع استاندارد است: روابط عمومی باز می شود، همه مجموعه ها راه اندازی می شوند، بررسی کد تکمیل می شود. اما پس از تکمیل بررسی کد و تصمیم توسعه دهنده بر روی “ادغام”، افزونه بررسی می کند که بررسی ها در کدام حالت توسعه اجرا شده است. اگر توسعه پس از بیلدها به روز شده باشد، افزونه اجازه نخواهد داد که چنین درخواست کششی در شاخه اصلی ادغام شود. این به سادگی بیلدهای یک توسعه نسبتاً جدید را مجدداً راه اندازی می کند.

تکامل CI در تیم توسعه موبایل

در مثال ما با تغییرات متناقض، چنین بیلدهایی به دلیل یک خطای کامپایل شکست خواهند خورد. بر این اساس، توسعه‌دهنده ویژگی B باید کد را تصحیح کند، بررسی‌ها را مجدداً راه‌اندازی کند، سپس افزونه به‌طور خودکار درخواست کشش را اعمال می‌کند.

قبل از پیاده سازی این افزونه، ما به طور متوسط ​​2,7 بار بررسی در هر درخواست کششی را محاسبه کردیم. با این افزونه 3,6 راه اندازی وجود داشت. این برای ما مناسب بود.

شایان ذکر است که این افزونه یک اشکال دارد: فقط یک بار بیلد را دوباره راه اندازی می کند. یعنی هنوز یک پنجره کوچک وجود دارد که از طریق آن تغییرات متناقض می توانند ایجاد شوند. اما احتمال این کم است و ما این مبادله را بین تعداد شروع و احتمال شکست انجام دادیم. در دو سال فقط یک بار شلیک کرد، بنابراین احتمالاً بیهوده نبود.

نوشتن اولین نسخه پلاگین Bitbucket دو هفته طول کشید.

چک های جدید

در همین حال، تیم ما به رشد خود ادامه داد. چک های جدید اضافه شده است.

ما فکر کردیم: چرا اشتباه کنیم اگر بتوان جلوی آنها را گرفت؟ و به همین دلیل اجرا کردند تجزیه و تحلیل کد استاتیک. ما با lint شروع کردیم که در Android SDK گنجانده شده است. اما در آن زمان او اصلاً نمی دانست که چگونه با کد کاتلین کار کند و ما قبلاً 75 درصد از برنامه را در کاتلین نوشته بودیم. بنابراین، توکارها به پرز اضافه شدند اندروید استودیو چک می کند.

برای انجام این کار، ما مجبور شدیم کارهای انحرافی زیادی انجام دهیم: اندروید استودیو را برداریم، آن را در Docker بسته بندی کنید و آن را روی CI با یک مانیتور مجازی اجرا کنید، به طوری که فکر کند روی یک لپ تاپ واقعی اجرا می شود. اما کار کرد.

همچنین در این مدت بود که شروع به نوشتن کردیم تست های ابزار دقیق و اجرا شد تست اسکرین شات. این زمانی است که یک اسکرین شات مرجع برای یک نمای کوچک جداگانه تولید می شود و آزمایش شامل گرفتن اسکرین شات از نما و مقایسه مستقیم آن با پیکسل به پیکسل استاندارد است. اگر مغایرت وجود داشته باشد، به این معنی است که چیدمان در جایی اشتباه شده است یا چیزی در سبک ها اشتباه است.

اما تست‌های ابزار دقیق و تست‌های اسکرین‌شات باید روی دستگاه‌ها اجرا شوند: روی شبیه‌سازها یا روی دستگاه‌های واقعی. با توجه به اینکه آزمایشات زیادی وجود دارد و به طور مکرر انجام می شود، یک مزرعه کامل مورد نیاز است. راه اندازی مزرعه خود بسیار کار بر است، بنابراین ما یک گزینه آماده پیدا کردیم - Firebase Test Lab.

آزمایشگاه تست Firebase

این انتخاب شد زیرا Firebase یک محصول Google است، به این معنی که باید قابل اعتماد باشد و بعید است که هرگز بمیرد. قیمت ها معقول است: 5 دلار در ساعت کارکرد یک دستگاه واقعی، 1 دلار در ساعت کارکرد شبیه ساز.

پیاده سازی Firebase Test Lab در CI ما تقریباً سه هفته طول کشید.

اما تیم به رشد خود ادامه داد و متأسفانه Firebase ما را ناامید کرد. در آن زمان او SLA نداشت. گاهی اوقات Firebase ما را وادار می‌کرد که منتظر بمانیم تا تعداد مورد نیاز دستگاه برای آزمایش آزاد شود و آنطور که می‌خواستیم بلافاصله شروع به اجرای آن‌ها نکنیم. انتظار در صف تا نیم ساعت طول کشید که بسیار طولانی است. آزمایش‌های ابزار دقیق روی هر روابط عمومی انجام شد، تاخیرها واقعاً توسعه را کند کرد و سپس صورت‌حساب ماهانه با مبلغی گرد آمد. به طور کلی، تصمیم گرفته شد که Firebase را رها کرده و در خانه کار کند، زیرا تیم به اندازه کافی رشد کرده بود.

داکر + پایتون + bash

ما Docker را گرفتیم، شبیه سازها را در آن قرار دادیم، یک برنامه ساده در پایتون نوشتیم که در لحظه مناسب تعداد مورد نیاز شبیه سازها را در نسخه مورد نیاز نمایش می دهد و در صورت لزوم آنها را متوقف می کند. و، البته، چند اسکریپت bash - بدون آنها کجا خواهیم بود؟

پنج هفته طول کشید تا محیط آزمایشی خود را ایجاد کنیم.

در نتیجه، برای هر درخواست کشش، فهرست گسترده‌ای از چک‌های مسدودکننده ادغام وجود داشت:

  • مونتاژ ARK;
  • تست های جونیت؛
  • پرز
  • بررسی اندروید استودیو؛
  • تست های ابزار دقیق؛
  • تست های اسکرین شات

این از بسیاری از خرابی های احتمالی جلوگیری کرد. از نظر فنی همه چیز کار می کرد، اما توسعه دهندگان شکایت داشتند که انتظار برای نتایج بسیار طولانی است.

چه مدت زمانی بیش از حد طولانی است؟ ما داده ها را از Bitbucket و TeamCity در سیستم تجزیه و تحلیل آپلود کردیم و متوجه شدیم میانگین زمان انتظار 45 دقیقه. یعنی یک توسعه دهنده، هنگام باز کردن یک درخواست کشش، به طور متوسط ​​45 دقیقه منتظر نتایج ساخت است. به نظر من این خیلی زیاد است و شما نمی توانید اینطور کار کنید.

البته ما تصمیم گرفتیم که به تمام ساخت‌هایمان سرعت دهیم.

بیایید سرعت خود را افزایش دهیم

با دیدن اینکه بیلدها اغلب در یک صف قرار می گیرند، اولین کاری که انجام می دهیم این است سخت افزار بیشتری خرید - توسعه گسترده ساده ترین است. ساخت‌ها در صف ایستادند، اما زمان انتظار فقط اندکی کاهش یافت، زیرا برخی از بررسی‌ها زمان زیادی را به طول انجامید.

حذف چک هایی که خیلی طول می کشد

ادغام مداوم ما می تواند این نوع خطاها و مشکلات را بگیرد.

  • قرار نیست. وقتی چیزی به دلیل تغییرات متناقض ساخته نمی شود، CI می تواند خطای کامپایل را بگیرد. همانطور که قبلاً گفتم ، هیچ کس نمی تواند چیزی را جمع کند ، توسعه متوقف می شود و همه عصبی می شوند.
  • اشکال در رفتار. به عنوان مثال، زمانی که برنامه ساخته شده است، اما با فشار دادن یک دکمه از کار می افتد، یا اصلا دکمه فشار داده نمی شود. این بد است زیرا چنین اشکالی می تواند به کاربر برسد.
  • اشکال در طرح. به عنوان مثال، یک دکمه کلیک شده است، اما 10 پیکسل به سمت چپ منتقل شده است.
  • افزایش بدهی فنی.

پس از بررسی این لیست، متوجه شدیم که تنها دو نقطه اول حیاتی هستند. ما می خواهیم اول چنین مشکلاتی را بگیریم. اشکالات در چیدمان در مرحله طراحی-بازبینی کشف می شوند و سپس به راحتی می توان آنها را اصلاح کرد. رسیدگی به بدهی فنی نیاز به یک فرآیند و برنامه ریزی جداگانه دارد، بنابراین تصمیم گرفتیم آن را در یک درخواست کشش آزمایش نکنیم.

بر اساس این طبقه بندی، کل لیست چک ها را تکان دادیم. لینت را خط زد و راه اندازی آن را یک شبه به تعویق انداخت: فقط برای اینکه گزارشی از تعداد مشکلات موجود در پروژه تهیه کند. توافق کردیم که با بدهی فنی جداگانه کار کنیم و بررسی های اندروید استودیو به طور کامل کنار گذاشته شد. اندروید استودیو در داکر برای اجرای بازرسی‌ها جالب به نظر می‌رسد، اما مشکلات زیادی در پشتیبانی ایجاد می‌کند. هر به روز رسانی به نسخه های اندروید استودیو به معنای مبارزه با باگ های نامفهوم است. همچنین پشتیبانی از تست های اسکرین شات سخت بود، زیرا کتابخانه چندان پایدار نبود و موارد مثبت کاذب وجود داشت. تست های اسکرین شات از چک لیست حذف شده اند.

در نتیجه ما با این موارد باقی ماندیم:

  • مونتاژ ARK;
  • تست های جونیت؛
  • تست های ابزار دقیق

حافظه پنهان از راه دور Gradle

بدون چک های سنگین همه چیز بهتر شد. اما هیچ محدودیتی برای کمال وجود ندارد!

برنامه ما قبلاً به حدود 150 ماژول gradle تقسیم شده بود. حافظه پنهان از راه دور Gradle معمولاً در این مورد به خوبی کار می کند، بنابراین تصمیم گرفتیم آن را امتحان کنیم.

Gradle remote cache سرویسی است که می‌تواند مصنوعات ساخت را برای وظایف فردی در ماژول‌های جداگانه ذخیره کند. Gradle، به جای کامپایل کردن کد، از HTTP برای ضربه زدن به حافظه پنهان از راه دور استفاده می کند و می پرسد که آیا کسی قبلاً این کار را انجام داده است یا خیر. اگر بله، به سادگی نتیجه را دانلود می کند.

اجرای کش از راه دور Gradle آسان است زیرا Gradle یک تصویر Docker ارائه می دهد. ما توانستیم این کار را در سه ساعت انجام دهیم.

تنها کاری که باید انجام می دادید این بود که Docker را راه اندازی کنید و یک خط در پروژه بنویسید. اما اگرچه می‌توان آن را به سرعت راه‌اندازی کرد، اما زمان زیادی طول می‌کشد تا همه چیز به خوبی کار کند.

در زیر نمودار از دست دادن حافظه پنهان است.

تکامل CI در تیم توسعه موبایل

در همان ابتدا، درصد از دست دادن حافظه پنهان حدود 65 بود. پس از سه هفته، ما موفق شدیم این مقدار را به 20 درصد برسانیم. مشخص شد که وظایفی که برنامه اندروید جمع آوری می کند وابستگی های گذرا عجیبی دارند که به همین دلیل Gradle حافظه پنهان را از دست داد.

با اتصال کش، سرعت ساخت را بسیار افزایش دادیم. اما علاوه بر مونتاژ، تست های ابزار دقیق نیز انجام می شود و زمان زیادی می برد. شاید لازم نباشد همه آزمایش ها برای هر درخواست کششی اجرا شوند. برای فهمیدن این موضوع از تحلیل تاثیر استفاده می کنیم.

آنالیز تاثیرات

در یک درخواست pull، git diff را جمع آوری می کنیم و ماژول های Gradle اصلاح شده را پیدا می کنیم.

تکامل CI در تیم توسعه موبایل

منطقی است که فقط تست های ابزار دقیقی را اجرا کنید که ماژول های تغییر یافته و همه ماژول هایی را که به آنها وابسته هستند بررسی می کنند. اجرای آزمایش‌ها برای ماژول‌های همسایه فایده‌ای ندارد: کد آنجا تغییر نکرده است و هیچ چیز نمی‌تواند خراب شود.

تست‌های ابزار دقیق چندان ساده نیستند، زیرا باید در ماژول Application سطح بالا قرار گیرند. ما از اکتشافی با تجزیه و تحلیل بایت کد استفاده کردیم تا بفهمیم هر آزمون به کدام ماژول تعلق دارد.

ارتقای عملکرد تست های ابزار دقیق به طوری که آنها فقط ماژول های درگیر را آزمایش می کنند حدود هشت هفته طول کشید.

اقدامات برای سرعت بخشیدن به بازرسی ها با موفقیت انجام شده است. از 45 دقیقه به حدود 15 رسیدیم. در حال حاضر طبیعی است که یک ربع ساعت برای ساخت منتظر بمانیم.

اما اکنون توسعه‌دهندگان شروع به شکایت کرده‌اند که نمی‌دانند کدام بیلدها راه‌اندازی می‌شوند، کجا باید لاگ را ببینند، چرا بیلد قرمز است، کدام آزمایش ناموفق است و غیره.

تکامل CI در تیم توسعه موبایل

مشکلات مربوط به بازخورد توسعه را کند می کند، بنابراین ما سعی کردیم تا حد امکان اطلاعات واضح و دقیق در مورد هر PR و ساخت ارائه کنیم. ما با نظراتی در Bitbucket به PR شروع کردیم که نشان می‌داد کدام بیلد شکست خورده است و چرا، و پیام‌های هدفمند را در Slack نوشتیم. در پایان، ما یک داشبورد روابط عمومی برای صفحه با لیستی از تمام بیلدهایی که در حال حاضر در حال اجرا هستند و وضعیت آنها ایجاد کردیم: در صف، در حال اجرا، خراب یا تکمیل شده است. می توانید روی ساخت کلیک کنید و به لاگ آن برسید.

تکامل CI در تیم توسعه موبایل

شش هفته صرف بازخورد دقیق شد.

طرح

بیایید به تاریخ اخیر برویم. پس از حل مشکل بازخورد، به سطح جدیدی رسیدیم - تصمیم گرفتیم مزرعه شبیه ساز خود را بسازیم. هنگامی که تست ها و شبیه سازهای زیادی وجود دارد، مدیریت آنها دشوار است. در نتیجه، همه شبیه سازهای ما با مدیریت منابع انعطاف پذیر به خوشه k8s منتقل شدند.

علاوه بر این، طرح های دیگری نیز وجود دارد.

  • لینت را برگردانید (و سایر تجزیه و تحلیل استاتیک). ما در حال حاضر در این مسیر کار می کنیم.
  • همه چیز را روی یک مسدود کننده روابط عمومی اجرا کنید تست های انتها به انتها در تمام نسخه های SDK

بنابراین، ما تاریخچه توسعه Continuous Integration را در Avito دنبال کردیم. حالا می‌خواهم از یک دیدگاه با تجربه، توصیه‌هایی کنم.

Советы

اگر بتوانم فقط یک نصیحت بکنم این خواهد بود:

لطفا مراقب اسکریپت های پوسته باشید!

Bash یک ابزار بسیار منعطف و قدرتمند است، برای نوشتن اسکریپت بسیار راحت و سریع است. اما شما می توانید با آن در دام بیفتید و متاسفانه ما در آن افتادیم.

همه چیز با اسکریپت های ساده ای که روی ماشین های ساخت ما اجرا می شد شروع شد:

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

اما، همانطور که می‌دانید، همه چیز با گذشت زمان توسعه می‌یابد و پیچیده‌تر می‌شود - بیایید یک اسکریپت را از اسکریپت دیگر اجرا کنیم، بیایید برخی از پارامترها را به آنجا منتقل کنیم - در پایان مجبور شدیم تابعی بنویسیم که تعیین می‌کند در حال حاضر در چه سطحی از تودرتوی bash قرار داریم. برای درج نقل قول های لازم، برای شروع همه چیز.

تکامل CI در تیم توسعه موبایل

می توانید هزینه های نیروی کار برای توسعه چنین فیلمنامه هایی را تصور کنید. به شما توصیه می کنم در این دام نیفتید.

چه چیزی را می توان جایگزین کرد؟

  • هر زبان برنامه نویسی بنویسید اسکریپت پایتون یا کاتلین راحت تر است زیرا برنامه نویسی است، نه اسکریپت.
  • یا تمام منطق ساخت را در فرم شرح دهید وظایف gradle سفارشی برای پروژه شما

ما تصمیم گرفتیم که گزینه دوم را انتخاب کنیم و اکنون به طور سیستماتیک همه اسکریپت های bash را حذف می کنیم و تعداد زیادی وظایف gradle سفارشی می نویسیم.

نکته شماره 2: زیرساخت را در کد ذخیره کنید.

هنگامی که تنظیمات Continuous Integration نه در رابط کاربری Jenkins یا TeamCity و غیره، بلکه در قالب فایل های متنی مستقیماً در مخزن پروژه ذخیره می شود، راحت است. این قابلیت نسخه پذیری را می دهد. بازگشت به عقب یا ساخت کد در شعبه دیگر دشوار نخواهد بود.

اسکریپت ها را می توان در یک پروژه ذخیره کرد. با محیط زیست چه کنیم؟

نکته شماره 3: داکر می تواند به محیط زیست کمک کند.

این قطعا به توسعه دهندگان اندروید کمک خواهد کرد؛ متاسفانه iOS هنوز یکی ندارد.

این نمونه ای از یک فایل docker ساده است که حاوی jdk و 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

با نوشتن این فایل Docker (من یک راز را به شما می گویم، لازم نیست آن را بنویسید، بلکه فقط آن را به صورت آماده از GitHub بکشید) و تصویر را مونتاژ کنید، یک ماشین مجازی دریافت می کنید که می توانید برنامه را روی آن بسازید. و تست های جونیت را اجرا کنید.

دو دلیل اصلی که این امر منطقی است مقیاس پذیری و تکرارپذیری است. با استفاده از داکر، می‌توانید به سرعت چندین عامل ساخت را افزایش دهید که دقیقاً همان محیط قبلی را خواهند داشت. این امر زندگی مهندسان CI را بسیار آسان تر می کند. انتقال android-sdk به داکر بسیار آسان است، اما با شبیه سازها کمی دشوارتر است: باید کمی سخت تر کار کنید (یا دوباره نسخه تمام شده را از GitHub دانلود کنید).

نکته شماره 4: فراموش نکنید که بازرسی ها برای بازرسی انجام نمی شود، بلکه برای مردم انجام می شود.

بازخورد سریع و از همه مهمتر واضح برای توسعه دهندگان بسیار مهم است: چه چیزی خراب شد، چه آزمایشی ناموفق بود، کجا می توانم buildlog را ببینم.

نکته شماره 5: هنگام ایجاد یکپارچگی مستمر عملگرا باشید.

به وضوح درک کنید که می خواهید از چه نوع خطاهایی جلوگیری کنید، چقدر منابع، زمان و زمان رایانه را می خواهید صرف کنید. به عنوان مثال، بررسی هایی که بیش از حد طولانی طول می کشد، می توانند یک شبه به تعویق بیفتند. و آنهایی از آنها که خطاهای نه چندان مهم را می گیرند باید کاملاً کنار گذاشته شوند.

نکته ششم: از ابزارهای آماده استفاده کنید.

در حال حاضر شرکت های زیادی وجود دارند که Cloud CI را ارائه می دهند.

تکامل CI در تیم توسعه موبایل

این یک راه حل خوب برای تیم های کوچک است. شما نیازی به پشتیبانی ندارید، فقط کمی پول بپردازید، اپلیکیشن خود را بسازید و حتی تست های ابزار دقیق را اجرا کنید.

نکته 7: در یک تیم بزرگ، راه حل های داخلی سود بیشتری دارند.

اما دیر یا زود، با رشد تیم، راه حل های داخلی سودآورتر خواهند شد. این تصمیمات یک مشکل دارد. یک قانون کاهش بازده در اقتصاد وجود دارد: در هر پروژه، هر پیشرفت بعدی دشوارتر است و نیاز به سرمایه گذاری بیشتر و بیشتر دارد.

اقتصاد کل زندگی ما را توصیف می کند، از جمله یکپارچگی مداوم. من برنامه‌ای از هزینه‌های نیروی کار برای هر مرحله از توسعه ادغام پیوسته خود تهیه کردم.

تکامل CI در تیم توسعه موبایل

واضح است که هر گونه بهبودی روز به روز دشوارتر می شود. با نگاهی به این نمودار، می توانید درک کنید که یکپارچگی مداوم باید مطابق با رشد اندازه تیم توسعه یابد. برای یک تیم دو نفره، صرف 50 روز برای توسعه یک مزرعه شبیه ساز داخلی یک ایده متوسط ​​است. اما در عین حال، برای یک تیم بزرگ، انجام ندادن یکپارچگی مداوم نیز ایده بدی است، زیرا مشکلات ادغام، رفع ارتباطات و غیره. حتی زمان بیشتری خواهد برد.

ما با این ایده شروع کردیم که اتوماسیون لازم است زیرا مردم گران هستند، اشتباه می کنند و تنبل هستند. اما مردم نیز خودکار می کنند. بنابراین، تمام مشکلات مشابه در مورد اتوماسیون صدق می کند.

  • اتوماسیون گران است. برنامه زایمان را به خاطر بسپارید.
  • وقتی صحبت از اتوماسیون می شود، مردم اشتباه می کنند.
  • گاهی اوقات خودکار کردن بسیار تنبل است، زیرا همه چیز به همین شکل کار می کند. چرا هر چیز دیگری را بهبود بخشیم، چرا این همه ادغام مداوم؟

اما من آمار دارم: خطاها در 20٪ مونتاژها کشف می شود. و این به این دلیل نیست که توسعه دهندگان ما کد را ضعیف می نویسند. این به این دلیل است که توسعه دهندگان مطمئن هستند که اگر اشتباهی مرتکب شوند، در نهایت به توسعه نمی رسد، بلکه توسط بررسی های خودکار دستگیر می شود. بر این اساس، توسعه دهندگان می توانند زمان بیشتری را صرف نوشتن کد و چیزهای جالب کنند، به جای اجرای و آزمایش چیزی به صورت محلی.

یکپارچه سازی مداوم را تمرین کنید. اما در حد اعتدال.

به هر حال، نیکولای نستروف نه تنها خودش گزارش های عالی ارائه می دهد، بلکه عضو کمیته برنامه است AppsConf و به دیگران کمک می کند تا سخنرانی های معناداری برای شما آماده کنند. کامل بودن و مفید بودن برنامه کنفرانس بعدی را می توان با موضوعات موجود در آن ارزیابی کرد برنامه. و برای جزئیات، در 22 تا 23 آوریل به Infospace بیایید.

منبع: www.habr.com

اضافه کردن نظر