Jekyll - HVboom/HowTo-DigitalOcean GitHub Wiki

Create a dedicated Jekyll user - see Development User

Create necessary directories

cd $HOME
mkdir -p Live Preview Projects

Create Jekyll projects

  • Example Gemfile for the HVKeyGuard product documentation

    #ruby=ruby-2.4
    #ruby-gemset=HVKeyGuard
    #
    
    source 'https://rubygems.org'
    
    git_source(:github) do |repo_name|
      repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
      "https://github.com/#{repo_name}.git"
    end
    
    gem 'jekyll'
    
    # This is the default theme for new Jekyll sites. You may change this to anything you like.
    gem 'minima'
    
    # If you have any plugins, put them here!
    group :jekyll_plugins do
      gem 'jekyll-feed'
      gem 'jekyll-haml'
      gem 'jekyll-admin'
    end
    
    # avoid polling of changed files
    require 'rbconfig'
    if RbConfig::CONFIG['target_os'] =~ /(?i-mx:bsd|dragonfly)/
      gem 'rb-kqueue', '>= 0.2'
    end
    
    # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
    gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
    
  • Create additional configuration files for the preview and live versions of the documentation

    • Example: Projects/HVKeyGuard/_config.preview.yml

      baseurl: '/Preview/HVKeyGuard'
      url: 'https://hvboom.org'
      source: '/home/jekyll/Projects/HVKeyGuard'
      destination: '/home/jekyll/Preview/HVKeyGuard'
      profile: true
    • Example: Projects/HVKeyGuard/_config.prod.yml

      baseurl: 'Projects/HVKeyGuard'
      url: 'https://hvboom.org'
      source: '/home/jekyll/Projects/HVKeyGuard'
      destination: '/home/jekyll/Live/HVKeyGuard'

Serve generated pages through Apache

Create following configuration /usr/local/etc/apache24/Includes/060_vhost_443_jekyll.conf

<VirtualHost *:443>
  ServerName jekyll.HVboom.org

  # fix, if user is lazy and does not add a trailing slash to a directory
  DirectorySlash On

  ProxyRequests     Off  <-- this is an important security setting
  ProxyPreserveHost On

  # LogLevel debug rewrite:trace3

  # default to the Homepage project
  RewriteEngine On
  RewriteRule   ^/$ %{REQUEST_SCHEME}://%{HTTP_HOST}/Homepage/ [redirect=permanent,last]

  # preview of the generated project
  AliasMatch "^/Preview/(.*)$" "/home/jekyll/Preview/$1"

  # allow access to the preview directories
  <Directory "/home/jekyll/Preview/">
    Require all granted
    Options none
  </Directory>

  # allow access to the internal proxy servers
  <ProxyMatch "^http://localhost:\d{4}/?">
    Require all granted
    Options none
  </ProxyMatch>

  <Macro JekyllProxy ${location} ${port}>
    # fix missing trailing slash for accessing project root or admin area
    RewriteRule ^/${location}$       %{REQUEST_SCHEME}://%{HTTP_HOST}/${location}/       [redirect=permanent,last]
    RewriteRule ^/${location}/admin$ %{REQUEST_SCHEME}://%{HTTP_HOST}/${location}/admin/ [redirect=permanent,last]

    # proxy project root
    <Location "/${location}/">
      ProxyPass        http://localhost:${port}/${location}/
      ProxyPassReverse http://localhost:${port}/${location}/
    </Location>
  
    # proxy project internal Jekyll API
    <Location "/${location}/_api/">
      ProxyPass        http://localhost:${port}/_api/
      ProxyPassReverse http://localhost:${port}/_api/
  
      SetOutputFilter INFLATE;substitute;DEFLATE
  
      Substitute "s|http://localhost:${port}|https://jekyll.HVboom.org|ni"
    </Location>
  
    # proxy project Jekyll Admin area
    <Location "/${location}/admin/">
      ProxyPass        http://localhost:${port}/admin/
      ProxyPassReverse http://localhost:${port}/admin/
  
      AddOutputFilterByType INFLATE;substitute;DEFLATE text/html
      AddOutputFilterByType INFLATE;substitute;DEFLATE text/css
      AddOutputFilterByType INFLATE;substitute;DEFLATE application/javascript
  
      Substitute "s|/admin|/${location}/admin|ni"
      Substitute "s|/_api|/${location}/_api|ni"
    </Location>
  </Macro>

  Use JekyllProxy Homepage   8001
  Use JekyllProxy HVKeyGuard 8002
  UndefMacro JekyllProxy

  SSLEngine on
  SSLCertificateFile      /usr/local/etc/letsencrypt/live/hvboom.org/fullchain.pem
  SSLCertificateKeyFile   /usr/local/etc/letsencrypt/live/hvboom.org/privkey.pem

  RequestHeader add X-Forwarded-Ssl on

  <IfModule headers_module>
    Header always edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure
    Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains"
  </IfModule>
