Automatic High Quality Releases

Recently, I invested some time into automating some of the work that goes into a Finatra release.

The work consists of updating:

  • The version in the XML fragment of the main README.markdown

  • The version in the pom.xml of the example app

  • Any API changes in the example app

  • The version in the template pom.xml of the app generator

  • The generated unit test of the app generator that demonstrates testing any new API features

  • Any API changes inside the app template of the app generator

Using sub, I was able to create a finatra command that automated all of the above based on a single template, which also happens to be the main unit test. This ensures that the README, the example app, and the app generator never fall out of sync with the frameworks API.

Last week we released 1.1.0, and the README was completely generated, as was the example app. Not to mention, all generated apps would also contain the latest templates and examples!

Let’s dive into how it all works:

The source of truth

I annotated our main unit test with special tokens, like so:

ExampleAppSpec.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class ExampleSpec extends SpecHelper {

  /* ###BEGIN_APP### */

  class ExampleApp extends Controller {

    /**
     * Basic Example
     *
     * curl http://localhost:7070/hello => "hello world"
     */
    get("/") { request =>
      render.plain("hello world").toFuture
    }

  }

  val app = new ExampleApp

  /* ###END_APP### */


  /* ###BEGIN_SPEC### */

  "GET /hello" should "respond with hello world" in {
    get("/")
    response.body should equal ("hello world")
  }

  /* ###END_SPEC### */
}

Using the special /* ### */ comments, the main app and its test can be extracted from the code of our test.

The app generator

Now that we have our “template”, we can build our app generator to use it. I customized base and ended up with: script/finatra/libexec/finatra-new

You can then run:

1
$ ./finatra new com.example.myapp

and it will generate myapp/ based on the tested example code from the test suite above.

The example app

The example app is just a generated app using the latest app generator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
# Usage: finatra update-example
# Summary: generates the example app from the template

set -e

source $_FINATRA_ROOT/lib/base.sh

tmpdir=$(mktemp -d /tmp/finatra_example.XXX)

$_FINATRA_ROOT/bin/finatra new com.twitter.finatra_example $tmpdir

cp -Rv $tmpdir/finatra_example/ $EXAMPLE_REPO

rm -rf $tmpdir

cd $EXAMPLE_REPO && mvn test

This also tests the app generator and the generated app!

Updating the README

Lastly, there’s a command for updating the README with the new example and version number.