Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building an Android CI Pipeline with GitHub Act...

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Nate Ebel Nate Ebel
September 28, 2020

Building an Android CI Pipeline with GitHub Actions - Chicago Roboto 2020

Learn to build a multi-functional continuous integration pipeline for Android using GitHub Actions.

Learn More:
[Samples] GitHubActionsAutomationSandbox:
https://github.com/n8ebel/GitHubActionsAutomationSandbox

[YouTube] Setup CI for Android Using GitHub Actions:
https://www.youtube.com/watch?v=K9w01h4-Wnc

[Doc] GitHub Actions Quickstart Guide:
https://docs.github.com/en/free-pro-team@latest/actions/quickstart

[Browse] GitHub Actions Marketplace:
https://github.com/marketplace?type=actions

Avatar for Nate Ebel

Nate Ebel

September 28, 2020
Tweet

More Decks by Nate Ebel

Other Decks in Programming

Transcript

  1. ANDROID DEVELOPER @n8ebel www.goobar.io Nate Ebel BUILDING AN ANDROID CI

    PIPELINE WITH GITHUB ACTIONS #chicagoroboto ANDROID DEVELOPER @n8ebel www.goobar.io Nate Ebel
  2. Step: Smallest individual element of a workflow. Steps perform tasks

    using a GitHub Action or your own scripts/ commands.
  3. Building Blocks Build Pull Request Job 1 Job 2 Job

    3 Step 1 Step 2 Step 3 Add Steps
  4. Building Blocks Build Pull Request Job 1 Job 2 Job

    3 Step 1 - Action Step 2 - Command Step 3 - Action Use Actions Use Commands
  5. Add a job .github/workflows/build_pull_request.yml name: Build Pull Request on: pull_request

    jobs: test_job: name: Assemble runs-on: ubuntu-latest steps:
  6. Checkout the code .github/workflows/build_pull_request.yml name: Build Pull Request on: pull_request

    jobs: test_job: name: Assemble runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2
  7. Assemble your apk .github/workflows/build_pull_request.yml name: Build Pull Request on: pull_request

    jobs: test_job: name: Assemble runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Assemble Debug run: ./gradlew assembleDebug
  8. Push to GitHub .github/workflows/build_pull_request.yml name: Build Pull Request on: pull_request

    jobs: test_job: name: Assemble runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Assemble Debug run: ./gradlew assembleDebug
  9. Add Gradle caching .github/workflows/build_pull_request.yml name: Build Pull Request on: pull_request

    jobs: test_job: name: Assemble runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Assemble Debug run: ./gradlew assembleDebug
  10. Add Gradle caching .github/workflows/build_pull_request.yml jobs: test_job: name: Assemble runs-on: ubuntu-latest

    steps: - name: Checkout uses: actions/checkout@v2 - name: Assemble Debug run: ./gradlew assembleDebug - name: Restore Cache uses: actions/cache@v2 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: | ${{ runner.os }}-gradle-
  11. Send notification to Slack .github/workflows/build_pull_request.yml - name: Assemble Debug run:

    ./gradlew assembleDebug - uses: technote-space/workflow-conclusion-action@v1 - uses: 8398a7/action-slack@v3 with: status: ${{ env.WORKFLOW_CONCLUSION }} fields: commit,ref,workflow,eventName author_name: ${{ github.actor }} icon_emoji: ':robot_face:' username: "Pull Request Build Status" text: | ${{ env.WORKFLOW_CONCLUSION }}: https://github.com/n8ebel/GitHubActionsAutomationSandbox/actions/runs/${{ github.run_id }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
  12. Send notification to Slack .github/workflows/build_pull_request.yml - name: Assemble Debug run:

    ./gradlew assembleDebug - uses: technote-space/workflow-conclusion-action@v1 - uses: 8398a7/action-slack@v3 with: status: ${{ env.WORKFLOW_CONCLUSION }} fields: commit,ref,workflow,eventName author_name: ${{ github.actor }} icon_emoji: ':robot_face:' username: "Pull Request Build Status" text: | ${{ env.WORKFLOW_CONCLUSION }}: https://github.com/n8ebel/GitHubActionsAutomationSandbox/actions/runs/${{ github.run_id }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
  13. Add additional steps .github/workflows/build_pull_request.yml - name: Assemble Debug run: ./gradlew

    assembleDebug - name: Run Debug Tests run: ./gradlew testDebugUnitTest - name: Run Debug ktlint run: ./gradlew ktlintDebugCheck - name: Run Debug Lint run: ./gradlew lintDebug
  14. Store build outputs as artifacts .github/workflows/build_pull_request.yml - name: Assemble Debug

    run: ./gradlew assembleDebug - name: Upload Test Reports if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: test-reports path: '**/build/reports/tests/' - name: Upload APK uses: actions/upload-artifact@v2 with: name: apk path: app/build/outputs/apk/debug/**.apk
  15. Add a Test job .github/workflows/build_pull_request.yml test_job: name: Test runs-on: ubuntu-latest

    steps: - name: Checkout uses: actions/checkout@v2 - name: Restore Cache uses: actions/cache@v2 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: | ${{ runner.os }}-gradle- - name: Run Debug Tests run: ./gradlew testDebugUnitTest --continue - name: Upload Test Reports if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: test-reports path: '**/build/reports/tests/'
  16. Add an Android Lint job .github/workflows/build_pull_request.yml lint_job: name: Lint runs-on:

    ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Restore Cache uses: actions/cache@v2 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: | ${{ runner.os }}-gradle- - name: Run Debug Lint run: ./gradlew lintDebug - name: Upload Lint Reports if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: lint-report path: '**/build/reports/lint-results-*'
  17. Add an Assemble job .github/workflows/build_pull_request.yml assemble_job: name: Assemble runs-on: ubuntu-latest

    steps: - name: Checkout uses: actions/checkout@v2 - name: Restore Cache uses: actions/cache@v2 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: | ${{ runner.os }}-gradle- - name: Assemble Debug run: ./gradlew assembleDebug - name: Upload APK uses: actions/upload-artifact@v2 with: name: apk path: app/build/outputs/apk/debug/**.apk
  18. Add a Slack notification job .github/workflows/build_pull_request.yml notification_job: needs: [test_job, lint_job,

    assemble_job] name: Notify Build Status runs-on: ubuntu-latest steps: - uses: technote-space/workflow-conclusion-action@v1 - uses: 8398a7/action-slack@v3 with: status: ${{ env.WORKFLOW_CONCLUSION }} fields: commit,ref,workflow,eventName author_name: ${{ github.actor }} icon_emoji: ':robot_face:' username: "Pull Request Build Status" text: | ${{ env.WORKFLOW_CONCLUSION }}: https://github.com/n8ebel/GitHubActionsAutomationSandbox/actions/runs/${{ github.run_id }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
  19. Add job for Android UI tests .github/workflows/build_nightly.yml android_test_job: name: Android

    Test runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v2 …
  20. Use android-emulator-runner action .github/workflows/build_nightly.yml - name: Run Android Tests uses:

    reactivecircus/android-emulator-runner@v2 with: api-level: 29 script: ./gradlew connectedDebugAndroidTest
  21. Store test results .github/workflows/build_nightly.yml - name: Run Android Tests uses:

    reactivecircus/android-emulator-runner@v2 with: api-level: 29 script: ./gradlew connectedDebugAndroidTest - name: Upload Android Test Reports if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: android-test-reports path: '**/build/reports/androidTests/'
  22. Pull VERSION_CODE from build environment app-versioning.gradle def DEFAULT_VERSION_CODE = 1

    ext.getUniqueVersionCode = { def code = System.getenv("VERSION_CODE") return (code != null && !code.isEmpty()) ? code.toInteger() : DEFAULT_VERSION_CODE }
  23. Set VERSION_CODE from GitHub Run Id app-versioning.gradle - name: Assemble

    env: VERSION_CODE: ${{ github.run_id }} run: ./gradlew assembleDebug
  24. Upload to Firebase App Distribution app-versioning.gradle - name: Upload to

    Firebase App Distribution uses: wzieba/[email protected] with: appId: ${{ secrets.FIREBASE_APP_ID }} token: ${{ secrets.FIREBASE_TOKEN }} groups: qa file: app/build/outputs/apk/debug/app-debug.apk
  25. Assemble Release variant .github/workflows/build_release.yml name: Build Release on: release: types:

    [published] jobs: assemble_job: name: Assemble runs-on: ubuntu-latest steps: … - name: Assemble Release run: ./gradlew assembleRelease
  26. Sign Release apk .github/workflows/build_release.yml … - name: Assemble Release run:

    ./gradlew assembleRelease - name: Sign Release uses: r0adkll/sign-android-release@v1 with: releaseDirectory: app/build/outputs/apk/release signingKeyBase64: ${{ secrets.SIGNING_KEY }} alias: ${{ secrets.ALIAS }} keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} keyPassword: ${{ secrets.KEY_PASSWORD }}
  27. Add APK as Release binary .github/workflows/build_release.yml - name: Sign Release

    uses: r0adkll/sign-android-release@v1 with: releaseDirectory: app/build/outputs/apk/release signingKeyBase64: ${{ secrets.SIGNING_KEY }} alias: ${{ secrets.ALIAS }} keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} keyPassword: ${{ secrets.KEY_PASSWORD }} - name: Upload APK to Release uses: skx/github-action-publish-binaries@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: args: app/build/outputs/apk/release/**.apk
  28. Upload APK to Google Play .github/workflows/build_release.yml - name: Upload APK

    to Release uses: skx/github-action-publish-binaries@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: args: app/build/outputs/apk/release/**.apk - name: Upload to Google Play uses: r0adkll/upload-google-play@v1 with: serviceAccountJson: ${{ SERVICE_ACCOUNT_JSON }} packageName: com.n8ebel.githubactionsautomationsandbox releaseFile: app/build/outputs/apk/release/app-release-signed.apk track: internal whatsNewDirectory: distribution/whatsnew mappingFile: app/build/outputs/mapping/release/mapping.txt
  29. EXPERIENCES WITH GITHUB ACTIONS Faster Builds Reduced Complexity More Approachable

    Improved Failure Visibility Increased Automation Great Documentation