</VirtualHost>

Startup script

Create following script as /home/jekyll/bin/jekyll-start

#!/usr/local/bin/bash

project=$1

if [[ -z $project ]]; then
  echo "Usage: project name needs to be provided!"
  exit 1
fi

project_path="$HOME/Projects"
pid_path="$HOME/tmp/pids"
log_path="$HOME/log"

project_dir="$project_path/$project"
pid_file="$pid_path/${project}.pid"
log_file="$log_path/${project}.log"

if [[ ! -d "$project_dir" ]]; then
  echo "Error: project directory '$project_dir' does not exist!"
  exit 1
fi

if ! mkdir -p "$pid_path"; then
  echo "Could not create the path '$pid_path' needed to store the pids."
  exit 1
fi
if ! mkdir -p "$log_path"; then
  echo "Could not create the path '$log_path' needed to store the log files."
  exit 1
fi

# ensure RVM is enabled before entering the project directory
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"

cd $project_dir
nohup bundle exec jekyll serve --verbose --watch 2>&1 1>>$log_file &
echo $! > $pid_file

exit

System startup

Add following script /usr/local/etc/rc.d/jekyll to start the project servers automatically as system startup.

#! /bin/sh

# Maintainer: Mario Lotz <[email protected]>

# PROVIDE: jekyll
# REQUIRE: LOGIN
# KEYWORD: shutdown
#
# Add the following line to /etc/rc.conf to enable Jekyll:
#
#  jekyll_enable="YES"

PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/local/share/bin"

. /etc/rc.subr

name=jekyll
rcvar=jekyll_enable
extra_commands=status

status_cmd="print_status"
start_cmd="start_jekyll"
stop_cmd="stop_jekyll"
restart_cmd="restart_jekyll"

: ${jekyll_enable:="NO"}

load_rc_config $name

### Environment variables

# Script variable names should be lower-case not to conflict with
# internal /bin/sh variables such as PATH, EDITOR or SHELL.
app_user="jekyll"
user_home="/home/$app_user"
app_root="$user_home/Projects"
pid_path="$user_home/tmp/pids"
start_server="$user_home/bin/jekyll-start"

