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://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