Sự phát triển của CI trong nhóm phát triển thiết bị di động

Ngày nay, hầu hết các sản phẩm phần mềm đều được phát triển theo tổ. Các điều kiện để phát triển nhóm thành công có thể được thể hiện dưới dạng một sơ đồ đơn giản.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Sau khi viết mã, bạn cần đảm bảo rằng:

  1. Làm.
  2. Nó không phá vỡ bất cứ điều gì, kể cả mã mà đồng nghiệp của bạn đã viết.

Nếu cả hai điều kiện đều được đáp ứng thì bạn đang trên con đường dẫn đến thành công. Để dễ dàng kiểm tra các điều kiện này và không đi chệch khỏi lộ trình sinh lời, chúng tôi đã đưa ra Tích hợp liên tục.

CI là quy trình làm việc trong đó bạn tích hợp mã của mình vào mã sản phẩm tổng thể thường xuyên nhất có thể. Và bạn không chỉ tích hợp mà còn liên tục kiểm tra xem mọi thứ có hoạt động không. Vì bạn cần phải kiểm tra rất nhiều và thường xuyên nên bạn nên nghĩ đến việc tự động hóa. Bạn có thể kiểm tra mọi thứ theo cách thủ công, nhưng bạn không nên làm như vậy và đây là lý do.

  • Gửi mọi người. Một giờ làm việc của bất kỳ lập trình viên nào còn đắt hơn một giờ làm việc của bất kỳ máy chủ nào.
  • Mọi người mắc sai lầm. Do đó, các tình huống có thể phát sinh khi các bài kiểm tra được chạy sai nhánh hoặc sai cam kết được biên dịch cho người kiểm tra.
  • Mọi người lười biếng. Thỉnh thoảng, khi tôi hoàn thành một nhiệm vụ, ý nghĩ lại nảy sinh: “Có gì cần kiểm tra? Tôi đã viết hai dòng - mọi thứ đều hoạt động! Tôi nghĩ một số bạn cũng có lúc có suy nghĩ như vậy. Nhưng bạn nên luôn luôn kiểm tra.

Nikolai Nesterov cho biết Tích hợp liên tục đã được triển khai và phát triển như thế nào trong nhóm phát triển thiết bị di động Avito, cách họ thực hiện từ 0 đến 450 bản dựng mỗi ngày và cách các máy xây dựng đó lắp ráp 200 giờ mỗi ngày (nnesterov) là người tham gia vào tất cả các thay đổi tiến hóa của ứng dụng CI/CD Android.

Câu chuyện dựa trên ví dụ về lệnh Android, nhưng hầu hết các phương pháp đều có thể áp dụng trên iOS.


Ngày xửa ngày xưa, có một người làm việc trong nhóm Avito Android. Theo định nghĩa, anh ấy không cần bất cứ thứ gì từ Tích hợp liên tục: không có ai để tích hợp cùng.

Nhưng ứng dụng ngày càng phát triển, ngày càng có nhiều nhiệm vụ mới xuất hiện và nhóm cũng phát triển theo đó. Tại một thời điểm nào đó, đã đến lúc thiết lập quy trình tích hợp mã một cách chính thức hơn. Nó đã được quyết định sử dụng luồng Git.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Khái niệm về luồng Git đã được nhiều người biết đến: một dự án có một nhánh phát triển chung và đối với mỗi tính năng mới, các nhà phát triển sẽ cắt một nhánh riêng, cam kết với nó, đẩy và khi họ muốn hợp nhất mã của họ vào nhánh phát triển, hãy mở một nhánh yêu cầu kéo. Để chia sẻ kiến ​​thức và thảo luận về các phương pháp tiếp cận, chúng tôi đã giới thiệu việc đánh giá mã, tức là các đồng nghiệp phải kiểm tra và xác nhận mã của nhau.

Séc

Nhìn code bằng mắt cũng thú vị nhưng chưa đủ. Vì vậy, kiểm tra tự động đang được giới thiệu.

  • Trước hết, chúng tôi kiểm tra lắp ráp ARK.
  • Rất nhiều Bài kiểm tra Junit.
  • Chúng tôi xem xét phạm vi bảo hiểm của mã, vì chúng tôi đang chạy thử nghiệm.

Để hiểu cách thực hiện các bước kiểm tra này, chúng ta hãy xem quá trình phát triển trong Avito.

