Automate the Boring Stuff

In the past months, the language I’ve used the most in the V-HCD is PowerShell. Considering the V-HCD is powered by Unreal Engine, which has support for Blueprint, C++ and Python; you might wonder why do I write PowerShell scripts. The answer is: to automate the boring stuff. And there’s a lot of it.

Context

To better understand what’s there to automate, you have to understand our workflow. It’s vaguely described in the Version control article, but I’ll try to explain it better here.

The VHCD is a template Unreal Engine project (vhcd.uproject), and is versioned as a Git superproject with the following stored in submodules

  • Marketplace products (>150)
  • Plugins (>25)
  • Custom assets (>10)
  • Dependencies (e.g. Virtual Driver)

When we develop a new experiment with the V-HCD, we fork the superproject for this experiment, and all developments specific to that experiment will be done in this fork. This fork is never intended to be merged; it will instead be frozen once the experiment starts, to always have a copy of the exact software used to run the experiment.

Making submodules

The first thing I automated was submodule creation for Marketplace products. As I mentioned, we have a lot of them, and doing that manually would just be too much work. This was very straightforward to automate, it’s really just a bunch of command executed in order. You just need to do it once per product, as it’s just a way to move it from Epic’s server to our own GitLab. So first you need to manually download and install the Marketplace product. Then, you invoke the script with the path of the submodule you want to create, e.g. ./make_submodule Content/MyAwesomeVehicle, and it does everything. The script is something like the following.

Add pre-configured .gitattribute and .gitignore to submodule
Git init, add *, commit
Git push // this automatically creates the repo on GitLab!
Back to the superproject: add submodule and commit

Build

Our workflow implies that each user needs to rebuild the source code to run the platform. This obviously isn’t ideal for non-developers, especially since the platform is itself a composition of multiple projects, each having its own build process (e.g. CMake). On top of that, each project needs to be built in a specific configuration to all work together, so automation is pretty much needed in such cases.

One very important aspect of this script (and some others), that differs from the previous one, is that those are user-oriented script, not developer-oriented script. For me, there’s a huge difference between the two. For user-oriented script, I have very strict rules about the script:

  • Should be run without having to manually spawn a shell (i.e. double click should suffice)
  • Should only write useful and understandable messages to the user (i.e. not to the developer)
  • Should only require user input when strictly necessary
  • Should be very clear about success and failure (e.g. color)

So with that in mind, we have a very basic PowerShell script that just builds everything in order. There’s a green message if it succeeds, and a red one if it fails. Here’s the detail of it.

Setting up Visual Studio (finding path)
For all dependencies
  cmake.exe ...
  deven.exe ...
Setting up Unreal (finding path)
build.bat ... -projectfiles // To regenerate solution
build.bat ...

Package

This script probably is the most straightforward of them all. It just uses the Unreal Automation Tool to start a Build Cook Run, with the (optional) added step of robocopying the packaged result to the deployment server that can then install the package on our nDisplay cluster

Keep your fork in sync!

One of the reason we have this “fork-template-project” workflow is that it allows super quick iteration cycle. If one experiment needs a new feature that was just added to the template project, it can just grab it. If an experiment added a feature that might be useful to the template, it can just as well grab it. This is very useful to us, and each experiment regularly merges updates from the template project (at least weekly).

However, “just grab it” is easier said than done. Especially with non-expert Git users. And especially when you have: Git LFS, forks, submodules, large repos and binary files. That’s a recipe for disaster. For the first months, we did that manually, and it was ugly. Especially for the end-user, which totally relied on me to do that, as the process wouldn’t work smoothly most of the time.

After a while, I realized that if I had a basic automated script for that “just grab it” process, I could then update it to handle each new weird error-case I encountered. Then the more we use the script, the more robust it gets, and the easier the “just grab it” process becomes.

It’s only been a few weeks since we started using this script, but it’s already showing its worth. Since it’s piped to the build script mentioned above, the user can run a single script and get a ready-to-use up-to-date version of the platform. All that with clear and simple messages written on the terminal, to give them better insights into what’s happening, and hopefully heal the scars of all the previous manual operations that they didn’t really understand.

And beyond

We also have many one-shot scripts, that mostly focus on making some aspect of our version control process easier, for example to clear space on the repository (since we have a 10GB limit), or to mass commit/push submodules.

But I’d really want to use scripting beyond the daily use of the platform. I want to use scripting for the experiment itself. I’ve already started doing that with our Passation system, however I think we can do even more.

I was really inspired by Dan Slimmon’s blog entry entitled Do-nothing scripting: the key to gradual automation. Basically, he says that for any process that can be reduced steps and a checklist, you can write a “do-nothing script”.

A do-nothing script is a script that encodes the instructions of a slog, encapsulating each step in a function. […] This script doesn’t actually do any of the steps of the procedure. That’s why it’s called a do-nothing script. It feeds the user a step at a time and waits for them to complete each step manually.

Not only is that a great way to implement checklists, it’s also a great way to partially automate them over time. I really think reproducible research could benefit from this, as it would bridge the gap between automated and manual tasks; both of which are heavily involved in experiments.

Written on July 15, 2021