osteel's blog Web development resources

Dynamic GitHub profile README with Github Actions and PHP

Been here before?

You can also subscribe to the RSS or Atom feed, or follow me on Twitter.

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?

You can also subscribe to the RSS or Atom feed, or follow me on Twitter.

Last updated by osteel on :: [ github ]

Comments