Nó có thể được biểu diễn dưới dạng sơ đồ như thế này:

  • Một nhà phát triển viết mã trên máy tính xách tay của mình. Bạn có thể chạy kiểm tra tích hợp ngay tại đây - bằng móc cam kết hoặc chỉ cần chạy kiểm tra ở chế độ nền.
  • Sau khi nhà phát triển đẩy mã, anh ta sẽ mở một yêu cầu kéo. Để mã của nó được đưa vào nhánh phát triển, cần phải trải qua quá trình xem xét mã và thu thập số lượng xác nhận cần thiết. Bạn có thể bật kiểm tra và xây dựng tại đây: cho đến khi tất cả các bản dựng thành công, yêu cầu kéo không thể được hợp nhất.
  • Sau khi yêu cầu kéo được hợp nhất và mã được đưa vào quá trình phát triển, bạn có thể chọn thời điểm thuận tiện: ví dụ: vào ban đêm, khi tất cả các máy chủ đều rảnh và chạy bao nhiêu lần kiểm tra tùy thích.

Không ai thích chạy quét trên máy tính xách tay của họ. Khi nhà phát triển hoàn thành một tính năng, anh ta muốn nhanh chóng đẩy nó và mở yêu cầu kéo. Nếu tại thời điểm này, một số bước kiểm tra dài được đưa ra, điều này không những không mấy dễ chịu mà còn làm chậm quá trình phát triển: trong khi máy tính xách tay đang kiểm tra thứ gì đó thì không thể hoạt động bình thường trên đó.

Chúng tôi thực sự thích chạy kiểm tra vào ban đêm, vì có nhiều thời gian và máy chủ, bạn có thể di chuyển khắp nơi. Nhưng thật không may, khi mã tính năng được phát triển, nhà phát triển có ít động lực hơn để sửa các lỗi mà CI tìm thấy. Tôi thường xuyên nghĩ rằng khi xem tất cả các lỗi được tìm thấy trong báo cáo buổi sáng rằng tôi sẽ sửa chúng vào một ngày nào đó sau đó, bởi vì bây giờ có một nhiệm vụ mới thú vị trong Jira mà tôi chỉ muốn bắt đầu thực hiện.

Nếu kiểm tra chặn yêu cầu kéo thì có đủ động lực, bởi vì cho đến khi các bản dựng chuyển sang màu xanh, mã sẽ không được phát triển, điều đó có nghĩa là nhiệm vụ sẽ không được hoàn thành.

Do đó, chúng tôi đã chọn chiến lược sau: chúng tôi chạy nhóm kiểm tra tối đa có thể vào ban đêm và khởi chạy những nhóm kiểm tra quan trọng nhất trong số đó và quan trọng nhất là những nhóm nhanh nhất theo yêu cầu kéo. Nhưng chúng tôi không dừng lại ở đó—đồng thời, chúng tôi tối ưu hóa tốc độ kiểm tra để chuyển chúng từ chế độ ban đêm sang kéo kiểm tra yêu cầu.

Vào thời điểm đó, tất cả các bản dựng của chúng tôi đều được hoàn thành khá nhanh chóng, vì vậy chúng tôi chỉ cần đưa bản dựng ARK, các bài kiểm tra Junit và tính toán phạm vi mã làm công cụ chặn cho yêu cầu kéo. Chúng tôi bật nó lên, nghĩ về nó và từ bỏ việc đưa mã vào vì nghĩ rằng mình không cần nó.

Chúng tôi mất hai ngày để thiết lập hoàn toàn CI cơ bản (sau đây ước tính thời gian là gần đúng, cần thiết cho quy mô).

Sau đó, chúng tôi bắt đầu suy nghĩ xa hơn - liệu chúng tôi có đang kiểm tra chính xác không? Chúng tôi có đang chạy các bản dựng theo yêu cầu kéo một cách chính xác không?

Chúng tôi đã bắt đầu xây dựng dựa trên cam kết cuối cùng của nhánh nơi yêu cầu kéo được mở. Nhưng việc kiểm tra cam kết này chỉ có thể cho thấy mã mà nhà phát triển đã viết hoạt động. Nhưng họ không chứng minh được rằng anh ta không phá vỡ bất cứ thứ gì. Trên thực tế, bạn cần kiểm tra trạng thái của nhánh phát triển sau khi một tính năng được hợp nhất vào đó.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Để làm điều này, chúng tôi đã viết một tập lệnh bash đơn giản premerge.sh:

#!/usr/bin/env bash

set -e

git fetch origin develop

git merge origin/develop

Tại đây, tất cả những thay đổi mới nhất từ ​​quá trình phát triển chỉ được kéo lên và hợp nhất vào nhánh hiện tại. Chúng tôi đã thêm tập lệnh premerge.sh làm bước đầu tiên trong tất cả các bản dựng và bắt đầu kiểm tra chính xác những gì chúng tôi muốn, đó là hội nhập.

Phải mất ba ngày để bản địa hóa vấn đề, tìm giải pháp và viết kịch bản này.

Ứng dụng phát triển, ngày càng có nhiều nhiệm vụ xuất hiện, nhóm phát triển và premerge.sh đôi khi bắt đầu làm chúng tôi thất vọng. Phát triển có những thay đổi xung đột đã phá vỡ bản dựng.

