Prelude

In the last few months I have been tasked with writing and maintaining an existing WordPress plugin. Normally this kind of thing is pretty straightforward. Development is development, and not always something to write about. So what gives? Well one of the problems I was tasked with solving was automating updates for this plugin, which was not in the public WordPress repository.

I thought I would kick off a two-part blog series. This post, the first part, will detail packaging up the plugin. The second part which I have yet to write will detail setting up automatic updates for it.

Things to keep in mind

So what goes into a plugin? Well if it has any sort of complexity at all it might rely on third party libraries and use compiled assets. This adds some extra steps to consider when releasing updates.

 - Compiled Assets

Here you have two options. You can either keep the compiled assets in version control, or you can build them manually before deployment.

On one hand, keeping the compiled assets in version control is simpler, but has some drawbacks like a) You need to remember to build and compile the assets whenever there is a change and b) automated updates to such large files whenever there is a small change really increases your VCS download times. On the other hand, you can leave the compiled assets out of version control. This has the main drawback of needing to build the dependencies after any contributor makes any change.

Personally, I don't like keeping these large compiled assets in version control (or remembering to update them) so I needed to find a way to automatically build everything.

 - Third party Libraries

This is another pain point. The easiest way is to keep all the third party dependencies in the project, so everyone using version control has all the dependencies they need. This can be tough because not only do you have hundreds or thousands of new files you need to keep track of, but if any of your dependencies have additional dependencies you need to keep those up to date as well. This is obviously not ideal.

Your second option is to take the libraries and their updates out-of-band and use a package manager to handle everything. This has the major advantage of not needing to keep track of all the dependencies, but requires everyone using the project to know how to use the package manager.

This project uses both NPM and Composer, so to automate the update process we will need to write a script that knows how to use them.

Lets Go

Because we are using GitLab, we can use their super handy CI system. It uses Docker, so we can customize our build process as much as we like.

Here is a slimmed down version of the .gitlab-ci.yaml file I use in this project. I will explain it in detail below.

image: ubuntu:16.04
before_script:
  - apt-get update
  - apt install -y composer npm zip
  - npm install -g grunt-cli npm
build:
  script:
    - composer install
    - cd src/ && npm install && cd ..
    - grunt build --gruntfile src/gruntfile.js
    - cd .. && zip -r myplugin.zip myplugin/ -x "myplugin/src/*" "myplugin/.git*" && cd myplugin/

 - Pre Build Process

This is stuff we need to setup on the docker system itself. Because this all needs to be done before we can build our app, we can add everything below to the before_script: section in our YAML file.

 -- Initial Docker Image

To start I decided to use the Ubuntu 16.04 image. No real reason why, its just an OS I am familiar with so I thought it would be a good place to start. To tell the CI process that we want to use that image, at the top of our YAML configuration file we can put this:

image: ubuntu:16.04

 -- Installing System Tools

Before we can really get too far ahead of ourself we need to update the OS software repository. Ubuntu uses APT, which is super easy to use.

apt update

Now we need to install the tools we need to pull in the third party libraries and compile our frontend assets. We can use this apt command to grab all of that:

apt install -y composer npm zip

Because the APT version of NPM is not always up to date we will want to double check we have the most recent version. We will also need the tool "Grunt," so we can use this command to grab the both of those:

npm install -g grunt-cli npm

- The Build Process

Everything here can be added to the build: section of your CI YAML file.

-- Installing Third Party Libraries

This process is fairly straightforward. Because we are using composer and NPM, we have a composer.lock and package.json file in our version control system. In the root of our project we can run:

composer install

And in the src/ directory (might not be the same in your project) we can run:

cd src/ && npm install && cd ../

It looks funny, but what it does is enter your src/ directory, run the install command, and go back to the parent directory.

-- Compiling Assets

Now that we have the system and application setup, we can compile the assets we need. This has numerous advantages, but if you are familiar with tools like webpack or grunt you know what I am talking about. For those unfamiliar, tools like these "pack" all of the assets you need for your application into a single small bundle. This reduces the number of network requests your clients need to make, and dramatically reduces the size of the final zip file.

Because we are using grunt, we just need to tell it where our gruntfile.js file is. For my specific project, it is in the src/ directory.

grunt build --gruntfile src/gruntfile.js

This command may be different in your project. I have a "build" command that does everything I need, you may need to write your own.

-- Compressing The Plugin

Now that you have everything compiled and third party stuff downloaded, we just need to put a bow on it and ship it.

We installed the zip software early on, so we can use that. We don't want to include any version control or files in our "src/" folder, so we will exclude those.

cd ../ && zip -r myplugin.zip myplugin/ -x "myplugin/src/*" "myplugin/.git*" && cd myplugin/

So Now What?

Well now that your plugin has been built you just need to host it somewhere. You could use tools like "s3cmd" to upload to Amazon S3, or maybe upload to a FTP or Dropbox folder.

How you get the finished product delivered is up to you, but stay tuned for the next blog post that details how to use GitLab pages to automatically host your plugin for you.