Jekyll

Introduction

Jekyll은 Hugo, Hexo와 함께 굉장히 많이 사용되는 SSG(Static Site Genertor) 입니다. Ruby로 개발되었고 Liquid 문법을 사용하여 템플릿을 통제할 수 있고, 여러가지 기능들 담은 Gem(RubyGem)을 추가하여 쉽게 여러가지 기능들을 만들어낼 수 있습니다.

Directory Structure

  • _config.yml: Jekyll 사이트이 Config 파일입니다. Jekyll에서 사용할 변수, 옵션 등 여러가지 설정이 이 파일에 포함됩니다.
  • _posts: 기본적인 Post 디렉토리입니다. 대다수 테마에선 블로그 글과 같이 날짜로 정렬되는 데이터가 여기에 포함됩니다.
  • _pages: 기본적인 Page 디렉토리입니다.
  • Gemfile: Ruby Gemfile입니다. Jekyll plugin 또한 Gem 파일에 명시하여 설치할 수 있습니다.

참고로 config.yml에 exclude로 제외한 디렉토리를 빼면 _ 가 붙은 디렉토리는 사이트에 포함되지 않고 없는 디렉토리는 내용 자체가 포함됩니다. 그래서 만약 _data란 디렉토리를 만들고 별도로 site에 포함(output: true)시키지 않는다면 사이트에 들어가지 않습니다. 그래서 빌드에서만 사용할 데이터를 별도로 넣고 관리할 수도 있습니다.

Usage

Basic

  • jekyll new: 사이트를 생성합니다.
  • jekyll new-theme: 테마를 생성합니다.
  • jekyll serve, server, s: jekyll 사이트를 로컬에서 구동합니다.
  • jekyll build: 사이트를 빌드합니다. (_site 경로에 static file들이 생성됩니다.)
  • jekyll doctor, hyde: Warnings가 있는지 체크합니다.

주로 사용되는건 위와 같으며 이외에 여러가지 subcommands는 jekyll -h로 확인하실 수 있습니다.

Advanced - Jekyll Compose Jekyll Compose 설치 시 Jekeyll Command에서 글을 쓰고 관리하는 기능들이 추가됩니다. 이 명령으로 생성한 페이지들은 Front matter의 일부가 자동으로 작성됩니다. Front matter 또한 사용자가 직접 원하는 포맷으로 설정이 가능합니다.

  • bundle exec jekyll post "My New Post": 새로운 Post를 만듭니다.
  • bundle exec jekyll page "My New Page": 새로운 Page를 만듭니다.
  • bundle exec jekyll draft "My new draft": 새로운 Draft 문서를 만듭니다.
  • bundle exec jekyll compose "My New Wiki" --collection "wikis": 새로운 Collection 내용을 만듭니다.
draft      # Creates a new draft post with the given NAME
post       # Creates a new post with the given NAME
publish    # Moves a draft into the _posts directory and sets the date
unpublish  # Moves a post back into the _drafts directory
page       # Creates a new page with the given NAME
rename     # Moves a draft to a given NAME and sets the title
compose    # Creates a new file with the given NAME

Bundle command

Ruby는 rvm, rbenv 그리고 bundler로 디렉토리 별 환경이 모두 다를 수 있습니다. 만약 시스템에 여러 ruby 환경이 구성되어 있다면 jekyll 명령을 직접 실행하는 것 보단 jekyll 디렉토리에서 bundler를 통해 구동하는게 가장 좋습니다.

bundle exec jekyll <subcommands> [options]

Go-to commands

post를 가장 최근 문서 한개만 빌드하여 서빙합니다.

bundle exec jekyll serve --limit-posts 1

--profile 옵션을 사용하여 jekyll serve 또는 build 시 파일 별 소모된 시간 등 자세한 내용을 볼 수 있습니다. 개인적으론 빌드 타임을 줄이거나, 잘못된 코드를 찾는데 자주 사용합니다.

bundle exec jekyll build --profile

Liquid

Liquid는 shopify에서 만든 오픈소스 Markup Language로 Jekyll에서 Layout, Posts 등 여러 곳에서 사용할 수 있는 템플릿입니다. Ruby로 개발되었기 때문에 Liquid 문법은 루비와 유사하고 많은 문법이 공유됩니다.

개인적으론 Template 관련 문법에선 가장 쉽다고 생각되네요.

<ul id="products">
  {% for product in products %}
    <li>
      <h2>{{ product.name }}</h2>
      Only {{ product.price | price }}

      {{ product.description | prettyprint | paragraph }}
    </li>
  {% endfor %}
</ul>

Variables

Global Var

  • site
  • page
  • content

Document Var

  • content
  • collection
  • relative_path
# content
# output: Hello from my_collection.
{{ site.my_collection.first.content }}

# collection
# output: my_collection
{{ site.my_collection.first.collection }}