Một ví dụ về cách điều này xảy ra:

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Hai nhà phát triển đồng thời bắt đầu làm việc trên các tính năng A và B. Nhà phát triển tính năng A phát hiện ra một tính năng không được sử dụng trong dự án answer() và, giống như một chàng trai trinh sát giỏi, loại bỏ nó. Đồng thời, nhà phát triển tính năng B thêm lệnh gọi mới vào chức năng này trong nhánh của mình.

Các nhà phát triển hoàn thành công việc của họ và đồng thời mở một yêu cầu kéo. Các bản dựng được khởi chạy, premerge.sh kiểm tra cả hai yêu cầu kéo liên quan đến trạng thái phát triển mới nhất - tất cả các kiểm tra đều có màu xanh lục. Sau đó, yêu cầu kéo của tính năng A được hợp nhất, yêu cầu kéo của tính năng B được hợp nhất... Bùm! Phát triển các ngắt vì mã phát triển chứa lệnh gọi đến một hàm không tồn tại.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Khi nó không phát triển được thì thảm họa địa phương. Toàn bộ nhóm không thể thu thập bất cứ thứ gì và gửi nó để thử nghiệm.

Tình cờ là tôi thường làm việc về các nhiệm vụ cơ sở hạ tầng: phân tích, mạng, cơ sở dữ liệu. Nghĩa là, chính tôi là người đã viết các hàm và lớp đó mà các nhà phát triển khác sử dụng. Vì điều này mà tôi rất thường xuyên rơi vào những tình huống tương tự. Tôi thậm chí đã treo bức tranh này một thời gian.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Vì điều này không phù hợp với chúng tôi nên chúng tôi bắt đầu khám phá các phương án để ngăn chặn điều này.

Làm thế nào để không phá vỡ sự phát triển

Tùy chọn đầu tiên: xây dựng lại tất cả các yêu cầu kéo khi cập nhật phát triển. Trong ví dụ của chúng tôi, nếu yêu cầu kéo với tính năng A là yêu cầu đầu tiên được đưa vào quá trình phát triển, thì yêu cầu kéo của tính năng B sẽ được xây dựng lại và theo đó, quá trình kiểm tra sẽ không thành công do lỗi biên dịch.

Để hiểu việc này sẽ mất bao lâu, hãy xem xét một ví dụ có hai PR. Chúng tôi mở hai PR: hai bản dựng, hai lần kiểm tra. Sau khi PR đầu tiên được sáp nhập vào phát triển, PR thứ hai cần được xây dựng lại. Tổng cộng, hai PR yêu cầu ba lần kiểm tra: 2 + 1 = 3.

Về nguyên tắc thì ổn. Nhưng chúng tôi đã xem xét số liệu thống kê và tình huống điển hình trong nhóm của chúng tôi là 10 PR mở, sau đó số lần kiểm tra là tổng của tiến trình: 10 + 9 +... + 1 = 55. Tức là chấp nhận 10 PR, bạn cần phải xây dựng lại 55 lần. Và đây là một tình huống lý tưởng, khi tất cả các bước kiểm tra đều vượt qua lần đầu tiên, khi không ai mở yêu cầu kéo bổ sung trong khi hàng tá yêu cầu này đang được xử lý.

Hãy tưởng tượng bạn là một nhà phát triển cần là người đầu tiên nhấp vào nút "hợp nhất", bởi vì nếu hàng xóm làm điều này, thì bạn sẽ phải đợi cho đến khi tất cả các quá trình xây dựng hoàn tất lại... Không, điều đó sẽ không hiệu quả , nó sẽ làm chậm sự phát triển một cách nghiêm trọng.

Cách thứ hai có thể: thu thập các yêu cầu kéo sau khi xem xét mã. Nghĩa là, bạn mở một yêu cầu kéo, thu thập số lượng phê duyệt cần thiết từ đồng nghiệp, sửa những gì cần thiết và sau đó khởi chạy các bản dựng. Nếu thành công, yêu cầu kéo sẽ được hợp nhất thành phát triển. Trong trường hợp này, không có lần khởi động lại bổ sung nào, nhưng phản hồi bị chậm lại rất nhiều. Là một nhà phát triển, khi tôi mở một yêu cầu kéo, tôi ngay lập tức muốn xem liệu nó có hoạt động hay không. Ví dụ, nếu một bài kiểm tra thất bại, bạn cần nhanh chóng sửa nó. Trong trường hợp quá trình xây dựng bị trì hoãn, phản hồi sẽ chậm lại và do đó toàn bộ quá trình phát triển sẽ chậm lại. Điều này cũng không phù hợp với chúng tôi.

