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

Df78ed2e647f52dadaac996721a6bdfb?s=47 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

Df78ed2e647f52dadaac996721a6bdfb?s=128

Nate Ebel

September 28, 2020
Tweet

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. CHICAGO ROBOTO #chicagoroboto

  3. GITHUB ACTIONS MAKES ANDROID CI EASY

  4. CI What is it?

  5. Continuous Integration: The process of regularly checking-in, building, and validating

    code
  6. Consistently build the Right thing at the Right time

  7. Automate the validation and distribution of your code

  8. Save your team time, energy, and headaches

  9. CONTINUOUS INTEGRATION PROVIDERS CircleCi Bitrise GitHub Actions

  10. #// concepts are transferable

  11. GitHub Actions How to start building?

  12. Build: A set of repeatable, automated tasks run on a

    remote server
  13. Github Actions: Self-contained commands that package specific, reusable functionality for

    your build
  14. Workflow: A triggerable set of build commands. Different workflows can

    be used for different build types.
  15. Job: A set of build steps run on the same

    virtual machine
  16. Step: Smallest individual element of a workflow. Steps perform tasks

    using a GitHub Action or your own scripts/ commands.
  17. Building Blocks

  18. Building Blocks Build Pull Request Create Workflow

  19. Building Blocks Build Pull Request Add Job Job

  20. Building Blocks Build Pull Request Add Multiple Jobs Job 1

    Job 2 Job 3
  21. Building Blocks Build Pull Request Job 1 Job 2 Job

    3 Add Multiple Jobs
  22. Building Blocks Build Pull Request Job 1 Job 2 Job

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

    3 Step 1 - Action Step 2 - Command Step 3 - Action Use Actions Use Commands
  24. Building Pull Requests How to create our first Android CI

    workflow?
  25. DEFINING A WORKFLOW

  26. Create a Pull Request Workflow File .github/workflows/build_pull_request.yml name: Build Pull

    Request
  27. Run on ‘push’ .github/workflows/build_pull_request.yml name: Build Pull Request on: push

  28. Run on ‘pull_request’ .github/workflows/build_pull_request.yml name: Build Pull Request on: pull_request

  29. 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:
  30. 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
  31. 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
  32. 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
  33. EXPANDING OUR BUILD

  34. 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
  35. 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-
  36. 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 }}
  37. Store keys, app ids, web hooks, etc using Secrets

  38. #github-actions-demo in Chicago Roboto Slack community

  39. 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 }}
  40. 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
  41. 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
  42. PARALLELIZING JOBS

  43. 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/'
  44. 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-*'
  45. 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
  46. 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 }}
  47. Handling job dependency failures .github/workflows/build_pull_request.yml notification_job: needs: [test_job, lint_job, assemble_job]

    name: Notify Build Status runs-on: ubuntu-latest steps:
  48. Handling job dependency failures .github/workflows/build_pull_request.yml test_job: name: Test runs-on: ubuntu-latest

    steps:
  49. Handling job dependency failures .github/workflows/build_pull_request.yml test_job: name: Test runs-on: ubuntu-latest

    continue-on-error: true steps:
  50. SINGLE VS MULTIPLE JOBS Build Cost Build Measurement Build Times

    Build Complexity
  51. Nightly Builds How to schedule workflows?

  52. Create a Nightly build Workflow File .github/workflows/build_nightly.yml name: Nightly Build

  53. Add a scheduled trigger using cron syntax .github/workflows/build_nightly.yml name: Nightly

    Build on: schedule: - cron: '0 0 * * *'
  54. RUNNING UI TESTS

  55. 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 …
  56. 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
  57. 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/'
  58. DISPLAYING BUILD STATUS

  59. Building Feature Branches How to customize workflows for specific branches?

  60. Create a Feature branch workflow .github/workflows/build_feature_branch.yml name: Build Feature Branch

  61. Restrict workflow to specific branch naming pattern .github/workflows/build_feature_branch.yml name: Build

    Feature Branch on: push: branches: 'feature/**'
  62. SETTING VERSION CODES

  63. 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 }
  64. Set VERSION_CODE from GitHub Run Id app-versioning.gradle - name: Assemble

    env: VERSION_CODE: ${{ github.run_id }} run: ./gradlew assembleDebug
  65. DISTRIBUTING TO TESTERS

  66. Upload to Firebase App Distribution app-versioning.gradle - name: Upload to

    Firebase App Distribution uses: wzieba/Firebase-Distribution-Github-Action@v1.2.1 with: appId: ${{ secrets.FIREBASE_APP_ID }} token: ${{ secrets.FIREBASE_TOKEN }} groups: qa file: app/build/outputs/apk/debug/app-debug.apk
  67. None
  68. Simplifying Releases How to automate release workflows?

  69. Create a Release workflow .github/workflows/build_release.yml name: Build Release

  70. Create a Release workflow .github/workflows/build_release.yml name: Build Release on: release:

    types: [published]
  71. 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
  72. 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 }}
  73. 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
  74. 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
  75. GITHUB ACTIONS GO FAR BEYOND ANDROID

  76. ACTION EXAMPLES First Time Contributors Danger Integration Semantic Versioning Auto

    Commit apk Size Comparison Code Coverage
  77. Real World Experience How do GitHub Actions perform in production

    projects?
  78. EXPERIENCES WITH GITHUB ACTIONS Faster Builds Reduced Complexity More Approachable

    Improved Failure Visibility Increased Automation Great Documentation
  79. Where are the pain points?

  80. Where is time lost?

  81. Build only what you need to take action

  82. #// where to go next?

  83. LEARN MORE Marketplace https:#//github.com/marketplace?type=actions Documentation https:#//docs.github.com/en/free-pro-team@latest/actions

  84. None
  85. THANKS FOR WATCHING ANDROID DEVELOPER @n8ebel www.goobar.io Nate Ebel #chicagoroboto