Preface

Goal: Deploy SSG in Github Workflows.

The github.io is not just for Jekyll. But also can be used for other SSG as well, such as Hugo, Hexo, Pelican and Eleventy.

Official Site

Github Actions

Each github actions has their own repository. I won’t discuss Github Actions source code in details. Github Actions deserve its own long long loong article. So here it is, this article, is about how to use it. But not a tutorial on how to make one.

Table of Content

  • Preface: Table of Content

  • 1: Introducing Github Actions

  • 2: Configuration Overview

  • 3: Configuration: Hexo

  • 4: Configuration: Eleventy

  • 5: Configuration: Hugo

  • 6: Configuration: Jekyll

  • 7: Configuration: Pelican

  • What is Next ?


1: Introducing Github Workflows

In other CI/CD, it is also called runner.

About Workflows

In a nutshell Github Workflows is

  • Configuration

  • Reusable Script

Github Actions is a smart move from Github It has a lot of potential, because it is not just a runner configuration. But it can also be modularized using scripting language. Each modul part is called actions, that is also reusable. A complex workflows can utilize some actions, so that the workflows become simple.

If you cannot find any actions that suit your needs, you can always revert to Circle-CI like configuration, with low level pure git commands in bash, as I will show you at the end of this article.

Configuration

Just like any other CI/CD, Github Workflows relies on YAML configuration.

❯ tree .github
.github
└── workflows
    └── deploy.yml

You can name it anything, and you can have more than one .

Workflow

Just like any other CI/CD, Github also support web based view, to view runner, that you can access at, for example:

Github Workflows: Eleventy Workflows

Job

Each workflow (or runner) can contain jobs, for example a deploy job. A job contain a few step or run, as show below.

Github Workflows: Eleventy Deploy Job

Run

And finally each run is just a bunch of command, just like any other CI/CD.

Github Workflows: Eleventy Run Checkout

The difference is, instead of bash commands, a run can contain a github actions.


2: Configuration Overview

What is it inside the configuration ?

Configuration Skeleton

All configurations has this same skeleton

name: My Cool Deploy Name

on:
  push:
    branches:
      - master

jobs:
  my_cool_build_name:
    ...

Start a new Job

And job for each SSG is also similar. The only thing to do is changing the language.

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout to master
      uses: actions/checkout@v2
    - name: Set up NodeJS 12
      uses: actions/setup-node@v1
      with:
        node-version: 12
        registry-url: https://registry.npmjs.org

For example in Python you might change to this:

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
    - name: Checkout to master
      uses: actions/checkout@v2
    - name: Set up Python 3.5
      uses: actions/setup-python@v1
      with:
        python-version: 3.5

And in Jekyll, this might be something as below:

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
    - name: Checkout to master
      uses: actions/checkout@master
    - name: Set up Ruby 2.6
      uses: actions/setup-ruby@v1
      with:
        ruby-version: 2.6.x

You can find each actions in github marketplace.

Git Configuration

This is also a common requirement before using any git command.

    - name: Prepare Git Initialization
      run: |
        git config --global user.name "someone"
        git config --global user.email "someone@somewhere"

Example Configurations

I copy paste from other site, with a few adjustment.

I hope that is clear. Now consider have a look at the example configuration files. One by one, from the simplest one to complex low level command.

Most of the configuration I copy paste from other site, with a few adjustment. I respect copyright, so I put credits, by showing the original URL.

Practice

Now you can make repository, with any name, for example:

  1. Hexo:

  2. Eleventy:

  3. Hugo:

  4. Jekyll:

  5. Pelican:

This is just an example, you should use your username account. and you should use your own repository.


3: Configuration: Hexo

Author

I get this configuration originally from

Secrets

You have to setup DEPLOY_KEY in github’s secrets setting.

YAML Configuration

After a few adjustment, here is what I have got:

name: Hexo Deploy

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout to master
      uses: actions/checkout@v2
    - name: Set up NodeJS 12
      uses: actions/setup-node@v1
      with:
        node-version: 12
        registry-url: https://registry.npmjs.org/
    - name: Setup Secret Secure Shell
      run: |
        mkdir -p ~/.ssh/
        echo "${{secrets.DEPLOY_KEY}}" > ~/.ssh/id_rsa
        chmod 600 ~/.ssh/id_rsa
        ssh-keyscan github.com >> ~/.ssh/known_hosts
    - name: Prepare Git Initialization
      run: |
        git config --global user.name "someone"
        git config --global user.email "someone@somewhere"
    - name: Install and Configure Dependencies
      run: npm install
    - name: Generate Hexo Static Files
      run: npm i -g hexo-cli
    - name: Deploy Static Files to gh-pages Branch
      run: hexo deploy -g