Kết quả là chỉ còn lại lựa chọn thứ ba - đạp xe. Tất cả mã của chúng tôi, tất cả các nguồn của chúng tôi đều được lưu trữ trong kho lưu trữ trên máy chủ Bitbucket. Theo đó, chúng tôi phải phát triển plugin cho Bitbucket.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Plugin này ghi đè cơ chế hợp nhất yêu cầu kéo. Sự khởi đầu là tiêu chuẩn: PR mở ra, tất cả các tổ hợp được khởi chạy, việc xem xét mã hoàn tất. Nhưng sau khi quá trình xem xét mã hoàn tất và nhà phát triển quyết định nhấp vào “hợp nhất”, plugin sẽ kiểm tra trạng thái phát triển mà quá trình kiểm tra đã được chạy. Nếu quá trình phát triển đã được cập nhật sau khi xây dựng, plugin sẽ không cho phép hợp nhất yêu cầu kéo như vậy vào nhánh chính. Nó sẽ chỉ khởi động lại các bản dựng của một bản phát triển tương đối gần đây.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Trong ví dụ của chúng tôi có những thay đổi xung đột, các bản dựng như vậy sẽ không thành công do lỗi biên dịch. Theo đó, nhà phát triển tính năng B sẽ phải sửa mã, khởi động lại quá trình kiểm tra, sau đó plugin sẽ tự động áp dụng pull request.

Trước khi triển khai plugin này, chúng tôi tính trung bình 2,7 lượt đánh giá cho mỗi yêu cầu kéo. Với plugin đã có 3,6 lần ra mắt. Điều này phù hợp với chúng tôi.

Điều đáng lưu ý là plugin này có một nhược điểm: nó chỉ khởi động lại bản dựng một lần. Nghĩa là, vẫn còn một cánh cửa nhỏ để qua đó những thay đổi xung đột có thể phát triển. Nhưng khả năng xảy ra điều này là thấp và chúng tôi đã thực hiện sự cân bằng giữa số lần khởi động và khả năng thất bại. Trong hai năm nó chỉ bắn một lần nên có lẽ không phải là vô ích.

Chúng tôi mất hai tuần để viết phiên bản đầu tiên của plugin Bitbucket.

Séc mới

Trong khi đó, nhóm của chúng tôi tiếp tục phát triển. Kiểm tra mới đã được thêm vào.

Chúng tôi nghĩ: tại sao lại phạm sai lầm nếu chúng có thể được ngăn chặn? Và đó là lý do tại sao họ thực hiện phân tích mã tĩnh. Chúng tôi bắt đầu với lint, được bao gồm trong SDK Android. Nhưng vào thời điểm đó anh ấy hoàn toàn không biết cách làm việc với mã Kotlin và chúng tôi đã có 75% ứng dụng được viết bằng Kotlin. Do đó, những cái tích hợp đã được thêm vào lint Kiểm tra Android Studio.

Để làm được điều này, chúng tôi đã phải thực hiện rất nhiều thủ thuật: lấy Android Studio, đóng gói trong Docker và chạy trên CI bằng một màn hình ảo, để nó nghĩ rằng nó đang chạy trên một máy tính xách tay thật. Nhưng nó đã hoạt động.

Cũng trong thời gian này chúng tôi bắt đầu viết nhiều kiểm tra thiết bị đo đạc và thực hiện kiểm tra ảnh chụp màn hình. Đây là khi ảnh chụp màn hình tham chiếu được tạo cho một chế độ xem nhỏ riêng biệt và thử nghiệm bao gồm chụp ảnh màn hình từ chế độ xem và so sánh nó với từng pixel trực tiếp tiêu chuẩn. Nếu có sự khác biệt, điều đó có nghĩa là bố cục đã sai ở đâu đó hoặc có gì đó không đúng về kiểu dáng.

Tuy nhiên, các thử nghiệm đo lường và thử nghiệm ảnh chụp màn hình cần phải được chạy trên các thiết bị: trên trình mô phỏng hoặc trên thiết bị thực. Vì có rất nhiều thử nghiệm và chúng được thực hiện thường xuyên nên cần có cả một trang trại. Việc bắt đầu trang trại của riêng bạn quá tốn nhiều công sức, vì vậy chúng tôi đã tìm thấy một lựa chọn có sẵn - Phòng thí nghiệm kiểm tra Firebase.

Phòng thí nghiệm kiểm tra Firebase

Nó được chọn vì Firebase là sản phẩm của Google, nghĩa là nó phải đáng tin cậy và khó có thể ngừng hoạt động. Giá cả hợp lý: 5 USD mỗi giờ hoạt động của thiết bị thực, 1 USD mỗi giờ hoạt động của trình mô phỏng.

Mất khoảng ba tuần để triển khai Phòng thí nghiệm kiểm tra Firebase vào CI của chúng tôi.

