Maximizing CI/CD in Tunaiku Deployment Process

Suyanwar Wawang
Tunaiku Tech
Published in
5 min readOct 30, 2020
Image from Unsplash

This story is actually the continuation of my previous article. If you have not read yet, I suggest you to read it first.

In the previous article, I explained about how we had faced the growing of resources using a new git branching scheme which was more suitable with tribe model management that had been implemented in Tunaiku. Another thing that I have not shared is about our app deployment processes to the production environment.

As we know, to update an app we have to upload the new AAB / APK to google play console which in Tunaiku, we have several steps before it is approved to be uploaded to google play console. There are:

  • Create tag for released version with a formatted tag name.
  • Pull request from master branch to each development-(squad).
  • Upload the AAB to google drive folder.
  • Send an mail which contains change log, link of APK, and AAB in the production environment.
  • Notify the product circle lead to upload the bundle.

Yep, those are so old and it takes some times to do all these tasks manually. Moreover, we are implementing release train method to schedule our deployment. Release train is a technique for coordinating releases across multiple teams or components that have runtime dependencies. All releases happen on a fixed and reliable schedule regardless of whether all expected features are ready (the train doesn’t wait for you — if you miss it you will have to wait for the next one). The schedule is every Thursday at 06.00 p.m. Therefore, at that time, I have to do all the tasks above.

What if all of these tasks are done by machine or we’ll call it automation?

Yes, it can. It will be done by Circle CI while we can do another thing that is more important than those tasks. I will explain it one by one.

  • Create Tag

Tags are refs that point to a specific point in the Git history. Tagging is generally used to capture a point in history that is used for a marked version release (i.e. v1.0.1). A tag is like a branch that doesn’t change. Unlike branches, tags, after being created, have no further history of commits.

Our formatted tag name is versionName_versionCode and we set tag message from Change Log file. Where can we get the version name and version code value? We can get the value by dump the AAB file. What is the content of the change log file? Its content is logs of features / tasks that are released in the current version. Here is the command. (The AAB file path is conditional based on build types and product flavors)

create_tag:
description: "Create Tag"
steps:
- run:
name: Setup User
command: git config user.email "xxx@gmail.com" && git config user.name "xxx"
- run:
name: Tagging
command: git tag $((java -jar bundletool.jar dump manifest --bundle app.aab --xpath /manifest/@android:versionName && echo '_' && java -jar bundletool.jar dump manifest --bundle app.aab --xpath /manifest/@android:versionCode) | tr -d '\n' | cat) -F ChangeLog.txt
- run:
name: Push Tag
command: git push origin $((java -jar bundletool.jar dump manifest --bundle app.aab --xpath /manifest/@android:versionName && echo '_' && java -jar bundletool.jar dump manifest --bundle app.aab --xpath /manifest/@android:versionCode) | tr -d '\n' | cat)
  • Pull request from master branch to each development-(squad)

Since we are implementing release train method and not all of the squads always attend the release train, after every squad releases their features, their features must be merged to the development branch of other squad that has not been released their features. It’s all about equality. Luckily, we are using bitbucket for our repository management which provides an API to create pull request using curl. However, we have to provide an account with username and password along with the curl command.

Pull request API that bitbucket provides is only for one time pull request. To simplify Circle CI command, we only call a bash file from config.yml

pull_request:
description: "Create pull request from release branch into each development"
steps:
- run:
name: Running Bash
command: bash pull_request.sh

And here is the pull_request.sh file:

declare -a squads=("growth" "experience")

for squad in "${squads[@]}"
do
curl https://api.bitbucket.org/2.0/repositories/{username}/{repository-name}/pullrequests \
-u {username}:{password} \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"title": "Release Pull Request",
"description": "Release pull request description",
"source": {
"branch": {
"name": "master"
}
},
"destination": {
"branch": {
"name": "development-'"$squad"'"
}
},
"reviewers": [
{
"uuid": "{uuid-reviewer-1}"
},
{
"uuid": "{uuid-reviewer-2}"
}
],
"close_source_branch": false
}'
done
  • Upload AAB to google drive folder and notify the product circle lead to upload the bundle.

This step is changed by directly uploading our AAB file to google play console using fastlane supply command. Supply command needs a generated API json key as an authentication for fastlane to upload to specific google play console account. Therefore, our product circle lead is no longer needed to upload it in google play console manually. If you want to know how to generate API json key, you can follow this tutorial. After generating the json file, place it in your root project (If you want to place it privately, you can upload it in your own server and download it using curl command and save it in the root project) then put the code below into the Appfile.

json_key_file("play_console.json")

After that, call the supply command.

deploy_to_play_console:
description: "Publish app into play console automatically with 30% roll out"
steps:
- run:
name: Deploy to Play Console
command: bundle exec fastlane supply --aab app.aab --rollout 0.3 --skip_upload_apk=true --skip_upload_metadata=true --skip_upload_changelogs=true --skip_upload_images=true --skip_upload_screenshots=true
  • Send an email which contains change log, link of APK, and AAB in the production environment.

Last step is sending the email. However, recently, this process was changed to uploading the change log file to slack channel since our daily communication for work is slack and there is a channel that is purposed to handle release train context. We are using fastlane slack upload plugin.

Same for the READ.ME of the repository. Say, we have to generate a slack token as an authentication of user to upload the file. After that, we add slack upload plugin to Pluginfile.

gem 'fastlane-plugin-slack_upload'

Then add slack upload command in Fastfile.

desc "Upload Change Log"
lane :sendChangeLog do |options|
slack_upload(
slack_api_token: "{Paste your token here}",
title: "Change Log #{options[:appversion]}",
channel: "#release-train",
file_path: "./ChangeLog.txt",
initial_comment: "Dear teams, Tunaiku Android app has been released just now. Checkout the appcenter on Tunaiku-LIVE to check the newest APK. And here is the Change Log v#{options[:appversion]}. Thanks!"
)
end

Finally, add this command to your config.yml.

upload_change_log:
description: "Upload change log file to slack"
steps:
- run:
name: Upload Change Log File
command: bundle exec fastlane sendChangeLog appversion:$((java -jar bundletool.jar dump manifest --bundle app.aab --xpath /manifest/@android:versionName) | cat)

And if you are curious how our change log format like is, here is our change log file content.

== Release Change Log Version 1.42.2 ==
created 2020-10-15

Squad-1:
- Feature 1
- Feature 2
- Feature 3

Squad-2:
- Feature 1
- Feature 2

This is the screenshot of our bot.

Our change log has been reported by bot

I think that’s all that I can share about maximizing CI/CD in Tunaiku Deployment Process. Feel free to comment and ask. Thank you!

References:
https://www.thoughtworks.com/radar/techniques/release-train

https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests#post

--

--