osteel's blog Web development resources

Dynamic GitHub profile README with Github Actions and PHP

A few weeks ago, GitHub quietly released a feature that didn't go unnoticed for long – profile READMEs. A profile README is essentially a README file for your GitHub profile, rather than a specific repository's. You can set one up by creating a repository whose name is identical to your GitHub username – in my case, that username is osteel, so I've created the osteel/osteel repository.

A little box like this one should appear during the process:

Special repository

Once the repository is created, simply add a README file to it, featuring a short description explaining how great you are. Commit and push – your GitHub profile page should now display it by default.

GitHub profile README

This is quite cool already, but as I was browsing some examples I stumbled upon Simon Willison’s version, which has some dynamic content like recent work and blog publications. He wrote about it on his blog, explaining how he used a combination of GitHub Actions and Python to automatically update the README, and I've decided to do something similar, but with PHP.

The PHP script

I am so used to working on projects with frameworks nowadays that it actually took me a quick search to get started with a basic PHP script featuring some Composer dependencies.

I initialised the project with composer init and installed a lightweight library to parse my blog's RSS feed:

$ composer require dg/rss-php

Here's the full PHP script (posts.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),

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

Nothing too complicated here – Composer's autoload is required at the top, allowing me to use the RSS parsing library to generate a Markdown list of blog posts as a string, which I then insert between the <!-- posts --> and <!-- /posts --> tags (which should be already present in the README file). Finally, I replace the file's content with the new one.

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.

GitHub actions are defined as YAML files to be placed in a .github/workflows folder at the root of the project, containing a list of steps that they are to execute.

Here's mine (posts.yml):

name: Update blog posts

    - cron:  '0 0 * * *'

    runs-on: ubuntu-latest
    - name: Clone repository
      uses: actions/checkout@v2
    - name: Install PHP
      uses: shivammathur/setup-php@v2
        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
        commit_message: Updated latest blog posts

Again, nothing too complicated. I first give the action a name, followed by a list of events that should trigger it – pushing some code to the repository, a manual trigger from the interface (workflow_dispatch), or every day at midnight (like a cron task).

I then indicate that it should run on an Ubuntu image, where it will:

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


This was a quick experiment aiming at exploring GitHub Actions, which I expect to use 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 more for implementation details.

Last updated by osteel on the :: [ github ]

Like this?

You can unsubscribe at any time by clicking the link in the footer of the emails.
By clicking to subscribe, you acknowledge that your email address will be transferred to Mailchimp for processing.
Learn more about Mailchimp's privacy practices here.