This is the continuation of “how I built the Friends Roster app” saga. If you missed the previous parts in this series, do check them out — part 1part 2part 3part 4part 5part 6part 7part 8 and part 9. I know it must be getting pretty boring reading about the app for so long. But I promise this is the last one in the series.


So far I have an app that can run both on android and on the web all from one code base. I have a lot of tests to make sure I don't break anything. The code coverage is good too. Now how do I make sure I maintain the same quality in the future too? And how about hosting the web app somewhere other than localhost so I can access it from anywhere in the world? Lets find out.


Presubmit checks

After writing all the tests, I wanted to make sure I always run tests before checking in my code to git repo. Thankfully git has a nifty feature to solve this use case. Enter githooks. I wanted my presubmit code to be also checked into git repo. So I created a new directory called .githooks in the root directory of my project. Then I created a pre-commit file under .githooks. The pre-commit code does the following before checking in my code

  1. Run flutter format to automatically format my code
  2. Run flutter analyze to check for any lint errors like unused imports
  3. Runs all my unit tests and widget tests and make sure they pass
  4. Run all integration tests
  5. Builds the app to make sure that release build does not fail


Here is the code in case you'd like to use something similar in your project.

#!/usr/bin/env bash

exit_with_code() {
  eval $1
  if [ $? -ne 0 ]; then
    printf "\e[31;1m%s\e[0m\n" "$3"
    git stash pop -q
    exit $2
  fi
}

# stash any unstaged changes
git stash -q --keep-index

exit_with_code 'flutter format --set-exit-if-changed .' 1 'Formatting on some files have changed'
exit_with_code 'flutter analyze' 2 'Found lint errors'
exit_with_code 'flutter test --test-randomize-ordering-seed=random' 3 'Unit/widget tests failed'
exit_with_code './run-integration-tests.sh' 4 'Integration tests failed'
exit_with_code 'flutter build web' 5 'Building web app failed'
exit_with_code 'flutter build apk' 6 'Building android app failed'

git stash pop -q
exit 0


The pre-commit takes about 4 minutes to run on my machine, but that is certainly a reasonable trade-off for a healthy code base. One thing to note is that .git/config has to be modified to pickup the location of .githhooks.


Hosting

I install the android app on my phone via adb. So I decided not to launch the app in play store. It is a pain to release and maintain it there. For the web, I decided to use Firebase hosting. Since I was using a Firebase project to store that call history, I used the same project to do the hosting. Firebase hosting is quite simple to use and I have my app running on the web so I can access it from anywhere.

#!/usr/bin/env bash
flutter build web
mkdir -p public
rm -rf public/*
cp -r build/web public/
firebase deploy


Continuous Integration and Continuous Deployment

So you might have heard about CI/CD if you worked as a software engineer for any length of time. Basically anytime I checkin my code to git, CI/CD will merge my changes, run all the tests and deploy the latest code. I chose GoCD for this purpose. It is a free open source server that I can run locally. The setup takes some effort but it isn't too bad.


Four stages of CI/CD


I've setup 4 stages. The first stage runs tests, second stage checks code coverage, third stage build the release version of the app and fourth stage deploys the app automatically to Firebase hosting. So every time I check in new code, it is ready to be tried out in production. Works fine for a one person team like me :).


The 4 stage pipeline runs every time I check in new code


Conclusion

That brings us to the conclusion of this series. Thank you for joining me in this journey. It is long but I hoping these posts will help me in future when I start a new project. Some other good resources can be found in the following video. Now on to my next fun project...