Make the Jump from Jekyll to JavaScript
By Steve Stedman
With the recent addition of Actions to GitHub’s product lineup, we now have the ability to create a 100% Pure, Unfiltered™ JavaScript alternative to Jekyll (and Ruby) in GitHub Pages. This is a big deal. Really.
Back story #
GitHub Pages, for the uninitiated, are public web pages automatically generated from your GitHub source files and freely hosted on github.io or your custom domain. There are no servers to provision and no databases to set up.
The automatic generation part is handled by Jekyll, a pretty sweet static site generator. It’s been around quite awhile, is battle tested, and is backed by a huge community. It’s also simple enough for beginners and yet powerful enough for advanced users. Unfortunately, customizing it requires running it locally and this is where things get sticky. Jekyll is based on Ruby. For casual users, Ruby is a challenge to set up and maintain—especially on Windows. Scaling is also an issue for Jekyll/Ruby as bigger builds are notorious for taking a long time to compile.
While there are plenty of competitive static site generators out there, the one piece that was missing was the integrated of build and deploy to GitHub’s web server. Pages+Jekyll took care of that build and deploy mumbo-jumbo for you, automagically. Just commit to the gh-pages
branch and poof, your site is updated.
More recently, workarounds from Netlify and others have popped up. They handle build/deploy and offer additional features worth considering, but at the end of the day they are yet another dependency to maintain.
If only we could run a static site generator on Pages with all the hooks and privileges of Jekyll but with the ease-of-setup and performance provided by more modern alternatives.
Well, now you can. All the pieces have finally fallen into place.
UPDATE: If you’re working with GitHub user or organization sites, you can only publish from the
master
branch. This effectively prevents alternative GitHub Pages deployments such as the one described below for those types of sites.What are user or organization sites? Those are the sites that publish from repos named
<user>.github.io
(or<organization>.github.io
) and have URLs that look likehttps://<user>.github.io/
.All other repos are considered project sites. The deploy steps defined in this tutorial will work with those sites since they can publish from the
gh-pages
branch.
The build #
This brief tutorial will cover the essentials of migrating from Jekyll to JavaScript. The following are required to get started:
- GitHub account
- NodeJS set up locally
Of course, it would also make sense to already have an existing Jekyll repo. If not, then create a new GitHub repo (public or private) with a README initialized. Follow the follow-up instructions to clone and set it up locally.
Set up the static site generator #
It took awhile for someone to distill the essence of Jekyll into JavaScript. With Eleventy, Zach Leatherman (@zachleat) has really distilled, bottled, and shipped it. Oh, and did I mention that it’s fast? I’ll skip the rest of the sales pitch here and let the experts do the talking.
- Dave Rupert: What I Like About Eleventy
- iandroid: Why I’m Digging Eleventy
- Paul Lloyd: Turn Jekyll Up to Eleventy
If you haven’t already guessed, I’m smitten with Eleventy. It’s that multi-purpose knife that fits just perfectly in your pocket and makes you smile every time you use it.
It also makes the transition from Jekyll to JavaScript almost completely seamless. So let’s roll with it.
Install and configure
-
Open a terminal and navigate to your repo.
-
If you don’t already have a
package.json
file in the root directory, then take this moment to initialize npm.npm init
-
Install Eleventy as a
devDependency
.npm install --save-dev @11ty/eleventy
-
To the site root, add an empty
.nojekyll
file to disable the default Jekyll build.touch .nojekyll
-
Keeping things simple, we’ll assume a default Jekyll file structure.
..
├── _layouts
│ ├── default.html
├── assets
│ ├── css
│ ├── images
│ └── js
├── index.html
..To the site root, add a
.eleventy.js
config file with the following contents. For more details, see the Eleventy configuration docs.module.exports = (eleventyConfig) => {
// Copy the "assets" directory to the compiled "_site" folder.
eleventyConfig.addPassthroughCopy('assets');
return {
dir: {
input: './',
output: './_site',
layouts: './_layouts',
},
templateFormats: [
'html',
'liquid',
'md',
'njk',
],
pathPrefix: '/<repo_name>/', // omit this line if using custom domain
};
};Looking at the
dir.output
above, notice that we are maintaining parity with Jekyll’s deployment, using the./_site
directory for our compiled code.Also note that, unless you’re using a custom domain, you’ll need to add a path prefix (
/<repo_name>/
) to the config options. This is because project sites are hosted on subdirectory URLs that look likehttps://<user>.github.io/<repo_name>/
. -
To the
scripts
section ofpackage.json
, add the following. We’ll need thebuild
script for our deployment later."scripts": {
"clean": "rm -rf ./_site",
"build": "npm run clean && eleventy",
"start": "eleventy --serve --watch"
},- clean: empties out the deployment directory
- build: cleans the deploy directory and builds the site with Eleventy
- start: runs Eleventy in developer mode with live browser refreshes
The deployment #
Now for the real magic sauce, GitHub Actions-style.
Set up a workflow #
-
Open a browser to your GitHub repo and then select the Actions tab.
-
Tap on the New workflow button.
-
Tap on the Set up a workflow yourself button.
-
In the {repo_name}/.github/workflows/
main.yml
field, enter “eleventy_build.yml
”. -
Into the Edit new file textarea, cut and paste the following:
name: Eleventy Build
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
push:
branches: [ master ]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Setup Node.js environment
uses: actions/setup-node@v1.4.1
- name: Install packages
run: npm ci
- name: Run npm build
run: npm run build
- name: Deploy to gh-pages
uses: peaceiris/actions-gh-pages@v3
with:
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
publish_dir: ./_site -
Tap the Start commit button.
-
Complete the Commit new file form and select Commit directly to the
master
branch.
The workflow script should be fairly self-descriptive. See that ${{ secrets.ACTIONS_DEPLOY_KEY }}
line near the end of the workflow? We need to add deploy keys to GitHub before attempting a deployment.
Set up deployment keys #
The following instructions were cribbed from a third-party GitHub Actions for GitHub Pages page. Please check that site for updates before proceeding as these instructions may have changed.
-
Generate a SSH key locally to use as your deploy key.
ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N ""
# You will get 2 files:
# gh-pages.pub (public key)
# gh-pages (private key) -
Open a browser to your repo and select the Settings tab.
-
Select Deploy keys from the left menu.
- Tap the Add deploy key button.
- In the Title field, enter “
Public key of ACTIONS_DEPLOY_KEY
”. - In the Key field, cut and paste the contents of your
gh-pages.pub
SSH key file made in step 1 above. - Check the Allow write access box.
- Tap the Add key button.
-
Select Secrets from the left menu.
- Tap the Add a new secret link.
- In the Name field, enter “
ACTIONS_DEPLOY_KEY
”. - In the Value field, cut and paste the contents of your
gh-pages
SSH key file from step 1 above. - Tap the Add secret button.
IMPORTANT: Delete the local
gh-pages
keys at this point. You certainly do not want to add them to your version control (for all to see). -
To initiate a deployment, commit/merge to the
master
branch. The first deploy will fail (because thegh-pages
branch has not been created yet). -
Go back to the Settings tab.
- Scroll down to the GitHub Pages section.
- From the Source options, select
gh-pages branch
.
-
Merge some more code into
master
to initiate another deploy.
Congrats. You’re done! From here on out, you should be able to trigger a fresh build and deploy by:
- working/saving locally, committing, and pushing to
origin master
. - editing your code at github.com and saving to
master
.
Your updates will deploy to live in mere seconds. All of this takes place within the ecosystem that you version your code in. There are no additional dependencies, no additional accounts, nada. How cool is that?
The custom domain #
GitHub provides project sites (e.g., https://<user>.github.io/<repo_name>/
) that may suit you just fine. If, however, you want to use a custom domain (e.g., https://example.com/
), there’s a bit more work involved.
Set up a custom domain #
-
If you haven’t already set up an apex domain (e.g.,
example.com
) for this repo, then you will need to add it. Follow the GitHub instructions for managing a custom domain. -
The
CNAME
file created when you entered your domain in the Custom domain field (above) needs to be included in our distribution directory.-
Open the
gh-pages
branch and copy theCNAME
file. -
Go back to the
master
branch and add thatCNAME
file to the source directory (in this case, the root directory). -
Open the
.eleventy.js
config file and add the CNAME file to the list of pass-through copies...
eleventyConfig.addPassthroughCopy('CNAME');
.. -
Save, commit, and push to origin.
-
Conclusion #
For 12 years, GitHub teased us with a web hosting product that almost did everything we needed. We were grateful and we made due. Along the way, we came up with hacks and workarounds, employing one or more outside services to complete the task. But nothing quite combined source code versioning, flexible site generation, and web hosting in one elegant package.
With Eleventy and GitHub Actions, we now have it all.