Mahdi
Mahdi
خواندن ۱۱ دقیقه·۲ سال پیش

همه چیز درباره Github Actions در اندروید( CI/CD )

شاید تا به حال براتون پیش اومده باشه یسری تست توی اندروید نوشته باشید ولی خب اجرای دستی اون تست ها زمان زیادی ازتون گرفته باشه یا خواسته باشید یه نسخه دیباگ یا ریلیز از اپ بگیرید و بیلدش کلی وقت ازتون گرفته باشه تو این مقاله قصد دارم ابزاری برای اتومات کردن این ۳ تا فرایند بهتون معرفی کنم ابزاری که به صورت کاملا رایگان گیتهاب و گیتلب در اختیارمون گذاشته .

اصلا CI/CD چیست؟

به مجموعه ای از فرآیندهای خودکار اطلاق می‌شود که در آن از تغییرات کد در سیستم‌های نرم‌افزاری استفاده می‌شود و باعث سرعت و کیفیت بیشتر تولید نرم‌افزار می‌شود.

در گیت‌هاب، شما می‌توانید با استفاده از CI/CD، کد خود را تست و ارسال کنید تا بتوانید در اعتماد و اطمینان به خود داشته باشید که تغییراتی که اعمال کرده‌اید، باعث خرابی‌های سیستم نمی‌شود. برای انجام این کار، می‌توانید از ابزارهایی مانند GitHub Actions استفاده کنید که برای شما فرآیند CI/CD را به صورت خودکار و ساده تر انجام می‌دهد. با این روش، کد شما به صورت مستمر تست می‌شود و هرگونه خطا یا مشکل در سیستم به راحتی تشخیص داده می‌شود.


شما برای استفاده از github actions کافیست هنگام ایجاد پروژتون یک پوشه به اسم github. درست کرده و داخل آن یک پوشه دیگر به اشم workflows درست کنید داخل پوشه workflows فایل های yaml شما قرار میگیره که خود گیتهاب به صورت اتومات این فایل ها رو تشخیص میده و طبق اون فرایند هایی که نوشتید رو براتون اجرایی میکنه در پایین یک مثال بسیار ساده از اینگونه فایل ها زدم

name: Android CI/CD on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 - name: Build with Gradle run: ./gradlew build

بخش name اسم اصلی اکشن ما رو مشخص میکنه که من اینجا Android CI/CD انتخاب کردم

name: Android CI/CD

بخش on شامل اکشن هایی است که درصورتی که روی گیت ما انجام شود فایل ما اجرا میشود این بخش شامل pull_request,push,release,delete,issues , ... است فهرست کامل اکشن ها رو در اینجا میتونید مشاهده کنید که مهترین اون ها در اینجا push , pull_request هستش و ما بررسیش میکنیم

در بخش push وقتی عمل push روی ریپازیتوری گیتهاب انجام میشود فایل اجرایی ما اجرا میشود خود این بخش شامل branches ( که مشخص میکند عمل push رو کدوم برنچ انجام شد فایل ما اجرا شود ) و paths (که مشخص میکند عمل push دقیقا روی کدوم ادرس پوشه انجام بشه فایل ما اجرا شود ) است که من در اینجا از branches استفاده کردم در این بخش اسم برنچ ( ها ) به صورت آرایه میدیم بهش

on: push: branches: [main]

میتونیم اسم برنچ ها و یا path هامون رو به این صورت بدیم (پترن شکل اطلاعات بیشتر در اینجا )

