أتمتة عمليات البناء باستخدام GitHub Actions
أتمتة عمليات البناء باستخدام GitHub Actions
بناء تطبيق Flutter يدوياً وتوقيعه ورفعه قبل كل إصدار عملية مرهقة وعرضة للأخطاء. تحل GitHub Actions هذه المشكلة بتشغيل خط أنابيب البناء بالكامل تلقائياً في كل مرة تدفع فيها إلى فرع أو تنشئ وسماً (tag). في هذا الدرس ستكتب ملف YAML لسير عمل جاهز للإنتاج يثبّت Flutter ويشغّل مجموعة الاختبارات ويبني AAB أندرويد موقّعاً وIPA لنظام iOS، ويرفع كلاهما كاداءات قابلة للتنزيل — كل ذلك دون تخزين بيانات اعتماد حساسة في مستودعك.
كيف تعمل GitHub Actions
سير العمل هو ملف YAML مخزّن تحت .github/workflows/. تقرأه GitHub وتشغّل كل مهمة (job) على مشغّل مستضاف في السحابة (Ubuntu أو macOS أو Windows). تحتوي المهام على خطوات (steps): أوامر shell أو actions جاهزة مسحوبة من Marketplace. لعمليات CI/CD في Flutter تحتاج عادةً إلى مهمتين منفصلتين — إحداهما على ubuntu-latest للأندرويد وأخرى على macos-latest لنظام iOS — لأن سلسلة أدوات iOS تعمل على macOS فقط.
تخزين الأسرار بأمان
مخزن مفاتيح الأندرويد وشهادات توقيع iOS يجب أن لا تُدرج أبداً في المستودع. توفر GitHub مخزن Secrets مشفراً (Settings → Secrets and variables → Actions). الملفات الثنائية كالمخازن تُشفَّر بترميز base-64 قبل التخزين وتُفك شفرتها داخل سير العمل:
KEYSTORE_BASE64— ملف.jks/.keystoreمشفَّر بـ base-64KEY_ALIAS— الاسم المستعار المستخدم عند إنشاء مخزن المفاتيحKEY_PASSWORD— كلمة مرور المفتاحSTORE_PASSWORD— كلمة مرور مخزن المفاتيحIOS_P12_BASE64— شهادة التوزيع (.p12) مشفَّرة بـ base-64IOS_P12_PASSWORD— كلمة مرور ملف .p12PROVISIONING_PROFILE_BASE64— ملف .mobileprovision مشفَّر بـ base-64
سير العمل الكامل: AAB أندرويد موقَّع
يُشغَّل سير العمل التالي عند كل دفع إلى فرع main وعند وسوم الإصدار (v*). تثبّت مهمة الأندرويد Flutter وتخزّن حزم pub مؤقتاً وتشغّل الاختبارات وتفك ترميز مخزن المفاتيح وتبني AAB نسخة إصدار وترفعه كادائة.
.github/workflows/flutter_ci.yml — مهمة الأندرويد
name: Flutter CI/CD
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
jobs:
# ── Android ─────────────────────────────────────────────
build_android:
name: Build Android AAB
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Java 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Cache pub packages
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: pub-${{ runner.os }}-${{ hashFiles('**/pubspec.lock') }}
restore-keys: pub-${{ runner.os }}-
- name: Install Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.0'
channel: stable
cache: true
- name: Install dependencies
run: flutter pub get
- name: Run tests
run: flutter test --coverage
- name: Decode keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode \
> android/app/keystore.jks
- name: Build release AAB
run: flutter build appbundle --release
env:
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
- name: Upload AAB artifact
uses: actions/upload-artifact@v4
with:
name: android-release-aab
path: build/app/outputs/bundle/release/app-release.aab
retention-days: 14
سير العمل الكامل: IPA لنظام iOS موقَّع
تعمل مهمة iOS على macos-latest. تستورد شهادة التوزيع إلى keychain مؤقت وتثبّت ملف provisioning profile وتبني التطبيق باستخدام flutter build ipa وترفع ملف IPA الناتج.
.github/workflows/flutter_ci.yml — مهمة iOS (أضفها إلى نفس الملف)
# ── iOS ─────────────────────────────────────────────────
build_ios:
name: Build iOS IPA
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.0'
channel: stable
cache: true
- name: Install dependencies
run: flutter pub get
- name: Import signing certificate
env:
P12_BASE64: ${{ secrets.IOS_P12_BASE64 }}
P12_PASSWORD: ${{ secrets.IOS_P12_PASSWORD }}
run: |
KEYCHAIN_PATH=$RUNNER_TEMP/build.keychain
security create-keychain -p "" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "" "$KEYCHAIN_PATH"
echo "$P12_BASE64" | base64 --decode > $RUNNER_TEMP/cert.p12
security import "$RUNNER_TEMP/cert.p12" \
-k "$KEYCHAIN_PATH" -P "$P12_PASSWORD" \
-A -t cert -f pkcs12
security list-keychain -d user -s "$KEYCHAIN_PATH"
- name: Install provisioning profile
env:
PP_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
run: |
PP_PATH=$RUNNER_TEMP/profile.mobileprovision
echo "$PP_BASE64" | base64 --decode > "$PP_PATH"
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp "$PP_PATH" \
~/Library/MobileDevice/Provisioning\ Profiles/
- name: Build iOS IPA
run: flutter build ipa --release --no-codesign
- name: Upload IPA artifact
uses: actions/upload-artifact@v4
with:
name: ios-release-ipa
path: build/ios/ipa/*.ipa
retention-days: 14
ضبط android/app/build.gradle لتوقيع CI
يُدخل سير العمل بيانات اعتماد التوقيع عبر متغيرات البيئة. يجب أن يقرأها ملف android/app/build.gradle حتى يلتقطها Gradle في وقت البناء:
android/app/build.gradle — كتلة signingConfigs
android {
signingConfigs {
release {
keyAlias System.getenv("KEY_ALIAS") ?: "mykey"
keyPassword System.getenv("KEY_PASSWORD") ?: ""
storeFile file("keystore.jks")
storePassword System.getenv("STORE_PASSWORD") ?: ""
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
استراتيجيات التخزين المؤقت لتسريع التشغيلات
يمكن أن تستغرق عمليات البناء الباردة 8–15 دقيقة. يختصر التخزين المؤقت الفعّال هذا إلى 2–4 دقائق في التشغيلات اللاحقة:
- ذاكرة pub المؤقتة — مفتاحها على
pubspec.lock؛ توفر ~30 ثانية لكل تشغيل - ذاكرة Flutter SDK المؤقتة — يحتوي إجراء
subosito/flutter-actionعلى خيارcache: trueمدمج - ذاكرة Gradle المؤقتة — أضف خطوة
actions/cacheمنفصلة تستهدف~/.gradle/cachesو~/.gradle/wrapper - ذاكرة CocoaPods المؤقتة — على macOS، خزّن
ios/Podsمؤقتاً مع مفتاح علىios/Podfile.lock
flutter-version: '3.22.0') بدلاً من الاعتماد على channel: stable وحده. هذا يجعل عمليات البناء قابلة للتكرار: إصدار Flutter الجديد لن يكسر خط أنابيبك صامتاً حتى ترفع الإصدار بشكل متعمد.الخلاصة
يمنحك سير عمل GitHub Actions المنظَّم جيداً عمليات بناء Flutter آلية وقابلة للتكرار عند كل دفع. النقاط الجوهرية هي: خزّن جميع بيانات الاعتماد كأسرار في المستودع وفك ترميزها في وقت التشغيل؛ شغّل الاختبارات قبل البناء حتى تُكتشف الفشوط مبكراً؛ استخدم التخزين المؤقت لإبقاء أوقات التشغيل أقل من خمس دقائق؛ وارفع الادائيات حتى يتمكن فريق ضمان الجودة وأصحاب المصلحة من تنزيل الثنائيات مباشرةً من تبويب Actions دون الحاجة إلى بيئة محلية.
.github/workflows/flutter_ci.yml واحد يمكنه استبدال ساعات من العمل اليدوي للإصدار بخط أنابيب آلي وقابل للتدقيق بالكامل.