The hardest part of YAML configuration for SSG is the deployment part. Hexo has simplified this in only one line command.

jobs:
  deploy:
    ...
    steps:
      ...
      run: hexo deploy -g

You should setup secure shell to use that oneline command above.

    - name: Setup Secret Secure Shell
      run: |
        mkdir -p ~/.ssh/
        echo "${{secrets.DEPLOY_KEY}}" > ~/.ssh/id_rsa
        chmod 600 ~/.ssh/id_rsa
        ssh-keyscan github.com >> ~/.ssh/known_hosts

Do not forget to to setup in Hexo’s _config.yml:

# Deployment
## Docs: https://hexo.io/docs/deployment.html
deploy:
  type: git
  repo: git@github.com:epsi-rns/actions-hexo.git
  branch: gh-pages

Preview

Please enjoy the render at


4: Configuration: Eleventy

Author

I adjust the previously made Hexo configuration, for use with Eleventy.

Secrets

You do not require to setup anything in github’s secrets setting. Just use the GITHUB_TOKEN.

YAML Configuration

After a few adjustment, here is what I have got:

name: Eleventy Deploy

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout to master
      uses: actions/checkout@v2
    - name: Set up NodeJS 12
      uses: actions/setup-node@v1
      with:
        node-version: 12
        registry-url: https://registry.npmjs.org/
    - name: Install and Configure Dependencies
      run: |
        npm install
        npm install -g @11ty/eleventy
    - name: Generate Eleventy Static Files
      run: |
        eleventy --pathprefix="/workflows-11ty/"
    - name: Deploy Static Files to gh-pages Branch
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./_site

The only different is, I replace the hexo deploy command, with action-gh-pages.

    - name: Deploy Static Files to gh-pages Branch
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./_site

I have got this idea of the deployment from:

No need to setup RSA key. This is very simple.

Preview

Please enjoy the render at


5: Configuration: Hugo

Author

I get this configuration originally from

Secrets

You do not require to setup anything in github’s secrets setting. Just use the GITHUB_TOKEN.

YAML Configuration

Almost copy paste from the link above:

name: Hugo Deploy

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
    - name: Checkout to master
      uses: actions/checkout@v2
    - name: Set up Hugo
      uses: peaceiris/actions-hugo@v2
      with:
        hugo-version: '0.62.2'
        # extended: true
    - name: Generate Hugo static Files
      run: |
        hugo
    - name: Deploy Static Files to gh-pages Branch
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./public

It works flawlessly.

Github Workflows: Hugo Run Checkout

I never know that, deploying Hugo using github actions could be this easy.

Preview

Please enjoy the render at


6: Configuration: Jekyll

Author

I get this configuration originally from mas Didik. The article is in local language.

He did it really great.

Secrets

You do not require to setup anything in github’s secrets setting. Just use the GITHUB_TOKEN.

YAML Configuration

After a few trial and error, I finally manage, to get this done.

name: Jekyll Deploy

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout to master
      uses: actions/checkout@master
    - name: Set up Ruby 2.6
      uses: actions/setup-ruby@v1
      with:
        ruby-version: 2.6.x
    - name: Install Gems and Configure Dependencies
      run: |
        gem install bundler
        bundle install --jobs 4 --retry 3
    - name: Deploy Static Files to gh-pages Branch
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        bundle exec rake site:publish

This is a little bit different. Just like Hexo configuration using internal commands, this Jekyll configuration is using oneliner rake commands, with GITHUB_TOKEN as a requirement.

    - name: Deploy Static Files to gh-pages Branch
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        bundle exec rake site:publish

Github Workflows: Jekyll Run Checkout

Rakefile

What is it this rake is all about? Ruby use a file named Rakefile. I copy-paste Didik’s Rakefile, and adjust a little. You can get Didik’s Rakefile from:

And I trace the source come from

After a few adjustment, my Rakefile looks like below code:

require "rubygems"
require "tmpdir"

require "bundler/setup"
require "jekyll"

