Cái giá đắt của việc triển khai ứng dụng di động thủ công
Triển khai thủ công là “kẻ thù” của năng suất. Bất kỳ nhà phát triển di động nào cũng hiểu quy trình này: bạn hoàn thành một tính năng, sau đó dành cả giờ tiếp theo để tạo các tệp .ipa hoặc .apk. Bạn phải loay hoay với các provisioning profile một cách thủ công trong khi cầu nguyện rằng bản build không bị lỗi khi đã chạy được 90%. Tôi đã từng mất ít nhất 4 đến 6 giờ mỗi tuần phát hành chỉ để ngồi “canh” Xcode và Google Play Console.
Chuyển sang quy trình CI/CD tự động đã thay đổi hoàn toàn cách làm việc của tôi. Thay vì nhìn chằm chằm vào các thanh tiến trình, nhóm của tôi giờ đây tập trung vào việc phát triển tính năng trong khi máy móc xử lý các công việc nặng nhọc. Chúng tôi sử dụng Fastlane để thực thi logic build và GitHub Actions để cung cấp hạ tầng đám mây. Thiết lập này đảm bảo rằng mọi bản build đều sạch sẽ, được ký đúng cách và được chuyển đến tay người kiểm thử mà không cần sự can thiệp của con người.
Thiết lập môi trường
Trước khi tự động hóa, chúng ta cần chạy Fastlane cục bộ. Fastlane dựa trên Ruby, vì vậy bạn sẽ cần một môi trường Ruby hoạt động tốt. Mặc dù macOS có sẵn Ruby, tôi khuyên bạn nên sử dụng rbenv hoặc asdf. Các trình quản lý này giúp tránh những vấn đề nhức đầu về quyền truy cập thường gây ra bởi phiên bản Ruby mặc định của hệ thống.
1. Cài đặt Fastlane qua Bundler
Tránh cài đặt Fastlane dưới dạng global gem. Thay vào đó, hãy sử dụng Gemfile trong thư mục gốc của dự án để đảm bảo toàn bộ nhóm sử dụng cùng một phiên bản. Chạy các lệnh sau để bắt đầu:
gem install bundler
echo 'source "https://rubygems.org"
gem "fastlane"' > Gemfile
bundle install
Bây giờ, hãy khởi tạo Fastlane trong thư mục dự án của bạn (nơi chứa tệp .xcodeproj hoặc build.gradle):
bundle exec fastlane init
Fastlane sẽ đưa ra một vài tùy chọn thiết lập. Đối với iOS, bạn thường sẽ chọn giữa tự động hóa ảnh chụp màn hình (screenshots), TestFlight hoặc phân phối đầy đủ lên App Store. Đối với Android, bạn sẽ cần package name và mã khóa bí mật JSON từ Google Play Console. Sau khi hoàn tất, Fastlane tạo một thư mục fastlane chứa tệp Fastfile của bạn.
2. Chuẩn bị cấu trúc GitHub Actions
GitHub Actions không yêu cầu cài đặt riêng biệt. Nó nằm trực tiếp trong kho lưu trữ (repository) của bạn. Bạn chỉ cần tạo thư mục nơi các kịch bản tự động hóa sẽ trú ngụ:
mkdir -p .github/workflows
Viết logic tự động hóa
Tệp Fastfile là nơi bạn chuyển đổi các thao tác nhấp chuột thủ công thành mã code. Bạn định nghĩa các “lane”, về cơ bản là các kịch bản cho các tác vụ cụ thể như kiểm thử beta hoặc phát hành chính thức.
Định nghĩa các Lane trong Fastfile
Hãy coi một lane như một công thức nấu ăn có thể lặp lại. Đây là một ví dụ thực tế về lane beta cho iOS giúp xử lý chứng chỉ, build ứng dụng và đẩy lên TestFlight:
platform :ios do
desc "Đẩy bản build beta mới lên TestFlight"
lane :beta do
setup_ci
match(type: "appstore") # Đồng bộ hóa chứng chỉ (certificates)
increment_build_number(build_number: ENV["GITHUB_RUN_NUMBER"])
build_app(scheme: "YourAppName")
upload_to_testflight
end
end
Đối với Android, quy trình cũng đơn giản tương tự. Lane này tạo ra một App Bundle sẵn sàng cho sản xuất và gửi nó đến nhánh thử nghiệm nội bộ (internal testing track):
platform :android do
desc "Gửi bản build mới lên Google Play Internal Track"
lane :beta do
gradle(task: "bundle", build_type: "Release")
upload_to_play_store(track: "internal")
end
end
Giải quyết cơn ác mộng ký code (Code Signing)
Ký code cho iOS nổi tiếng là khó khăn trên các máy chủ CI vì bạn không thể nhấp vào “Allow” thủ công trên các thông báo keychain. Fastlane Match giải quyết vấn đề này bằng cách lưu trữ các chứng chỉ trong một kho lưu trữ Git riêng tư, được mã hóa. Cách tiếp cận này cung cấp một nguồn dữ liệu tin cậy duy nhất (single source of truth) cho toàn bộ nhóm của bạn. Chạy fastlane match init để thiết lập. Nó đảm bảo rằng trình chạy CI của bạn luôn có chính xác các thông tin xác thực cần thiết để ký ứng dụng.
Cấu hình GitHub Actions Workflow
Tạo tệp .github/workflows/deploy.yml. Kịch bản này yêu cầu GitHub kích hoạt các lane Fastlane của bạn bất khi nào mã nguồn được đẩy lên nhánh main.
name: Triển khai bản Beta
on:
push:
branches: [main]
jobs:
deploy:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.1'
bundler-cache: true
- name: Chạy Fastlane
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
run: bundle exec fastlane ios beta
Một mẹo quan trọng: đừng bao giờ viết cứng (hardcode) mật khẩu. Hãy lưu trữ các giá trị nhạy cảm như MATCH_PASSWORD trong GitHub Settings > Secrets. Điều này giữ cho thông tin xác thực của bạn an toàn trong khi vẫn cho phép trình build truy cập được.
Giám sát và Tối ưu hóa
Khi bạn đẩy cấu hình này lên, tab “Actions” trong GitHub sẽ hiển thị tiến trình build theo thời gian thực. Nếu bản build thất bại, Fastlane thường cung cấp một bảng thông báo lỗi rõ ràng. Bạn có thể thấy “Error 403” nếu khóa API của bạn thiếu quyền hoặc “Version Conflict” nếu bạn quên tăng số hiệu bản build (build number).
Theo dõi thời gian Build
Hãy chuẩn bị tâm lý rằng các bản build iOS trên trình chạy macos-latest của GitHub sẽ mất từ 15 đến 30 phút. Vì các trình chạy macOS đắt hơn đáng kể so với Linux — thường tốn gấp 10 lần mỗi phút — hãy tối ưu hóa các điều kiện kích hoạt (triggers). Đừng chạy quy trình triển khai đầy đủ cho mọi commit nhỏ. Thay vào đó, chỉ kích hoạt build khi một Pull Request được merge hoặc một tag cụ thể được tạo.
Thông báo cho cả nhóm
Cuối cùng, tôi khuyên bạn nên thêm thông báo Slack vào Fastfile của mình. Sử dụng hành động slack để đăng tin nhắn lên kênh của nhóm khi bản build thành công hoặc thất bại. Điều này giúp mọi người luôn được cập nhật mà không buộc họ phải kiểm tra nhật ký GitHub một cách thủ công. Khi hệ thống này hoạt động, bạn sẽ nhận ra mình đã từng lãng phí bao nhiêu năng lượng tinh thần cho việc triển khai.
after_all do |lane|
slack(message: "Đã triển khai thành công phiên bản #{get_build_number} lên TestFlight!")
end

