Article
Make your github profile README self updated
Goal
The github profile readme is not a new fancy thing around, however, one might find it painful updating personal news like latest blog posts or recent researches.
Hence the goal is clear, for those who wanna make their github profile readme self-updated, you could check out the implementation proposed in this article as a reference and perhaps make your own version.
Being more specific, the goal will be making the profile readme being like this, with the Recent blog posts section being updated when there is new article published on this site
Plan
To accomplish the goal, there are few necessary components:
1. A way to apply changes to github automatically
The easiest solution I've seen so far is using the github scheduled action, with which you can simply writting a unix crontab syntax to schedule the update.
e.g.,
## Cron Syntax Ref: https://crontab.guru
on:
schedule:
- cron: '0 0 * * *' # Every day at 00:00
2. A way to get the latest articles from the blog
Since my blog have rss feed implemented, I can easilly use a any language with a RSS parser to fetch the latest changes. In this post I did the script in Golang and gofeed, however, any other language like python, java, typescript are very easy to go too, just pick one of those with which you are more familiar.
func parseFeeds(rssFeed string) *gofeed.Feed {
fp := gofeed.NewParser()
feed, err := fp.ParseURL(rssFeed)
if err != nil {
log.Fatalf("error getting feed: %v", err)
panic(err)
}
return feed
}
3. A way to update the README.md file using the latest articles fetched in the previous step
With any template engine, you can effortlessly format parsed RSS data into a README.md.tmpl
### :memo: Recent blog posts
{{ range . }}
- **[{{.Title}}]({{.Link}})** *{{.PublishedParsed.Format "Jan 02, 2006" }}*
{{ end }}
Read more on [cwc1222.github.io](https://cwc1222.github.io/)
Summary
flowchart LR
githubaction["Github Scheduled Action"]
subgraph golangScript
direction TB
gofeed["gofeed"]
gotemplate["go template engine"]
updatedreadme["updated readme.md"]
githubrepository["The Readme Profile Repo."]
gofeed --> |pass fetched articles| gotemplate --> |render articles to readme.md | updatedreadme -.-> |commit if the file changes| githubrepository
end
githubaction --> golangScript
Implementation
Alright! let's dig into the actual implementation
Github Action
name: Update Readme
on:
push:
workflow_dispatch:
schedule:
- cron: '0 0 * * *' # Every day at 00:00
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22.3'
- name: Generate README.md
run: |-
go run main.go
cat README.md
- name: Commit and push if changed
run: |-
git diff
git config --global user.email "readme-bot@cwc1222.com"
git config --global user.name "README-bot"
git add -A
git commit -m "Updated content" || exit 0
git push
Golang script
package main
import (
"html/template"
"log"
"os"
"github.com/mmcdole/gofeed"
)
const (
blogRssFeed = "https://cwc1222.github.io/rss.xml"
maxPostsToShow = 5
readmeTmplPath = "README.md.tmpl"
readmePath = "README.md"
)
func parseFeeds(rssFeed string) *gofeed.Feed {
fp := gofeed.NewParser()
feed, err := fp.ParseURL(rssFeed)
if err != nil {
log.Fatalf("error getting feed: %v", err)
panic(err)
}
return feed
}
func main() {
feed := parseFeeds(blogRssFeed)
tmpl, err := template.ParseFiles(readmeTmplPath)
if err != nil {
log.Fatalf("create file: %v", err)
panic(err)
}
readme, err := os.Create(readmePath)
if err != nil {
log.Fatalf("create file: %v", err)
panic(err)
}
defer readme.Close()
n := min(maxPostsToShow, len(feed.Items))
err = tmpl.Execute(readme, feed.Items[0:n])
if err != nil {
log.Fatalf("create file: %v", err)
panic(err)
}
}
Conclusion
Well, that's all! after pushed the changes to your profile readme repository, i.e., github.com/{your_github_username}/{your_github_username}, you will see this few days later:
That's it for today, see ya in other shares.