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

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

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

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