Automating README Updates with Hashnode Articles Using GitHub Actions

Why Automate README Updates?

I was trying to spice up my github profile page by adding an automated list of my latest posts on hashnode to showcase. This is how I did it.

Tools Used

  • GitHub Actions: A powerful automation tool that integrates directly into GitHub repositories.

  • Hashnode API: A GraphQL API that allows you to fetch the latest articles from your Hashnode blog.

  • Python: To interact with the Hashnode API and manipulate the README.md file.

Step1: get your hashnode ID

This might not be necessary, but it looked tidier and more pythonical.
You can head over to https://gql.hashnode.com/?source=legacy-api-page playground and get your pub id by querying the api

query Publication($host: String = "surestride.hashnode.dev") {
  publication(host: $host) {
    id
  }
}
{
  "data": {
    "publication": {
      "id": "6714bcb54bcdc5c7d9980730"
    }
  }
}

You will need to change the url for your own url in the snippet

Step 2: Set Up the Hashnode API Request

The first step is to fetch the latest articles from your Hashnode blog. Hashnode provides a GraphQL API that allows you to query your blog posts. Here’s the Python code I used to retrieve the latest posts:
I used Postman to verify contents and then hacked them into a python script

import requests

# Hashnode API endpoint
url = "https://gql.hashnode.com"

# GraphQL query
query = """
query Publication(
    $id: ObjectId="6714bcb54bcdc5c7d9980730"
  ) {
    publication(
      id: $id
    ) {
      posts(first:3) {
        edges {
          node {
            title,
            brief,
            slug,
            url
          }
        }
      }
    } 
  }
"""

response = requests.post(url, json={"query": query})
data = response.json()
posts = data['data']['publication']['posts']['edges']

# Format posts for markdown
md_content = ""
for idx, post in enumerate(posts, start=1):
    md_content += (f"{idx}. [{post['node']['title']}]({post['node']['url']})\n")

This script queries the latest three posts from my Hashnode blog and formats them into a markdown-friendly list.

Step 3: Update the README.md File

Now just update the readme file.

As I have other stuff in there, i only wanted to replace the relevant contents. To help with this I added comment before and after the text I wanted to replace in the README.md

<!-- BEGIN HASHNODE ARTICLES -->
1. [Why Python Decorators Are Essential for Clean and Readable Code](https://surestride.hashnode.dev/why-python-decorators-are-essential-for-clean-and-readable-code)
2. [Why you should user Gherkin in your user stories...](https://surestride.hashnode.dev/why-you-should-user-gherkin-in-your-user-stories)
3. [Best Practices for Python Type Annotations](https://surestride.hashnode.dev/best-practices-for-python-type-annotations)
<!-- END HASHNODE ARTICLES -->

Here is the script for your entertainment

start_marker = "<!-- BEGIN HASHNODE ARTICLES -->"
end_marker = "<!-- END HASHNODE ARTICLES -->"

# Read the current content of README.md
with open("README.md", "r") as readme:
    content = readme.read()

# Find the markers
start_idx = content.find(start_marker)
end_idx = content.find(end_marker)

# Update content between markers or append new section
if start_idx != -1 and end_idx != -1:
    updated_content = (
        content[:start_idx + len(start_marker)]  # Content before the start marker
        + "\n" + md_content.strip() + "\n"      # New content
        + content[end_idx:]                      # Content after the end marker
    )
else:
    updated_content = content + f"\n{start_marker}\n{md_content.strip()}\n{end_marker}\n"

# Write the updated content back to README.md
with open("README.md", "w") as readme:
    readme.write(updated_content)
    print("README.md has been updated.")

Step 4: Automate with GitHub Actions

Using GitHub Actions you can automate script execution, theres a few workflows available and I jsut adapted one to my own use

name: Update README with Hashnode Articles

on:
  schedule:
    - cron: "0 0 * * *"  # Run daily at midnight
  workflow_dispatch: # Allow manual triggering

jobs:
  update-readme:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.12"  # Specify the Python version you're using

      - name: Install Dependencies
        run: |
          python -m pip install --upgrade pip
          pip install requests  # Install requests if you don't have a requirements.txt

      - name: Run Update Script
        run: |
          python automate_profile_hashnode_article_population.py

      - name: Check if README.md is modified
        run: |
          git diff README.md
          if git diff --quiet README.md; then
            echo "No changes to commit"
            exit 0
          else
            git config --global user.name "GitHub Actions"
            git config --global user.email "actions@github.com"
            git add README.md
            git commit -m "Update README with latest Hashnode articles"
            git push
          fi

Key Points of the Workflow:

  • Schedule: The workflow is set to run daily at midnight using a cron expression (0 0 *), but you can also trigger it manually.

  • Python Setup: The workflow sets up Python 3.12 using actions/setup-python@v4. I had to explicitly define python version here (3.x gave me errors)

  • Dependencies: It installs the requests library, which is required for interacting with the Hashnode API.

  • Run Script: The script is executed to fetch the latest Hashnode articles and update the README.md file.

  • Git Commit: After the script runs, it checks if the README.md file has been modified. If changes are detected, it commits and pushes the updates to the repository.