# Change your GitHub reponame 
# eg. "kippt/jekyll-incorporated"
# or "id-ruby/id-ruby"
GITHUB_REPONAME = "epsi-rns/workflows-jekyll"
GITHUB_TOKEN    = ENV["GITHUB_TOKEN"]


namespace :site do
  desc "Generate blog files"
  task :generate do
    Jekyll::Site.new(Jekyll.configuration({
      "source"      => ".",
      "destination" => "_site"
    })).process
  end


  desc "Generate and publish blog to gh-pages"
  task :publish => [:generate] do
    current_directory = Dir.pwd

    Dir.mktmpdir do |tmp|
      cp_r "_site/.", tmp
      Dir.chdir tmp
      system "git init"
      system "git config user.name someone"
      system "git config user.email someone@somewhere"
      system "git add ."
      message = "Site updated at #{Time.now.utc}"
      system "git commit -m #{message.inspect}"
      system "git remote add origin #{git_origin}"
      system "git push origin master:refs/heads/gh-pages --force"
      Dir.chdir current_directory
    end
  end

  def git_origin
    if GITHUB_TOKEN
      "https://x-access-token:#{GITHUB_TOKEN}@github.com/#{GITHUB_REPONAME}.git"
    else
      "git@github.com:#{GITHUB_REPONAME}.git"
    end
  end
end

Wow… I have so much to learn from this Rakefile. You have done great job, mr. Didik!.

Preview

Please enjoy the render at


7: Configuration: Pelican

Author

I get this configuration originally from

But I failed miserably. It is just me, that cannot configure publishconf.py, so I cannot ge make publish to works.

I failed miserably.

Secrets

You have to setup PRIVATE_KEY in github’s secrets setting.

Workaround

So I use my own CircleCI configuration as a based.

And combined with the RSA setup from Jim Kubicek above.

YAML Configuration

After a few trial and error. I finally got this low level configuration as below.

name: Pelican Deploy

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout to master
      uses: actions/checkout@v2
    - name: Set up Python 3.5
      uses: actions/setup-python@v1
      with:
        python-version: 3.5
    - name: Setup the SSH key
      env:
        PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
      run: |
        mkdir $HOME/.ssh
        echo "$PRIVATE_KEY" > $HOME/.ssh/id_rsa
        chmod 600 $HOME/.ssh/id_rsa
        ssh-keyscan -t rsa github.com >> $HOME/.ssh/known_hosts
    - name: Prepare Git Initialization
      run: |
        git config user.email "someone@somewhere"
        git config user.name "someone"
        git remote -v
        echo "[Checking origin/gh-pages]"
        git fetch
        git branch -a -l | cat
        if [ $(git branch -a -l | grep gh-pages | wc -l) -eq "0" ]; then
          echo "[Create gh-pages for the first time]"
          git checkout -b gh-pages
          git commit --allow-empty -m "Create gh-pages for the first time"
          git push --set-upstream origin gh-pages
          git checkout master
        fi
    - name: Upgrade PIP and Configure Dependencies
      run: |
        python3 -m venv venv
        . venv/bin/activate
        pip install --user --upgrade pip
        pip install -r requirements.txt
    - name: Generate Pelican Static Files
      run: |
        . venv/bin/activate
        make html
    - name: Precheck Output 
      run: |
        pwd
        source=$(pwd)
        git worktree add -B gh-pages ~/public origin/gh-pages
        git worktree list
        cd ~/public
        ls -lah
        find . -maxdepth 1 ! -name '.git' -exec rm -rf {} \;
        mv ${source}/output/* .
        touch .nojekyll
        ls -lah
    - name: Deploy Release to GitHub
      run: |
        cd ~/public
        git add --all
        git status
        git commit --allow-empty -m "$(git log master -1 --pretty=%B)"
        git push --set-upstream origin gh-pages
        echo "[Deployed Successfully]"

Whoaaa… this is long. But at least this works for me.

It is working

Generic Configuration

Why that long configuration? Well, as I said eralier, if you do not want to use third party github actions, you can always turn back to low level configuration with pure git.

It is generic, and hypothetically works for most traditional SSG, such as Jekyll, Hexo, Hugo, Pelican and Eleventy.

Preview

Please enjoy the render at


What is Next ?

We are almost finished. For the hunger, here is a food intellectual curiosity. Consider continue reading [ CI/CD - Git Hooks ].