I knew when I decided that I wanted to turn my site into more of a platform for my writing that I wanted to keep everything static. I had been dealing with a dedicated server for years, and only really using it to host a bunch of static sites and a few utilities, and I had decided that I wanted to get rid of the server and instead use some cheap, reliable and low- or zero-maintenance static hosting instead.
I settled on using Forge for my hosting, as it was the most competitively priced for someone who, like me, wanted to host a few small sites. It is also a small and growing business, and I like to support things like that where I can. Forge suppors (semi) automatic builds from Hammer out of the box, but I'm more of a command line kinda guy now, and Hammer just doesn't really do it for me.
Instead, I looked around at (oh. so. many) static site generators and played around with a few and found that I really liked the way that Roots works. It isn't the fastest, it may not be the most fully featured or flexible, but these things are a matter of choice and Roots was what felt right for me.
The question then became “How do I manage deployments of my site?”
As a platform for my writing, this site is going to change a lot more regularly than my old 1-pager that only really got updated if I moved house. Sure, I could run builds manually on my local machine and then manually deploy them to Forge, but that seemed like a lot of effort, considering that I use Git to manage pretty much any code I write anyway.
There are a great number of continuous integration tools out there, but it seems that if you want to keep any of your code closed-source, you usually have to pay for them, or they have quite slim limits on the number of builds you can run in a given period. CI was definitely the way to go, but I didn't really fancy paying extra for it, and that seemed to be a bit of an issue. Then I happened across an announcement from GitLab, saying they were launching a free CI tool, integrated right into their hosted SCM offering.
Thankfully, Forge have their own CLI tool, which allows you to programatically push code to your Forge sites. All I had to do was get this working with GitLab's runners and we would be good to go!
GitLab CI and the gitlab-ci.yml file
For GitLab to start automatically running builds on your code, you have to include a
gitlab-ci.yml file in the root of your project. GitLab are much better placed to document this than I am, so I will just show you how my particular version is set up for this website:
image: node:4.2.2 cache: paths: - node_modules/ before_script: - apt-get update -qq && apt-get install -qq -y nodejs npm ruby-dev rubygems git - npm --loglevel=silent install roots 2>&1 >/dev/null - npm --loglevel=silent install 2>&1 >/dev/null - gem install bundler - git clone https://github.com/beachio/forge-cli.git - cd forge-cli - ruby install.rb - cd .. build_dev: only: - dev script: - node_modules/roots/bin/roots clean - node_modules/roots/bin/roots compile - cd public - ../forge-cli/bin/forge-cli login --email "firstname.lastname@example.org" --password "password" - ../forge-cli/bin/forge-cli add mydevsite.getforge.io - ../forge-cli/bin/forge-cli deploy - ../forge-cli/bin/forge-cli logout artifacts: paths: - public/ tags: - shared environment: development build_prod: only: - master script: - node_modules/roots/bin/roots clean - node_modules/roots/bin/roots compile -e production - cd public - ../forge-cli/bin/forge-cli login --email "email@example.com" --password "password" - ../forge-cli/bin/forge-cli add mycustomproductiondomain.com - ../forge-cli/bin/forge-cli deploy - ../forge-cli/bin/forge-cli logout artifacts: paths: - public/ tags: - shared environment: production
Note that I don't consider this “optimal” in any way... It was basically the simplest possible way to get what I wanted to achieve done, which was to enable automated development builds to a dev host set up on Forge, and production builds to my custom domain hosted on Forge when I merge into
It is also worth saying that I considered splitting this build process up into stages, but GitLab CI, when using their free shared runners, sometimes has a bit of a waiting time to get a runner available, and separate stages mean separate waiting times, so I bundled it all together instead.
Anyway, so the
image rule defines a docker machine for GitLab CI's runners to use when building this project, and then the
before_script stage runs and fetches everything we need to do a build – So it installs a few modules from APT, as well as roots itself, and then the npm dependencies from the actual website project. Once that is complete, it will run one of the other two stages, depending on whether I have merged into the
master branches of my git repo.
These two stages basically do the same thing, except that
build_prod triggers a
roots compile in "production" mode, and it then deploys to a different domain on Forge. You'll notice the
only: dev and
only: master rules in each of the two stages, which define which branches will trigger these build steps.
It is important here that the
forge-cli folder created when the
before_script stage clones the git repo is outsise of the
public folder – We don't want to try and deploy that to our site, now, do we?
Basically once you have this all set up, you can check in to your repository at GitLab and a few minutes later you will have a working site up on Forge. Obviously all of this is totally unofficial and unsupported, but it works so long as they don't massively change things. Given that Forge seems pretty set on enhancing developer support it seems unlikely that they will pull forge-cli, or make any breaking changes, without giving existing users the option to still get to the old version. I hope.
So that is, in a nutshell at least, how I get my Roots sites deploying to Forge using GitLab CI. I urge you to check out all those offerings, as I think they are all pretty awesome, and I've had great support from the developers of all of them in the process of trying to get this up and running.
This is one of those posts which might inspire people to ask questions where something isn't fully explained, or they don't get something. For this reason, I have opened comments on this post for the 2 or 3 people who might eventually happen across this post whilst looking for something entirely different and more interesting.
Please be nice.