Membuat CI/CD Pipeline untuk Aplikasi Flutter dengan GitHub Actions: Build, Test, dan Deploy ke Play Store
Deployment aplikasi Flutter secara manual memakan waktu dan rawan error. Kita harus menjalankan flutter build di mesin lokal, memastikan semua test sudah lewat, lalu mengupload file APK atau AAB ke Play Console secara berulang setiap kali ada rilis baru. Proses ini semakin berat ketika tim berkembang dan jumlah commit bertambah.
Bayangkan sebuah tim yang merilis versi baru setiap minggu. Setiap rilis, developer harus memastikan branch main bersih, menjalankan seluruh test di mesin masing-masing, lalu menunggu flutter build appbundle yang memakan waktu lama. Ketika rilis ditangani oleh orang berbeda, hasil build tidak selalu identik karena environment mesin lokal berbeda. Satu langkah terlewat seperti lupa menandatangani build baru baru disadari setelah artefak sampai ke Play Console, dan perbaikannya mengulang seluruh proses dari awal.
Belum lagi proses review dari tim QA yang sering menunggu artefak dikirim manual melalui chat atau email. Setiap revisi kecil memaksa build baru dan menunggu persetujuan berikutnya, sehingga komunikasi bolak-balik justru menghabiskan waktu yang seharusnya dipakai untuk mengembangkan fitur baru.
CI/CD pipeline menjawab tantangan tersebut dengan otomasi. Setiap perubahan kode yang masuk ke repository langsung di-build, di-test, dan disiapkan untuk rilis tanpa intervensi manual. Keuntungan utamanya adalah umpan balik yang cepat: jika ada error, developer mengetahuinya dalam hitungan menit, bukan saat aplikasi sudah sampai ke pengguna. Selain itu, setiap anggota tim memakai alur yang sama, sehingga tidak ada lagi proses rilis yang bergantung pada pengetahuan pribadi satu orang.
Pipeline yang akan kita bangun mengikuti alur trigger -> test -> build -> deploy. GitHub Actions mendeteksi perubahan kode, menjalankan test dan analisis statis, membangun Android App Bundle, lalu menguploadnya ke Play Store. Konsistensi juga terjaga karena setiap build berjalan di environment yang sama, sehingga artefak release selalu siap ketika dibutuhkan.
Gambar: Diagram alur Continuous Delivery (check in → build & unit test → acceptance test → release) - Sumber: [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Continuous_Delivery_process_diagram.svg)
Struktur Dasar Workflow GitHub Actions untuk Proyek Flutter
Workflow GitHub Actions didefinisikan dalam file YAML di direktori .github/workflows/. Komponen utamanya adalah on yang menentukan trigger, jobs yang berisi langkah eksekusi, runs-on yang memilih environment runner, dan steps yang dijalankan secara berurutan. Untuk proyek Flutter, kita memerlukan action subosito/flutter-action@v2 yang menginstall Flutter SDK beserta channel yang dipilih.
File pertama berikut menunjukkan workflow dasar: checkout repository, setup Flutter, dan install dependencies.
name: Flutter CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- name: Install dependencies
run: flutter pub getBlok on memastikan workflow berjalan saat ada push ke branch main dan setiap kali ada pull_request ke branch yang sama. Action subosito/flutter-action@v2 dengan parameter channel: stable menginstall Flutter SDK versi stabil. Parameter cache: true membuat action ini menyimpan cache berdasarkan pubspec.lock, sehingga flutter pub get tidak mengunduh ulang package yang sama di setiap run. Job verify berjalan di ubuntu-latest, sebuah environment Linux sementara yang disediakan GitHub.
Menjalankan Test Otomatis dengan Flutter Test dan Analisis Kode
Test adalah gerbang utama sebelum perubahan kode diterima. Setiap pull request harus lolos semua test sebelum di-merge, dan GitHub Actions menjadikan aturan ini mudah diterapkan lewat required status check. Kita menambahkan dua langkah kualitas: flutter analyze untuk mendeteksi masalah statis seperti error tipografi atau unused import, dan flutter test untuk menjalankan unit test serta widget test. Error kecil seperti typo pada nama variabel atau format kode yang tidak konsisten akan terdeteksi lebih awal, sehingga code review bisa fokus pada logika dan arsitektur daripada hal-hal mekanis.
Advanced Flutter State Management with BLoC
Master advanced Flutter state management by building a production-ready applicat...
- name: Analisis kode statis
run: flutter analyze
- name: Jalankan unit dan widget test
run: flutter test --coverageflutter analyze membaca seluruh kode dan melaporkan masalah yang berpotensi menjadi bug sebelum test dijalankan. Tool ini juga menangkap pemakaian API yang sudah deprecated serta pelanggaran lint rule yang didefinisikan di analysis_options.yaml, sehingga gaya kode tim tetap konsisten. flutter test --coverage mengeksekusi setiap test file dan menghasilkan laporan coverage di direktori coverage/. Widget test sangat penting bagi aplikasi mobile karena tool ini memverifikasi perilaku UI seperti ketukan tombol, pengisian form, dan navigasi antar halaman, yang tidak bisa dijamin oleh unit test biasa. Hasilnya tampil di halaman Actions sebagai daftar test per file dengan status pass atau fail. Jika ada satu test yang gagal, exit code dari perintah ini bernilai bukan nol, dan GitHub menghentikan workflow pada step tersebut. Developer langsung melihat test mana yang bermasalah beserta pesan assertion-nya, tanpa harus menjalankan ulang seluruh suite di mesin lokal. Kehadiran test yang lengkap juga menjadi dokumentasi hidup tentang bagaimana setiap fitur seharusnya berperilaku, sehingga developer baru bisa memahami ekspektasi aplikasi hanya dengan membaca kode test.
Build Android App Bundle dan Publikasi Artefak Release
Ada dua format build untuk distribusi Android: flutter build apk menghasilkan file APK untuk distribusi manual atau sideload, sedangkan flutter build appbundle menghasilkan file AAB yang dioptimalkan untuk upload ke Play Store. Play Store memakai AAB untuk menyusun APK yang disesuaikan dengan perangkat pengguna, sehingga ukuran unduhan lebih kecil.
Gambar: Struktur Android App Bundle (base module, feature modules, dan asset packs) - Sumber: [Android Developers](https://developer.android.com/guide/app-bundle/app-bundle-format)
Untuk menandatangani release build, kita memerlukan keystore dan file key.properties. Keduanya tidak boleh masuk ke repository. Pendekatan yang aman adalah menyimpan keystore dalam bentuk base64 di repository secret, lalu mendekode dan menulisnya di dalam workflow.
- name: Siapkan keystore dan key.properties
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > keystore.jks
echo "storePassword=${{ secrets.KEYSTORE_PASSWORD }}" > android/key.properties
echo "keyPassword=${{ secrets.KEYSTORE_PASSWORD }}" >> android/key.properties
echo "keyAlias=${{ secrets.KEYSTORE_ALIAS }}" >> android/key.properties
echo "storeFile=../../keystore.jks" >> android/key.properties
- name: Build appbundle release
run: flutter build appbundle --release
- name: Upload artifact AAB
uses: actions/upload-artifact@v4
with:
name: app-release
path: build/app/outputs/bundle/release/app-release.aabFile key.properties ditulis di dalam direktori android/ agar dibaca oleh konfigurasi signing di build.gradle. Nilai password dan alias diambil dari secrets, sehingga tidak pernah tertulis secara literal di repository. Setelah build selesai, action actions/upload-artifact@v4 menyimpan file app-release.aab sebagai artifact di halaman workflow. Tim QA dapat mengunduhnya untuk pengujian manual tanpa perlu build ulang.
Mengintegrasikan Fastlane untuk Upload Otomatis ke Play Store
Upload ke Play Store masih bisa dilakukan manual melalui Play Console, tetapi cara ini tidak scalable ketika rilis dilakukan rutin. Fastlane menyelesaikan masalah tersebut: otomatisasi penuh, manajemen release notes, dan autentikasi lewat service account tanpa perlu login browser.
Fastlane dikonfigurasi melalui file Fastfile berbahasa Ruby. Kita perlu menginstall Fastlane di runner terlebih dahulu, lalu mendefinisikan lane deploy. Cara paling sederhana adalah gem install fastlane, tetapi untuk memastikan versi yang sama dipakai di semua environment, pendekatan yang lebih disarankan adalah mencantumkan gem fastlane di dalam Gemfile dan menjalankan bundle install. Dengan begitu mesin lokal dan runner CI selalu sinkron, sehingga perilaku pipeline tidak berubah karena perbedaan versi gem. Sebelum Fastlane bisa mengakses Play Store, kita perlu membuat service account dari Google Cloud Console dan mengaktifkan Google Play Developer API untuk project tersebut. Account yang dibuat kemudian didaftarkan di Play Console dengan permission Release, dan file JSON yang diunduh dari Google Cloud menjadi kredensial yang dipakai sebagai json_key_file.
platform :android do
desc "Build appbundle dan upload ke track internal"
lane :deploy do
sh "flutter build appbundle --release"
upload_to_play_store(
track: "internal",
aab: "build/app/outputs/bundle/release/app-release.aab",
json_key_file: "service_account.json",
release_status: "completed"
)
end
endLane deploy menjalankan build appbundle melalui perintah sh, lalu memanggil action upload_to_play_store. Parameter track: "internal" menentukan bahwa build dikirim ke track internal testing, bukan langsung ke production. Track ini cocok untuk pengujian awal oleh tim internal, dan build yang sudah terbukti stabil bisa dipromosikan ke track beta atau production dari halaman Play Console tanpa perlu build ulang. Autentikasi dilakukan dengan service_account.json, file JSON dari Google Cloud yang memiliki akses ke Play Console dengan permission Release. Parameter json_key_file menunjuk ke lokasi file kredensial tersebut, dan Fastlane menggunakannya untuk meminta token akses ke Google Play API secara otomatis tanpa login manual. File ini disimpan sebagai secret dan didecode di workflow, sehingga kredensial Play Console tidak pernah terekspos. Setelah eksekusi, log Fastlane menampilkan versi build yang berhasil diupload beserta nomor versi dan link menuju release di Play Console. Di dalam workflow, lane ini dijalankan melalui step bundle exec fastlane deploy yang ditempatkan setelah proses build appbundle selesai, sehingga seluruh rangkaian upload berjalan tanpa intervensi manual.
Best Practices untuk Workflow yang Stabil dan Aman
Workflow yang stabil dimulai dari manajemen secret yang disiplin. Keystore password, API key, dan service_account.json hanya boleh berada di repository secrets, bukan ditulis langsung di file workflow. GitHub memastikan nilai secret tidak pernah muncul di log, dan aksesnya dapat dicabut kapan saja.
Branch protection adalah lapisan keamanan kedua. Aktifkan required status check pada branch main, lalu pilih job verify sebagai syarat merge. Pull request yang gagal test tidak bisa di-merge, sehingga kode berkualitas rendah tidak pernah masuk ke branch utama.
Otomatiskan juga nomor versi build agar tidak terjadi tabrakan versionCode di Play Store. Setiap upload menuntut versionCode yang lebih besar dari sebelumnya, dan mengelolanya manual sangat rawan kelupaan. Kita bisa memanfaatkan github.run_number dari GitHub Actions sebagai sumber nilai tersebut dan meneruskannya ke build.gradle, sehingga setiap run pipeline menghasilkan build yang unik tanpa mengedit file secara manual.
Manfaatkan juga caching agar setiap run lebih cepat. Selain cache: true pada subosito/flutter-action, pastikan flutter pub get jarang mengunduh ulang dengan menyimpan direktori ~/.pub-cache ke dalam cache GitHub Actions. Pipeline yang berjalan setiap hari akan menghemat banyak waktu dari hal kecil seperti ini, dan hasilnya terasa ketika build mulai sering dijalankan.
Terakhir, tambahkan notifikasi kegagalan. GitHub mengirim email secara default, tetapi integrasi Slack atau Discord memberi visibilitas lebih baik bagi tim. Pantau juga durasi workflow secara berkala; jika build mulai melambat, evaluasi kembali strategi caching dan kebutuhan dependency. Pipeline yang cepat dan dapat diandalkan akan membuat tim lebih produktif tanpa rasa khawatir saat merilis versi baru.
Mau membangun pipeline CI/CD yang solid untuk proyek Flutter atau menguasai DevOps secara sistematis? Bergabunglah dengan kursus dan bootcamp di Rumah Coding. Kurikulum praktis dengan proyek real-world dan mentorship dari praktisi industri akan membantumu menerapkan otomasi ini langsung di proyek nyata.
Kursus Terkait
Advanced Flutter State Management with BLoC
Master advanced Flutter state management by building a production-ready application. This intermediate course uses a top-down, problem-driven approach, plunging you into real-world engineering challenges. You will learn to architect scalable applications, handle complex reactive states, manage multi-BLoC communication, synchronize real-time data, and implement optimistic UI updates using industry-standard BLoC patterns.
TaskSync: Real-Time Collaborative Task Manager
- Role-Based Authentication: Secure login and session management, dynamically reflecting user states across the entire application.
- Real-Time Task Board: A Kanban-style board that instantly updates across all devices when any team member creates, moves, or deletes a task.
- Advanced Search & Filtering: High-performance local search with event debouncing to prevent unnecessary API calls.
Flutter Mobile Development
Launch your mobile development journey with this immersive, project-based Flutter course. Designed specifically for beginners, this program takes you from coding fundamentals in Dart to deploying a fully functional mobile app. You will learn to craft beautiful, responsive UIs, handle global state management, and integrate cloud backends. By the end of the course, you will have built a real-world, cloud-synced application from scratch.
DailyQuest: Gamified Habit Tracker
- Secure Authentication: User registration and login functionality using email and password.
- Cloud Data Synchronization: Real-time database integration (using Supabase or Firebase) to securely store and retrieve user habits.
- Full CRUD Operations: The ability for users to Create, Read, Update, and Delete their daily tasks and habits.