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

Android development flow

Deyine
February 24, 2018

Android development flow

At TeamX we deliver android apps using a special flow. I tried to describe it through this slides. Presented at Dakar Android user group session of 24, Feb 2018

Deyine

February 24, 2018
Tweet

More Decks by Deyine

Other Decks in Programming

Transcript

  1. FROM DEVELOPMENT TO PRODUCTION 14 PIPELINE image: mingc/android-build-box:latest pipelines: default:

    - step: name: Build caches: - gradle - gradlewrapper script: - ./gradlew assembleDevelopmentDebug
  2. FROM DEVELOPMENT TO PRODUCTION 15 PIPELINE image: mingc/android-build-box:latest pipelines: default:

    - step: name: Build caches: - gradle - gradlewrapper script: - ./gradlew assembleDevelopmentDebug
  3. FROM DEVELOPMENT TO PRODUCTION 16 PIPELINE image: mingc/android-build-box:latest pipelines: default:

    - step: name: Build script: - ./gradlew assembleDebug caches: - gradle - gradlewrapper
  4. FROM DEVELOPMENT TO PRODUCTION 17 PIPELINE image: mingc/android-build-box:latest pipelines: default:

    - step: name: Build script: - ./gradlew assembleDebug caches: - gradle - gradlewrapper definitions: caches: gradlewrapper: ~/.gradle/wrapper
  5. FROM DEVELOPMENT TO PRODUCTION 19 STAGE public class Config {

    // Dev url //public static String API_URL = "http://localhost"; // Dev prod public static String API_URL = "http://url.daug.com"; }
  6. FROM DEVELOPMENT TO PRODUCTION 21 STAGE Build Variants flavorDimensions "stage"

    productFlavors { development { dimension "stage" getProps('./config/development.props').each { p -> buildConfigField 'String', p.key, p.value } } production { dimension "stage" getProps('./config/production.props').each { p -> buildConfigField 'String', p.key, p.value } } }
  7. FROM DEVELOPMENT TO PRODUCTION 22 STAGE Build Variants RETROFIT =

    Retrofit.Builder() .baseUrl(BuildConfig.API_URL) .build()
  8. FROM DEVELOPMENT TO PRODUCTION 24 STAGE Build Variants image: mingc/android-build-box:latest

    pipelines: default: - step: name: Build script: - ./gradlew assembleDevelopementDebug caches: - gradle - gradlewrapper definitions: caches: gradlewrapper: ~/.gradle/wrapper
  9. FROM DEVELOPMENT TO PRODUCTION 25 VERSIONING ext.buildConfig = [ 'applicationName':

    ‘DaugApp’, 'version': [ 'major': 1, 'minor': 2, 'patch': 0, 'build': 0 ] ] ext.buildConfig.version['name'] = “${buildConfig.version.major}.${buildConfig.version.minor}.${buildConfig.version.patch}" ext.buildConfig.version['code'] = buildConfig.version.major * 1000000 + buildConfig.version.minor * 10000 + buildConfig.version.patch * 100 + buildConfig.version.build
  10. FROM DEVELOPMENT TO PRODUCTION 26 VERSIONING defaultConfig { applicationId ‘sn.daug.app'

    minSdkVersion 23 targetSdkVersion 26 versionCode buildConfig.version.code versionName buildConfig.version.name testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true }
  11. FROM DEVELOPMENT TO PRODUCTION 27 VERSIONING buildTypes { applicationVariants.all {

    variant -> variant.outputs.all { outputFileName = "${buildConfig.applicationName}-${variant.buildType.name}-$ {defaultConfig.versionName}.apk" } } } DaugApp-release-8.apk
  12. FROM DEVELOPMENT TO PRODUCTION 28 SIGNING signingConfigs { if (file(‘daug.jks').exists())

    { project.logger.lifecycle(“[DAUG] - Signing config found") project.logger.lifecycle("[DAUG] - Generate release version $ {buildConfig.version.code} - ${buildConfig.version.name} ") release { storeFile file('daug.jks') storePassword propOrEmpty('DAUG_STORE_PASSWORD') keyAlias 'daugkey' keyPassword propOrEmpty('DAUG_KEY_PASSWORD') } }else { project.logger.lifecycle("[DAUG] - Signing config not found") } }
  13. FROM DEVELOPMENT TO PRODUCTION 29 SIGNING signingConfigs { if (file(‘daug.jks').exists())

    { project.logger.lifecycle(“[DAUG] - Signing config found") project.logger.lifecycle("[DAUG] - Generate release version $ {buildConfig.version.code} - ${buildConfig.version.name} ") release { storeFile file('daug.jks') storePassword propOrEmpty('DAUG_STORE_PASSWORD') keyAlias 'daugkey' keyPassword propOrEmpty('DAUG_KEY_PASSWORD') } }else { project.logger.lifecycle("[DAUG] - Signing config not found") } }
  14. FROM DEVELOPMENT TO PRODUCTION 30 SIGNING signingConfigs { if (file(‘daug.jks').exists())

    { project.logger.lifecycle(“[DAUG] - Signing config found") project.logger.lifecycle("[DAUG] - Generate release version $ {buildConfig.version.code} - ${buildConfig.version.name} ") release { storeFile file('daug.jks') storePassword propOrEmpty('DAUG_STORE_PASSWORD') keyAlias 'daugkey' keyPassword propOrEmpty('DAUG_KEY_PASSWORD') } }else { project.logger.lifecycle("[DAUG] - Signing config not found") } }
  15. FROM DEVELOPMENT TO PRODUCTION 31 SIGNING signingConfigs { if (file(‘daug.jks').exists())

    { project.logger.lifecycle(“[DAUG] - Signing config found") project.logger.lifecycle("[DAUG] - Generate release version $ {buildConfig.version.code} - ${buildConfig.version.name} ") release { storeFile file('daug.jks') storePassword propOrEmpty('DAUG_STORE_PASSWORD') keyAlias 'daugkey' keyPassword propOrEmpty('DAUG_KEY_PASSWORD') } }else { project.logger.lifecycle("[DAUG] - Signing config not found") } }
  16. FROM DEVELOPMENT TO PRODUCTION 32 SIGNING def propOrEmpty(String name) {

    return hasProperty(name) ? getProperty(name) : '' }
  17. FROM DEVELOPMENT TO PRODUCTION 35 LOGS / CRASH REPORT Custom

    Report private void configureLogs(){ Timber.plant(BuildConfig.DEBUG ? new Timber.DebugTree() : new CrashReportingTree()); Fabric.with(this, new Crashlytics()); }
  18. FROM DEVELOPMENT TO PRODUCTION 36 LOGS / CRASH REPORT private

    static final String CRASHLYTICS_KEY_PRIORITY = "priority"; private static final String CRASHLYTICS_KEY_TAG = "tag"; private static final String CRASHLYTICS_KEY_MESSAGE = "message"; @Override protected void log(int priority, String tag, String message, Throwable throwable) { if (priority == Log.VERBOSE || priority == Log.DEBUG) { return; } Throwable t = throwable != null ? throwable : new Exception(message); Crashlytics.setInt(CRASHLYTICS_KEY_PRIORITY, priority); Crashlytics.setString(CRASHLYTICS_KEY_TAG, tag); Crashlytics.setString(CRASHLYTICS_KEY_MESSAGE, message); Crashlytics.logException(t); }
  19. FROM DEVELOPMENT TO PRODUCTION 37 TAGS / RELEASE image: mingc/android-build-box:latest

    pipelines: tags: release-*: - step: name: Generate release caches: - gradle - gradlewrapper script: - ./gradlew clean assembleProductionRelease -PPAYALL_STORE_PASSWORD=${PAYALL_STORE_PASSWORD} - PPAYALL_KEY_PASSWORD=${PAYALL_KEY_PASSWORD} - curl -X POST --user "${BB_AUTH_STRING}" "https://api.bitbucket.org/2.0/repositories/$ {BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}/downloads" --form files=@"app/build/outputs/apk/production/release/ payall-${BITBUCKET_TAG}.apk" definitions: caches: gradlewrapper: ~/.gradle/wrapper
  20. FROM DEVELOPMENT TO PRODUCTION 38 TAGS / RELEASE image: mingc/android-build-box:latest

    pipelines: tags: release-*: - step: name: Generate release caches: - gradle - gradlewrapper script: - ./gradlew clean assembleProductionRelease -PPAYALL_STORE_PASSWORD=${PAYALL_STORE_PASSWORD} - PPAYALL_KEY_PASSWORD=${PAYALL_KEY_PASSWORD} - curl -X POST --user "${BB_AUTH_STRING}" "https://api.bitbucket.org/2.0/repositories/$ {BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}/downloads" --form files=@"app/build/outputs/apk/production/release/ payall-${BITBUCKET_TAG}.apk" definitions: caches: gradlewrapper: ~/.gradle/wrapper
  21. FROM DEVELOPMENT TO PRODUCTION 39 TAGS / RELEASE image: mingc/android-build-box:latest

    pipelines: tags: release-*: - step: name: Generate release caches: - gradle - gradlewrapper script: - wget https://api.bitbucket.org/2.0/repositories/${BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}/downloads/ daug.jks - curl -X POST --user "${BB_AUTH_STRING}" "https://api.bitbucket.org/2.0/repositories/$ {BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}/downloads" --form files=@"app/build/outputs/apk/production/release/ payall-${BITBUCKET_TAG}.apk" definitions: caches: gradlewrapper: ~/.gradle/wrapper
  22. FROM DEVELOPMENT TO PRODUCTION 40 TAGS / RELEASE image: mingc/android-build-box:latest

    pipelines: tags: release-*: - step: name: Generate release caches: - gradle - gradlewrapper script: - ./gradlew clean assembleProductionRelease -PDAUG_STORE_PASSWORD=${DAUG_STORE_PASSWORD} - PDAUG_KEY_PASSWORD=${DAUG_KEY_PASSWORD} - curl -X POST --user "${BB_AUTH_STRING}" "https://api.bitbucket.org/2.0/repositories/$ {BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}/downloads" --form files=@"app/build/outputs/apk/production/release/ payall-${BITBUCKET_TAG}.apk" definitions: caches: gradlewrapper: ~/.gradle/wrapper
  23. FROM DEVELOPMENT TO PRODUCTION 41 TAGS / RELEASE image: mingc/android-build-box:latest

    pipelines: tags: release-*: - step: name: Generate release caches: - gradle - gradlewrapper script: - ./gradlew clean assembleProductionRelease -PDAUG_STORE_PASSWORD=${DAUG_STORE_PASSWORD} - PDAUG_KEY_PASSWORD=${DAUG_KEY_PASSWORD} - curl -X POST --user "${BB_AUTH_STRING}" "https://api.bitbucket.org/2.0/repositories/$ {BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}/downloads" --form files=@“app/build/outputs/apk/production/release/daug-$ {BITBUCKET_TAG}.apk" definitions: caches: gradlewrapper: ~/.gradle/wrapper
  24. FROM DEVELOPMENT TO PRODUCTION 44 PRIVATE / UPDATE { "is_run_mode":

    "true", "name": "Oolu mobile app", "app_id": "oolusolar", "uri_current": "com.oolusolar.crm.app", "version_code_current": "8", "version_code_min": "8", "version_url": “https://private.url/mobile/latest.apk”, "update_info": “Improve offline mode", "update_date": "28/01/2018" }