# relative_path
# output: _my_collection/item.md
{{ site.my_collection.first.relative_path }}

Collection Var

  • directory
  • docs
  • files
  • label
  • output
  • relative_path
# directory
# output: /Users/hahwul/jekyll-app/_my_collection
{{ site.my_collection.directory }}

# docs
# output: /my_collection/item.html
{{ site.my_collection.docs.first.url }}

# files
# output: 4
{{ site.my_collection.files | size }}

# label
# my_collection
{{ site.my_collection.label }}

# output
# output: true
{{ site.my_collection.output }}

# relative_path
# output: _my_collection
{{ site.collections.my_collection.relative_path }}

Filter

Filter는 Pipe(|) 를 통해 값을 전달하여 처리하고 결과를 다시 받아오는 형태의 기능들입니다.

Array

# page.my_array is ['a', 'b', 'c']
# --------------------------------

# output: a, b, and c
{{ page.my_array | array_to_sentence_string }}

# output: a
{{ page.my_array | first }}

# output: a,b,c
{{ page.my_array | join: ', ' }}

# output: ["a","b","c"]
{{ page.my_array | jsonify }}

# output: c
{{ page.my_array | last }}

# output: 3
{{ page.my_array | size }}

# output: ['a','b','c']
{{ page.my_array | sort }}

# my_array => ['a','a','b','c','c','c','a']
# output: 'a', 'b', 'c'
{{ page.my_array | uniq }}

# output: my_array => ['a', 'b', 'c', 'd']
{% assign my_array = my_array | push: 'd' %}

# page.people is
#  - name: "John"
#    school: "Stanford"
#  - name: "Jane"
#    school: "Stanford"
#  - name: "Joe"
#    school: "Harvard"
# --------------------------------

# output: JohnJaneJoe
{{ page.people | map: "name" }}

# output: {"name"=>"John", "school"=>"Stanford"}{"name"=>"Jane", "school"=>"Stanford"}
{{ page.people | where: "school", "Stanford" }}

# output: {"name"=>"John", "school"=>"Stanford", "year"=>2016}{"name"=>"Joe", "school"=>"Harvard", "year"=>2015}
{{ page.people | where_exp: "item", "item.name contains 'Jo'" }}

# output: {"name"=>4, "items"=>[{"name"=>"John", "school"=>"Stanford", "year"=>2016}, {"name"=>"Jane", "school"=>"Stanford", "year"=>2017}], "size"=>2}{"name"=>3, "items"=>[{"name"=>"Joe", "school"=>"Harvard", "year"=>2015}], "size"=>1}
{{ page.people | group_by_exp: "item", "item.name | size" }}

# output: {,"name"=>"Stanford",,"items"=>[{,"name"=>"John",,"school"=>"Stanford",},{,"name"=>"Jane",,"school"=>"Stanford",}],}
{{ page.people | group_by: "school" }}

Date

# output: 09 Sep 2022
{{ site.time | date_to_long_string }}

# output: Fry, 09 Sep 2022 22:51:14 +0900
{{ site.time | date_to_rfc822 }}

# output: 09 Sep 2022
{{ site.time | date_to_string }}

# output: 2022-09-09T22:51:14+09:00
{{ site.time | date_to_xmlschema }}

# output: Fry, Sep 09, 22
{{ site.time | date: "%a, %b %d, %y" }}

Integer

# output: 2
{{ 1.2 | ceil }}

# output: 3
{{ 10 | divided_by:3 }}

# output: 1
{{ 1.2 | floor }}

# output: 3
{{ 4 | minus:1 }}

# output: 1
{{ 3 | modulo:2 }}

# output: 5
{{ 4 | plus:1 }}

# output: 2
{{ 1.8 | round }}

# output: 30
{{ 10 | times:3 }}

String

# output: http://example.com/mysite/images/dog.jpeg
{{ '/images/dog.jpeg' | absolute_url }}

# output: jekyll.jpg
{{ 'jekyll' | append: '.jpg' }}

# output: Static site generator
{{ "static site generator" | capitalize }}

# output: foo%2Cbar%3Bbaz%3F
{{ "foo,bar;baz?" | cgi_escape }}

# output: hello
{{ nil | default: "hello"  }}

# output: static site generator
{{ "STATIC Site generator" | downcase }}

# output: &amp;lt;p&amp;gt;Jekyll&amp;lt;/p&amp;gt;
{{ "<p>Jekyll</p>" | escape }}

# output: I love Jekyll!
{{ '          I love Jekyll!          ' | lstrip }}

# output: Hello <strong>Jekyll</strong>
{{ "Hello **Jekyll**" | markdownify }}

# output: 2
{{ "Hi Jekyll!" | number_of_words }}

# output: I love Jekyll
{{ 'Jekyll' | prepend: 'I love ' }}

