Ceiba Web

Creating a Statamic Bootstrapper

Mario Vega

20 June 2018

As a developer cut from the cloth of WordPress customizations and incremental improvements, I've had a tendency to stay away from the DevOps side of development, seeing it as something "other", or separate from the process of making websites.

My first motivation for learning to code was changing the appearance of my own websites. I wanted to write code so I could see the results for myself, see the validation that what I was learning could make a direct visual impact.

This is why my first web development goal was to learn CSS. DevOps seemed, and seems even still, the exact opposite of CSS. The best DevOps is the DevOps that is invisible — to the end user but also to the developer, who in "ideal" circumstances would develop functions without consideration of form or context.

Learning programming for me has always been about finding the balance between learning and building. My primary motivation in learning is to build, but building too quickly without learning often leads to lots of rebuilding.

This being said, I learned quickly that some automation was instrumental to a speedier and more secure development process. I only needed to drag-and-drop my application files into my production server a handful of times to realize that risking my website's stability on my ability to drag-and-drop correctly was not a smart move.

Now, I'm still substantially far away from what you would call a DevOps expert. But after a particularly creative development period had me setting up several Statamic websites within the course of a few weeks, I realized that there were certain addons, theme setups, and JavaScript libraries that I was pulling into every new project by hand.

One of my earliest DevOps lessons was that it's better to type things into existence then it is to drag-and-drop them. Using your computer's terminal with regularity is, without a doubt, one of the most intimidating aspects of learning development. Instead of a friendly, intuitive graphical interface to navigate, you're presented with a blank screen

In that spirit, I set out to replace my drag-and-drop bootstrap experience with a CLI command that would install and configure my ideal Statamic bootstrap with minimal input on my part.

The Goal

The goal is to reduce the time commitment for creating a new website to be as close to instant as possible. The easier it is to start new projects, the quicker the turnaround you can have and the sooner you can provide value to your clients and partners.

The link to the Github project is here.

The Challenges

Without a way (yet) to install and configure Statamic themes from the command line, we'll need to find a way to automate the theme configuration ourselves.

The Outline

We'll create a template website alongside a template theme that's preloaded with our default JavaScript setup. From there, we'll write a Statamic command within our template site that will automatically reconfigure our default template's information with the information specific to our website. Finally, we'll write a Bash command that creates a new Statamic website before running our Statamic command with the information we provide it.

The Process

Before we can build an automated bootstrap, we'll need to have something we want to bootstrap first.

We'll start with the most mission-critical part first: picking a name. In this case, I decided to name my Statamic bootstrapper Jukung, after a type of traditional Indonesian fishing boat. Just like its physical counterpart, our Jukung will allow us to move quickly and with very little overhead.

With our name in place, let's get to work!

After installing the Statamic Command Line Interface (CLI), making a new Statamic site is as easy as typing a single command in our terminal. Setting it up to your own preferences is another story — but that's what this tutorial is for 😄.

Building the Bootstrapping Script

Before we get started writing code, let's define exactly what we want our bootstrapper to do. It's helpful to think of programming challenges in terms of inputs and outputs. What should our solution accept as inputs, and what should it produce as output?

Inputs:

  • The template website, with our JavaScript configuration included
  • The name of the new website we're looking to bootstrap

Outputs:

  • The bootstrapped website, with all generic template names replaced with the name of our new website

So, based on this description, we first need to define exactly what needs to be changed within our template site to customize it for our new website.

Because the name of our template is Jukung, which is not a common word, we'll use the word "jukung" in our template website in every place where the name of our new website should be. Then, when the bootstrapper is run, we'll go through each file and folder that we need to change, find the word "jukung", and replace it with our new website name.

To stay within the Statamic ecosystem as much as possible, we'll use a Statamic command to do the search-and-replace. Essentially, this is a command that you can run within your terminal to accomplish tasks with full access to the Statamic API. This will help make it easier for us to find the files that we need to change.

We'll go over the code line by line, but first, the whole thing:

Still here? Glad to see it! The code's long, but most of it is comments, and by the end of this you'll be bored by how simple it appears.

The first thing to notice is the $signature at the top. This is the text we'll use in the command line to call our generator. The {name} is where we'd insert the name of the site we're going to generate.

