Pro builds in your small startup (Berlindroid 29-Nov-2017)

5f69045a2ca496221cfc624405917cdf?s=47 Nelson Osacky
November 29, 2017

Pro builds in your small startup (Berlindroid 29-Nov-2017)

How we scaled our build system at Zenjob.

5f69045a2ca496221cfc624405917cdf?s=128

Nelson Osacky

November 29, 2017
Tweet

Transcript

  1. Pro builds in your small startup Nelson Osacky

  2. About me • Android developer since before Uni • New

    to Berlin - Started working in September • Formerly at Square in San Francisco • Worked on Reader Experience, Square Register and build systems: Buck
  3. Every build is a special snowflake

  4. In the beginning there were no tests, no CI…

  5. None
  6. None
  7. Why Travis? • Other engineering teams at Zenjob teams were

    already using it • Integrates easily with GitHub • Good Android examples and documentation • There’s a million options, this isn’t really the point.
  8. How to add Travis? language: android jdk: - oraclejdk8 android:

    components: - tools - platform-tools - tools - build-tools-25.0.0 - android-26 script: ./gradlew assemble check notifications: email: false cache: directories: - $HOME/.gradle
  9. Only ran checkstyle and unit tests

  10. Next step: Boot emulator before_install: 
 - start emulator install:

    ./gradlew assemble before_script: android-wait-for-emulator script: ./gradlew check connectedCheck
  11. before_install: # Install the system image. - sdkmanager “system-images;android-19;default;armeabi-v7a" #

    Create and start emulator for the script. # Meant to race the install task. - echo no | avdmanager create avd --force -n test -k "system- images;android-19;default;armeabi-v7a" - $ANDROID_HOME/emulator/emulator -avd test -no-audio -no-window &
  12. Travis takes: 15 min 13 sec

  13. Next: Run UI tests

  14. Hard to debug • Button isn’t visible? Why? • No

    screenshots • Spoon doesn’t support AGP 3.0 (yet) • Screenshot on failure is tricky • Unsolved problem
  15. After enabling our first UI test

  16. None
  17. 2 tests later

  18. None
  19. Soon we had a problem

  20. None
  21. Make the tests faster • Replace SystemClock.sleep() in tests with

    Espresso idling resources • Replace real API calls with mock API calls • Make mock API calls return instantly
  22. Success…?

  23. None
  24. Time to try something else

  25. AWS Device Farm

  26. • AWS Device Lab failed with cryptic error and no

    support • AWS Device Lab $10/hr • Firebase Test Lab $1/hr virtual and $5/hr real devices
  27. Easy Decision

  28. So much faster!

  29. Why faster? • Do not have to download or install

    emulator • Emulator doesn’t slow down the rest of the build • Tests are now run on faster emulator
  30. None
  31. 111 secs = 1m 51 secs

  32. None
  33. Can we go faster?

  34. if [ "$COMPONENT" == "checkstyle" ]; then ./gradlew checkstyle elif

    [ "$COMPONENT" == "unit" ]; then ./gradlew test elif [ "$COMPONENT" == "lint" ]; then ./gradlew lint elif [ "$COMPONENT" == "release" ]; then ./gradlew assembleRelease elif [ "$COMPONENT" == "instrumentation" ]; then ./gradlew assembleDebug assembleAndroidTest .travis/run-ui-tests.sh else echo "This module doesn't exist" exit 1 fi
  35. None
  36. Yes, but…

  37. Faster individual builds but slower with multiple builds.

  38. if [ "$COMPONENT" == "build" ]; then ./gradlew checkstyle check

    assembleRelease elif [ "$COMPONENT" == "instrumentation" ]; then ./gradlew assembleDebug assembleAndroidTest .travis/run-ui-tests.sh else echo "This module doesn't exist" exit 1 fi
  39. None
  40. Less is more!

  41. Problems solved!

  42. …but then we wrote more UI tests

  43. None
  44. 60+ Tests, 11 mins

  45. Sharding?

  46. Flank!

  47. • https://github.com/TestArmada/flank • Splits up UI tests amongst Firebase Test

    Lab devices • Splits up tests to minimize costs
  48. Test time stays constant

  49. None
  50. Prints out cost

  51. Still one problem; manual uploads

  52. Automation

  53. before_deploy: # Verify that the apk was signed. - apksigner

    verify -v app/build/outputs/apk/release/app-release.apk # Publish to GitHub releases only on tagged commits. deploy: provider: releases api_key: secure: blah-blah-blah file: - "app/build/outputs/apk/debug/app-debug.apk" - "app/build/outputs/mapping/release/mapping.txt" - "app/build/outputs/apk/release/app-release.apk" skip_cleanup: true on: tags: true condition: $COMPONENT = build # Publish to Google Play after successful deployment to GitHub releases. after_deploy: - ./gradlew publishApkRelease
  54. before_deploy: # Verify that the apk was signed. - apksigner

    verify -v app/build/outputs/apk/release/ app-release.apk
  55. before_deploy: # Verify that the apk was signed. - apksigner

    verify -v app/build/outputs/apk/release/app-release.apk # Publish to GitHub releases only on tagged commits. deploy: provider: releases api_key: secure: blah-blah-blah file: - "app/build/outputs/apk/debug/app-debug.apk" - "app/build/outputs/mapping/release/mapping.txt" - "app/build/outputs/apk/release/app-release.apk" skip_cleanup: true on: tags: true condition: $COMPONENT = build # Publish to Google Play after successful deployment to GitHub releases. after_deploy: - ./gradlew publishApkRelease
  56. # Publish to GitHub releases only on tagged commits. deploy:

    provider: releases api_key: secure: blah-blah-blah file: - "app/build/outputs/apk/debug/app-debug.apk" - "app/build/outputs/mapping/release/mapping.txt" - "app/build/outputs/apk/release/app-release.apk" skip_cleanup: true on: tags: true condition: $COMPONENT = build
  57. before_deploy: # Verify that the apk was signed. - apksigner

    verify -v app/build/outputs/apk/release/app-release.apk # Publish to GitHub releases only on tagged commits. deploy: provider: releases api_key: secure: blah-blah-blah file: - "app/build/outputs/apk/debug/app-debug.apk" - "app/build/outputs/mapping/release/mapping.txt" - "app/build/outputs/apk/release/app-release.apk" skip_cleanup: true on: tags: true condition: $COMPONENT = build # Publish to Google Play after successful deployment to GitHub releases. after_deploy: - ./gradlew publishApkRelease
  58. None
  59. None
  60. Automatic upload to the play store

  61. apply plugin: 'com.github.triplet.play' android { playAccountConfigs { defaultAccountConfig { jsonFile

    = file('keys.json') } } defaultConfig { playAccountConfig = playAccountConfigs.defaultAccountConfig } }
  62. before_deploy: # Verify that the apk was signed. - apksigner

    verify -v app/build/outputs/apk/release/app-release.apk # Publish to GitHub releases only on tagged commits. deploy: provider: releases api_key: secure: blah-blah-blah file: - "app/build/outputs/apk/debug/app-debug.apk" - "app/build/outputs/mapping/release/mapping.txt" - "app/build/outputs/apk/release/app-release.apk" skip_cleanup: true on: tags: true condition: $COMPONENT = build # Publish to Google Play after successful deployment to GitHub releases. after_deploy: - ./gradlew publishApkRelease
  63. # Publish to Google Play after successful deployment to GitHub

    releases. after_deploy: - ./gradlew publishApkRelease
  64. None
  65. TODOs • Automate bumping version numbers • Run UI tests

    on more real devices with different APIs before release • Move to AWS or another service with faster machines
  66. TODOs • Screenshots on failure ( should be built in)

    • Gradle plugin for Firebase tests • Gradle plugin for Flank (for running locally)
  67. TODOs • Merge on green button • Automate release branch

    cutting • Speed up local builds (Buck?)
  68. Thank you nelson.osacky@zenjob.com
 https://www.zenjob.de/careers/