Android DevOps Gitlab CI/CD

print

Gitlab project https://gitlab.com/agascompany/android-devops

DevOps is a set of practices that combines software development (Dev) and information-technology operations (Ops) which aims to shorten the systems development life cycle and provide continuous delivery with high software quality.[1][2]

https://about.gitlab.com/blog/2018/02/14/setting-up-gitlab-ci-for-android-projects/

A successful Git branching model

https://nvie.com/posts/a-successful-git-branching-model/

Image for building Android apps on Gitlab CI  https://hub.docker.com/r/unitedclassifiedsapps/gitlab-ci-android-fastlane/dockerfile

GitLab CI/CD Pipeline Configuration Reference

https://docs.gitlab.com/ee/ci/yaml/

Static code analysis for Kotlin https://arturbosch.github.io/detekt/

image: unitedclassifiedsapps/gitlab-ci-android-fastlane

cache:
  paths:
    - .m2/
    - .gradle/

variables:
  ANDROID_COMPILE_SDK: "28"
  ANDROID_BUILD_TOOLS: "28.0.2"
  ANDROID_SDK_TOOLS:   "4333796"
  LC_ALL: "en_US.UTF-8"
  LANG: "en_US.UTF-8"



before_script:
  - gem install bundler
  - bundle install
  - export GRADLE_USER_HOME=$(pwd)/.gradle
  - chmod +x ./gradlew
  - apt-get --quiet update --yes
  - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
  - wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
  - unzip -d android-sdk-linux android-sdk.zip
  - echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
  - echo y | android-sdk-linux/tools/bin/sdkmanager "platform-tools" >/dev/null
  - echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
  - export ANDROID_HOME=$PWD/android-sdk-linux
  - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
  - chmod +x ./gradlew
  # temporarily disable checking for EPIPE error and use yes to accept all licenses
  - set +o pipefail
  - yes | android-sdk-linux/tools/bin/sdkmanager --licenses
  - set -o pipefail



stages:
  - build
  - test
  - static_analysis
  - fabric
  - prod

#build:
#  stage: build
#  script:
#    - ./gradlew assembleDebug
#  artifacts:
#    paths:
#      - app/build/outputs/
#
lintDebug:
  stage: build
  script:
    - ./gradlew -Pci --console=plain :app:lintDebug -PbuildDir=lint

assembleDebug:
  stage: build
  script:
    - ./gradlew assembleDebug
  artifacts:
    paths:
      - app/build/outputs/

debugTests:
  stage: test
  script:
    - ./gradlew -Pci --console=plain :app:testDebug

coverageTests:
  stage: test
  script:
    - ./gradlew -Pci --console=plain jacocoTestReport coveralls

static_analysis:
  stage: static_analysis
  script:
    - ./gradlew lint
    - ./gradlew detekt
  artifacts:
    paths:
      - app/build/reports/


fabricBeta:
  stage: fabric
  script:
    - fastlane beta
  only:
    - master

playstore:
  stage: prod
  script:
    - fastlane deploy
  only:
    - master

After some fails and changes come success

Build a debug APK

For immediate app testing and debugging, you can build a debug APK. The debug APK is signed with a debug key provided by the SDK tools and allows debugging through adb.

To build a debug APK, open a command line and navigate to the root of your project directory. To initiate a debug build, invoke the assembleDebug task:

gradlew assembleDebug

Beta by Crashlytics

https://docs.fabric.io/android/beta/beta-walkthrough.html#beta-process-walkthrough

FASTLANE App automation done right

https://fastlane.tools/

https://github.com/fastlane/fastlane/issues/729#issuecomment-263406336

I was dealing with the same issue, but adding this line to ~/.bash_profile (as suggested during install) fixed it for me:export PATH="$HOME/.fastlane/bin:$PATH"
# Customise this file, documentation can be found here:
# https://github.com/fastlane/fastlane/tree/master/fastlane/docs
# All available actions: https://docs.fastlane.tools/actions
# can also be listed using the `fastlane actions` command

# Change the syntax highlighting to Ruby
# All lines starting with a # are ignored when running `fastlane`

# If you want to automatically update fastlane if a new version is available:
# update_fastlane

# This is the minimum version number required.
# Update this, if you use features of a newer version
fastlane_version "2.28.3"

default_platform :android

ENV["SLACK_WEB_HOOK_URL"] = "YOUR_SLACK_WEBHOOK"