Nhưng nhóm vẫn tiếp tục phát triển và thật không may, Firebase đã bắt đầu làm chúng tôi thất vọng. Vào thời điểm đó, anh ấy không có bất kỳ SLA nào. Đôi khi, Firebase bắt chúng tôi phải đợi cho đến khi đủ số lượng thiết bị cần thiết để thử nghiệm và không bắt đầu thực thi chúng ngay lập tức như chúng tôi muốn. Việc xếp hàng chờ đợi mất tới nửa giờ, đó là một khoảng thời gian rất dài. Các cuộc thử nghiệm thiết bị được thực hiện trong mỗi đợt PR, sự chậm trễ thực sự đã làm chậm quá trình phát triển và sau đó, hóa đơn hàng tháng đi kèm với một khoản tiền tròn trịa. Nói chung, họ đã quyết định từ bỏ Firebase và làm việc nội bộ vì nhóm đã đủ trưởng thành.

Docker + Python + bash

Chúng tôi lấy Docker, nhét các trình mô phỏng vào đó, viết một chương trình đơn giản bằng Python, chương trình này vào đúng thời điểm sẽ hiển thị số lượng trình mô phỏng cần thiết trong phiên bản được yêu cầu và dừng chúng khi cần thiết. Và tất nhiên, một vài tập lệnh bash - chúng ta sẽ ở đâu nếu không có chúng?

Phải mất năm tuần để tạo ra môi trường thử nghiệm của riêng chúng tôi.

Kết quả là, đối với mỗi yêu cầu kéo đều có một danh sách kiểm tra chặn hợp nhất mở rộng:

  • lắp ráp ARK;
  • bài kiểm tra Junit;
  • xơ vải;
  • Kiểm tra Android Studio;
  • Kiểm tra thiết bị;
  • Kiểm tra ảnh chụp màn hình.

Điều này đã ngăn chặn nhiều sự cố có thể xảy ra. Về mặt kỹ thuật, mọi thứ đều hoạt động nhưng các nhà phát triển phàn nàn rằng thời gian chờ đợi kết quả quá lâu.

Bao lâu là quá dài? Chúng tôi đã tải dữ liệu từ Bitbucket và TeamCity lên hệ thống phân tích và nhận ra rằng thời gian chờ trung bình 45 phút. Tức là, nhà phát triển khi mở yêu cầu kéo sẽ đợi trung bình 45 phút để có kết quả xây dựng. Theo tôi, điều này là rất nhiều và bạn không thể làm việc như vậy.

Tất nhiên, chúng tôi quyết định tăng tốc tất cả các bản dựng của mình.

Hãy tăng tốc

Nhận thấy các bản dựng thường phải xếp hàng dài, điều đầu tiên chúng tôi làm là mua thêm phần cứng - phát triển mở rộng là đơn giản nhất. Các bản dựng đã ngừng xếp hàng nhưng thời gian chờ đợi chỉ giảm một chút vì bản thân một số quá trình kiểm tra đã mất rất nhiều thời gian.

Xóa các bước kiểm tra mất quá nhiều thời gian

Tích hợp liên tục của chúng tôi có thể phát hiện ra những loại lỗi và sự cố này.

  • Sẽ không. CI có thể gặp lỗi biên dịch khi có thứ gì đó không được xây dựng do những thay đổi xung đột. Như tôi đã nói, khi đó không ai có thể lắp ráp bất cứ thứ gì, quá trình phát triển dừng lại và mọi người đều lo lắng.
  • Lỗi trong hành vi. Ví dụ: khi ứng dụng được xây dựng nhưng bị treo khi bạn nhấn nút hoặc hoàn toàn không nhấn nút đó. Điều này thật tệ vì một lỗi như vậy có thể đến tay người dùng.
  • Lỗi trong bố cục. Ví dụ: một nút được nhấp nhưng đã di chuyển 10 pixel sang trái.
  • Tăng nợ kỹ thuật.

Sau khi xem danh sách này, chúng tôi nhận ra rằng chỉ có hai điểm đầu tiên là quan trọng. Chúng tôi muốn nắm bắt những vấn đề như vậy trước tiên. Các lỗi trong bố cục được phát hiện ở giai đoạn xem xét thiết kế và sau đó có thể dễ dàng sửa chữa. Việc xử lý nợ kỹ thuật đòi hỏi một quy trình và lập kế hoạch riêng biệt, vì vậy chúng tôi quyết định không thử nghiệm nó theo yêu cầu kéo.

Dựa trên sự phân loại này, chúng tôi đã thay đổi toàn bộ danh sách séc. Gạch bỏ Lint và hoãn việc ra mắt qua đêm: chỉ để đưa ra một báo cáo về số vấn đề tồn tại trong dự án. Chúng tôi đã đồng ý làm việc riêng với khoản nợ kỹ thuật và Quá trình kiểm tra Android Studio đã bị bỏ hoàn toàn. Android Studio trong Docker để chạy kiểm tra nghe có vẻ thú vị nhưng lại gây ra nhiều rắc rối trong khâu hỗ trợ. Bất kỳ bản cập nhật nào lên phiên bản Android Studio đều đồng nghĩa với việc phải vật lộn với những lỗi khó hiểu. Việc hỗ trợ kiểm tra ảnh chụp màn hình cũng gặp khó khăn vì thư viện không ổn định lắm và có nhiều kết quả dương tính giả. Kiểm tra ảnh chụp màn hình đã bị xóa khỏi danh sách kiểm tra.

