Setting up Jekyll, Gulp, and Automated Git Deployments
I have been using Jekyll for a while for my personal site and long-abandoned blog, but with the latest redesign I decided to play with a more automated workflow for dependency management, development, and deployments. With a little luck that means more frequent postings, since I don’t have to go through the hassle of manually building and deploying my content.
So far I’ve settled on using a mixture of Gulp, Bower, and Jekyll for building the site and Git for deployment to my server. It’s still a work in progress that is being tweaked, but it’s finally settling down to where I felt comfortable writing about it.
Installing Global Dependencies
For my current setup there are a few global dependencies that need to be installed: node, gulp, bower, compass, and jekyll. After installing node (following the instructions from their site), the following commands install bower and gulp.
I already had a ruby install through homebrew, so installing Jekyll and Compass
is just as easy as gem install jekyll
and gem install compass
.
Creating the Folder Structure
Once all of the global dependencies are installed, I created the initial folder
structure using Jekyll’s new command jekyll new personalsite
. This creates
a new folder called personalsite
with the basic folder structure
required by Jekyll. This is where I stopped before, using the normal jekyll build
and jekyll serve
commands to build and post my site.
For the new workflow, I made a few changes to the default folder structure. First
I removed the css
folder Jekyll made. Instead I created an assets
folder
with two sub folders: css
and fonts
. These folders are going to be generated
by gulp and bower, so I also added the whole assets folder to my .gitignore file.
That’s the only real change to the default folder structure.
Setting up Bower
For right now my only two bower requirements for my site are fontawesome and
normalize.scss. I added those dependencies by creating a bower.json
file at
the root of personalsite
with the following contents.
Other than some boilerplate, the main part is the list of dependencies which gives
the name and the version. These dependencies can be installed by running
bower install
at the root of the site. This will create a bower_components
folder which contains the files for each of the dependencies.
Setting up Node/NPM
To manage the Node dependencies, I created a package.json
file. This mostly has
the dependencies for gulp to manipulate css and integrate with browserSync
and compass.
This sets up everything so gulp can compile sass files, minify them, and place them
in the assets
folder so Jekyll can pick it up during the build. The dependencies
can be installed with npm install
from the root of the site folder.
Setting up Gulp
The most complicated part of this setup is the gulp.js
file. The full file for
this can be found here.
Below is a breakdown of the parts.
The first part imports the requirements we installed with our package.json
file.
The only one not from the requirements file is child_process
which is used later
for running the Jekyll build commands.
This sets up some paths used in the later tasks, plus a message to indicate
when a Jekyll build is running (for browserSync). The outputDirectory
matches
the build directory for Jekyll. It’s used so instead of having to trigger a
build every time a file changes (like a css file), I can directly place the css
into the output directory and stream the changes through browserSync.
The bower task configures the gulp-bower plugin by passing it the config.bowerDir.
This is the gulp task for moving the fontawesome font files from the bower directory to both the asset directory (so jekyll will pick it up during a build) and to the output directory’s assets folder. I don’t really use the second part, but it matched what I was doing with the css task so I left it in.
The css task compiles the main scss file from which I import all other files, adds the paths for the bower scss files, minifies it, outputs it to the build assets directory and the final assets directory, then triggers a browserSync stream to cause a reload.
These two tasks handle the integration between gulp and Jekyll. The jekyll-build
command has dependencies on the css
, icons
, and bower
tasks so those are
completed beforehand. The first part sends a notification that there is a build
occurring to browserSync. The second runs the jekyll build
command.
jekyll-rebuild
is the actual rebuild command for development, it depends on
jekyll-build
and runs a browserSync reload when the build is complete.
The rest of the file defines the entry points into the system. The build
task
runs the other tasks to create an initial build. This is the task I use during the
deployment step on my server.
The serve
task is what I use during development. In addition to running an initial
build, it starts browserSync against the output directory and sets up watch commands
on the files so browserSync can keep the browser up to date with any changes. Changes
to the scss files triggers the css
task which streams changes to browserSync. Changes
to html files or post files trigger a full jekyll-rebuild
, which will run a new
build and reload the browser.
Configure Jekyll
Because of how Jekyll processes files, the gulp, bower, and npm files will end
up being sent to the output directory _site
. To avoid this, I added the following
to my _config.yml
file
Using for Development and Writing
Once all that is setup, developing or writing for my site can be handled by running
gulp serve
. This compiles the scss files, builds the jekyll site,
and loads the output in a browser window with browserSync.
If it is a fresh checkout, the gulp serve
command needs to be preceded by
Adding the Site to Git
For my automated deployment, I went with a git work flow. The first thing I did
was put the site code under revision control by running git init
from the root
of the personalsite
folder.
Before adding and committing the first set of files, I made sure my .gitignore
file ignores everything that is either auto-generated or doesn’t need to be added
for the build processes.
Then I commited the files with a normal git workflow.
Configuring the Server
So far all this is local to my workstation. On my remote server I initiated a bare git repository to act as the “deploy” base.
To support automated building and deploying, I also installed all of the global dependencies I installed on the local machine.
Finally I added a post-receive git hook by creating the file ~/personalsite.git/hooks/post-receive
and setting it as executable chmod +x ~/personalsite.git/hooks/post-receive
This
file will be executed every time there is a push to this repository. In my case,
I use this script to create a temporary checkout, build the site, and then move it
to the directory being served by my web server.
Configuring Git Deployments
Back on my local machine, I added the repo on my server as a remote named deploy
.
I’m using ssh to add the remote, so there is no need for any additional server
configuration beyond normal remote access (and installing git)
With that done, the only thing required to deploy my site is to push my changes
to the remote server with git push deploy master