platform :android do
  before_all do
    # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..."
  end

  desc "Runs all the tests"
  lane :test do
    gradle(task: "test")
  end

  desc "Submit a new Beta Build to Crashlytics Beta"
  lane :beta do
    begin
          gradle(task: "clean assembleRelease")
          gradle(task: "doIncrementVersionCode")
         upload_beta()
           on_success("build is successfully uploaded to crashlytics")
            rescue =>exception
           on_error(exception)
     end
  end

  desc "Deploy a new version to the Google Play"
  lane :deploy do
   on_success("Deploy a new version to the Google Playv")
    gradle(task: "assembleRelease")
    upload_to_play_store
  end

  # You can define as many lanes as you want

  after_all do |lane|
    # This block is called, only if the executed lane was successful

    # slack(
    #   message: "Successfully deployed new App Update."
    # )
  end

  error do |lane, exception|
    # slack(
    #   message: exception.message,
    #   success: false
    # )
  end
end

def upload_beta()

      crashlytics(
                  api_token: "YOUR_API_TOKEN",
                  build_secret: "YOUR_BUILD_SECRET_KEY"
               )

end


# More information about multiple platforms in fastlane: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
# All available actions: https://docs.fastlane.tools/actions

# fastlane reports which actions are used
# No personal data is sent or shared. Learn more at https://github.com/fastlane/enhancer

def on_success(success_msg)
    slack(
     message: success_msg,
     success:true,
     slack_url:ENV["SLACK_WEB_HOOK_URL"]
   )
 end

 def on_error(exception)
    slack(
     message: "something went wrong check *Error Info* for more details.",
     success:false,
     slack_url:ENV["SLACK_WEB_HOOK_URL"],
     payload:{"ErrorInfo" => exception.to_s}
   )
 end

And then we have uploaded a beta version for testing. After that, we can invite testers through Fabric.

Next, we will upload our release apk to play store and publish it. By making a merge request from develop branch to master, and trigger fabric beta and upload to play store only then.

And the build.gradle at app level

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'io.gitlab.arturbosch.detekt'

apply plugin: 'io.fabric'

android {
    compileSdkVersion 29
//    buildToolsVersion "29.0.2"
    def versionPropsFile = file('version.properties')
    if (versionPropsFile.canRead()) {

        def Properties versionProps = new Properties()


        versionProps.load(new FileInputStream(versionPropsFile))

        def name = versionProps['VERSION_NAME']

        def code = versionProps['VERSION_CODE'].toInteger() + 1

        versionProps['VERSION_CODE'] = code.toString()

        versionProps.store(versionPropsFile.newWriter(), null)

        defaultConfig {
            applicationId "com.nickagas.androiddevops"
            minSdkVersion 21
            targetSdkVersion 29

            versionName name

            versionCode code
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }

        signingConfigs {

            release {

                keyAlias 'key'

                keyPassword 'nikos1987'

                storeFile file('../android_devops_key.jks')

                storePassword 'nikos1987'

                v2SigningEnabled false

            }

        }

        buildTypes {
            release {
                minifyEnabled false
                debuggable false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

                signingConfig signingConfigs.release
            }
        }

    } else {

        throw new GradleException("Could not read version.properties!")

    }
    increaseVersionCode()


}


def increaseVersionCode() {

    gradle.taskGraph.whenReady { taskGraph ->

        if (taskGraph.hasTask(assembleRelease)) {

            /* when run release task */

            def versionPropsFile = file('version.properties')



            if (versionPropsFile.canRead()) {

                def Properties versionProps = new Properties()



                versionProps.load(new FileInputStream(versionPropsFile))



                def build = versionProps['VERSION_BUILD'].toInteger() + 1

                def code = versionProps['VERSION_CODE'].toInteger() + 1



                versionProps['VERSION_BUILD'] = build.toString()

                versionProps['VERSION_CODE'] = code.toString()

                versionProps.store(versionPropsFile.newWriter(), null)

            } else {

                throw new GradleException("Could not read version.properties!")

            }

        }

    }

}

task doIncrementVersionCode  {
    doLast {
        println("Incrementing the Version Code")

        increaseVersionCode()
    }


}



dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

    implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') {
        transitive = true;
    }

}

version.properties file

VERSION_NAME=1.0.0
VERSION_BUILD=3
VERSION_CODE=11

More interesting resources for reading

https://about.gitlab.com/blog/2019/01/28/android-publishing-with-gitlab-and-fastlane/

https://docs.fastlane.tools/actions/upload_to_play_store/

Fabric is closing, so we have to move from Fabric Beta to a Firebase App Distribution, links below will help you to migrate.

https://medium.com/@ryanisnhp/firebase-app-distribution-and-fastlane-5303c17b4395

https://github.com/nuhkoca/kotlin-android-fastlane-firebase-app-distribution

https://proandroiddev.com/firebase-app-distribution-fastlane-docker-bitbucket-pipelines-telegram-and-all-that-jazz-2dcb770da7dd

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.