Blog

Blog

Learning – Jekyll

Learning - Jekyll

Developers describe Jekyll as "Blog-aware, static site generator in Ruby". It is classified as "Static Site Generators" and "Self-Hosted Blogging / CMS", similar to WordPress for blogging.

Pros

  • Simple - No more databases, file based.
  • Static - Markdown (or Textile), Liquid templating language, HTML & CSS, etc.
  • Blog-aware - Includes permalinks, categories, pages, posts, and custom layouts.
  • Theme - Can be downloaded from rubygem
  • Github - Integration allow publish to Github

Cons

  • No search, but it is SEO friendly
  • No plugin
  • No image resizing
  • No tag

Installation

Both Ruby and Gem need to be installed.

gem install jekyll bundler

Verify

jekyll -v

Create a Site

jekyll new ga_blog

Then there is ga_blog folder created.

Launch the Site

For the first time, run

bundle exec jekyll serve

Subsquence time

jekyll serve

Then the website can be accessed from http://localhost:4000/

Folder structure

The _posts folder hosts posts.

The _site folder hosts the finial product of website.

Front Matter

Content

___
layout: post
title: "Welcome To Jekyll!"
date: 2017-09-24 15:58:59 -0700
categories: jekyll update
___

URL

http://localhost:4000/jekyll/update/2017/09/24/welcome-to-jekyll!.html

Also can create custom attribute in Front Matter.

Writing Posts

The posts can be in subfolders too, the URL will add subfolder in the path.

Filename format

_posts/2017-09-24-my-first-blog-post.md

Layout

layout: "post"

Overwrite

title: "This is the new title"
date: 2017-09-20 15:58:59 -0700

Drafts

Create folder called _drafts, and save draft posts in this folder with name only, the date will be the today's date.

my-draft-blog-post.md

Then run following command

jekyll serve --draft

Pages

Create page in root folder, such as donate.md, and set layout as page

layout: "page"

Then the URL will be

http://localhost:4000/donate

Premalinks

permalink: "/my-new-url/"

or

permalink: /:categories/:year/:month/:day/:title.html

Default Front Matter

Defined in _config.yml file

defaults:
  -
    scope:
      path: ""
      type: "posts"
    values:
      layout: "post"
      title: "My Title"

Then restart jekyll server.

Theme

Default theme is minima, other themes can be downloaded from https://rubygems.org, and search jekyll-theme.

Update Gemfile

gem: "jekyll-theme-hacker"

Then stop Jekyll server and run command

bundle install

Update _config.yml file

theme: jekyll-theme-hacker

Then start jekyll server using following command

bundle exec jekyll serve

If there is no output on the page, normally, it is because no layout defined in theme.

Layouts

Create <layout_name>.html in _layouts folder

<h1>This is page</h1>
<hr>

{{ content }}

Define sub layout, then can be used in other layout.

For example, in post.html layout

---
layout: wrapper
---

<h1>This is a post</h1>
<hr>

{{ content }}

Then it will use wrapper layout defined in wrapper.htm below

<html>
<head>
  <meta charset="UTF-8">
  <title>Doument</title>
</head>
<body>
  Wrapper <br>
  {{ content }}
  <br> Wrapper
</body>
</html>

Use this method for other layouts, then can have html skeleton.

Variables

Front Matter variables

{{ layer.author}}
{{ page.title }}
{{ page.author }}
{{ site.title }}

The site values are from _config.yml file

Other variables can be found in https://jekyllrb.com/docs/variables/.

Includes

Create a folder called _includes

Create a file called header.html in _includes folder as below

<h1>{{ site.title }}</h1>

Then update wrapper.html file

<html>
<head>
  <meta charset="UTF-8">
  <title>Doument</title>
</head>
<body>
  {% include header.html %}
  {{ content }}
</body>
</html>

Then every page using wrapper.html will use header.html to display header.

To define variable during include as below

{% include header.html color="blue" %}

Then access in header.html file as below

<h1 style="color: {{ include.color }}">{{ site.title }}</h1>

Looping Through Posts

Update _layouts/home.html

{% for post in site.posts %}
  <li><a href="{{ post.url }}">{{ post.title }}</a></li>
{% endfor %}

Conditionals

{% if page.title == ... %}
{% elsif page.title == ... %}
{% else %}
{% endif %}

For example

{% for post in site.posts %}
  <li><a style="{% if page.url == post.url %}color:red;{% endif %}" href="{{ post.url }}">{{ post.title }}</a></li>

Data file

Create a folder called _data, put YAML, JSON or CSV files in it.

For example, people.yml file

- name: "Mike"
  occupation: "Giraffe Academy"

- name: "Steve"
  occupation: "Firefighter"

...

In home.html file

{{ site.data.people }}

or

{% for person in site.data.people %}
  {{ person.name }}, {{ person.occupation }} <br>
{% endfor %}

Static Files

Those files have no Front Matter, can be stored in any folder. For example, in assets folder, in home.html file,

{% for file in site.static_files %}
  {{ file.path }} <br>
{% endfor %}

Configure Front Matter in _config.yml file

defaults:
  - scope:
      path: "assets/img"
    values:
      image: true

Then restart jekyll server