Kết quả là chúng tôi còn lại:

  • lắp ráp ARK;
  • bài kiểm tra Junit;
  • Kiểm tra thiết bị.

Bộ nhớ đệm từ xa của Gradle

Không cần kiểm tra kỹ lưỡng, mọi thứ trở nên tốt hơn. Nhưng không có giới hạn cho sự hoàn hảo!

Ứng dụng của chúng tôi đã được chia thành khoảng 150 mô-đun cấp độ. Bộ nhớ đệm từ xa của Gradle thường hoạt động tốt trong trường hợp này, vì vậy chúng tôi đã quyết định dùng thử.

Bộ nhớ đệm từ xa của Gradle là một dịch vụ có thể lưu vào bộ nhớ đệm các cấu phần phần mềm xây dựng cho từng tác vụ trong từng mô-đun riêng lẻ. Gradle, thay vì thực sự biên dịch mã, sử dụng HTTP để kích hoạt bộ nhớ đệm từ xa và hỏi xem ai đó đã thực hiện tác vụ này chưa. Nếu có, nó chỉ cần tải kết quả xuống.

Chạy bộ đệm từ xa của Gradle thật dễ dàng vì Gradle cung cấp hình ảnh Docker. Chúng tôi đã làm được điều này trong ba giờ.

Tất cả những gì bạn phải làm là khởi chạy Docker và viết một dòng trong dự án. Nhưng mặc dù có thể ra mắt nhanh chóng nhưng sẽ mất khá nhiều thời gian để mọi thứ hoạt động tốt.

Dưới đây là biểu đồ thiếu bộ đệm.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Lúc đầu, tỷ lệ lỗi bộ nhớ đệm là khoảng 65. Sau ba tuần, chúng tôi đã tăng giá trị này lên 20%. Hóa ra các tác vụ mà ứng dụng Android thu thập có sự phụ thuộc bắc cầu kỳ lạ, do đó Gradle đã bỏ lỡ bộ đệm.

Bằng cách kết nối bộ đệm, chúng tôi đã tăng tốc đáng kể quá trình xây dựng. Nhưng ngoài việc lắp ráp còn có các công đoạn kiểm tra thiết bị đo đạc và mất nhiều thời gian. Có lẽ không phải tất cả các thử nghiệm đều cần được chạy cho mọi yêu cầu kéo. Để tìm hiểu, chúng tôi sử dụng phân tích tác động.

Phân tích tác động

Theo yêu cầu kéo, chúng tôi thu thập git diff và tìm các mô-đun Gradle đã sửa đổi.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Sẽ hợp lý hơn nếu chỉ chạy thử nghiệm thiết bị đo để kiểm tra các mô-đun đã thay đổi và tất cả các mô-đun phụ thuộc vào chúng. Chẳng ích gì khi chạy thử nghiệm các mô-đun lân cận: mã ở đó không thay đổi và không gì có thể phá vỡ.

Kiểm thử thiết bị không đơn giản như vậy vì chúng phải được đặt trong mô-đun Ứng dụng cấp cao nhất. Chúng tôi đã sử dụng phương pháp phỏng đoán với phân tích mã byte để hiểu mỗi bài kiểm tra thuộc về mô-đun nào.

Việc nâng cấp hoạt động kiểm tra thiết bị đo đạc để chúng chỉ kiểm tra các mô-đun liên quan mất khoảng tám tuần.

Các biện pháp tăng tốc độ kiểm tra đã phát huy tác dụng thành công. Từ 45 phút, chúng tôi đã tăng lên khoảng 15. Việc đợi một phần tư giờ để xây dựng là điều bình thường.

Nhưng bây giờ các nhà phát triển đã bắt đầu phàn nàn rằng họ không hiểu bản dựng nào đang được khởi chạy, xem nhật ký ở đâu, tại sao bản dựng có màu đỏ, thử nghiệm nào không thành công, v.v.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Các vấn đề về phản hồi làm chậm quá trình phát triển, vì vậy chúng tôi đã cố gắng cung cấp thông tin rõ ràng và chi tiết về từng hoạt động PR và xây dựng nhất có thể. Chúng tôi bắt đầu bằng các nhận xét trong Bitbucket cho PR, cho biết bản dựng nào bị lỗi và lý do, đồng thời viết các thông báo được nhắm mục tiêu trong Slack. Cuối cùng, chúng tôi đã tạo một bảng điều khiển PR cho trang với danh sách tất cả các bản dựng hiện đang chạy và trạng thái của chúng: đang xếp hàng, đang chạy, bị lỗi hoặc đã hoàn thành. Bạn có thể nhấp vào bản dựng và truy cập nhật ký của nó.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Sáu tuần được dành cho việc phản hồi chi tiết.