# Switch to the app_user if it is not he/she who is running the script.
if [ "$USER" != "$app_user" ]; then
  eval su - "$app_user" -c $(echo \")/usr/local/etc/rc.d/jekyll "$@"$(echo \")
  exit
fi

# Switch to the jekyll path, exit on failure.
if ! cd "$app_root" ; then
  echo "Failed to cd into '$app_root', exiting!"
  exit 1
fi


### Init Script functions

## Get the pid for one project
get_pid(){
  project=$1

  pid_file="$pid_path/${project}.pid"
  if [ -f "$pid_file" ]; then
    pid=$(cat "$pid_file")
    # If the server is running kill -0 $pid returns true, or rather 0.
    if ! kill -0 "$pid" 2>/dev/null; then
      # process is not running
      pid=0
      # Cleaning up unused pids
      rm "$pid_file" 2>/dev/null
    fi
  else
    pid=0
  fi
  return $pid
}

## Start all projects, if they are not already running
start_jekyll(){
  echo "Start servers ..."
  find * -prune -type d | while IFS= read -r project; do 
    get_pid $project
    pid=$?
    if [ "$pid" -ne 0 ]; then
      echo "  The server for project '$project' is already running with pid $pid."
    else
      echo "  Starting server for project '$project' ..."
      $start_server $project

      sleep 1
      get_pid $project
      pid=$?
      echo "  The server for project '$project' is running with pid $pid."
    fi  
  done
  echo "Starting servers finished"
}

## Stop all projects, if they are still running
stop_jekyll(){
  echo "Stop servers ..."
  find * -prune -type d | while IFS= read -r project; do 
    get_pid $project
    pid=$?
    if [ "$pid" -eq 0 ]; then
      echo "  The server for project '$project' is already stopped."
    else
      echo "  Shutting down project '$project' ..."
      kill -- "$pid"

      sleep 1
      get_pid $project
      pid=$?
      if [ "$pid" -eq 0 ]; then
        echo "  The server for project '$project' is stopped."
      else
        printf "  The server for project '$project' is \033[31mstill running\033[0m.\n"
      fi
    fi  
  done
  echo "Stopping servers finished"
}

## Restart all projects, even if they are already running
restart_jekyll(){
  echo "Restart servers ..."
  find * -prune -type d | while IFS= read -r project; do 
    get_pid $project
    pid=$?
    if [ "$pid" -ne 0 ]; then
      echo "  Shutting down project '$project' ..."
      kill -- "$pid"

      sleep 1
      get_pid $project
      pid=$?
      if [ "$pid" -eq 0 ]; then
        echo "  The server for project '$project' is stopped."
      else
        printf "  The server for project '$project' is \033[31mstill running\033[0m.\n"
        break
      fi
    fi  

    echo "  Starting server for project '$project' ..."
    $start_server $project

    sleep 1
    get_pid $project
    pid=$?
    echo "  The server for project '$project' is running with pid $pid."
  done
  echo "Restart servers finished"
}

## Print the server status of all projects
print_status(){
  echo "Print status ..."
  find * -prune -type d | while IFS= read -r project; do 
    get_pid $project
    pid=$?
    if [ "$pid" -ne 0 ]; then
      echo "  The server for project '$project' is running with pid $pid."
    else
      printf "  The server for project '$project' is \033[31mnot running\033[0m.\n"
    fi  
  done
  echo "Print status finished"
}

### Finally the input handling.

case $jekyll_enable in
  [yY][eE][sS])
    case "$1" in
      start)
        start_jekyll
        ;;  
      stop)
        stop_jekyll
        ;;  
      restart)
        restart_jekyll
        ;;  
      status)
        print_status
        ;;  
      *)  
        echo "Usage: service jekyll {start|stop|restart|onestart|onestop|onerestart|status}"
        exit 1
        ;;  
    esac
    ;;  
  *)  
    case "$1" in
      onestart)
        start_jekyll
        ;;  
      onestop)
        stop_jekyll
        ;;
      onerestart)
        restart_jekyll
        ;;
      status)
        print_status
        ;;
      *)
        echo "Usage: service jekyll {start|stop|restart|onestart|onestop|onerestart|status}"
        exit 1
        ;;
    esac
esac

exit

Useful plugins

A Jekyll plugin that provides users with a traditional CMS-style graphical interface to author content and administer Jekyll sites. The project is divided into two parts. A Ruby-based HTTP API that handles Jekyll and filesystem operations, and a Javascript-based front end, built on that API.