Update home.html file

{% for file in site.static_files %}
  {% if file.image %}
    <img src="{{ file.path }}" alt="{{ file.name }}">
  {% endif %}
{% endfor %}

Hosting on Github Pages

Create repository called ga_blog, and don't init with README.

Update _config.yml file

baseurl: "ga_blog"

Then

git init
git checkout -b gh-pages
git add .
git commit -m "initial commit"

Add remote repo

git remote add origin https://github.com/<name>/ga_blog.git

Push

git push origin gh-pages

Then go to setting in Github page, find out published URL

References

Tutorials

Learning – Hugo – Static Site Generator

Learning - Hugo - Static Site Generator

Installation

Windows

hugo version

MacOS

  • Install Homebrew

  • Install hugo

brew install hugo
hugo version

Create a new site

hugo new site <site_name>

Then the folder created.

Site contents

  • archetypes - data type
  • content
  • data
  • layouts
  • static
  • themes
  • config.toml

Theme

theme = "ga-hugo-theme"

Create content

hugo new a.md
hugo new dir1/b.md

Run hugo in draft mode

hugo server -D

Single content

Display one page content.

List content

List other single contents in the page. The list page for Level 1 directories are automatically created hugo.

Manually create list page

Force hugo create list page, create _index.md file in the directory, and update the content of _index.md file will show on the list page

hugo new dir1/dir2/_index.md

Front Matter

YAML

---
title: "title"
date: 2017-...
draft: true
---

TOML

+++
title = "title"
date = 2017-...
draft = true
+++

Archetypes

The file archetypes/default.md has the template of new files.

---
tltle: "{{ replace .TranslationBaseName "-" " " | title }}"
date: {{ .Date }}
draft: true
---

The template file can be named as directory name, such as dir1.md, it will be used for contents created in dir1

hugo new dir1/b.md

Shortcodes

The predefined shortcodes can be found in hugo shortcode page, it also can be customized using shortcode template.

{{< youtube w7Ft2ymGmfc >}}

Taxonomies

Tags and categories

---
tags: [ "tag1", "tag2", "tag3"]
categories: [ "cat1"]
---

The tags and categories will be shown in contents if using learning theme.

Access tags and categories from page

https://localhost:1313/tags/tag1/
https://localhost:1313/categories/cat1/

Custome taxonomy

In content file

---
Moods: [ "Happy", "Upbeat" ]
---

In config.toml file

[taxonomies]
  tag = "tags"
  category = "categories"
  mood = "moods"

Note: If taxonomies session exists in config.toml file, then tag and category also must be included.

  • Restart hugo server

Template Basics

Theme template

Templates are in themes/ga-hugo-theme/layouts/_default. The list.html is for list page, the single.html is for single page.

Custome template

List page

Create in layouts/_default/list.html

<html>
<head>
  <meta charset="UTF-8">

  <title>Document</title>
</head>
<body>
  {{.Content}}
  <ul>
    {{ range .Pages }}
      <li><a href="{{.URL}}">{{.Title}}</a></li>
    {{end}}
  </ul>
</body>
</html>

Single page

Create in layouts/_default/page.html

<html>
<head>
  <meta charset="UTF-8">

  <title>Document</title>
</head>
<body>
  <h1>Header</h1>
  <h3>{{.Title}}</h3>
  <h4>{{.Date}}</h4>
  {{.Content}}
  <h2>Footer</h2>
</body>
</html>

Home page

Home page is a special list page. Create in layouts/index.html.

Section template

Create in layouts/_default/<dir_name>/single.html, which replace the template for files in <dir_name>.

Base Templates & Blocks

Create in layouts/_default/baseof.html, it is the template for all pages.

<html>
<head>
  <meta charset="UTF-8">

  <title>Document</title>
</head>
<body>
  Top of baseof
  <hr>
  {{ block "main" . }}

  {{end}}

  {{ block "footer" . }}
  <br>
  {{end}}
  <hr>
  Bottom of baseof
</body>
</html>

The create layouts/_default/single.html

{{ define "main" }}
  This is the single template
{{end}}
{{ define "footer" }}
  This is the single footer
{{end}}

The create layouts/_default/list.html

{{ define "main" }}
  This is the list template
{{end}}

Note: no overwrite in layouts/_default/list.html for footer.

Variables

Hugo variables only can be used in templates in layout.

For example:

<h1 style="color: {{ .Params.color }}">Single Template</h1><hr><br>

{{ .Title }} {{ .Date }} {{ .URL }}
{{ .Params.myVar }}

The myVar and color are custom variables defined in markdown file

---
myVar: "myValue"
color: "blue"
---

Define in template

{{ $myVarName := "string value" }}

<h1>{{ $myVarName }}</h1>

https://gohugo.io/variables

Function

Hugo functions only can be used in templates in layout.

{{ funcName param1 param2 }}

For example:

{{ truncate 10 "This is a very long string" }}
{{ add 1 5 }}
{{ sub 1 5 }}
{{ singularize "dogs" }}
{{ range .Pages }}
  {{ .Title }} <br>
{{ end }}

https://gohugo.io/functions

If Statements

{{ $var1 := "dog" }}
{{ $var2 := "cat" }}

