port
makes it easy to run a simple markdown-driven blog. There is no admin UI or database. (no need!)
You use the command-line interface to create a new site, which makes a directory where you can store different folders (folders are treated as categories), and within those folders you write markdown files.
Installation is easy:
$ pip install port
An example site is my own blog: space and times
- Supports GitHub-Flavored Markdown
- Supports MathJax syntax (in the default theme)
- Includes RSS feeds for each category and all posts
- Includes search
To create a site, use the port
command-line tool:
$ port create my_new_site
You will be walked through the basic configuration.
A folder will created at whatever directory you specified during configuration.
In there you will notice three folders:
.build
- this is where your compiled posts are stored. This folder is destroyed every build, so don't store anything important there.assets
- this is where your static files will be served from. So for example you could placemy_image.png
in this folder and then in your posts you could refer to/assets/my_image.png
.default_category
- this is the default category folder. You can rename this or replace it.pages
- this is where you can put non-post/non-category pages, also as markdown. For example, an "About" page.
port
treats any folder in this directory (except for the .build
, assets
, and pages
folders) as a "category".
Within each folder/category, including the pages
folder, you can write posts as markdown documents (with the .md
extension).
When you've added or edited documents, you need to re-build the site:
$ port build my_new_site
When writing a post, you can include any arbitrary metadata using YAML front matter, i.e. by including a section demarcated by ---
at the very top of your file.
For example:
---
published_at: 6/17/2015 20:45
draft: true
---
This will be parsed and included as part of the post
object passed to your templates (see below).
You should at least include the published_at
data; without it port
will default to using the last build time as the published at value. This can mess up your post ordering.
Other than that, port
supports GitHub-Flavored markdown, so go wild!
Pages are written exactly the same as posts - in Markdown and with optional YAML front matter as well.
To run a site that you've created, just do:
$ port serve my_new_site
You can optionally specify a port with --port
.
The main endpoints are:
/
- your index page :)/<category name>
- a category index page/<category name>/<post slug>
- a single post/rss
- the rss feed for all your posts (20 most recent published)/rss/<category name>
- the rss feed for one category (20 most recent published)/search?query=<query>
- the search endpoint/<page>
- a non-post/non-category page
The new site process will walk you through the basic configuration, which creates a yaml file in the ~/.port
folder. You can edit this yaml file to update your config, or add in arbitrary data which gets passed to your templates as a dictionary called site_data
.
- Draft posts are not listed in the category and index pages (and RSS feeds) but can be accessed by their direct url
- Posts are ordered by reverse chron
- Arbitrary category metadata can be added for each category by creating a
meta.yaml
file in the category's directory. You can override the template used for a category here and/or the posts per page value, e.g.:
template: a_special_template.html
per_page: 20
- Pages can similarly have a different template specified in their YAML front matter.
port
has support for theming - custom themes are super easy to write using Jinja.
New themes go into ~/.port/themes/
. Each theme must, at minimum, include the following templates:
category.html
- used to render category pagesindex.html
- used to render the home pagesingle.html
- used to render single post pagessearch.html
- used to render search resultspage.html
- used to render non-post/non-category pages404.html
- 404 error page500.html
- 500 error page
Within each of these templates, you have access to the following variables:
site_data
- an object consisting of the data stored in your site's yaml config file and additional metadata, such ascategories
. Note that the attribute names corresponding to keys in your site's config are lowercase (e.g. if you haveSITE_NAME
in your config, it is accessed atsite_data.site_name
)- post data:
single.html
includes apost
object,category.html
andindex.html
include aposts
list - pagination data (
category.html
andindex.html
): you get apage
variable (current page number) and alast_page
variable (max page number)
post
objects at minimum consist of:
title
- the raw markdown title, extracted from the firsth1
tagtitle_html
- the compiled titlehtml
- the compiled markdown, not including the title and metadatapublished_at
- a datetime objectcategory
- the post's categoryslug
the post's slugdraft
- a bool of whether or not the post is a draft
Whatever else you include as metadata in your files will also show up as attributes on the post
object.
page
objects are similar, at minimum consisting of:
title
- the raw markdown title, extracted from the firsth1
tagtitle_html
- the compiled titlehtml
- the compiled markdown, not including the title and metadataslug
the post's slugdraft
- a bool of whether or not the post is a draft
You can refer to static files in your theme at the /static
url. For instance, if in my new theme I had the file css/index.css
, I could refer to it at the url /static/css/index.css
.
See the default theme for an example.
A convenience command is included for hosting your site on a server.
First, install the following on your server:
$ sudo apt-get install uswgi nginx supervisor python3 python3-pip uwsgi-plugin-python3
Then run:
$ port host <site name> <host name> <user> [--port]
For example:
$ port host my_new_site blog.mysite.com ftseng --port 5005
The <user>
arg is so that supervisor
can locate the port
site config and themes.
What this does is generate the necessary config files to host the site and restart nginx
and supervisor
.
I work on my posts on my local machine, and when they are ready, I build them and then sync the local folder to my remote server which hosts the live site.
There's a convenience command for doing this:
$ port sync <site name> <remote>
For example:
$ port sync my_new_site user@mysite.com:~/my_site
If you want to "unhost" the site, you can just run:
$ port unhost <site name> <host name>
If you're having issues with hosting the site, these logs help:
$ tail -f /var/log/supervisor/supervisord.log
$ tail -f /var/log/nginx/error.log
$ tail -f /var/log/port_<site name>.log
You should also set DEBUG: true
in your site's config when debugging.
-
If you're using vim, you can configure a keybinding to drop in the current datetime for you, which is useful for setting the
published_at
value in a post's yaml frontmatter, e.g.:nnoremap , "=strftime("%m.%d.%Y %H:%M")P