Panjie SW's Blog

Static Blog with Pelican
Panjie SW

Static Blog with Pelican

Been months since my last blog post lol :)) I'll begin to cover my lack of writing enthusiasm by writing about, well, how i create this blog, a happy static site hosted in Github Pages.

Pelican is a static site publishing tool. This blog of mine is one of running example using Pelican. This awesome Python based tool is simple, tidy while also has the power to be extendable and scalable. Combine it with gh-pages of Github and you will get a fun and free blogging experience without the much more bloated system in conventional CMS.

You can see a list of Pelican feature at their blog. Some of features i'd like to mention are Markdown + rst content writing and Code syntax highlighting. Markdown and rst are a pleasant to work in writing content. Instead of HTML tag or button-clicking / shortcut remembering in WYSIWYG rich text editor, they let you concentrate in making a great writing with a little syntax. And as this blog will mostly be filled with techie code things (among my other randomness), code syntax highlighting of Pygments will surely comes in handy. Already have a site of contents published in WordPress? Pelican can import it to static Pelican compatible contents. GREAT!! Enough blabbering and let's get started :)

Jumps To

Requirements

  • Python >=2.6
  • Github account
  • Git
  • Writing and tinkering enthusiasm :p

Setting Up Github Account and Github Pages

We'll use Github Pages as our static content host. As it's static, your contents can easily be hosted anywhere. But with Git, you can track and get version history of your contents. Besides, it's free!!

Go to Github and sign a new account up if you haven't had one, then create a new repository for our blog. For this tutorial i'll name it blog.

Now, for our blog to be accessible, we need to create a new branch called gh-pages and place our site content (.html, .css files, images, etc.) into it. Later, your blog will have a URL like http://youraccount.github.io/blog.

Personally, i'd like to organize my branches like so:

// in 'master' (default branch)
blog
├── .gitignore
├── local_settings.py
├── local_settings_dev.py
├── README.md
├── requirements.txt
├── settings.py
├── plugins
    ├── plugin_one.py
    ├── other_plugin.py
├── src
    ├── posts
        ├── 2014
            ├── 01
                ├── some-post.md
                ├── another-post.md

And in gh-pages branch, includes all above structures plus generated static content in root blog directory. Messy, don't you think? Don't worry, we'll do almost all work in master branch so it's ok to have a messy gh-pages, i think.

Setting Up Our Development Environment

If you already have pip and virtualenv set, then you can skip to the next section.

  • Install setuptools. Download ez_setup.py and run it using target Python version. This will install easy_install CLI command to manage packages.

    wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | python
    

    or, to install to the system Python

    wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | sudo python
    
  • Install pip. Using easy_install above, use superuser privileges if needed.

    easy_install pip
    
  • Install virtualenv. Virtualenv is an environment wrapper to run and install Python packages independent to the system Python packages.

    pip install virtualenv
    
  • Create a new virtual environment for our blog project. In this tutorial, i'll name it blog-env.

    virtualenv blog-env
    
  • Activate the virtual environment.

    cd blog-env
    source ./bin/activate
    # to deactivate it
    deactivate
    

Installing Pelican

To get Pelican to work we'll need to install it. duh.

In our activated virtualenv blog-env, run this command

pip install pelican

The Project Structure

Inside the blog-env folder, create a folder for our blog project, i'll call it blog same as our repository name we created earlier. Then we need to setup a local git in this folder.

cd blog
# Initialize git
git init .
# Add remote origin
git remote add origin [email protected]:youraccount/blog.git

Create a directory structure of blog like in master branch organization above. I'll explain each of its use.

  • .gitignore, place here any path you don't want to include in your git repository. Here is the example content you can use.

    local_settings*.py
    output
    
    # Python
    *.py[cod]
    
    # C extensions
    *.so
    
    # Packages
    *.egg
    *.egg-info
    dist
    build
    eggs
    parts
    bin
    var
    sdist
    develop-eggs
    .installed.cfg
    lib
    lib64
    
    # Installer logs
    pip-log.txt
    
    # Unit test / coverage reports
    .coverage
    .tox
    nosetests.xml
    
    # Translations
    *.mo
    
    # Mr Developer
    .mr.developer.cfg
    .project
    .pydevproject
    
  • local_settings.py, contains Pelican settings used in deploying the blog, while local_settings_dev.py is used in development. Like this, we can customize the settings both when in development, and when we're ready to publish in gh-pages branch.

    We don't want to include this in our repo because it may contain personal data.

    You can see all Pelican available settings here.

  • settings.py, contains the same contents as local_settings* except we strip all value from it. We'll make this as our reminder of how our settings are defined and include it in our repository. Here is an example of settings.py contents

    # -*- coding: utf-8 -*-
    
    AUTHOR = 'xxxxxxxx xxxxxxxxxxxxx'
    SITENAME = "xxxxxxxxxxxx"
    SITEURL = 'http://xxxxxxxxxxxxxxxxx'
    TIMEZONE = "Europe/Madrid"
    
    GITHUB_URL = 'http://github.com/XXXXX'
    DISQUS_SITENAME = "XXXXXXXXXX"
    EMAIL = "[email protected]"
    PDF_GENERATOR = False
    REVERSE_CATEGORY_ORDER = True
    LOCALE = 'en_US'
    DEFAULT_PAGINATION = 5
    
    THEME = "iris"
    
    FEED_RSS = 'feeds/all.rss.xml'
    CATEGORY_FEED_RSS = 'feeds/%s.rss.xml'
    
    LINKS = (('XXXXX XXXX', 'http://YYYYYYYYYYY.ZZZ'),)
    
    SOCIAL = (('twitter', 'http://twitter.com/XXXXXX'),
              ('linkedin', 'http://www.linkedin.com/in/XXXXXXX'),
              ('github', GITHUB_URL),)
    
    OUTPUT_PATH = 'output'
    PATH = 'src'
    
    ARTICLE_URL = "posts/{date:%Y}/{date:%m}/{slug}/"
    ARTICLE_SAVE_AS = "posts/{date:%Y}/{date:%m}/{slug}/index.html"
    
    GOSQUARED_SITENAME = "XXX-YYYYYY-X"
    
    # global metadata to all the contents
    #DEFAULT_METADATA = (('yeah', 'it is'),)
    
    # static paths will be copied under the same name
    STATIC_PATHS = ["images", ]
    
    # A list of files to copy from the source to the destination
    #FILES_TO_COPY = (('extra/robots.txt', 'robots.txt'),)
    
  • plugins folder contains all Pelican plugins used in our blog.

  • src folder contains all Pelican sources which will be generated to static contents. Inside it, there is a posts folder which contains all our blog posts organized by year, month and date of publishing.

