Auto update readme on GitHub with Deno

GitHub has this nice feature where you can have a readme file in the repository named after your GitHub handle and it will be displayed on your profile page. I used to have this file automatically generated by a script that would fetch my latest blog posts and display them in the readme. I used Paweł solution and it worked fine.

I used to run this workflow every week on Sundays with a GitHub Action schedule](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule). Unfortunately recently it started failing - it turns out that switching to Astro broke the RSS structure. As I was fixing the script I decided to completely rewrite it with Deno.

I choose Deno because I wanted out-of-the-box support for TypeScript. One of the objectives of the rewrite was to have better handling of errors when iterating through the RSS feed. To accomplish that I’ve used Zod.

You can see the result in the snippet below:

import { z } from "https://deno.land/x/zod@v3.21.4/mod.ts";

const ResponseSchema = z
  .array(
    z.object({
      title: z.string(),
      url: z.string().url(),
    }),
  )
  .nonempty();

const response = await fetch("https://krzysztofzuraw.com/blog.json");
const blogPosts = ResponseSchema.parse(await response.json());

const markdownPosts = blogPosts
  .map(({ title, url }) => `- [${title}](${url})`)
  .join("\n");

const openTag = `<!-- FEED-START -->`;
const closeTag = `<!-- FEED-END -->`;
const filePath = "./README.md";

const readme = await Deno.readTextFile(filePath);
const indexBefore = readme.indexOf(openTag) + openTag.length;
const indexAfter = readme.indexOf(closeTag);
const readmeContentChunkBreakBefore = readme.substring(0, indexBefore);
const readmeContentChunkBreakAfter = readme.substring(indexAfter);

const readmeNew = `${readmeContentChunkBreakBefore}\n${markdownPosts}\n${readmeContentChunkBreakAfter}`;

await Deno.writeTextFile(filePath, readmeNew);

console.log("Readme updated! 🎉");

I did a couple of changes to the script. I decided to add a new JSON page to Astro - it contains the last 3 blog posts - you can see the code here. I fetch blog.json and validate it using Zod. To be honest I’m impressed by this library - I previously used it only for form field validation but it works great for validating API responses as well.

I also found out that there is nifty Deno GitHub Action that I can use to set up the Deno environment in CI and that Deno has something like package.json called deno.json - it allows you to specify dependencies and scripts. I’ve used it to set up deno task.

The last change was to run this script not on the schedule but to trigger it when I push any changes to the blog repository. It works well but there is one caveat though - Netlify deployment runs on its own (via the GitHub app) and GitHub action runs too early to catch the changes. I poke around the internet to see if there was a way to trigger GitHub Action after Netlify deployed my blog but I didn’t find anything officially supported by Netlify. I decided for now that I can live with that and I will just run this script when I or Renovate merge changes to the master. Maybe I will find a better solution in the future.