If we were creating a theme for plumbers and feeling particularly creative, for example, we'd perhaps name our website plumbing. In that case, we'd run php please jukung:generate plumbing to convert our template into a plumbing-ready boilerplate.

The $description property is self-explanatory - it describes functionality of the command we're creating. This is useful in case someone is accessing our website from the terminal and wants to know what our command does at a glance.

You can ignore the __construct() method for now. It comes included with any command you create using Statamic, and it helps set some things up under the hood. It would also come in handy if we wanted to pull in some external libraries, but we don't want to do that here, so the method mostly just sits there and looks pretty.

The handle() method is where all the action happens:

Let's go through this slowly.

On the method's first line, we're defining our new name: plumbing, to continue our previous example. The $this→argument() method is used to get the name we're entering from the command line, and we're passing in the name argument to match the {name} we used in the signature.

On the next line, we're using a Statamic-specific method to get the name of the current theme from the site we're about to bootstrap. Here, it would have been possible to simply write jukung because that's the name of the theme we created, but fetching that value from the config allows us to keep our code the same if we were to change the template name to something besides jukung.

On the third line, we're using another Statamic-specific function to get the path to our template theme. Because this definition is using the $current_theme variable we declare the line before, this code can also stay the same should we decide to change our theme later on. Robustness! Yay!

These next few lines are where the bootstrapping. In total, there are five places where we need to change the name from jukung to our new name:

  1. The theme's primary CSS (or SCSS) file
  2. The theme's primary JS file
  3. The webpack.config.js that powers Laravel Mix
  4. The theming.yaml file that Statamic uses to define the current theme
  5. The theme directory itself (located within site/themes)

The order in which we do these things doesn't matter too much, with one notable exception: we've got to change the theme directory after we've already changed all of the files within the theme. Because we're using the theme name to determine our $theme_path, if we change the theme name, we'll no longer be able to find our theme files because our $theme_path will no longer match an existing path.

Our changeCss() and changeJs() methods are nearly identical:

In both cases, we're using PHP's native rename() function to rename our theme's SCSS and JS files from jukung.scss and jukung.js to plumbing.scss and plumbing.js.

Could we have called rename() directly within our handle() method and avoided the need to define two new functions? Absolutely. But as part of my love for declarative programming, I like to give any unique functionality its own function. That way, if I ever need to extend or change how one part of my application works, I know just where to go to accomplish it. There's no performance overhead for declaring new methods or functions — my recommendation is to do so whenever it's convenient, which is always often.

Next, we use one function to alter two files: our webpack.mix.js and our theming.yaml files. That function looks like this:

Here we're using more native PHP functions. In the first line, we grab the text from the file, comb through it, and replace any instances of $old_name with $new_name. In the second, we replace the old file with our updated text.

The last method, changeThemeDir(), is very similar to ones we've defined before, so we won't retread old ground here.

And with that, our bootstrapper is complete!

Finishing Touches

Finally, we've got to bring the whole thing together. We need something that will create a new project from our template before running the bootstap And here, I'll admit I hacked together a solution that I'm not particularly happy with.

My first inclination was to use PHP's package manager, Composer, which provides all sorts of functionality for automating tasks in response to events. However, because of some intricacies regarding Statamic and its relation to Laravel (soon to be rectified) this wasn't as ideal an option as I had hoped.

Besides, when I did more research under the hood, I realized that Composer's create-project command just calls on Git and installs composer dependencies. That sounded like something I could replicate!

What I ultimately settled on was an old-fashioned Bash command, tucked away in my .bash_profile. Here's what I came up with:

Basically, this does 3 things:

  1. Clones the Jukung repository into a directory with the name of your website.
  2. Moves into that directory.
  3. Calls the bootstrapper from within that directory.

With this, all you need to do is navigate to your project directory, call jukung {your site name}, and you're off to the races.

If I were feeling particularly fancy, I'd have it move into the theme directory and npm install to preload my JavaScript environment. However, because I often end up adding other packages on top of the bootstrap, I left that process out of this command to keep things simple.

As I write this, I'm recognizing it would probably be a good idea to include a command to update Statamic as well, as there's no guarantee the version of Statamic I have in the repository is current (in all likelihood, it's not).

Feel free to adopt this template to your own needs! Again, the link to the Github project is here.