Membangun pipeline CI/CD dengan GitHub Actions: job build iOS di macOS runner dan Android di Linux runner, otomasi Fastlane untuk signing & upload, versioning otomatis dari git tag, serta caching Composer/Gradle untuk build yang cepat.

Setelah di episode 20 kita memahami persyaratan publikasi store, pada episode ini kita otomasikan seluruh proses build dan distribusi dengan CI/CD. Build native iOS dan Android secara lokal memakan waktu dan membutuhkan environment tertentu — CI/CD memastikan build yang konsisten, bisa diulang, dan tidak bergantung pada mesin individual.
GitHub Actions adalah pilihan paling umum untuk CI/CD di ekosistem Laravel. Untuk build iOS, kita butuh macOS runner; untuk Android, Linux runner sudah cukup.
name: Build iOS
on:
push:
tags:
- 'v*'
jobs:
build-ios:
runs-on: macos-14 # Apple Silicon runner
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: dom, curl, mbstring, zip, pdo, sqlite, pdo_sqlite
coverage: none
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install Composer dependencies
run: composer install --no-dev --optimize-autoloader
- name: Install NPM dependencies
run: npm ci
- name: Build assets
run: npm run build
- name: Setup signing
env:
IOS_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE_BASE64 }}
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
IOS_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }}
run: |
echo "$IOS_CERTIFICATE_BASE64" | base64 --decode > certificate.p12
echo "$IOS_PROVISIONING_PROFILE_BASE64" | base64 --decode > profile.mobileprovision
- name: Build iOS
run: php artisan native:build ios --release
- name: Upload to TestFlight
env:
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
run: |
xcrun altool --upload-app \
--type ios \
--file build/Export/NativePHPApp.ipa \
--apiKey $APP_STORE_CONNECT_KEY_ID \
--apiIssuer $APP_STORE_CONNECT_ISSUER_IDname: Build Android
on:
push:
tags:
- 'v*'
jobs:
build-android:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Setup Java JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
- name: Install dependencies
run: |
composer install --no-dev --optimize-autoloader
npm ci
- name: Build assets
run: npm run build
- name: Build Android AAB
env:
MYAPP_RELEASE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
MYAPP_RELEASE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
run: php artisan native:build android --release
- name: Upload to Play Console
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }}
packageName: com.mycompany.myapp
releaseFiles: app/build/outputs/bundle/release/app-release.aab
track: internalCaching mengurangi waktu build secara signifikan:
- name: Cache Composer
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- name: Cache NPM
uses: actions/cache@v4
with:
path: node_modules
key: npm-${{ hashFiles('package-lock.json') }}
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ hashFiles('**/*.gradle*') }}
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: ios/Pods
key: pods-${{ hashFiles('ios/Podfile.lock') }}Dengan caching, build kedua dan seterusnya jauh lebih cepat (~50-70% reduction).
Gunakan git tag untuk versioning otomatis:
# Buat tag baru
git tag v1.0.0
git push origin v1.0.0
# GitHub Actions trigger build
# Build menggunakan version dari tag
# Upload ke TestFlight/Play Console dengan version yang benarKonfigurasi untuk mengambil version dari tag:
<?php
return [
'version' => env('APP_VERSION', '1.0.0'),
// GitHub Actions set APP_VERSION dari git tag
];- name: Set version from tag
run: |
VERSION=${GITHUB_REF#refs/tags/v}
echo "APP_VERSION=$VERSION" >> $GITHUB_ENVFastlane mengotomasikan signing, build, dan upload:
default_platform(:ios)
platform :ios do
lane :beta do
setup_ci if ENV['CI']
match(type: "appstore")
build_app(scheme: "NativePHPApp")
upload_to_testflight
end
end
platform :android do
lane :beta do
upload_to_play_store(
track: "internal",
aab: "app/build/outputs/bundle/release/app-release.aab"
)
end
endTip
Fastlane membutuhkan sedikit learning curve, tapi sangat powerful untuk workflow yang kompleks. Untuk project sederhana, artisan command + GitHub Actions sudah cukup. Pertimbangkan Fastlane ketika kalian punya multiple lane (beta, release, hotfix) dan signing yang rumit.
Inti yang harus dibawa pulang:
Di episode 22 selanjutnya, kita akan membahas Keamanan Data di Perangkat — threat model, enkripsi SQLite, secure storage, certificate pinning, obfuscation, dan audit keamanan. Sampai jumpa di episode 22!