Skip to content

Add category to RSS feed #106

Add category to RSS feed

Add category to RSS feed #106

Workflow file for this run

# This script creates a nice README.md with an index of all TIL files.
# TIL files are Markdown files named anything other than README.md.
#
# The readme is split into two sections: the "header" and the "index",
# separated by three hyphens on their own line.
# Anything above the three hyphens is the "header" and will be kept as is.
# Anything below will be replaced by the "index".
name: Build README
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v3
with:
fetch-depth: 0 # get full history or else it'll be overwritten
- name: Regenerate README
uses: actions/github-script@v6
with:
script: |
const { readFile, writeFile } = require("node:fs/promises");
console.log("Building index…");
// load readme
let readme = await readFile("README.md").then(file => file.toString());
// add header separator
const separator = "\n---\n";
const index = readme.indexOf(separator);
if (index === -1) readme += separator;
else readme = readme.substring(0, index + separator.length);
// collect entries
const files = await glob.create("./**/*.md").then(globber => globber.glob());
const entries = files
.filter(name => !name.endsWith("/README.md")) // exclude README.md
.sort()
.map(name => name.split("/").slice(-2));
// add summary
readme += `\n${entries.length} TILs so far:\n`;
// create category map
const categories = new Map();
for (const [category, file] of entries) {
const list = categories.get(category) || [];
categories.set(category, [...list, file]);
}
// create a section for each category
for (const [category, entries] of categories.entries()) {
// write category header
readme += `\n## ${category}\n\n`;
// write link for each file
for (const file of entries) {
const filepath = [category, file].join("/");
const contents = await readFile(filepath).then(file => file.toString());
const [, title] = contents.match(/^# (.+)$/m);
readme += `- [${title}](/${filepath})\n`;
}
}
// write readme
await writeFile("README.md", readme);
- name: Install NPM libraries
run: npm install marked he
- name: Generate RSS
uses: actions/github-script@v6
with:
script: |
const { readFile, writeFile, stat } = require("node:fs/promises");
const { promisify } = require("node:util");
const run = promisify(require("node:child_process").exec);
const marked = require("marked").marked;
const he = require("he");
console.log("Building feed…");
// collect entries
const files = await glob.create("./**/*.md").then(globber => globber.glob());
const modified = files
.filter(name => !name.includes("/node_modules/")) // exclude node_modules
.filter(name => !name.endsWith("/README.md")) // exclude README.md
.map(name => name.split("/").slice(-2).join("/"))
.map(async file => [file, await run("git log --follow --format=%ad --date=unix " + file)]);
const entries = await Promise.all(modified);
const sorted = entries
.map(([name, created]) => [name, created.stdout.trim().split("\n").at(-1)])
.sort(([, a], [, b]) => b.localeCompare(a))
.slice(0, 10)
.map(async ([name, date]) => ({
name,
date: new Date(Number(date) * 1000),
content: await readFile(name).then(buf => buf.toString())
}));
const feed = await Promise.all(sorted);
const user = context.repo.owner;
const repo = context.repo.repo;
const branch = context.payload.repository.default_branch;
const link = `https://github.com/${user}/${repo}`;
const rss = [];
rss.push(`<?xml version="1.0" encoding="UTF-8"?>`);
rss.push(`<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">`);
rss.push(`<channel>`);
rss.push(`<title>${user} TIL</title>`);
rss.push(`<description>A collection of useful things I've learned.</description>`);
rss.push(`<link>${link}</link>`);
const dateOptions = { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: "short" };
for (const item of feed) {
rss.push(`<item>`);
const itemLink = `${link}/blob/${branch}/${item.name}`;
rss.push(`<guid>${itemLink}</guid>`);
rss.push(`<link>${itemLink}</link>`);
const category = item.name.split("/")[0];
const [, title] = item.content.match(/^# (.+)$/m);
rss.push(`<title>TIL (${category}): ${he.escape(title)}</title>`);
const pubDate = new Intl.DateTimeFormat("en-US", dateOptions).format(item.date);
rss.push(`<pubDate>${item.date.toUTCString()}</pubDate>`);
const content = item.content.split("\n").slice(1).join("\n").trim();
const encoded = he.escape(marked(content));
rss.push(`<content:encoded>${encoded}</content:encoded>`);
rss.push(`</item>`);
}
rss.push(`</channel>`);
rss.push(`</rss>`);
// write feed
await writeFile("rss.xml", rss.join("\n"));
- name: Commit and push if README or RSS changed
run: |-
git config --global user.email "[email protected]"
git config --global user.name "tilbot"
git diff --quiet || git commit --all --message "Update README.md or rss.xml"
git push