I host my blog on Ghost.org, and I'm using a (slightly, but still) customized theme. Custom theme packages (i.e. a zip file) need to be uploaded to the Ghost's Admin using a browser.
The thing is, I'm using GitLab for most of my personal and professional source code management needs. The sources for the customized theme I'm using, which is Ghost's official Massively with rather light adjustments for my Blog, are no exception and are on my personal GitLab account.
I could switch that repository into my GitHub account and be done with it, but where's the fun in that?
Rather, let's see if we can use GitLab CI to build and deploy my theme!
Forking a theme
I'm using a "manual" fork of the Massively theme. By "manual" I mean that it was not created by clicking "fork" on GitHub. Instead I cloned that repository from GitHub onto my local machine, then added my repository for it that I created on GitLab as a second git remote, like so:
This setup allows me to use GitLab, while still being able to pull in upstream changes from the official repository.
After I'm done updating my theme I would run the build action that comes with the theme, which packages up everything into a zip file in the project's
That file needs to be uploaded manually to the Ghost admin interface.
That works, but it's getting a bit cumbersome and is no fun at all.
Automate ALL THE THINGS
I'm a heavy user of GitLab and GitLab CI for a while now, and am working with it on a daily basis. With the latest change to my custom blog theme I decided that I was done with manual theme uploading and wanted to automate the process.
I knew there was an official GitHub action, so I figured this wouldn't be too hard.
Well, it wasn't, but it was not that straight-forward either.
Checking out the GitHub action
Disclaimer: I haven't had the chance to look into GitHub actions much yet, so I've no idea what I'm talking about.
GitHub actions are quite different to what GitLab CI does. It's not necessarily about what you can do with it, but rather how things are done and implemented.
I had a look at the official GitHub action, and dove right into where the action (pun intended) happens:
I was able to navigate the sources and make sense of it. I think.
GitHub actions vs. GitLab CI
I believe the main difference is that GitLab CI is rather about configuring what needs to happen in what stage, and what stages make up your pipeline, while GitHub actions go beyond that in a way and also provide infrastructure to implement the code that makes things happen, and distribute it.
When porting the deploy action to GitLab, I had to find a place for the code that makes the deployment happen first. A GitLab CI file (usually) does not contain much code other than commands to execute during a given CI step.
There's already npm/yarn/gulp scripts for developing, building and packaging the theme. The theme uses gulp and includes tasks for building and packaging already. I'm going to leverage those and add a deploy task to gulp, add in the dependencies needed as dev dependencies using yarn, and add a script action to package.json for consistency with existing tasks.
Eventually I need to create a GitLab-CI configuration that calls all those tasks for building, packaging and deploying and be done with it. Easy!
Create a new Ghost custom integration
Same as with the GitHub action, you need to add a new custom integration to your Ghost site to get an API key. This key is later used by the CI task to authenticate to your blog for uploading your theme.
In Ghost Admin, navigate to
Integrations and create a new custom integration called "GitLab CI":
You are going to need the
Admin API Key and the
API URL in the next step.
Configure GitLab CI variables
Next, copy and paste your API key and the API URL into the GitLab repository's CI / CD environment variables. You find this under
Settings > CI / CD / Variables.
I named the variables
These variables will be available to CI tasks as environment variables later.
Adding this to my customized theme using yarn:
Implement the "deploy" task
I implemented the deploy action as a Gulp task, adding it to the theme's gulpfile.js. The code was adapted from the GitHub action:
Then I added "deploy" to the script section of package.json, so I can just call
yarn deploy from CI later:
Create a GitLab-CI configuration
GitLab-CI is configured by placing a file called
gitlab-ci.yml into the root of your repository.
I want my CI pipeline to test, build and package whenever there are any changes to my theme repository. And I want it to additionally deploy my theme automatically whenever there are changes to the master branch.
My pipeline is divided into four stages, which GitLab-CI will execute sequentially in order:
.pre is a built in stage that is always executed first. I use it for a job I called
install. This task runs
yarn install, which fetches all dependencies for building, packaging and deploying the theme. The result of this task is a
node_modules directory which is going to be cached for subsequent pipeline runs to speed things up a bit.
Additionally it is going to be stored with GitLab as an "artifact", which makes it automatically available for the subsequent build and deploy stages.
See Cache vs Artifacts in GitLab's Docs.
tl;dr cache stores stuff for subsequent pipeline runs, while artifacts store files generated by a job and are available to later stages in the same pipeline.
This job just calls the
test:citask that came with the Massively theme.
The build job calls the
zip task that also came with the theme. This will build the theme and package it up as a zip file. That file is going to be saved with GitLab as an artifact, which will automatically be handed down to subsequent tasks.
Note that this task will run for all branches and commits. The package will be available for download from GitLab's UI if needed.
At last, the fun part of actually deploying the theme to my blog!
It calls that new
yarn deploytask I added earlier, which will take the theme package zip file created by the build job and upload it to the ghost blog.
I don't want all changes I make in any branch to be deployed to my production blog right away, so this job will only be executed for changes on the master branch.
Whenever I commit something to master or merge in a branch to master, GitLab-CI will automatically build and deploy the theme. Automation ftw! 🎉