Simplifiable Builds: CI Properties

Jeff Gaston
3 min readFeb 9, 2024

--

When you encounter a reproducible build failure in CI, it can help to know what caused it: not just the full command being tested, but also which parts are necessary to reproduce the error.

To reproduce a build failure might require not just running a certain set of tasks, but also enabling some other special CI behaviors: perhaps your CI build disallows new warnings or runs tasks twice to check that they are all up-to-date the second time.

Because simplifying a build failure may require simplifying these CI behaviors, it helps to make the set of enabled CI behaviors be easy to simplify. It can help to have each behavior be controlled by its own flag (with each flag able to be specified in the same place) so you can easily binary search this set of behaviors. If instead there were several places in various parts of code saying if (properties[“ci”]), it would be more difficult to find each instance and change it.

It can also help to have the syntax that developers use to enable these flags be familiar to them. It’s possible to introduce a wrapper script that adds a special behavior but it tends to be easier for developers to interact with if a behavior can be added into the existing build tool (or standard wrapper script): instead of telling developers to switch from ./gradlew $arguments to ./build.sh $arguments, we can tell them to switch from ./gradlew $arguments to ./gradlew $newFlag $arguments. Since developers already interact with gradlew, they’re already familiar with it and confident that it’s not going to change in a significantly incompatible way.

Because the most common settings for these CI behaviors are either all disabled (for development) or all enabled (for CI), it helps to have concise ways to request those sets of properties too.

In AndroidX we have a --strict flag here that enables various checks, and a --ci flag here that enables --strict plus some other CI behaviors (like higher memory limits). These two flags also display the other flags that they expand into, to make it easy to find them.

As a result, a developer that observes an AndroidX CI failure in $sometask can just try ./gradlew $sometask --ci to enable the same CI checks without having to remember or type very much: just --ci.

Note that how you might choose to implement something like this might depend on how you need to use it and where you can implement it. Our UP-TO-DATEness check is difficult to implement directly within Gradle itself, so we added it to our copy of the standard Gradle wrapper script. As a result, we implemented our --ci flag there too. Our editor, Android Studio, doesn’t invoke gradlew and therefore doesn’t have access to the --ci flag that we added, but that’s ok because we haven’t needed the --ci flag in our editor yet.

--

--