Publishing my notes with Jekyll + git

Disclaimer

This post is translated from its original version in Spanish. As I'm not a native English speaker, I apologize in advance for any mistake I can make.

Maybe some of you are using the Github students pack offer and have rented a VPS at DigitalOcean (by using the cheapest configuration you can enjoy a simple VPS for free for almost two years), but after renting it, the next question is... And now WTF am I supposed to do with it?

I've been using Markdown when taking my class notes for so long, because I'm able to:

  1. Have my notes in an easily Internet-exportable format
  2. With an editor like atom I can see them rendered in real time (although I use Vim/Emacs)
  3. As they usually contain code snippets, I'm able to have syntax highlighting in lots of platforms

Until now, I had a copy of the .md files at my Dropbox (lately at my Owncloud), and time to time I had to export from .md to .html, and then upload them via FTP to the correct folder at the VPS... As you can see, it's a tedious work.

Be engineer. Be lazy

Graphic

As good engineers, we need to use those technology wonders, called computers, that were created in order to avoid us doing those repetitive tasks.

I had even created a bash script that automated the .md -> .html generation, but the uploading part was still missing. Also, it would have been great uploading only the files that had changed... Oh wait.

That sounds like a great task for git.

Git + Jekyll to the rescue

Once I had decided that the best choice for uploading files would be using git, the only thing missing was automating the .html generation from the .md files (that is, if we are going to be lazy, let's be veeery lazy).

I searched and tried some static code generation tools, but I finally decided using Jekyll, which is generally oriented to blogs, but one of the goods of its layout system is that, literally, you can tweak it in any way you want (ie: using categories for subjects).

After messing around a little with Jekyll, finding a decent theme, adapting it to my needs (and learn that you don't need to run jekyll serve permanently, only jekyll build) the only thing remaining was to create and configure the git repo.

Let's get started

Disclaimer

I am not an expert in git nor in Unix systems, so I'm just going to explain how I solved my problem, being conscious that of course there will be thousands of better solutions

I will suppose you have the git tools installed and configured.

After this little comment, here it comes the first suprise in this post: You won't be creating one but two repositories. How does that feel?

That's why: if we initialize a git repository with git init, if we are on MASTER branch and we make a push via git push origin master it will complain, because if we initialize it with git init, git will save an active working copy with the repository.
In other words, you will also have a physical copy of the files contained in the repository.

In order not to happen, you have two choices:

  1. Change branch before and after making a push (which of course is silly)
  2. Create the repository by git init --bare

If we create the repository marking it as bare, we won't have that physical copy of the files (that in our case would be necessary in order to work with them (aka convert them to html)), so it seems that this also does not solve our problem, right?. Let's see how this actually will save our lifes.

The part where commands appear

Now I'm going to explain step by step how I ended up configuring my git server

Creating the git user

By SSH-ing into the server, we will create a new user called git, assign it a password and create its directory at /home

sudo useradd git
sudo passwd git

sudo mkdir /home/git
sudo chown -R git /home/git

Adding SSH keys

We will need to add our SSH key to the file located at /home/git/.ssh/authorized_keys

If you don't know how to do it, I recommend you reading this tutorial at DigitalOcean that explains it perfectly.

Creating the main repository

Once we are able to SSH as the git user we will create a bare repository that, as we said earlier, will not have a physical copy of the files.
It's recommended not to place it in the same folder we will be showing our public content, so I've decided to place it at /home/git/apuntes.git.

All the following commands will be run as the git user

cd ~/
mkdir apuntes.git
cd apuntes.git/
git init --bare

After running these commands, we can see that the apuntes.git folder has all the files that in any other git repo we could have found inside the .git folder.

Accessing the repository from our computer

In order to check if we are doing okay, from our current computer let's try to clone the repository:

git clone git@[myserver.com]:/home/git/apuntes.git

In order to avoid complications, I've decided to write the full path.
As you can see, after the [myserver.com] (which you will have to replace), you append a : and add the full path to the repository.

If all works as it should, you should get a message saying that you have cloned an empty repository.
It you get it, congratulations!

Now, also in your current computer, try to add a local file (whatever you want. ie: hello.txt) and run:

git add .
git commit -m 'First commit'
git push origin master

And if you don't get any error, it means that all is working perfectly.

Enter Jekyll

After installing Jekyll's gem (I won't explain how to install a gem nor how to use Jekyll, as that's not the main point of this post) we will place our Jekyll site wherever we wont.

In my case, I decided to track only the _posts folder, as I'm pretty satisfied with the theme I've modified and I'm not changing anything of it, nor editing any of the configuration files.
So by now, we will access our posts folder (as an example I will use the path /var/apuntes/_posts).

Once we are placed in our _posts folder, we will run git init and add the main repository as a remote:

git remote add origin /home/git/apuntes.git
git pull origin master

If all is working well, we should see how in our _posts folder now we can find the file we had previously uploaded from our local computer.

Now we only need to create the content from our current computer, make a commit, push it to the git server, and any time we push something, ssh into our server, go to our _posts folder and run git pull origin master . And of course, generate the content with jekyll build.

This is something that, obviously, we are not going to allow. We want to be lazy!

The power of the hooks

Git provides a very powerful tool called hooks. Those hooks are scripts that will be executed whenever some conditions happen at the repository.

As in our case we will need to execute some actions every time the repository gets updated, we will use the post-receive hook.

In order to do it, we will go to the hooks directory placed at /home/git/apuntes.git/ and create a file called post-receive with the following content:

#!/bin/sh

unset GIT_DIR
cd /var/apuntes/_posts
git pull origin master && cd /var/apuntes && jekyll build

Once we've created it, we will need to give it execution permissions:

sudo chmod 755 /home/git/apuntes.git/hooks/post-receive

If we are lucky and everything is working correctly, whenever we make a change in our current computer and push it to the main repository, we will see how in the git push output it shows the static content generation made by Jekyll (useful for detecting errors).

The final step

Now you only need to configure the access to the web site.
In order to do it, you only need to link your website to /var/apuntes/_site/. And as we have delegated the static content generation to Jekyll, and automated the generation to every time it get's updated, the content served will always be the last available.

Of course, if you want to keep the content protected, you only need to add a Restriction Policy via an .htaccess, but again, that isn't the main point of this post.

I hope this merge of things that aren't usually related will be useful for someone.