{{ if not (eq $var1 $var2) }}
  True
{{ else if ... }}
{{ else }}
  False
{{ end }}
{{ if and (lt $var1 $var2) (lt $var1 $var3) }}
eq
lt
le
gt
ge
not
{{ $title := .Title }}
{{ range .Site.Pages }}
  <li><a href="{{.URL}}" style="{{ if eq .Title $title }} background-color:yellow; {{end}}">{{.Title}}</a></li>
{{ end }}

Data Files

The JSON, YAML, TOML files, for example, data/states.json

{{ range .Site.Data.states }}
  {{.name}} <br> {{.capital}}
{{end}}

Partial Templates

Header, footer, etc. Create file in layouts/partials/header.html

<h1>{{.Title}}</h1>
<p>{{.Date}}</p>
<hr>
<br>

In layouts/_default/single.html

{{ partial "header" . }}
<h1>Single Template</h1><hr><br>

Note: The . after "header" is indicating the entire scope of variables.

Custom dictionary

<h1>{{.myTitle}}</h1>
<p>{{.myDate}}</p>
<hr>
<br>

In layouts/_default/single.html

{{ partial "header" (dict "myTitle" "myCustomTitle" "myDate" "myCustomDate")}}
<h1>Single Template</h1><hr><br>

Note: It is the same as myTitle := myCustomTitle, myDate := myCustomDate

Shortcode Templates

Shortcode is used in markdown file, created in layouts/shortcodes.

Using variable name

For example, layouts/shortcodes/myshortcode.html

This is my shortcode file.
<p style="color: {{.Get `color`}}">This is the frameworks text</p>

Note: the " is replaced to "`"

In markdown file

{{< myshortcode color="blue">}}

Using variable number

For example, layouts/shortcodes/myshortcode.html

This is my shortcode file.
<p style="color: {{.Get 0}}">This is the frameworks text</p>

Note: the " is replaced to "`"

In markdown file

{{< myshortcode blue >}}

Multiple lines

In markdown file

{{< myshortcode >}}
  This is the text inside the shortcode tags
{{< /myshortcode >}}

In template file

<p style="background-color: yellow;">{{.Inner}}</p>

If you want to use markdown in shortcode, do follow

{{% myshortcode %}}
  **bold text**
{{% /myshortcode %}}

Building your site

huge server -D

If you just want to build the site into public folder

huge

Note: delete public folder first.

References

Hugo - Static Site Generator | Tutorial

Learning – Hexo – Static Site Generator

Learning - Hexo - Static Site Generator

Installation

Install NodeJS and git.

node -v
git version

Install Hexo

npm install -g hexo-cli
hexo -v

Init project

hexo init ga-hexo

This will create a folder called ga-hexo.

Run

cd ga-hexo
hexo server

The server will be run as http://localhost:4000.

Directories

node_modules - used by NodeJS
scaffolds - content templates
source - content folder
source/_post - the posts
themes - themes used
themes/landscape - default theme
_config.yml - define default theme, etc.
.gitignore
db.json
package.json - used by NodeJS package

Write content

Create post

Posts are written in source/_post.

cd ga-hexo
hexo new <page_name>

This will create a file source/_post/<page_name>.md

Create draft

hexo new draft <page_name>

This will create a file source/_draft/<page_name>.md. To show draft in hexo server, run following command

hexo server --draft

Publish draft

hexo publish <page_name>

Create page

hexo new page <page_name>

This will create a folder with index.html file source/<page_name>/index.md. To show page in hexo server, access URL http://localhost:4000/<page_name>/

Default type of page

The default layout can be found in _config.yml file as below

default_layout: post

If modify it as below,

default_layout: draft

Then the following command will create draft

hexo new <page_name>

Front Matter

Can be YAML or JSON

title: A's Title
date: 2017-09-14 15:11:10
tags: [ Tag1, Tag2, Tag3 ]

Scaffolds

In scaffolds folder, has 3 files as template, draft.md, page.md and post.md.

For example the post.md file.

---
title: {{ title }}
date: {{ date }}
tags:
---

Default content

Custom scaffold file

For example scaffolds/giraffee.md

title: {{ title }}
date: {{ date }}
layout: {{ layout }}

Following command

hexo new griaffe f

Will create following file

title: f
date: 2017-09-14 15:11:10
layout: griaffe

Tags & Categories

Tags

tags: [ Tag1, Tag2, Tag3 ]

The page will be

http://localhost:4000/tags/Tag1/

Categories

Categories:
- [Cat1, Cat1.1]
- [Cat2]
- [Cat3]

Tag Plugins

{% codeblock lang:javascript %}
  alert("Hello world");
  var myVar = "Hello World";
{% endcodeblock %}
{% youtuble hY7m5jjJ9mM %}

Asset Folders

The asset folders are static file folders.

To use asset folders, set following line in _config.yml file.

post_asset_folder: true

When run following command, source/_posts/a.md and source/_posts/a/ are created.

hexo new a

To use asset, following statement can be used in markdown file

{% asset_img hexo.jpg Hexo Logo %}

Note: Hexo Logo is the title of image.

To link to asset, use following line

{% asset_link hexo.jpg Hexo Logo %}

