Just my website. Visit fpira.com for a live version.
This website is powered by Jekyll. It started in 2015 as an overhauled fork of Jekyll Now with custom CSS designed from scratch. Over the years, I've implemented many additional features, tailored on my needs.
- Ruby (check
.ruby-version
) - Node.js (check
.nvmrc
)
gem install bundler
bundle install
npm install -g @redocly/cli@latest
npm install -g @openapitools/openapi-generator-cli
Via rake, running either:
rake build
rake serve
to build or serve.
Or, as usual for Jekyll websites, you can use:
bundle exec jekyll serve
bundle exec jekyll
jekyll serve
jekyll s
add --future
to compile and show post with later date than today;
add --drafts
to compile and show drafts.
As many other Ruby-based projects, most tasks listed in this readme are also available as rake
tasks for brevity and consistency.
You can check the rakefile
and use rake
to list all of them.
Thanks to the jekyll-environment-variables
plugin, you can use {{ site.env.MYENV }}
in Liquid expressions.
Prepend JEKYLL_ENV=production
to commands above.
Algolia settings are stored in _config.yml
. It uses the search-only API key.
To update the Algolia index run:
ALGOLIA_API_KEY='123abc123abc123abc123abc123abc12' bundle exec jekyll algolia
where the ALGOLIA_API_KEY
is the Admin API Key you get from your account dashboard.
You can run the development server in a docker container. The image is not specifi to the blog and it's built as a general purpose Jekyll one. It contains: rvm
, ruby
, jekyll
, nvm
, and nodejs
.
On Linux:
docker build -t pirafrank/jekyll -f ./Dockerfile .
docker run -it --name fpiracom -v $(pwd -P):/home/jekyll/app -p 4001:4001 pirafrank/jekyll:latest
On Windows:
docker build -t pirafrank/jekyll -f .\Dockerfile .
docker run -it --name fpiracom -v ${PWD}:/home/jekyll/app -p 4001:4001 pirafrank/jekyll:latest
You can add --build-arg RUBY_VERSION=x.y.z --build-arg NODE_VERSION=x.y.z
to build command to specify which Ruby and/or nodejs version to use.
After the container has started, you need to run bundle install
. This is because the source is mounted via Docker and not included in the image.
Important: when launching jekyll serve, be sure to bind to all interfaces:
bundle exec jekyll serve --host=0.0.0.0
or just use the alias:
jks
otherwise the server won't be accessible from the host even if the port has been bound.
CI/CD is achieved via GitHub Actions. To make workflows readable and to share code, some intermediate steps are written as composable actions in .github/actions/*
.
Part of the CI workflow is running rake ci
which builds the website and performs multiple checks to ensure consistency. For example:
- checking Jekyll installation
- looking for internal broken links
- perform API linting
- checking consistency of produces JSON files
- checking calendar
.ics
files - checking RSS, Atom, and JSON feeds to be readable.
To run a GitHub Actions workflow on any branch use the --ref
flag. This works even if you have never merged the workflow file in the repository's default branch.
gh workflow run workflow --ref branch-name
or for input params
gh workflow run workflow --ref branch-name -f myparameter=myvalue
For futher info, check the docs.
Configure the following environment variables at build time if web analytics have to be set. Don't set any of them to disable it.
ANALYTICS_GOOGLE='UA-1234567-1'
ANALYTICS_GTAG='UA-1234567-8'
ANALYTICS_HEATMAP=abc123abc123
ANALYTICS_MATOMO_HOST=somematomo.host.com
ANALYTICS_MATOMO_ID=abc123abc123
ANALYTICS_CLOUDFLARE=abc123abc123
ANALYTICS_UMAMI_WEBSITEID=abc123abc123
ANALYTICS_UMAMI_ENDPOINT=umami.instance.com
Environment variables must be set where the website is actually built, e.g. if the GitHub Action pipeline builds and deploys via Vercel, then set env vars there, not in the pipeline.
The website supports the following feeds:
- RSS
- Atom
- JSON Feed, (more)
and allows them to be easily discovered.
Use standard markdown format:

E.g.

This is done using the _includes/image.html
include.
{% include image.html
url="/static/postimages/2020-06-08/office.jpg"
desc="Image by Markus Spiske from Pixabay"
alt="An office with a desk and a computer mouse"
credits="https://pixabay.com/users/markusspiske-670330/"
%}
or if caption has a link
{% include image.html
url="/static/postimages/2020-06-08/office.jpg"
desc="Image by Markus Spiske from Pixabay"
alt="An office with a desk and a computer mouse"
link="https://somehost.local/some/article"
credits="https://pixabay.com/users/markusspiske-670330/"
%}
attribute | type | required | use |
---|---|---|---|
url |
URL path | yes | image url relative path, prepended by {{ site.baseurl }} |
alt |
text | if no desc |
alt text |
desc |
text | if no alt |
image caption. Also alt text if alt is not specified |
link |
URL | no | if your caption needs to point to a url, this will be the link to it |
credits |
URL | no | url to credit image author |
This is done using the _includes/accordion.html
include.
{% include accordion.html title="this is a toggle" file="some_file.md" %}
where:
title
is the title of the togglesome_file.md
is a markdown file in_posts/accordions/YYYY-MM-DD/
folderYYYY-MM-DD
is the date of the post the toggle is in- the markdown file contains the content of the toggle
Included file
can be .md
or .html
. Content is rendered depending on the file extension.
You only need the video ID from the link URL (e.g. abc123abc123)
YouTube
{% youtube abc123abc123 %}
Vimeo
{% vimeo abc123abc123 %}
TED talks
{% ted abc123abc123 %}
Write the gist id in a Liquid tag like the following:
{% gist 40880dbc3e2dcfbdc1dd817b8880fa66 %}
Powered by https://github.com/jekyll/jekyll-gist
Just post the twitter post URL standalone in the markdown file. For example:
https://twitter.com/pirafrank/status/1353708824558002177
Powered by LazyTweetEmbedding
plugin (link).
The plugin is in the _plugins
folder.
Posts can be easily linked using:
[some text]({% post_url 2016-09-13-my-jekyll-workflow-part1 %})
where 2016-09-13-my-jekyll-workflow-part1
is the filename in _posts
dir without extension.
Anything else can be linked like:
{% link static/postfiles/my-jekyll-workflow-part3/policy.txt %}
which specifies the full path from Jekyll root folder to the file, including filename and extension.
More details here: https://mademistakes.com/mastering-jekyll/how-to-link/.
To add target="_blank" rel="noopener noreferrer"
to markdown, write links as follows:
[Awesome link]({{ site.data.external.awesomelink }}){:target="_blank"}{:rel="noopener noreferrer"}.
which generates:
<a href="https://www.someexternal.site" target="_blank" rel="noopener noreferrer">here</a>
To add a font-awesome icon:
<i class="fa-solid fa-arrow-up-right-from-square" aria-hidden="true"></i>
<i class="fa-brands fa-github" aria-hidden="true"></i>
Static media files are stored in static
dir.
resource | path |
---|---|
page | https://fpira.com/static/pageimages/ + page.seoimage |
post | https://fpira.com/static/postimages/ + page.seoimage |
project | https://fpira.com/static/projectimages/ + page.seoimage |
page.seoimage
refers to the seoimage
variable specified in the front-matter.
If seoimage
is not specified, https://fpira.com/assets/images/og_image.png
will be used instead, as written in _includes/seo.html
.
A folder for generic SEO images in posts exists, it's called common
. So the resulting URL for those is https://fpira.com/static/postimages/common/
+ image filename.
Check the guide below to make IFTTT EntryImageURL work (source).
First specify the xmlns:content
namespace.
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
Then add content:encoded
tag to every feed item. See below how to specify the URL in the img
tag.
<item>...
<content:encoded><![CDATA[<img src="Your Image URL" />]]></content:encoded>
...</item>
Now IFTTT can get the URL from the img
tag and make it available to applet via the EntryImageURL
ingredient.
IFTTT may be used as a bridge between blog post publishing and social sharing. The SEO image is not part of the RSS feed, so an IFTTT Make a web request action is used to retrieve its URL via API. The action performs a GET
request to https://fpira.com/api/v1/ifttt/posts/latest
. The URL is then used in a filter code to set the image URL in next Then action.
Below there's a filter code example to set the image URL in a LinkedIn Share an update with image action.
let imageURL = "https://fpira.com/assets/images/og_image.png";
if(MakerWebhooks.makeWebRequestQuery[0].StatusCode == "200" && MakerWebhooks.makeWebRequestQuery[0].Value3){
imageURL = MakerWebhooks.makeWebRequestQuery[0].Value3;
}
Linkedin.shareImagePost.setPhotoUrl(imageURL)
Docs:
Apart from Jekyll plugins available as gems and declared in Gemfile
, there are quite a few custom plugins in the _plugins
folder. Not all code is mine, and some has been modified to fit my needs, but I've tried to give credit where it's due.
Most plugins are used to extend Jekyll functionalities, by adding new Liquid tags or filters, to edit the markdown conversion process, or to populate site.data
with additional info (for example, with current git commit data).
This plugin reorders the post.updates
array and adds two property to each post:
most_recent_edit
: this is the most recent edit, which either the creation date or the last update datemost_recent_update
: this is the last update date, if any, otherwise it's null
api
folder contains an attempt to provide APIs out of a Jekyll website, being almost plugin-less, with plugins being part of this repository in _plugins
dir.
APIs can be useful for integrations, e.g. I use /api/v1/ifttt/posts/latest
to fetch details of the last published blog post from an RSS trigger on IFTTT.
OpenAPI definition is available for v1
in /api/v1/openapi.yaml
. API documentation is generated as part of the website and available at /api/docs
(link).
An early attempt was made using vrypan/jekyll-post-via-web. But it was not maintained and too basic for my needs.
I tried to experience CMS-like functionality using prose.io.
Prose.io uses _prose.yml
in the repo root. To provide a list of actual tags and categories available on the website two JSONP files are used, jsonp/categories.jsonp
and jsonp/tags.jsonp
.
Looking for a CMS, I found Notion to be a good platform to build my own. But it required a way to convert Notion pages to Jekyll posts, so I built notion-to-jekyll.
A Notion workspace is used to write drafts and publish them as Jekyll posts when ready. The conversion is done using notion-to-jekyll running on a daily scheduled GitHub workflow.
notion-to-jekyll uses the Notion API to fetch pages and convert them to markdown files, as well download images and other assets. Finally, it commits and pushes changes to the repository via github-commit-sign action.
<meta name="theme-color" content="#3344aa">
Links:
has_fa
variable is not used right now. Font-awesome is always loaded. Variables in front-matter are kept in case I change my mind and put an if
in font-awesome loading in head.html
file.
Source code is released under GNU GPLv3 license. Any source code coming from other projects maintains its original license.
Website and blog content are released under Creative Commons Attribution-NonCommercial 4.0.
To know more about terms and license, please read the terms page.