Teammates with CI and CD


Firstly, I’d say the Teammates assignment in this semester is one of the worst coding experiences I have ever experienced. It is a bad practice for learning Google App Engine or Java EE related stuff.  Why? Have a look at these:

wow, such looooooooooooooonnnng class name, much java, very enterprise…
110 map() calls in teammates.ui.controller.ActionFactory?! 

(There are about 100 calls in the original project, we’ve added another 10. We don’t have time to refactor these stuff so we just followed they have done before.)
and that’s what I feel…

Anyway, let’s forget about the code.  This is a quick note for what I did recently. I have managed to add continuous integration and continuous delivery configurations into Teammates with Travis CI. When a commit is submitted, Travis CI will pull and compile our code, test it and deploy it to Google App Engine.

Travis CI & CI tests

The original Teammates comes with a working Travis CI configuration. We have modified a little bit to make it works.

Here is the original configuration:

Don’t forget there are some separated unit tests and integration tests in the CI scripts and it is DIFFERENT from the local tests!!

In order to make it works for us, we need to make some changes:

  • Some original unit tests and integration tests cannot work correctly. I have fixed 600+ tests and made it works again. But there are still 29 left we couldn’t fix, which are mainly caused by frontend changes. Teammates’ tests use some hard-coded HTML files and compare the HTML generated/rendered from Teammates server. We have to write a script and crawl all the new web pages down, or manually do the job on our own, which is even more impossible. Later our tutors noticed us that we are not required to fix that as it is way too time-consuming. So we just simply disabled them as required.
  • Remove the Codecov in the Travis configuration (i.e. the bash <(curl -s It is not free for private repos, and we don’t need that according to our assignment spec. Most importantly, I’m not a crazy rich Asian and same as my friends in the group. So, we just removed it.
  • Some linting issues need to be fixed
Yay now it works…

Deployment & Continuous Delivery

On the local side, we just need to follow the instructions provided by our tutors and run appengineDeployAll Gradle task. It should work fine as we expected. But for the Travis CI side, it is a bit more complex than I thought. For some reasons, Travis CI’s GAE deployment function doesn’t work for Teammates. But we can do it by our custom script instead.

Here I assume the Travis CLI tool is installed and logged in. Follow this link for more info.

Firstly, we need to create a service account configuration from Google Cloud Console:

Download the JSON file and put in under the root of Teammate source directory

Then, pack the scripts and encrypt the archive. This requires a macOS or Linux environment as Travis CI’s CLI client cannot encrypt the file properly on Windows. According to Travis, if you are a victim of Windows, please consider upgrading to Windows 10 and use WSL. I guess msys2 or Cygwin environment should also work, but I haven’t tried it yet.

Open the terminal and do this:

~$ cd ~/wherever-your-teammate-source-is/
~$ tar Jcvf secrets.tar.xz src/main/resources/ src/main/webapp/WEB-INF/appengine-web.xml gae.json
~$ travis encrypt-file secrets.tar.xz
Yes I am a bit too lazy to switch back to Ubuntu, so I’m also a victim of Windows.

After the archive is encrypted, remember to add the original files into .gitignore, especially for the service account configuration. Otherwise some days later you may get bankrupted.

Then create a Bash script for deployment, like this:


# Decrypt config & key files
openssl aes-256-cbc -K $encrypted_*******_key -iv $encrypted_******_iv -in secrets.tar.xz.enc -out secrets.tar.xz -d
tar xvf ./secrets.tar.xz

# Clean up browser path
rm -rf firefox/

# Authorise GCloud SDK
gcloud auth activate-service-account [email protected] --key-file=gae.json --project=your-project-name

# Run Gradle deployment task
./gradlew appengineDeployAll

Now everything is done except for the Travis configuration. Just add a deploy section at the end of the configuration file:

  provider: script
  script: bash
    branch: master

Here we only deploy our app for master branch, not for other buggy branches.

Setting up Caddy in Ubuntu 18.04 for this blog

There’s a new web implementation called Caddy, written in Go, with many new features. But the documentation is relatively poor and outdated compared to Nginx and Apache. But Anyway, I have spent an hour or so, and I figured out how to set up migrate my blog to this lovely new server.

Currently on this blog, I’m using:

  • Ubuntu 18.04.1
  • MariaDB 10.3
  • PHP 7.2
  • Caddy 0.11
  • Let’s Encrypt certification service brought by Caddy


Step 1: Add MariaDB mirror and upgrade the system: 

apt-get install software-properties-common
apt-key adv --recv-keys --keyserver hkp:// 0xF1656F24C74CD1D8
add-apt-repository 'deb [arch=amd64,arm64,ppc64el] bionic main'
apt dist-upgrade

Step 2: Install MariaDB and PHP:

apt install mariadb-server php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip php-fcgi php-fpm php-mysql php-curl

When installing MariaDB in the last step, it will ask you to enter the root password to create a new root account. Remember to create a stronger one. For me, I prefer using uuidgen and generate a random one. Then note it down somewhere offline.

Step 3: Install Caddy binary, from here:

MariaDB Configuration 

Create a new database by:

CREATE DATABASE your_wp_db_name DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Then create a user and assign to this database (remember to change the placeholder):

CREATE USER [email protected] IDENTIFIED BY 'whatever_the_password_is';
GRANT ALL PRIVILEGES ON your_wp_db_name.* to [email protected] IDENTIFIED BY 'whatever_the_password_is';

Finally, commit the changes and exit:


Cuddy Configuration & WordPress Installation

Step 1: Create directories and set the correct privileges:

mkdir /var/www
mkdir /etc/ssl/caddy
mkdir /etc/caddy
chown -R root:www-data /etc/caddy
chown -R www-data /etc/ssl/caddy
chmod 0770 /etc/ssl/caddy

Step 2: Create the configuration file, i.e. /etc/caddy/Caddyfile

Here I assume:

  • I put my WordPress instance to /var/www/wordpress
  • GZip enabled
  • Using Email to apply the certificate
  • Using the newest rewrite rules for WordPress
  • HSTS header enabled
  • PHP FastCGI running with default domain socket, {
 root /var/www/wordpress
 tls [email protected]
 header / Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
 fastcgi / /run/php/php7.2-fpm.sock php
 rewrite {
        if {path} not_match ^\/wp-admin
        to {path} {path}/ /index.php?{query}

Step 3: Download WordPress

Download the tarball and extract to /var/www:

wget -qO- | tar zxv -C /var/www

Then set the permission:

chown -R www-data:www-data /var/www/wordpress
chmod -R 770 /var/www/wordpress

Step 4: Systemd configuration


curl -s -o /etc/systemd/system/caddy.service

Then edit the line started with “ExecStart” and add ” –quicin the end of this line to enable QUIC. But it seems like QUIC doesn’t work correctly as there is a known issue for the QUIC library. But anyway I enabled it in case they fix it in the later versions. Ref

After that, refresh systemd and start the server by:

systemctl daemon-reload
systemctl enable caddy
systemctl start caddy

It’s time for fun

Now just open the browser and fill in the database settings created a moment ago.

Some extra issues I’ve solved

502 Bad Gateway issue

Simply try figuring out where the domain socket is for the PHP FastCGI and correct the settings in the Caddyfile. 

Gutenberg Editor doesn’t work, saying “show_ui not found”

Some of the online tutorials are outdated,  which may lead the newest Gutenberg editor failed to initialize. Use the one in the official sample Caddyfile instead, i.e.

 rewrite {
        if {path} not_match ^\/wp-admin
        to {path} {path}/ /index.php?{query}

HSTS issue

Some online HSTS enforcement checking service saying my blog’s HSTS doesn’t work correctly. To fix this, correct the HSTS header configuration in the Caddyfile:

header / Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"