To use image path, use following line

{% asset_path hexo.jpg %}

Theme

Install theme

Themes can be downloaded from https://hexo.io/themes/index.html.

Then change _config.yml file as below

theme: alpha-dust

Restart the hexo server.

Create own theme

  • Create folder themes/ga-theme

  • Create file themes/ga-theme/config.yml

  • Create folders

themes/ga-theme/languages
themes/ga-theme/layout
themes/ga-theme/scripts
themes/ga-theme/source
  • Modify _config.yml file
theme: ga-theme
  • Restart hexo server

A blank page will be displayed.

Layout

layout.ejs

Layout is in themes/ga-theme/layout.

  • Create a file called layout.ejs in layout folder.
<html>
...
<body>
  This is the layout.ejs file
  <br>
  <%- body %>
  <br>
  This is the layout.ejs file
</body>
</html>

index.ejs

  • Create another file called index.ejs in layout folder.
This is a index.ejs file.

Then the output will be as follow for all index files, such as http://localhost:4000/, http://localhost:4000/a/, http://localhost:4000/2017/09/13/a/, etc.

This is the layout.ejs file
This is a index.ejs file.
This is the layout.ejs file

So all the pages will use layout.ejs file. And the <%- body %> will be replaced by actual ejs file. In above case, it is index.ejs file.

post.ejs

Create a file called themes/ga-theme/layout/post.ejs

<h1>This is a post</h1>

This will be used for all posts.

page.ejs

Create a file called themes/ga-theme/layout/page.ejs, this will impact all pages.

<h1>This is a page</h1>

tag.ejs

<h1>This is a tag</h1>

category.ejs

Partials

  • Create a folder themes/layout/partial

  • Then create a header.ejs in partial folder.

<h1><%= title %></h1>
<hr> <br>
  • In layer.ejs file
<html>
...
<body>
  <%- partial('partial/header', {title: 'hello world'}) %>
  <br>
  <%- body %>
  <br>
  Footer
</body>
</html>

Variables

Access front matter variable inside layout. For example, in post.ejs

<h1><%- page.title %></h1>
<p><%- page.date %></p>
<hr>
<%- page.content %>

The variables can be found in https://hexo.io/docs/variables.html

Custom variable

<%- page.author %>

If statement

<% if (page.author == "Mike") { %>
  Mike is the author, he's the best
<% } else { %>
  this author sucks
<% } %>

For Loops

<% site.posts.forEach(function(post) { %>
  <li><a href="<%- post.path %>"<%- post.title %></a></li>
  <br>
<% }) %>

Helper

Helpers are small functions.

<%- trim('    This is my string   ') %>
<%- titlecase('This is my string') %>
<%- date(Date.now(), 'YYYY/M/D') %>
<%- date(Date.now(), 'h:mm:ss a') %>

Helps can be found in https://hexo.io/docs/helpers.html.

Data Files

Create folder source/_data, and data file can be JSON or YAML file. Such as myData.yml

var1: "Var1's Value"
var2: "Var2's Value"
var3: "Var3's Value"

In template, such as index.ejs

<%- site.data.myData.var1 %> <br>
<%- site.data.myData.var2 %> <br>
<% for (var value in site.data.myData) { %>
  <%- value %> <br>
<% } %>
<br>
<% for (var value in site.data.myData) { %>
  <%- site.data.myData[value] %> <br>
<% } %>

Plugins

Download from https://hexo.io/plugins/.

Normally, use npm install to install plugin. For example,

npm install hexo-admin

After installed, go to _config.yml, make sure that plugin is listed in dependencies.

Generating Site

Run following command to generate website

hexo generate

The site will be created in public folder.

References

Hexo - Static Site Generator

Learning – Gatsby – Static Site Generator

Learning - Gatsby - Static Site Generator

Installation

  • Install NodeJS
node --version
npm --version
  • Install gatsby
npm install --global gatsby-cli
gatsby --version

Create site

gatsby new ga_site https://github.com/gatsbyjs/gatsby-starter-hello-world

A new folder called ga_site is created.

Run

npm run develop

The site will be run on http://localhost:8000

Folders

node_modules - Used by NodeJS.
public - the site to be published
src - pages
src/pages/index.js - start up page
package.json - define NodeJS packages

Adding Content

In index.js, there are ReactJS scripts.

import React from "react"

export default() =>
  <div style={{color: 'tomato', backgroundColor: 'blue'}} >
    <h1>Hello world!</h1>
    <p>This is a paragraph</p>
  </div>

Note: When writing contents in ReactJS, only one HTML element can be used in one statement.

Linking Pages

Add Link in index.js

import React from "react"
import Link from "gatsby-link"

export default() =>
  <div style={{color: 'tomato', backgroundColor: 'blue'}} >
    <h1>Hello world!</h1>
    <p>This is a paragraph</p>
    <Link to="/page-2/">Page 2</Link>
  </div>

Create a file src/pages/page-2.js

import React from "react"
import Link from "gatsby-link"

export default() =>
  <div style={{color: 'tomato', backgroundColor: 'blue'}} >
    <h1>Page 2</h1>
    <Link to="/">Go Home</Link>
  </div>

Note: Pages also can be in sub directory