Kế hoạch

Hãy chuyển sang lịch sử gần đây. Sau khi giải quyết được vấn đề phản hồi, chúng tôi đã đạt đến một cấp độ mới - chúng tôi quyết định xây dựng trang trại giả lập của riêng mình. Khi có nhiều thử nghiệm và trình giả lập, chúng rất khó quản lý. Do đó, tất cả trình mô phỏng của chúng tôi đã chuyển sang cụm k8s với khả năng quản lý tài nguyên linh hoạt.

Ngoài ra còn có những kế hoạch khác.

  • Trả lại Lint (và phân tích tĩnh khác). Chúng tôi đã làm việc theo hướng này.
  • Chạy mọi thứ trên trình chặn PR các bài kiểm tra đầu cuối trên tất cả các phiên bản SDK.

Vì vậy, chúng tôi đã theo dõi lịch sử phát triển Tích hợp liên tục trong Avito. Bây giờ tôi muốn đưa ra một số lời khuyên từ quan điểm có kinh nghiệm.

Советы

Nếu tôi có thể đưa ra chỉ một lời khuyên thì đó sẽ là:

Hãy cẩn thận với các tập lệnh shell!

Bash là một công cụ rất linh hoạt và mạnh mẽ, viết script rất tiện lợi và nhanh chóng. Nhưng bạn có thể rơi vào bẫy với nó, và thật không may, chúng tôi đã rơi vào đó.

Tất cả bắt đầu với các tập lệnh đơn giản chạy trên máy xây dựng của chúng tôi:

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

Tuy nhiên, như bạn biết, mọi thứ phát triển và trở nên phức tạp hơn theo thời gian - hãy chạy tập lệnh này từ tập lệnh khác, chuyển một số tham số vào đó - cuối cùng chúng ta phải viết một hàm xác định mức độ lồng nhau của chúng ta hiện tại theo thứ tự để chèn các trích dẫn cần thiết, để bắt đầu tất cả.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Bạn có thể tưởng tượng chi phí lao động cho việc phát triển các tập lệnh như vậy. Tôi khuyên bạn không nên rơi vào cái bẫy này.

Những gì có thể được thay thế?

  • Bất kỳ ngôn ngữ kịch bản nào. Viết thư cho Tập lệnh Python hoặc Kotlin thuận tiện hơn vì đó là lập trình chứ không phải tập lệnh.
  • Hoặc mô tả tất cả logic xây dựng dưới dạng Nhiệm vụ lớp tùy chỉnh cho dự án của bạn.

Chúng tôi đã quyết định chọn tùy chọn thứ hai và hiện tại chúng tôi đang xóa tất cả các tập lệnh bash một cách có hệ thống và viết rất nhiều tác vụ gradle tùy chỉnh.

Mẹo số 2: Lưu trữ cơ sở hạ tầng bằng mã.

Thật thuận tiện khi cài đặt Tích hợp liên tục không được lưu trữ trong giao diện UI của Jenkins hay TeamCity, v.v. mà ở dạng tệp văn bản trực tiếp trong kho lưu trữ dự án. Điều này mang lại khả năng phiên bản. Sẽ không khó để khôi phục hoặc xây dựng mã trên nhánh khác.

Các tập lệnh có thể được lưu trữ trong một dự án. Phải làm gì với môi trường?

Mẹo số 3: Docker có thể giúp ích cho môi trường.

Nó chắc chắn sẽ giúp ích cho các nhà phát triển Android; Thật không may, iOS vẫn chưa có.

Đây là ví dụ về tệp docker đơn giản chứa jdk và 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

Sau khi viết tệp Docker này (tôi sẽ cho bạn biết một bí mật, bạn không cần phải viết nó mà chỉ cần lấy nó làm sẵn từ GitHub) và tập hợp hình ảnh, bạn sẽ có được một máy ảo mà bạn có thể xây dựng ứng dụng trên đó và chạy thử nghiệm Junit.

Hai lý do chính khiến điều này có ý nghĩa là khả năng mở rộng và khả năng lặp lại. Bằng cách sử dụng docker, bạn có thể nhanh chóng tạo ra hàng tá tác nhân xây dựng có môi trường giống hệt như môi trường trước đó. Điều này làm cho cuộc sống của các kỹ sư CI dễ dàng hơn rất nhiều. Việc đẩy android-sdk vào docker khá dễ dàng, nhưng với trình giả lập thì khó hơn một chút: bạn sẽ phải làm việc chăm chỉ hơn một chút (hoặc tải lại bản hoàn thiện từ GitHub).

