Slide 1

Slide 1 text

FROM DEVELOPMENT TO PRODUCTION DEYINE JIDDOU Presented by:

Slide 2

Slide 2 text

FROM DEVELOPMENT TO PRODUCTION 2 Startup Studio

Slide 3

Slide 3 text

FROM DEVELOPMENT TO PRODUCTION 3 Startup Studio

Slide 4

Slide 4 text

FROM DEVELOPMENT TO PRODUCTION 4 Stack

Slide 5

Slide 5 text

FROM DEVELOPMENT TO PRODUCTION 5 Tools

Slide 6

Slide 6 text

FROM DEVELOPMENT TO PRODUCTION 6 CONTINUOUS DELIVERY SPRINT DELIVERY FEATURE DELIVERY

Slide 7

Slide 7 text

FROM DEVELOPMENT TO PRODUCTION 7 PULL REQUEST BRANCHING

Slide 8

Slide 8 text

FROM DEVELOPMENT TO PRODUCTION 8 PULL REQUEST BRANCHING

Slide 9

Slide 9 text

FROM DEVELOPMENT TO PRODUCTION 9 PULL REQUEST BRANCHING

Slide 10

Slide 10 text

FROM DEVELOPMENT TO PRODUCTION 10 PEU DE REUNION, DISCUTER TOUT LE TEMPS

Slide 11

Slide 11 text

FROM DEVELOPMENT TO PRODUCTION 11 FLOW Push Build Signing Delivery

Slide 12

Slide 12 text

FROM DEVELOPMENT TO PRODUCTION 12 FLOW Git Bitbucket pipeline Push Build Signing Delivery

Slide 13

Slide 13 text

FROM DEVELOPMENT TO PRODUCTION 13 PIPELINE

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

FROM DEVELOPMENT TO PRODUCTION 18 STAGE Development Production

Slide 19

Slide 19 text

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"; }

Slide 20

Slide 20 text

FROM DEVELOPMENT TO PRODUCTION 20 STAGE API_URL="http://192.168.43.176:3000/api/" USER_TOKEN="USER_TOKEN" USER_NAME="USER_NAME" Flavors

Slide 21

Slide 21 text

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 } } }

Slide 22

Slide 22 text

FROM DEVELOPMENT TO PRODUCTION 22 STAGE Build Variants RETROFIT = Retrofit.Builder() .baseUrl(BuildConfig.API_URL) .build()

Slide 23

Slide 23 text

FROM DEVELOPMENT TO PRODUCTION 23 STAGE Build Variants

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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 }

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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") } }

Slide 29

Slide 29 text

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") } }

Slide 30

Slide 30 text

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") } }

Slide 31

Slide 31 text

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") } }

Slide 32

Slide 32 text

FROM DEVELOPMENT TO PRODUCTION 32 SIGNING def propOrEmpty(String name) { return hasProperty(name) ? getProperty(name) : '' }

Slide 33

Slide 33 text

FROM DEVELOPMENT TO PRODUCTION 33 LOGS / CRASH REPORT

Slide 34

Slide 34 text

FROM DEVELOPMENT TO PRODUCTION 34 LOGS / CRASH REPORT

Slide 35

Slide 35 text

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()); }

Slide 36

Slide 36 text

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); }

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

FROM DEVELOPMENT TO PRODUCTION 42 PUBLISH / UPDATE

Slide 43

Slide 43 text

FROM DEVELOPMENT TO PRODUCTION 43 PRIVATE / UPDATE

Slide 44

Slide 44 text

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" }

Slide 45

Slide 45 text

FROM DEVELOPMENT TO PRODUCTION 45 SLACK IT ALL

Slide 46

Slide 46 text

FROM DEVELOPMENT TO PRODUCTION 46 /app-release 1.6 /deploy_backend SLACK IT ALL Slack Bot

Slide 47

Slide 47 text

THANK YOU!