Interactive Pages

Create a file called src/pages/counter.js.

import React from "react"

class Counter extends React.Component {
  constructior() {
    super()
    this.state = { count: 0 }
  }
  render() {
    return
      <div>
        <h1>Counter</h1>
        <p>current count: {this.state.count}</p>
        <button onClick={() => this.setState({ count: this.state.count + 1})}>plus</button>
        <button onClick={() => this.setState({ count: this.state.count - 1})}>minus</button>
      </div>
  }
}

Building site

npm run build

The site will be built in folder public.

Components

Import a component in another page, such as index.js

import React from "react"
import Link from "gatsby-link"
import Counter from "./counter.js"

export default() =>
  <div style={{color: 'tomato', backgroundColor: 'blue'}} >
    <h1>Hello world!</h1>
    <p>This is a paragraph</p>
    <Link to="/page-2/">Page 2</Link>

    <Counter />
  </div>

Component Parameters

In index.js file

<Counter color="blue" />

Read parameter in counter.js.

{this.props.color}

or

<div style={{ clor: this.props.color}}>
  ...
</div>

Plugins

Plugins can be downloaded from https://www.gatsbyjs.org/docs/plugins

For example, install typography plugin

npm install --save gatsby-plugin-typography

Create gatsby-config.js file.

module.exports = {
  plugins: [`gatsby-plugin-typography`]
}

Then restart website

gats develop

Layouts

Create default layout in src/layouts/index.js

import React from "react"

export default({ children }) =>
  <div>
    <h1>Header</h1>
    {children()}
    <h3>Footer</h3>
  </div>

Site Data

Create a file called gatsby-config.js.

  module.exports = {
    siteMetadata: {
      title: "Giraffe Academy's Website",
      author: Mike,
    }
  }

In index.js, use graphql

import React from "react"

export default({data}) => (
  <div style={{color: 'tomato', backgroundColor: 'blue'}} >
    <h1>{data.site.siteMetadata.title}</h1>
    <p>{data.site.siteMetadata.author}</p>
  </div>
)

export const query = graphql`
  query FirstQuery {
    site {
      siteMetadata {
        title
        author
      }
    }
  }
`

Restart gatsby server.

Testing GraphiQL page

Access http://localhost:8000/___graphql

{
  site {
    siteMetadata {
      title
      author
    }
  }
}

File Data

Install gatsby-source-filesystem plugin

npm install --save gatsby-source-filesystem

Update gatsby-config.js

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `src`,
        path: `${__dirname}/src/`,
      }
    }
  ]
}

Restart gatsby server.

Use GraphiQL to query file or allFile

{
  allFile {
    edges {
      node {
        id
      }
    }
  }
}

Note: after key in allFile, press Control-Enter or Command-Enter, the information will be auto filled up.
Note: If delete id, press Control-Space, the available attributes will be shown.

For example,

node: {
  extension
  relativePath
}

Update index.js

import React from "react"

export default({data}) => (
  <div style={{color: 'tomato', backgroundColor: 'blue'}} >
    <table>
      <thead>
        <tr>
          <th>relativePath</th>
          <th>prettySize</th>
          <th>extension</th>
          <th>birthTime</th>
        </tr>
      </thead>
      <tbody>
        {data.allFile.edges.map({ node }) =>
          <tr>
            <td>
              {node.relativePath}
            </td>
            <td>
              {node.prettySize}
            </td>
            <td>
              {node.extension}
            </td>
            <td>
              {node.birthTime}
            </td>
          </tr>
        }
      </tbody>
    </table>
  </div>
)

export const query = graphql`
  query MyFilesQuery {
    allFile {
      edges {
        node {
          relativePath
          prettySize
          extension
          birthTime(fromNow: true)
        }
      }
    }
  }
`

Working with Markdown

Install plugin gatsby-transformer-remark

npm install --save gatsby-transformer-remark

