osteel's blog Web development resources

Dynamic GitHub profile README with Github Actions and PHP

Been here before?

A few weeks ago, GitHub quietly released a feature that was quickly noticed by the community – profile READMEs. A profile README is a global README file for your GitHub profile, which you can set up by creating a public repository whose name is identical to your GitHub username. For instance, as my username is osteel, I created the osteel/osteel repository.

A little box like this one should appear while you add your own:

Special repository

Once the repository is created, add a README file with a short description explaining how great you are, and your GitHub profile page will display its content by default:

GitHub profile README

Neat and simple.

As I was browsing examples for some inspiration, I stumbled upon Simon Willison's version, which features some dynamic content like recent work and blog publications. He explained how he used a combination of GitHub Actions and Python to achieve this in a blog post, and I decided to do something similar with PHP.

The placeholder

The first thing to do is to create a placeholder in the README file where the dynamic content will go. Since I wanted to automatically insert the latest publications of my blog, I used the following tags:

<!-- posts --><!-- /posts -->

You might recognise this format; since Markdown files also support HTML, I used some HTML comment tags to make sure they wouldn't show up on my profile page.

The PHP script

I can't remember the last time I wrote some PHP without a framework; as a result, I had to do a quick search just to get started with a basic PHP script and some Composer dependencies.

Turn out it's quite simple! The first step is to initialise the project with the following command:

$ composer init

From there, I installed a lightweight library to parse my blog's RSS feed:

$ composer require dg/rss-php

I then added a posts.php file at the root of the project, with the following content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

// Load Composer's autoload
require_once __DIR__ . '/vendor/autoload.php';

// Load the RSS feed
$feed = Feed::loadRss('https://tech.osteel.me/feeds/rss.xml')->toArray();

// Generate the list of blog posts
$posts = '';
foreach (array_slice($feed['item'], 0, 5) as $post) {
    $date   = date('d/m/Y', strtotime($post['pubDate']));
    $posts .= sprintf("\n* **[%s]** [%s](%s \"%s\")", $date, $post['title'], $post['link'], $post['title']);
}

// Generate the new content
$content = preg_replace(
    '#<!-- posts -->.*<!-- /posts -->#s',
    sprintf('<!-- posts -->%s<!-- /posts -->', $posts),
    file_get_contents('README.md')
);

// Overwrite the file
file_put_contents('README.md', $content);

Nothing too complicated here – Composer's autoload is required at the top, allowing me to load the RSS parser to generate a list of blog posts as a string, in Markdown format.

The existing content of the README file is then loaded into the $content variable, and the Markdown string is inserted between its <!-- posts --> and <!-- /posts --> tags with preg_replace.

Finally, the file's entire content is replaced with the new one, using the file_put_contents function.

The GitHub action

GitHub Actions are a fairly recent addition to GitHub, allowing developers to automate various CI/CD tasks, like running test suites or deploying web services.

They must be defined using YAML format in a .github/workflows folder at the root of the project, and contain a list of steps that they are to execute.

Here's mine, which I named posts.yml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
name: Update blog posts

on:
  push:
  workflow_dispatch:
  schedule:
    - cron:  '0 0 * * *'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Clone repository
      uses: actions/checkout@v2
    - name: Install PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '7.4'
    - name: Install Composer dependencies
      run: composer install
    - name: Insert blog posts
      run: php posts.php
    - name: Push changes
      uses: stefanzweifel/git-auto-commit-action@v4
      with:
        commit_message: Updated latest blog posts

Again, nothing too complicated. We first give the action a name, and then define a list of events that should trigger it – pushing some code to the repository, a manual trigger from the interface (workflow_dispatch), or periodically like a cron job (here, every day at midnight).

We then indicate that the action should run on an Ubuntu image, where it will:

That's it! My GitHub profile is now automatically updated every time I publish a new article.

Conclusion

This was a quick experiment aiming at exploring GitHub Actions, which I expect to use more and more in the future. It was also fun to use PHP as a simple scripting language again, in a procedural way.

I've voluntarily left out a few things in order to keep this article short and simple – please refer to the repository for implementation details.

Resources

Enjoying the content?

Last updated by osteel on :: [ github ]

Comments