A great theme with awesome features. 🌟

  • Setup is really simple - see Quick start. But do not forget to copy the whole _data directory (bundle show jekyll-text-theme) into your own directory and adjust the files accordingly.
  • Set default author
    Mario:
      name      : Mario Lotz
      type      : # "person" (default), "organization"
      url       :
      avatar    : /assets/images/Mario.jpg
      bio       :
      email     : [email protected]
      linkedin  : mario-lotz # "user_name" the last part of your profile url, e.g. https://www.linkedin.com/in/user_name
      github    : HVboom # "user_name" the last part of your profile url, e.g. https://github.com/user_name
  • Adjust the _config.yml to your needs
    ...
    
    theme: jekyll-text-theme
    
    author:
      name      : Mario Lotz
      type      : # "person" (default), "organization"
      url       :
      avatar    : /assets/images/Mario.jpg
      bio       :
      email     : [email protected]
      linkedin  : mario-lotz # "user_name" the last part of your profile url, e.g. https://www.linkedin.com/in/user_name
      github    : HVboom # "user_name" the last part of your profile url, e.g. https://github.com/user_name
    
    defaults:
      -
        scope:
          path: ''
        values:
          author: Mario
          layout: 'article'
          full_width: false
          show_title: true
          show_tags: true
          aside:
            toc: true
      -
        scope:
          path: '_features'
        values:
          author: Mario
          layout: 'article'
          full_width: false
          show_title: true
          show_tags: true
          nav_key: 'features'
          sidebar:
            nav: 'features'
          aside:
            toc: true

Optional theme adjustment

  • To use a different font size and font family please adjust the file _sass/custom.scss:
    // Use a bigger font - default is 16px
    $font-size-root: 21px;
    
    // use Lato as default font - others are 1:1 from the theme
    $font-family: (Lato, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif);
    // original definition: $font: map-get($base, font-weight) #{map-get($base, font-size)}/#{map-get($base, line-height)} map-get($base, font-family);
    // $font: map-get($base, font-weight) #{map-get($base, font-size)}/#{map-get($base, line-height)} $font-family;
    
    // use Anonymous Pro as default monospace font - others are 1:1 from the theme
    $font-family-code: ('Anonymous Pro', Menlo, Monaco, Consolas, Andale Mono, lucida console, Courier New, monospace);
    
    
    html {
     // font-size: map-get($base, font-size-root);
     font-size: $font-size-root;
    }
    
    body {
      // font: $font;
      font-family: $font-family;
    }
    
    input, textarea, select, button {
      // font: $font;
      font-family: $font-family;
    }
    
    pre, code {
      // font-family: map-get($base, font-family-code);
      font-family: $font-family-code;
    }
  • Add the necessary stylesheet for the font to the file _includes/head/custom.html as described on the Google Fonts page:
    <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Lato:400,400i,700,700i&display=swap'>
     <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Anonymous+Pro:400,400i,700,700i&display=swap'>

Helper plugin to automatically open external links in a new window.

  • In addition following custom stylesheet shows an external link icon _sass/_target-blank.scss:
    /* Step 1: Common Properties: All required to make icons render reliably */
    @mixin icon {
      display: inline-block;
      font-style: normal;
      font-variant: normal;
      text-rendering: auto;
      -webkit-font-smoothing: antialiased;
    
      font-family: "Font Awesome 5 Free";
      font-weight: 900;
    }
    
    /* Step 2: Reference Individual Icons */
    /* external link icon, but not for buttons like GitHub or LinkedIn */
    :not(.button).target-blank {
      &__icon::after {
        @include icon;
      
        font-size: smaller;
        vertical-align: super;
        margin-left: .15em;
        content: " \f35d";
      }
    }
  • Import that additional stylesheet into the _sass/custom.scss file
    @import '_target-blank';
    ...
  • To activate the icon following entry is necessary in _config.yml, which adds the external class to the link:
    ...
    target-blank:
      add_css_classes: target-blank target-blank__icon
    ...

❗ Remark: Not necessary anymore, because the the jekyll-text-theme provides the same possibility with the mermaid extension

It's an add-on to integrate UML diagrams into your static pages

  • Example: Sequence diagram
  • Precondition
    • Installation of Java and Graphviz
    • Download the necessary plantuml.jar file and create a setup script /usr/local/share/bin/plantuml:
      #!/usr/local/bin/bash
      
      java -jar /usr/local/share/bin/plantuml.jar "$1" "$2"
      • Adopt the system startup script /usr/local/etc/rc.d/jekyll to include the path /usr/local/share/bin
  • Usage documentation see PlantUML.com
    • Before you do a final build it is necessary to cleanup the generated uml subdirectory
⚠️ **GitHub.com Fallback** ⚠️