Mẹo số 4: đừng quên rằng việc thanh tra được thực hiện không phải vì mục đích thanh tra mà vì con người.

Phản hồi nhanh chóng và quan trọng nhất là phản hồi rõ ràng là rất quan trọng đối với các nhà phát triển: cái gì đã hỏng, thử nghiệm nào không thành công, tôi có thể xem nhật ký bản dựng ở đâu.

Mẹo số 5: Hãy thực dụng khi phát triển Tích hợp liên tục.

Hiểu rõ loại lỗi nào bạn muốn ngăn chặn, bạn sẵn sàng chi bao nhiêu tài nguyên, thời gian và thời gian sử dụng máy tính. Ví dụ, việc kiểm tra mất quá nhiều thời gian có thể bị trì hoãn qua đêm. Và những cái mắc lỗi không quá quan trọng thì nên loại bỏ hoàn toàn.

Mẹo số 6: Sử dụng các công cụ làm sẵn.

Hiện nay có rất nhiều công ty cung cấp CI đám mây.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Đây là một giải pháp tốt cho các nhóm nhỏ. Bạn không cần hỗ trợ bất cứ điều gì, chỉ cần trả một ít tiền, xây dựng ứng dụng của mình và thậm chí chạy thử nghiệm thiết bị đo đạc.

Mẹo số 7: Trong một nhóm lớn, các giải pháp nội bộ mang lại nhiều lợi nhuận hơn.

Nhưng sớm hay muộn, khi nhóm phát triển, các giải pháp nội bộ sẽ mang lại nhiều lợi nhuận hơn. Có một vấn đề với những quyết định này. Có một quy luật lợi nhuận giảm dần trong kinh tế học: ở bất kỳ dự án nào, mỗi lần cải tiến tiếp theo ngày càng khó khăn hơn và đòi hỏi phải đầu tư nhiều hơn.

Kinh tế học mô tả toàn bộ cuộc sống của chúng ta, bao gồm cả Hội nhập liên tục. Tôi đã xây dựng biểu đồ chi phí nhân công cho từng giai đoạn phát triển của Tích hợp liên tục của chúng tôi.

Sự phát triển của CI trong nhóm phát triển thiết bị di động

Rõ ràng là bất kỳ sự cải thiện nào ngày càng trở nên khó khăn hơn. Nhìn vào biểu đồ này, bạn có thể hiểu rằng Tích hợp liên tục cần được phát triển phù hợp với sự phát triển của quy mô nhóm. Đối với một nhóm gồm hai người, việc dành 50 ngày để phát triển trang trại giả lập nội bộ là một ý tưởng tầm thường. Nhưng đồng thời, đối với một nhóm lớn, việc không thực hiện Tích hợp liên tục cũng là một ý tưởng tồi, bởi vì các vấn đề về tích hợp, khắc phục giao tiếp, v.v. nó sẽ mất nhiều thời gian hơn.

Chúng tôi bắt đầu với ý tưởng rằng tự động hóa là cần thiết vì con người rất tốn kém, họ mắc sai lầm và lười biếng. Nhưng con người cũng tự động hóa. Do đó, tất cả các vấn đề tương tự đều áp dụng cho tự động hóa.

  • Tự động hóa là tốn kém. Hãy nhớ lịch trình lao động.
  • Khi nói đến tự động hóa, mọi người mắc sai lầm.
  • Đôi khi rất lười tự động hóa vì mọi thứ đều hoạt động như vậy. Tại sao phải cải thiện bất cứ điều gì khác, tại sao lại có sự Tích hợp liên tục này?

Nhưng tôi có số liệu thống kê: lỗi xảy ra ở 20% số tổ hợp. Và điều này không phải do các nhà phát triển của chúng tôi viết mã kém. Điều này là do các nhà phát triển tin tưởng rằng nếu họ mắc một số sai sót, lỗi đó sẽ không được phát triển mà sẽ bị kiểm tra tự động phát hiện. Theo đó, các nhà phát triển có thể dành nhiều thời gian hơn để viết mã và những thứ thú vị, thay vì chạy và thử nghiệm thứ gì đó cục bộ.

Thực hành tích hợp liên tục. Nhưng trong chừng mực.

Nhân tiện, Nikolai Nesterov không chỉ tự mình đưa ra những báo cáo xuất sắc mà còn là thành viên ban chương trình Ứng dụngConf và giúp người khác chuẩn bị những bài phát biểu ý nghĩa cho bạn. Tính đầy đủ và hữu ích của chương trình hội nghị tiếp theo có thể được đánh giá qua các chủ đề trong lịch trình. Và để biết thêm chi tiết, hãy đến với Infospace vào ngày 22-23/XNUMX.

Nguồn: www.habr.com

Thêm một lời nhận xét