An automated blogroll with Jekyll, Newsblur and Github Actions
This blog's blogroll is updated automatically and this post explains how it works.
A blogroll is a list of links to blogs and their RSS feed. It was popular in the early 2000s when many people still had a blog. Nowadays most people post their thoughts to a handful of social networks, and writing a blog can feel outdated.
Anyway, I’ve automated the updating of my blogroll to link to blogs I subscribe to and recommend others to do the same.
Every night a GitHub Actions workflow builds and deploys this website. Before the build starts, a Ruby script fetches all my blog subscriptions from Newsblur, my feed reader. The script outputs a YAML file that Jekyll reads as data. The blogroll itself is then built as a regular HTML page in Jekyll.
This post explains how it works.
The Ruby script
Save this script in bin/blogroll and afterwards chmod +x bin/blogroll.
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'bundler/inline'
gemfile(true) do
source 'https://rubygems.org'
gem 'faraday'
gem 'faraday-cookie_jar'
end
require 'json'
require 'fileutils'
require 'yaml'
class Newsblur
def initialize(username, password)
conn.post 'api/login', "username=#{username}&password=#{password}"
end
def logout
conn.post 'api/logout'
end
def reader_feeds(include_favicons: false, flat: true, update_counts: false)
JSON.parse conn.get(
'reader/feeds',
{
include_favicons: include_favicons,
flat: flat,
update_counts: update_counts
}
)
.body
end
private
def conn
@conn ||=
Faraday.new(
url: 'https://newsblur.com/', headers: { accept: 'application/json' }
) do |builder|
builder.use :cookie_jar
builder.adapter Faraday.default_adapter
end
end
end
# 1. signin to get newsblur_sessionid cookie and set it with all following requests
newsblur = Newsblur.new(ARGV[0], ARGV[1])
# 2. find all feeds in desired folder
reader_feeds = newsblur.reader_feeds
feeds_in_folder = reader_feeds.dig('flat_folders', ARGV[2])
feeds = feeds_in_folder.map { |feed| reader_feeds.dig('feeds', feed.to_s) }
# 3. write the feeds to _data/blogroll.yml
FileUtils.mkdir_p File.join(__dir__, '..', '_data')
file = File.join(__dir__, '..', '_data', 'blogroll.yml')
File.write(file, feeds.to_yaml)
# final step: logout
newsblur.logout
Test run the script:
bin/blogroll USERNAME PASSWORD FOLDER
For USERNAME and PASSWORD, provide your Newsblur credentials. FOLDER is the name of the folder containing your blog subscriptions, for example Cool Blogs
. If any of these strings contain a space or special characters, wrap them in quotes.
The script creates a blogroll.yml file in _data. The _data directory is where Jekyll looks for data files. Jekyll reads each file there and exposes the contents on the global site object as site.data.blogroll.
Finally, exclude the bin/ folder from the build in _config.yml.
exclude:
- bin/
… and echo _data/blogroll.yml > .gitignore
Render the blogroll page
Create the blogroll/index.html file.
mkdir -p blogroll
touch blogroll/index.html
Add your HTML. Here’s what I’m using:
---
layout: default
---
{% raw %}<ul>
{% assign blogroll = site.data.blogroll | sort_natural: "feed_title" %}
{% for blog in blogroll %}
<li>
<a href="{{blog.feed_link}}" target="_blank" rel="noopener">{{ blog.feed_title }}</a>
<a href="{{blog.feed_address}}" target="_blank"
rel="noopener">Feed</a>
</li>
{% endfor %}
</ul>{% endraw %}
The Github Actions workflow
To automatically update the blogroll, schedule a run of your deployment workflow.
Example:
{% raw %}name: jekyll build and publish artifact with rsync
on:
push:
branches:
- main
schedule:
- cron: "15 3 * * *"
jobs:
build:
runs-on: ubuntu-latest
steps:
# clipped: the usual setup stuff
- name: update blogroll
env:
NB_USERNAME: ${{secrets.NB_USERNAME}}
NB_PASSWORD: ${{secrets.NB_PASSWORD}}
NB_FOLDER: ${{secrets.NB_FOLDER}}
run: bin/blogroll "$NB_USERNAME" "$NB_PASSWORD" "$NB_FOLDER"
# clipped: the usual build and deploy stuff{% endraw %}
Add the required secrets (NB_*) to your website’s repository settings.
Conclusion
Blogrolls are valuable to the independent web. Let’s not forget them. There are still plenty of blogs out there, but they can be hard to find. A blogroll makes discovery easier. If you write a blog, why not add one? It does not have to be automated; you can update it manually.
Jekyll’s support for data files is powerful. In conjunction with scheduled deployments you can display not so static content with little effort.
If you’re familiar with Ruby, the script is straightforward. If not, adapt the idea. YAML is a common data format, so you can use whatever language runs on your continuous deployment service.