همه چیز درباره 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 V33 . Setup-Java V3(JDK)4 . upload-a-build-artifact V35 . Setup-Android(SDK)6 . Setup-Android(NDK)7 . Setup-Android-Emulator8 . sign-android-release9 . 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 CI2 . Unit Tests3 . Instrumentations Tests4 . Sign and upload to telegramممنون که تا اخر مقاله همراهم بودین راستی اگه دوست داشتی در این باره بیشتر بخونی داکیومنت گیتهاب و قسمت مارکت اکشن های گیتهاب رو یه نگاه بنداز ;) ممنون میشم با لایکتون از من حمایت کنید ❤️? :)