The Workflow

Now that everything is setup, let's define a workflow to write and publish our post.

We have 2 branches in our local git repo

  • master contains the sources of the blog. We'll write a new markdown or rst file for our posts here, add images, and stuff. Basically we do almost all the work in this branch.
  • gh-pages contains the static content generated from the sources. We'll merge the sources from master branch and regenerate the static contents.

Creating a Blog Post

To create a blog post, we'll do these steps, in master branch of our repository.

  1. master -> Write the post with markdown or rst.

    mkdir -p src/posts/2014/04
    touch src/posts/2014/04/hello-world.md
    
  2. master -> Generate the static contents to default output path, which is folder output.

    pelican -s ./local_settings_dev.py
    
  3. master -> Preview the generated static contents in a local server. These commands will run a local server in localhost:8000

    cd output
    # using python 2.6 or 2.7
    python -m SimpleHTTPServer
    # using python 3.x
    python3 -m http.server
    

You can repeat those steps in development time as much as needed until you satisfied and ready to publish the contents.

Publishing Contents

When you're ready to publish your contents, we'll do these steps, in gh-pages of our repository.

  1. master -> Commit the sources change and push it to Github. This won't be the blog, it's just our sources.

    git add .
    git commit -m "Create post: Hello World"
    git push origin master
    
  2. master -> Create gh-pages branch if it's not exist yet.

    git branch gh-pages
    

    Then switch to that branch.

    git checkout gh-pages
    
  3. gh-pages -> Merge the sources from master branch.

    git merge master
    
  4. gh-pages -> Regenerate the static contents. This time we'll use local_settings.py settings file and set the output path to the root of our repository, so Github Pages can find and serve the contents directly, and because we exclude default output dir from our Git repo so that won't be available in Github.

    pelican -s ./local_settings.py -o ./
    
  5. gh-pages -> Preview it again if you like, admire it, kiss it a good luck charm, etc etc

  6. gh-pages -> Commit the changes.

    git add .
    git commit -m "Publish post Hello World, to the World"
    git push origin gh-pages
    # Remember to switch to master after publishing
    git checkout master
    
  7. Visit your Github Pages in a browser with URL http://youraccount.github.io/blog to see what world see in your blog.

  8. Do step 5 again for as much as you want, or make another post if you have lots to share to world.

What's Next

Custom Domain with Github Pages

Now maybe you want to use your more attractive domain for your blog, like this blog. Here is how to do that

  1. Create a text file named CNAME in the root dir of your gh-pages branch.

    git checkout gh-pages
    touch CNAME
    
  2. Write the domain you want to use in there.

    blog.derp.com
    
  3. Push the change.

    git add .
    git commit -m "Use blog.derp.com domain"
    git push origin gh-pages
    git checkout master
    
  4. Go to your domain's registrar or wherever you can properly add a DNS record to your domain. Create a new CNAME record and point it to youraccount.github.io.

  5. Wait a little while for the change propagate. This may take minutes or even hours.

To see a more detailed explanation, for example to setup an apex / top-level domain like derp.com instead of blog.derp.com like above, go to this Github Help Article

Automation Tool

With above workflow, it'll become much more repetitive as you develop later. We can cut number of commands we input to create / publish a post with automation tool.

There are a lot of automation / task management we can use in Pelican blog project such as

  • Fabric A Python (2.5-2.7) library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks. It provides a basic suite of operations for executing local or remote shell commands.

    For a Python based project, a Python tool like this is usefull and doesn't nedd anyother dependencies / language framework. But if you use Python > 3.0, Fabric doesn't support it, as of this time of writing. I'll update this if a version which support Python > 3.0 is available via pip.

  • Grunt JavaScript Task Runner. If you have Node.js installed, this automation tool is great for any project. Even better, someone made a plugin grunt-pelican to generate static contents with Pelican, great!! I'll write about how to do this in a post sometime.

Alternatives

Static publishing is fun and beside Pelican there are other tools you can use

  • Frozen-Flask freezes a Flask application into a set of static files. It uses Flask, a microframework for Python based on Werkzeug, Jinja 2 and good intentions.

  • Jekyll Transform your plain text into static websites and blogs. It's made with Ruby language and the one which powers the Github Pages.

  • Octopress A framework for Jekyll. It gives a basic Jekyll project with ton of features such as rake tasks to manage creation and content publishing, a semantic HTML5 template, responsive layout, Built in 3rd party support for Twitter, Google Plus, Disqus, and many more.

Tada

There you are, what a fun way to make blog isn't it? :)

Hope this helps you start your own Pelican blog. If you have any suggestion or want to share your experience, please tell me more about it, Thanks!