# output: /mysite/images/dog.jpeg
{{ '/images/dog.jpeg' | relative_url }}

# output: I really like Jekyll
{{ 'I really really like Jekyll' | remove_first: 'really' }}

# output: I like Jekyll
{{ 'I really really like Jekyll' | remove: 'really' }}

# output: I kinda really like Jekyll
{{ 'I really really like Jekyll' | replace_first: 'really', 'kinda' }}

# output: I truly truly like Jekyll
{{ 'I really really like Jekyll' | replace: 'really', 'truly' }}

# output:          I love Jekyll!
{{ '          I love Jekyll!          ' | rstrip }}

# output: 6
{{ page.my_string | size }}

# output: ell
{{ "hello" | slice: 1, 3 }}

# output: the-config-yml-file
{{ "The _config.yml file" | slugify }}

# output: Use &ldquo;Jekyll&rdquo; &mdash; the static generator&hellip;
{{ 'Use "Jekyll" --- the static generator...' | smartify }}

# output: ['a', 'b']
{{ "a~b" | split:"~" }}

# output: Jekyll is cool
{{ "<p>Jekyll is cool</p>" | strip_html }}

# output: Hello there
{{ "Hello
there" | strip_newlines }}

# output: I love Jekyll!
{{ '          I love Jekyll!          ' | strip }}

# output: I love Je...
{{ "I love Jekyll" | truncate: 12 }}

# output: I love...
{{ "I love Jekyll" | truncatewords: 2 }}

# output: STATIC SITE GENERATOR
{{ "static site generator" | upcase }}

# output: foo,%20bar%20%5Cbaz?
{{ "foo, bar \baz?" | uri_escape }}

# output: john%40example.com
{{ "john@example.com" | url_encode }}

# output: &amp;lt;p&amp;gt;Hi Jekyll&amp;lt;/p&amp;gt;
{{ "<p>Hi Jekyll</p>"| xml_escape }}

# output: sass to css code
{{ sass_code | sassify }}

# output: scss to css
{{ scss_code | sassify }}

Loop

# page.my_array => []
# output: There are no items!
{% for item in page.my_array %}
  {{ item }}
{% else %}
  There are no items!
{% endfor %}

# page.my_array => ['a','b','c']
# output: First! Not first Not first
{% for item in page.my_array %}
  {% if forloop.first %}
    First!
  {% else %}
    Not first
{% endif %}
{% endfor %}

# page.my_array => ['a','b','c']
# output: 1 2 3
{% for item in page.my_array %}
  {{ forloop.index }}
{% endfor %}

# page.my_array => ['a','b','c']
# output: 0 1 2
{% for item in page.my_array %}
  {{ forloop.index0 }}
{% endfor %}

# page.my_array => ['a','b','c']
# output: Not last Not last Last!
{% for item in page.my_array %}
  {% if forloop.last %}
    Last!
  {% else %}
    Not last
  {% endif %}
{% endfor %}

# page.my_array => ['a','b','c']
# output: 3 3 3
{% for item in page.my_array %}
  {{ forloop.length }}
{% endfor %}

# page.my_array => ['a','b','c']
# output: a b
{% for item in page.my_array limit: 2 %}
  {{ item }}
{% endfor %}

# page.my_array => ['a','b','c','d','e','f','g]
# output: c d e f g
{% for item in page.my_array offset: 2 %}
  {{ item }}
{% endfor %}

# page.my_array => ['a','b','c']
# output: c b a
{% for item in page.my_array reversed %}
  {{ item }}
{% endfor %}

# page.my_array => ['a','b','c']
# output: 3 2 1
{% for item in page.my_array %}
  {{ forloop.rindex }}
{% endfor %}

# page.my_array => ['a','b','c']
# output: 2 1 0
{% for item in page.my_array %}
  {{ forloop.rindex0 }}
{% endfor %}

Conditional

조건문들은 Ruby를 따라갑니다. 아래 문법들로 Jekyll 내부에서 분기할 수 있습니다.

  • if
  • elsif
  • case
  • operations
  • unless

e.g

{% if page.name == 'about' %}
  About Page
{% elsif page.name == 'contact' %}
  Contact Page
{% else %}
  Other Page
{% endif %}

Documents

Jekyll이 Documentaion이 잘 되어 있어서 공식 Documents를 참고하시면 Jekyll을 사용하는데 있어 큰 도움을 받을 수 있습니다.

https://jekyllrb.com/docs/

Plugins

Lists

Useful Gem

Security

Check List

  • Misconfigration
    • include and exclude
  • Vulnerabilities
    • DOM XSS
    • DOM Clobbering
    • Prototype Pollution
    • Dependency Confusion (rubygem)
    • Reverse Tabnabbing
    • Broken Link
    • Mixed Contents
    • Sensitive/Secret Disclosure
    • Liquid Injection
  • CVEs

Articles

References