releases/**
  • که در این صورت برنچ ( path ) که شروعش با release باشه رو در نظر میگیره

بخش بعدی jobs یکی از مهم ترین و اصلی ترین قسمت کار هستش این بخش شامل اکشن هایی که باید اجرا بشه هنگام اجرای فایل ما هستش حالا این اکشن ها از قاعده job.<job_id>.name پیروی میکند حالا این یعنی چی؟ بزارید منظورم رو با یه مثال توضیح بدم

jobs: my_first_job: name: My first job my_second_job: name: My second job

که در اینجا my_first_job و my_second_job جزو job_id حساب میشن و باید یونیک باشن My first job و My second job جزو name ها حساب میشن و میتونن یونیک نباشن هر job که میسازیم باز خودش شامل runs-on , steps , strategy , continue-on-error , needs,name , ... هستش که لیست کاملش رو از اینجا میتونید ببینید که ما در اینجا مهمتریناشون که runs-on , steps,strategy هستش رو توضیح میدیم

در اینجا runs-on سیستم عاملی که باید کد ها روش اجرا بشن رو مشحص میکنه که ما از ubuntu-latest استفاده میکنیم ( لیست کامل سیستم عامل ها رو میتونید اینجا مشاهده کنید)

و اما steps این بخش از دو قاعده ی jobs.<job_id>.steps[*].id و jobs.<job_id>.steps[*].if پیروی میکند حالا این اصلا یعنی چی؟ بزارید این قسمت هم با یک مثال توضیح بدم تا بهتر متوجهش بشید

اولین قاعده jobs.<job_id>.steps[*].id است که برای خود این id شامل بخش های زیادی است ( لیست کامل آن را میتوانید در اینجا ببینید) مهمترین اونها github,runner,secrets,matrix است .

خود github شامل قسمت هایی است (لیست کاملش اینجا ) اما اینجا workspace مهم هستش که مسیر رانر ما رو نشون میده و دیگ لازم نیست دستی بنویسیم مسیر کاملش رو

- name: Upload APK uses: actions/upload-artifact@v2 with: name: app-debug path: ${{ github.workspace }}/app/build/outputs/apk/debug

بخش runner شامل محتویات و variable های رانر ما هستش (لیست کاملش اینجا) ما اینجا از os که شامل سیستم عامل ما هستش استفاده کردیم

- uses: actions/cache@v2 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: ${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('**/buildSrc/**/*.kt') }}

بخش secrets شامل variable های مهم ما هستش برا مثال شما نباید توکن بات تلگرامتون رو مستقیم بیاین تو فایل yml اتون تعریف کنید ( حالا اگه پروژتون خصوصی باشه باز یه توجیهی براش هست ولی اگه پابلیک باشه ....) خب حالا این بخش دقیقا کجای گیتهاب هستش؟ وارد پروژه خود شوید و بخش settings/secrets and variables/actions رو باز کنید طبق تصویر زیر

خب حالا اینجا از تگ Secrets روی new repository secret مثل تصویر زیر کلیک کنید

بخش name شامل اسم variable شماست که باید یونیک باشه و بخش secret شامل مقدار variable شماست

  • حواستون باشه شما موقع مقدار دهی مقدارش یادتون باشه چون بعد اگر یادتون بره نمیتونید ببینید مقدارش رو (اسمشرو خودشه دیگ secret ) فقط میتونید ادیتش کنید

بعدش دیگ Add secret رو میزنید و تمام

اینم کد استفادش که ما ازش استفاده میکنیم برای اپلود فایل به تلگرام

- name: Upload to telegram uses: appleboy/telegram-action@master with: to: ${{ secrets.CHAT_ID }} token: ${{ secrets.BOT_TOKEN }} document: ${{ github.workspace }}/app/release/app-release-signed.apk

بخش اخر که matrix هستش نیز یکی از بخش های مهم ما هستش تصور کنید پروژتون رو میخواید روی چند نسخه از اندروید ران کنید ( ۱ جاب اما روی چند نسخه اندروید به صورت همزمان ) اینجاست که matrix میاد کمکتون باید این بخش رو قبل از step ها در بخش strategy تعریف کنید مثالش اینجوری هستش

test: runs-on: ubuntu-latest needs: build strategy: matrix: api-level: [21, 23, 29,30,31] target: [default]

این میاد جاب test ( که وابسته به جاب build هستش اون needs نشانه وابستگی هست ) رو همزمان روی اندروید های 21,23,29,30,31 با استفاده از تارگت دیفالت اجرا میکنه

حالا از این ماتریسه چطور استفاده کنیم؟ یه مثال برا قسمت تست های ui ازش میزنم

