How I automated my Github Readme with Hashnode articles

How I automated my Github Readme with Hashnode articles

I live on Hashnode and Github now. So I thought, how can I show my Hashnode contributions on my Github Profile Readme?

Asset_59_1.png

Note: This post is a little intense if you are a Git beginner but source code to all of this tutorial is linked to the bottom of this post! Even if you are new, give it a read and see if you can learn something out of it. Cheers!


If you write on Hashnode want to make your Github Profile lively like I did, then this post is 100% for you. Here is what I managed to do using the Hashnode API and Github actions. I made my Readme automatically fetch my most recent posts from Hashnode and it auto updates every few hours.

Screenshot 2021-02-02 at 7.08.34 PM.png


What exactly do I need to do?

  1. Well the GitHub Profile Readme basically is simply the Readme.md file that you make in a repository on GitHub which has name as your username on github. So basically it is a repository at https://github.com/[username][username]. You can read more about that here .

  2. And this Readme will show up on https://github.com/[uername]. So if we want to make sure that this file has our Hashnode posts, we could either update them ourselves every now and then.

But we like to automate around here.

To automate contents of a Readme we need to use Github Actions. Actions are simple instructions that we can run within Github for each repository on a timely basis or in response to certain events(push, pull, commit, pull-request or external events etc).

Asset_58.png

A GitHub Action that I use

Long ago when Github Actions were first opened to public, I created an action to update the contents of my Readme and it looked something like this.

Scary script ahead. But don't worry, you don't have to care about the code here too much. Just look at the names of each of the steps in my build job.

name: README build

on:
  push:
    branches:
      - master
  schedule:
    - cron: '0 */3 * * *'
  repository_dispatch:
    types: [deploy-github-readme]
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: checkout
        uses: actions/checkout@v1
      - name: setup node
        uses: actions/setup-node@v1
        with:
          node-version: '13.x'
      - name: cache
        uses: actions/cache@v1
        with:
          path: node_modules
          key: ${{ runner.os }}-js-${{ hashFiles('package-lock.json') }}
      - name: Install dependencies
        run: npm install
      - name: Get top github contributions
        id: top-github-contributions
        uses: imbhargav5/actions/top-gh-contributions@master
        with:
          token: ${{ secrets.TOKEN }}
      - name: Get Github summary
        id: github-summary
        uses: imbhargav5/actions/contentful-entry@master
        with:
          ...
      - name: Get recent blog posts from my older blog service
        id: recent-blog-posts
        uses: imbhargav5/actions/contentful-entries@master
        with:
          ...
      - name: Generate README file
        run: node action.js
        env:
          TOP_CONTRIBUTIONS: ${{steps.top-github-contributions.outputs.top-contributions}}
          NOTABLE_CONTRIBUTIONS: ${{steps.top-github-contributions.outputs.notable-contributions}}
          GITHUB_SUMMARY: ${{steps.github-summary.outputs.entry}}
          BLOG_POSTS: ${{steps.recent-blog-posts.outputs.entries}}
      - name: Push new README.md
        uses: mikeal/publish-to-github-action@master
        env:
          GITHUB_TOKEN: ${{ github.token }}
  • I just try to get data from a bunch of services that I use Codementor where I teach code or some of my notable contributions on Github etc and then populate my readme using a simple Node.js script.

Render_vs_Commit_Phase_Copy_7.png

  • I want to do something very similar with a custom Github Action for Hashnode pots and integrate into my workflow.

Render_vs_Commit_Phase_Copy_8.png

Understanding the Hashnode API

The most important step is to grab Hashnode posts. Hashnode allows you to pull in a fair amount of public data using their api at api.hashnode.com.

Screenshot 2021-02-02 at 6.00.04 PM.png

So I tried checking if I can even fetch posts by username and the Hashnode guide was actually outdated on this one. So I dug into other GitHub actions (I ended up making my own action because the other action used it's own UI and I didn't want to give up control over the UI and wanted to make this usable for anyone who wanted just the posts without custom HTML.) with Hashnode links and found that I can run this query today and get the posts I need to show on my Readme.

{
    user(username: "imbhargav5"){
    name
    username
    numFollowers
    publication{
      posts{
        title
        coverImage
        brief
      }
    }
  }
}

So first I wrote a Node script to test out fetching the content using the GraphQL api.

const { gql, GraphQLClient } = require("graphql-request");

const client = new GraphQLClient("https://api.hashnode.com");

const query = gql`
  query getUserPosts($username: String!) {
    user(username: $username) {
      name
      username
      photo
      coverImage
      numFollowers
      numReactions
      publicationDomain
      publication {
        posts {
          title
          coverImage
          brief
          cuid
          bookmarkedIn
        }
      }
    }
  }
`;

async function getHashnodePosts(username) {
  try {
    const data = await client.request(query, { username });
    return data;
  } catch (err) {
    console.log(err);
    return {};
  }
}

module.exports = getHashnodePosts;
  • I can now use this as Javascript Github Action and use it to populate my Readme.

  • I thought others would also find this useful. So I created a simple GitHub action that gave this output in the form a JSON and put it on the marketplace that anyone can use.

Check it out over here !

Screenshot 2021-02-02 at 6.52.15 PM.png

Using the action

The usage is pretty simple.

  • First. Create a simple Node file in your repository like so. This Node script simply grabs hashnodeProfile from the env it runs in and can then create HTML content out of it.
//./main.js

const main = require("./main");
//...
const hashnodeProfile = JSON.parse(process.env.HASHNODE_PROFILE)
//...




function generateReadme(){

const username = `${hashnodeProfile.user.name}`

// Generate postsHTML too
const postsHTML = '' 



return `
    Hey. I am ${username}
   ${postsHTML}    
`
}

generateReadme()
  • Secondly, In the workflow to update Readme add this step to fetch your posts and also supply your username as input.
      - name: Get recent blog posts from hashnode
        id: hashnode-recent-blog-posts
        uses: imbhargav5/hashnode-user-with-posts-json@master
        with:
          username: "imbhargav5"
  • Finally, create a step to run the node script and add HASHNODE_PROFILE to its env like so.

      - name: Generate README file
        run: node action.js
        env:
          OTHER_STUFF: whatever
          HASHNODE_PROFILE: ${{steps.hashnode-recent-blog-posts.outputs.data}}     
      - name: Push new README.md
        uses: mikeal/publish-to-github-action@master
        env:
          GITHUB_TOKEN: ${{ github.token }}

Results time

Once I managed to get my Github Action and Node script to generate Readme sorted. I put them all together and now this is how my readme looks like.

Screenshot 2021-02-02 at 7.08.34 PM.png

The Hashnode posts are populated at the bottom right in a list format!

More details

I used Mustache.js to generate my HTML. It's pretty easy to use and comfortable so I used it, but you can use any tool that you look. End goal is to generate HTML and push to your repository.

You can read the entire source here .

Thanks for reading!!

Thanks and Please support my work

  • Writing blog posts and making videos is an effort that takes many hours in my day. I do it because I love teaching and make great content.
  • However, I need your support too. Please support my work and follow my social accounts to help me continue to make great content.
  • Here are my social links.

Follow me on Twiter

Subscribe to my channel on Youtube

Thank you!