GitHub Pages basically runs your project's
gh-pages branch through
jekyll build --safe. "Unfortunately", the
--safe flag prevents from loading any plugins, when many people would want to use some not supported by GitHub Pages. There is no way to bypass this flag, but there is a workaround.
The idea is quite straightforward:
- do not rely on GitHub Pages to generate your content
- run the
jekyllcommand locally (allowing for using any plugins)
- generate a clean, local
gh-pagesbranch ready for deployment
- deploy by pushing to GitHub's remote
This workflow is well known. Now, the challenging point might be #2: generating the cleanest
gh-pages branch possible. It's by no means mandatory, but a nice optimization to perform when generating the website locally. Kind of a challenge if you will!
Say you have the following brand-new Jekyll project:
$ jekyll new mywebsite New jekyll site installed in /home/user/mywebsite/ $ cd mywebsite $ ls about.md _config.yml css feed.xml _includes index.html _layouts _posts _sass
jekyll build will generate your static content into a
_site/ folder (default configuration).
Once this project is tracked with git, one could simply create a
gh-pages branched off of
master. It could be nice though to have the
gh-pages branch contain only the latest version of the website as available in
_site/, without any git history or non-production content.
The goal is to have
# While on gh-pages: $ tree . . ├── about │ └── index.html ├── css │ └── main.css ├── feed.xml ├── index.html └── jekyll └── update └── 2015 └── 10 └── 18 └── welcome-to-jekyll.html 7 directories, 5 files
And it shall not have any remembrance of
# While on gh-pages: $ git log --pretty=oneline a05b0365bf2e91e852a0440a230957a975ed7275 New release
Such a branch would be perfect for deployment and could be considered a one-time branch, ie. one would delete it once the website has been deployed.
Note that this workflow is viable for any hosting platform, not GitHub Pages only. As long as you are able to generate
_site/and deploy it somewhere for a webserver to, well, serve, you're pretty much done. Deployment can use git to push to a remote repository, as is the case with GitHub Pages, but may also leverage (s)ftp/scp/rsync/capistrano/whatever. The core idea is always: compile the site locally then deploy. Even if you may not use git to deploy, tracking the project's history with git locally is a good idea.
GitHub Pages’ documentation once advised folks to create a root
gh-pages branch. Although not the case anymore, I still believe it's a great, elegant solution.
A root branch in our Jekyll context is kind of a "static",
master-based, short-lived branch which will exist within the same git repository, but here with a different content structure than
master's. Such a branch is thus unlike your traditional
git checkout -b generated branch: our goal is for it to not have a proper "history" tracking of what happened in the base
master branch, and to feature custom content which will not necessarily match the shape of the base
We can create such a strange beast leveraging git references and the
clean git commands. First, let's "duplicate"
master, its whole content:
# Make sure your are on master, with a clean working directory. # Generate latest _site/. $ jekyll build # Let's "switch" to a gh-pages branch by overriding HEAD's semantic. $ git symbolic-ref HEAD refs/heads/gh-pages # Let's make all content untracked by git while on gh-pages, so it may # be removed at will. $ rm .git/index
Then comes the important step: remove everything except
# Let's remove all untracked content, but the _site/ folder. $ git clean -fdx -e _site/
Be aware running
git cleanlike that is a dangerous operation. For one thing it will wipe out any content untracked by git. Prevent this from happening by either tracking or .gitignoring any file or directory. A more advanced workflow could selectively remove lines from .git/index and feed a whitelist of files to remove to
git clean. The simpler the better IMO though. Think "Spiderman" here.
Let's move all of the content at root level, otherwise Jekyll's
base_url would need to include "/_site/".
$ mv _site/* . $ rm -R _site/
You’re pretty much done with generating the "static"
gh-pages branch! At this stage, it contains only the generated website, has no proper history (yet), and is basically ready for deployment… So let's deploy:
$ touch .nojekyll # Deploying to GitHub Pages for the sake of the example. # You could deploy to any host, using any method, really. $ git add . $ git ci -m "New release" $ git push origin gh-pages # Cleaning up. $ git checkout master $ git branch -D gh-pages
Note an empty .nojekyll file has been added along the way, so as to instruct GitHub not to bother trying to assess whether Jekyll should run. For it should not.
That strategy, although efficient, is a lot of git gymnastic to remember about: it should be encapsulated within a proper deployment script. A raw bash script, capistrano, a Thor task… Below is an example of a
deploy.sh bash script I use to deploy this very website:
#!/bin/sh # Building release. jekyll build git symbolic-ref HEAD refs/heads/release rm .git/index git clean -fdx -e _site/ mv _site/* . rm -R _site/ # Deploying to DigitalOcean. git add . git ci -m "New release" rsync -avzhe ssh --delete --progress . 18.104.22.168:public_html/kaibun.net # Cleaning up. git checkout master git branch -D release
Works for me!