- name: Run instrumentation tests uses: ReactiveCircus/android-emulator-runner@v2.21.0 with: api-level: ${{ matrix.api-level }} arch: x86_64 profile: pixel_2 disable-animations: true script: ./gradlew connectedCheck --stacktrace

اینجا بعد از اجرای شبیه ساز همزمان میاد تست های ui امون رو روی api-level هایی که تو ماتریسمون هست اجرا میکنه

خب قاعده jobs.<job_id>.steps[*].id تموم شد نوبتی هم باشه نوبت قاعده jobs.<job_id>.steps[*].if هستش این قاعده همونطور که از اسمش پیداست اگر بخواید برا step هاتون شرط بزارید از این قاعده استفاده میکنید شرط هایی که میزارید مدل نوشتاریشون دقیقا شبیه برنامه نویسی هستش( اوپراتور ها و ...) حالا باز اگر این قسمت یادتون رفته و یا به هر دلیلی دوست داشتید مدلش رو ببینید اینجا هستش

steps: - name: My first step if: ${{ github.event_name == 'pull_request' && github.event.action == 'unassigned' }} run: echo This event is a pull request that had an assignee removed.

خب میرسیم به اخرین بخش یعنی strategy این بخش شامل matrix هستش که قبلا دربارش توضیح دادیم و مثال هم زدیم

test: runs-on: ubuntu-latest needs: build strategy: matrix: api-level: [21, 23, 29,30,31] target: [default]

خب تموم شد ؟ کل ci/cd برا اندروید همش این بود ؟؟!!! نه عزیزانم :)) این تازه سمپلش بود که شما باهاش و طرز کارش آشنا بشین حالا بریم تو عمقش ببینیم اصلا چجوری فرایند و تسک تعریف کنیم محدودش کنیم با کرون جاب ساعت اجرا براش تایین کنیم و ...

خب از on شروع میکنیم تو این قسمت یه بخشی هست به اسم workflow_dispatch این بهتون توانایی اجرای دستی workflow رو میده
on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch:

همین بخش یه قسمتی داره به اسم schedule اینجا میتونیم کرون جاب تنظیم کنیم که مثلا تسکمون هر n ساعت یا هر n روز یا ... اجرا بشه اطلاعات بیشتر در اینجا

الگوی صحیح نوشتن کرون در تصویر زیر هستش

فرض کنید من یه تسک دارم میخوام هر Monday در هر هفته سر ساعت ۱۱ صبح این تسک اجرا بشه طبق الگوی بالا کدش این شکلی میشه

pattern : * 11 * * 1

حالا فرض کنید بخوایم با کمک اطلاعات بالا یه تسک بنویسیم که issue ها رو ساعت ۱۲ هر روز بعد از غیر فعال بودن اون issue ببنده اونو کدش این شکلی میشه :

name: &quotClose stale issues&quot on: schedule: - cron: &quot0 0 * * *&quot jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v1.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue is closed due to inactivity'

خب یادتون باشه گفتم بخش steps رو قراره بیشتر بازش کنیم الگو اش رو بالا گفتم حالا ببینیم این قسمت میتونه شامل چه چیز هایی باشه؟

هر step یه name داره که شامل اسم اون و uses که معمولا اسم کتاب خونه ای که اون step ازش استفاده میکنه هست

steps: - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11

این step مثلا با استفاده از لایبرری خود گیتهاب اقدام به نصب JDK 11 میکنه که اون with (قبلا دربارش توضیخ دادم ) مشخص میکنه که این JDK ما شامل جاوا ورژن ۱۱ هستش

اما گاهی تسک هامون نیاز دارن تا یه دستور رو توی ترمینالی که باز هست روی رانر ما اجرا کنن برای این کار از run به شکل زیر استفاده میکنیم

steps: - name: Check and Report run: ./gradlew lint

و اما env یکی دیگ از keyword های مهم ماست که گاهی شما میخواید یه ثابت تو کدتون تعریف کنیو و ازش استفاده کنید به مثال زیر توجه کنید

steps: - name: Load API Token from secrets env: API_TOKEN: ${{ secrets.TOKEN }} run: echo API_TOKEN=\&quot$API_TOKEN\&quot > ./local.properties