Update plugin session in gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-transformer-remark`,
  ]
}

In GraphiQL, can see two new options

markdownRemark
allMarkdownRemark

Create markdown file src/pages/myFirstPost.md

---
title: "My Markdown File"
author: "Mike"
---

This is the content in my markdown

To access attributes in markdown file

id
html
formatmatter
formatmatter {
  title
  date
  parent
}

References

Gatsby - Static Site Generator | Tutorial

Learning – Middleman – Static Site Generator

Learning - Middleman - Static Site Generator

Install

  • Install Ruby
ruby --version
  • Install middleman
gem install middleman
middleman version

Install on Mac

Install xcode on Mac

xcode-select --install

Fix version issue in Mac

If encounter error on Mac indicating version low, run following command to install ruby version manager

curl -L https://get.rvm.io | bash -s stable

Restart terminal and run following command

rvm install ruby-2.4.2
rvm --default use ruby-2.4.2
ruby -v

Creating a site

middleman init ga_site

Then a folder called ga_site is created.

Run

middleman server

Access website via http://localhost:4567

Folders

source
source/images
source/javascripts
source/layouts
source/layouts/layout.erb - default layout
source/stylesheets
source/index.html.erb - default home page
config.rb
Gemfile

Activate LiveReload

LiveReload is able to reload the pages when they changed.

In config.rb file, add following line.

...
page...
...

activate :livereload

In Gemfile, add following line

gem 'middleman-livereload'

In project file, run following command

bundle install middleman-livereload

Restart middleman server

middleman server

Create html file ga_site/source/a.html.erb or ga_site/source/dir1/b.html.erb, then can access file from browser.

Note: If use .html without .erb, then it will not use default template.

Create markdown file as ga_site/source/c.html.md.erb.

Front Matter

Front matter can use JSON or YAML

YAML

---
title: Middleman is Running
author: "Mike"
---

JSON

;;;
"title": "New Title"
;;;

Access front matter variable

<%= current_page.data.author %>

Layouts

All pages are using source/layouts/layout.erb to display erb files. So, can modify the layout.erb file to change layout.

Helper Methods

Helpers are small codes can be used in markdown file.

<%= link_to 'Giraffe Academy', 'http://giraffeacademy.com' %>

To include ga_site/sources/styllesheets/ga_style

<%= stylesheet_link_tag 'ga_style' %>

To include ga_site/sources/javascripts/ga_code.js

<%= javascript_include_tag 'ga_code' %>

To include ga_site/source/images/log.png

<%= image_tag 'logo.png' %>

Other helpers can be found in middlemanapp.com

Layout

Default layout

The file ga_site/source/layouts/layout.erb is for all pages.

Specific layout

Create another layout2.erb file, then can be used in other markdown file using front matter as below a.html.erb.

---
layout: layout2
---

This is a.html

Layout for one directory

If want to specify all files in one directory use specific layout, can configure in config.rb file as below.

page '/dir1/*', layout: 'layout2'

Wrap layout

For example, the layout2 needs to use default layout and wrap it around, can be defined in layout2.erb file

<% wrap_layout :layout do %>
  New Layout <br> <hr>
  <%= yield %>
  <br><hr>New Layout
<% end %>

Partial

  • Create a folder ga_site/source/partials/, this is not mandatory.

  • Create partial file called _header.erb file, the leading _ is mandatory.

<h1>This is the Title</h1>
<br><hr>

To include in layout.erb file,

<%= partial 'partials/header' %>

Note: the leading _ and the tailing .erb are not shown in this statement, middleman knows how to find the file.

Passing variable

In layout.erb file

<%= partial(:'partials/header', :locals => { :title => 'My Title', :color => "blue" })

In _header.erb file

<h1 style="color: <%= color %>"><%= title %></h1>
<br><hr>

Data Files

Create a data folder as ga_site/data, create data file can be YAML or JSON files. For example test.yml

dog: "Golden Retriever"
products:
  - Tooth Brush
  - Shampoo
  - Soap

In layout.erb or index.erb file

<%= data.test.dog %>

or

<% data.test.products.each do |f| %>
  <li><%= f %></li>
<% end %>

If Statement

<% if current_page.data.title == "A" and current_page.data.author %>
  This is the A file
<% elsif current_page.data.title == "B" %>
  This is the B file
<% else %>
  This is not A or B
<% end %>

Sitemap

In layout.erb file

<% sitemap.resources.each do |f| %>
  <% if f.content_type.include? "html" %>
    <% f.data.title %> <br>
    <% f.path %> <br>
    <% f.content_type %> <br>
    <li><a href="<%= f.path %>"><%= f.data.title %></a></li>
  <% end %>
<% end %>

or

<% current_resource.parent.data.title %>

<% current_resource.siblings.each do |f| %>
  <% f.data.title %> <br>
<% end %>

<% current_resource.children.each do |f| %>
  <% f.data.title %> <br>
<% end %>

Building a Blog

First install blog generator

gem install middleman-blog

Initialize blog site

middleman init myBlog --template=blog

Start middleman blog server

cd myBlog
middleman server

Any file wants to be blog needs to be named as YYYY-MM-DD-title.md or created by following command

middleman article MyNewArticle
middleman article MyNewArticle

Also can add tags in the article

tags: example

or

tags: [example, tag2]

To list down all articles

<% blog.articles[0...10].each do |article| %>

<% end %>

Project Templates

Go to https://directory.middlemanapp.com/#/templates/all to download middleman templates

Once select the good one, run command

middleman init -T <template> <folder_name>

For example

middle init -T dtcristo/middleman-cactus cactus

Pretty URLs

Normally, to remove the extension of file in URL, create a folder, move the file in the folder, and rename the file as index.html.erb.

This can be done using configuration in config.rb

activate :directory_indexes

Install Extensions

Download extension from https://directory.middlemanapp.com/#/extensions/all website.

First, update Gemfile, add gem

gem 'middleman-minify-html'

The add configure in config.rb file

activate :minify_html

Then install gem

bundle install

Building Site

Run following command

middleman build

Then the website will be built in build directory.

References

Middleman - Static Site Generator | Tutorial

Learning – Dockerfile

Learning - Dockerfile

This is to refresh my Dockerfile knowledge.

alphine

Create Dockerfile

FROM alphine:3.4
MAINTAINER Mark Takacs mark@takacsmark.com

RUN ark update
RUN ark add vim
RUN apk add curl

Build

docker build -t taka/alpine-smarter:1.0 .

Intermediate images

The Docker intermediate images can speed up the rebuilding process.

docker images -a

To reduce number of intermediate images, update Dockerfile as below

FROM alphine:3.4
MAINTAINER Mark Takacs mark@takacsmark.com

RUN ark update && \
       ark add vim && \
       apk add curl

Clean up dangling image

docker images --filter "dangling=true"
docker rmi $(docker images -q --filter "dangling=true")

python

Dockerfile

  • For normal python
FROM python:3.6.1

RUN pip install numpy
  • For alpine version takes much longer time to build
FROM python:3.6.1-alpine

RUN apk update && apk add build-base
RUN ln -s /usr/include/locale.h /usr/include/xlocale.h

RUN pip install numpy scipy

conda3

  • miniconda3
docker run --rm -ti continuumio/miniconda3 /bin/bash
conda list
conda install numpy
  • anaconda3
docker run --rm -ti continuumio/anaconda3 /bin/bash
conda list

phpslim

Dockerfile

Choose php:7.1.2-apache

Got to https://getcomposer.org, and run installation commands in container

FROM php:7.1.2-apache

RUN ....

COPY ./composer.json /var/www/html/

RUN apt-get update & apt-get install -y git

RUN composer install

Map directory

To run docker container with option -v /var/www/html/vendor to indicate using image /var/www/html/vendor folder.

docker run --rm -v $(pwd):/var/www/html/ -v /var/www/html/vendor -p 80:80 takacsmark/phpslim-tut:1.0

As the result, the /var/www/html is mapped to local directory, but /var/www/html/vendor is image directory.

Change DocumentRoot

Add following line into Dockerfile to change the DocumentRoot to /var/www/html/public

RUN set -i 's/DocumentRoot.*$/DocumentRoot \/var\/www\/html\/public/' /etc/apache2/sites-enabled/000-default.conf

Enable default hello message

Create a file /var/www/html/public/.htaccess

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Run following command in container to enable module rewrite

a2enmod rewrite

Then add following line in Dockerfile

RUN a2enmod rewrite

Options

COPY

COPY ./config.txt /usr/src/config.txt

ADD

  • Add URL file from internet

  • Add tar file

  • Add compressed file

RUN

EXPOSE

Port

USER

WORKDIR

ARG

LABEL

ENTRYPOINT & CMD

To run command echo Welcome after container created, following configuration can be used.

CMD "Welcome"
ENTRYPOINT echo

The CMD option can be replace using docker run command line, ENTRYPOINT can't. For example, following command will run echo Hello.

docker run echo_image 'Hello'

ENV

References

Dockerfile Tutorial by Example - ( Part I - Basics )
Dockerfile Tutorial by Example - ( Part II - Best practices )
Dockerfile Tutorial by Example - ( Part III - Creating a docker PHP Slim image )

Learning – Docker Swarm – Basic

Learning - Docker Swarm - Basic

Files

server.js

const express = require("express");
const os = require("os");

const app = express();

app.get("/", (req, res) => {
  res.send("Hello from Swarm " + os.hostname());
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

Dockerfile

FROM node:11.1.0-alpine

WORKDIR /home/node

COPY . .

RUN npm install

CMD npm start

docker-compose.yml

version: "3"

services:
  web:
    build: .
    image: takacsmark/swarm-example:1.0
    ports:
      - 80:3000
    networks:
      - mynet
    deploy:
      replicas: 6
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]

networks:
  mynet:

requirements.txt

flash
redis

Nodes

docker swarm init
docker swarm init --advertise-addr eth1
docker swarm join ...
docker swarm leave -f
docker demote
docker promote

Stack

docker stack ls
docker stack ps

Services

docker service ls
docker service ps
docker service create

To run a service in all nodes

docker service create --name 'service_name' -p 8000:8000 --mode global demoapp

To run a server in 3 nodes

docker service create --name 'service_name' -p 8000:8000 --replicas 2 demoapp

Tasks

It is scheduling the container, which is managed using docker service command.

docker service ps nodeapp_web

Container

The containers should be managed using swarm commands, but they also can be seen using container command.

docker ps
docker kill

Deploy

docker stack deploy -c docker-compose.yml nodeapp

This will create network nodeapp_mynet, and two services (nodeapp_web and nodeapp_db)

Note: The images need to be pre-built using docker-compose build command, and push into docker hub.

List

docker stack ls
docker service ls
docker stack services ls

scale

docker service scale nodeapp_web=4

To access all 4 services, just need to access http://localhost, then the 4 services will be supporting same port on localhost.

Note: The hostname appeared in http://localhost is changing

Sample of overlay network

  • Create overlay network
docker network create -d overlay myoverlay1
  • Create webapp on overlay network
docker service create --name webapp1 -d --network myoverlay1 -p 8001:80 test/webapp
  • Create database on overlay network
docker service create --name mysql -d --network myoverlay1 -p 3306:3306 test/mysql:5.5

Sample with docker-machine

Docker machine is to create a virtual machine with minimum Linux packages installed with docker running.

docker-machine ls
docker-machine start myvm1
docker-machine start myvm2
docker-machine ssh myvm1

Init swarm

docker swarm init --advertise-addr eth1
docker swarm join --token ...

Push image

docker-compose push

Access master from docker machine

To access docker swarm master in docker machine from other host

docker-machine env myvml
eval $(docker-machine env myvm1)

Deploy

docker stack deploy -c docker-compose.yml nodeapp
docker stack ls
docker stack services nodeapp
docker-machine ls

After the services started, all nodes, including master and worker nodes, will provide the services by routing to the correct host.

replicas and parallelism

    deploy:
      replicas: 6
      update_config:
        parallelism: 2
        delay: 10s

This indicates total of 6 replicas, and deploy 2 in parallel with 10 seconds delay.

constraints

    deploy:
      placement:
        constraints: [node.role == manager]

This indicates that only manager node will be deployed.

Scale

docker service scale nodeapp_web=4

Monitor

docker stack deploy -c docker-compose.monitoring.yml nodemon
docker stack ls
docker stack services nodemon

The service nodemon_visualizer running on port 8080

Deploy to specific node

Label

docker node update myvm1 --label-add db=mongo
docker node inspect myvm1 -f {{.Spec.Labels}}

placement

    deploy:
      placement:
        constraints: [node.labels.db == mongo]

Redeploy

docker stack deploy -c docker-compose.yml nodeapp

Drain & Active

docker node update --availability drain myvm2
docker node update --availability active myvm2

force update

docker node update --availability=active myvm2
docker service update --force nodeapp_web

Change version number

Update image: option in docker-compose.yml file

Then build again

docker-compose build
docker-compose push
docker stack deploy -c docker-compose.yml nodeapp
docker stack ps nodeapp

Then the containers will redeploy 2 at each time.

Cloud

AWS supports Docker Swarm.

References

Introduction to Docker Swarm | Tutorial for Beginners | Examples | Typical Tasks (Video)

Docker Swarm Tutorial | Code Along | Zero to Hero under 1 Hour

Learning – Docker Prune

Learning - Docker Prune

Container

docker container prune
docker stop $(docker ps -q)
docker container ls -a
docker container prune -f
docker --rm ...
docker swarm

Image

docker image ls
docker image prune -f

Network

docker network ls
docker network prune -f

Volume

docker volume ls
docker volume prune -f

System

docker system prune
docker system prune --volumes
docker system prune --volumes --all
docker system prune --volumes --help

References

Docker prune explained - usage and examples

Learning – Docker Swarm Network Drivers

Table of Contents

Learning - Docker Swarm Network Drivers

Bridge

The default network driver. Needs to map the port to host in order to access port of container.

Host

Removes network isolation between the container and the Docker host, and uses the host's networking directly. So the containers can not have port conflicting with other containers and also host.

The IP will be the same as host.

None

Disables all networking for containers. Usually used in conjunction with a custom network drive.

Overlay

Connect multiple Docker daemons together and enable swarm services to communicate with each other daemons.

Using this overlay network, the container on different hosts can communicate with each other.

Macvlan

Allow you to assign a MAC address to a container, making it appears as a physical device on the network. The Docker daemon routes traffic to container by their MAC addresses.

This allows container has different IP address on the host network.

Learning – Ansible Quick Start

Learning - Ansible - Quick Start

Install (CentOS)

yum update -y
yum install epel-release -y
yum install ansible -y

Add managed hosts

Add following line in /etc/ansible/hosts

[linux]

45.56.72.153
45.79.56.223

[linux:vars]

ansible_user=root
ansible_password=P@ssword123

Disable host key checking

Edit ansible.cfg file as below

host_key_checking = false

Run module

ping

ansible linux -m ping

Run ad hoc command

ansible linux -a "cat /etc/os-release"
ansible linux -a "reboot"

Playbook

Playbook => Plays => Tasks

Servers

For example, following yaml file iluvnano.yml

---
  - name: iluvnano
    host: linux
    tasks:
      - name: ensure nano is there
        yum:
          name: nano
          state: latest

The ilvunano is Play, the ensure nano is there is Task.

Run playbook

ansible-playbook iluvnano.yml

If change the state to absent in iluvnano.ymland rerun above command, the nano will be removed.

Routers

In /etc/ansible/hosts file

[routers]

ios-xe-mgmt-latest.cisco.com
ios-xe-mgmt.cisco.com

[routes:vars]

ansible_user=developer
ansible_password=C1sco12345
ansible_connection=network_cli
ansible_network_os=ios
ansible_port=8181

In /etc/ansible/ansible.cfg

host_key_checking = false

Run commands

ansible routers -m ping
ansible routers -m ios_command -a "commands='show ip int brief'"

Playbook devnet.yml

---
  - name: General Config
    hosts: routers
    tasks:
    - name: Add Banner
      ios_banner:
        banner: login
        text: |
          Nicolas Cage is the
          Tiger King
        state: present
      - name: Add loopback
        ios_interface:
          name: Loopback21
          state: present

Run following command

ansible-playbook devnet.yml

To remove the changes, update the state in yaml file

state: absent

Run following command again

ansible-playbook devnet.yml

Then the routers will be modified.

show ip int brief
show run | beg banner

References

you need to learn Ansible RIGHT NOW!! (Linux Automation)
get started with Ansible Network Automation (FREE cisco router lab)