این کد توکن رو از سکرت های گیتهاب ( قبلا دربارش توضیح دادم که سکرت چیه کاربردش چیه ) درمیاره و مقدارش رو توی API_TOKEN ذخیره میکنه و در نهایت هم با استفاده از run که بالا توضیح دادم میاد مقدارش رو میریزه توی فایل local.properties

خب قبلا گفتم با استفاده از لایبرری های خود گیتهاب ( و البته اونایی که اوپن سورس هستن و کسایی به غیر از خود گیتهاب توسعشون دادن ) تو بخش steps میتونیم یسری تسک هامون مثل نصب JDK رو به راحتی انجام بدیم اینجا من چند تا از معروف هاشو برای اندروید بهتون معرفی میکنم

1 . Checkout V3

2 . Cache V3

3 . Setup-Java V3(JDK)

4 . upload-a-build-artifact V3

5 . Setup-Android(SDK)

6 . Setup-Android(NDK)

7 . Setup-Android-Emulator

8 . sign-android-release

9 . telegram-action

یکی از پرکابرد ترین لایبرری ها از لایببری هایی که معرفی کردیم upload-a-build-artifact V3 هستش این لایببری به شما کمک میکنه فایل ها رو اپلود کنید قسمت Atrifact گیتهاب

- name: Upload Reports uses: actions/upload-artifact@v3 with: name: Test-Reports path: ${{ github.workspace }}/app/build/reports/tests if: always()

این کد نتیجه تست های ما رو تو قالب Zip میفرسته Artifact

خب همونطور که قبلا گفتم میتونیم با استفاده از CI/CD اپ امون رو ریلیز بگیریم امضا کنیم و درنهایت بفرستیم توی تلگرام sign-android-release به ما امکان امضا اپ رو میده و telegram-action امکان فرستادن اون به تلگرام رو میده

برا امضای اپ همونطور که میدونید باید تو اندروید استادیو کلید ساین بسازید اما برای پر کردن قسمت secret ها SIGNING_KEY شما باید یه دور کلیدی که دارید به فرمت base64 دربیارید و اونو قدارش بزارید توی سکرت ها

openssl base64 < some_signing_key.jks | tr -d '\n' | tee some_signing_key.jks.base64.txt

که به جای some_signing_key.jks اسم کلید خودتونو میزارید حالا با استفاده از دستور زیر از اپ ریلیز بگیرید

- uses: r0adkll/sign-android-release@v1 name: Sign app APK # ID used to access action output id: sign_app with: releaseDirectory: app/build/outputs/apk/release signingKeyBase64: ${{ secrets.SIGNING_KEY }} alias: ${{ secrets.ALIAS }} keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} keyPassword: ${{ secrets.KEY_PASSWORD }} env: # override default build-tools version (29.0.3) -- optional BUILD_TOOLS_VERSION: &quot30.0.2&quot

و در نهایت با استفاده از دستور زیر اونو به تلگرام اپلود میکنید

- name: Upload to telegram uses: appleboy/telegram-action@master with: to: ${{ secrets.CHAT_ID }} token: ${{ secrets.BOT_TOKEN }} document: ${{steps.sign_app.outputs.signedReleaseFile}}

خب به پایان مقاله رسیدیم در اینجا من به شما ۴ فایل ( یکی سمپل دومی اجرای یونیت تست ها سومی اجرای تست های ui و چهارمی امضا و فرستادن اپ به تلگرام ) رو ادرسشون رو توی زیر میدم

1 . Simple CI

2 . Unit Tests

3 . Instrumentations Tests

4 . Sign and upload to telegram

ممنون که تا اخر مقاله همراهم بودین راستی اگه دوست داشتی در این باره بیشتر بخونی داکیومنت گیتهاب و قسمت مارکت اکشن های گیتهاب رو یه نگاه بنداز ;)

ممنون میشم با لایکتون از من حمایت کنید ❤️? :)

ci cdandroidandroid cigithub actions
شاید از این پست‌ها خوشتان بیاید