Introduced in 2020, the GitHub user profile README allow individuals to give a long-form introduction. This multi-part tutorial explains how I setup my own profile to create dynamic content to aid discovery of my projects:
- with the Liquid template engine and Shields (Part 1 of 4)
- using GitHub's GraphQL API to query dynamic data about all my repos (Part 2 of 4)
- fetching RSS blog post details from third-party sites (Part 3 of 4)
- automating updates with GitHub Actions (keep reading below)
You can visit github.com/j12y to see the final result of what I came up with for my own profile page.
Dynamic Updates in Response to Changes
The problem to solve is how to dynamically update my GitHub Profile if there is a content change in a template, an update to the metadata of a repos, or a new blog post. Conveniently GitHub Actions provides a solution for continuous integration (CI) / continuous delivery (CD).
A GitHub Action can be triggered:
- manually
- when a commit or branch is merged
- at a scheduled interval
One big advantage is that by running directly on the GitHub platform it becomes convenient for storing secrets and enabling changes in repositories by utilizing an existing authorization.
Getting Started with GitHub Actions
The Learn GitHub Actions documentation provides a good introduction to terminology and for understanding how it works. I don't want to repeat those details and focus on how I applied it, so reference the docs if anything is unclear.
The configuration file for my project is .github/workflows/build-readme.yml. The folder naming convention is critical, but the YAML file can be named to suit the purpose and there may be multiple workflows kept in separate configuration files.
I found the easiest way to get started was with a manual trigger to make sure the job behaves in the way I expect before fully automating it. This is done by specifying a workflow_dispatch
configuration.
name: Update README.md
on:
workflow_dispatch:
From the Actions menu in the repo, you can select the Update README.md workflow and click the Run workflow
button to trigger the job to run at will.
GitHub Actions Jobs and Build Steps
What happens when the button is clicked depends on how the job is configured. Here's the first few steps of my build job.
jobs:
build:
runs-on: ubuntu-latest
environment: buildenv
steps:
- name: Trigger event
run: echo "Triggered by ${{ github.event_name }}"
- name: Checkout repo
uses: actions/checkout@v3
- name: Install dependencies
run: |
npm install
The runs-on
configures the type of machine to run the job. There are a number of different types of runners but a github hosted ubuntu-latest is versatile enough for my needs.
We'll come back to the environment
in the next section.
These first few steps demonstrate how you can run commands like npm install
or import other workflows such as how it uses
the actions/checkout to copy the contents of the repository into a working directory on the runner host. Read Reusable workflows for more about the syntax for referencing them.
Using Environment Variables with Actions
The previous section referenced the buildenv
environment. This enables variables or secrets that you want to be able to reference but not expose in code to be stored.
The environment distinguishes between secrets (which will be redacted from logs) and vars which may be captured in output for debugging and can be modified without changing any code. There are also some universally available github values that can be used.
- name: Build source
env:
TOKEN: ${{ secrets.TOKEN }}
REPO: ${{ github.repository_owner }}
USERNAME: ${{ github.repository_owner }}
MEDIUM_ID: ${{ vars.MEDIUM_ID }}
DEVTO_ID: ${{ vars.DEVTO_ID }}
DOLBYIO_ID: ${{ vars.DOLBYIO_ID }}
run: |
npm start
There is a bit of redundancy to this configuration that is not ideal. The variable names need to be mapped between the GitHub environment and the runner environment. I asked the community for tricks on Passing All GitHub Environment Variables and Secrets in a GitHub Actions Workflow but it seems this may be a common feature request.
One workaround would be to take all the secrets and variables, encode as JSON, pass that as a variable, and then unpack it again. Alternatively, could just store the JSON structure as the environment variable itself. In my case, since there were only a few variables I didn't add the extra complexity.
run: |
echo "$ALL_VARS"
env:
ALL_VARS: ${{ toJSON(vars) }}
The npm start
in my project generates the README.md output and is the primary build output from this workflow.
Next, I need to do something with the artifact.
Conditional in a GitHub Action Workflow
If there are no template changes, no new blog posts, and no repo updates it would be unnecessary to commit changes to the README.md. Worse, that can cause the job to fail since the commit would fail with no differences.
To solve for this, you can run a step to run a git diff
and store the result as an environment variable.
- name: Check for changes
run: |-
if git diff --exit-code; then
echo "README_CHANGED=0" >> "$GITHUB_ENV"
else
echo "README_CHANGED=1" >> "$GITHUB_ENV"
git diff
fi
The variable can then be checked with the if
syntax which means this step will be optional depending on the value of the environment variable.
- name: Commit change
if: env.README_CHANGED != 0
run: |-
git config user.name "GitHub Action: ${{ github.workflow }} #${{ github.run_number }}"
git config user.email "<>"
git add README.md
git commit -m 'GitHub Action: ${{ github.workflow }} #${{ github.run_number }}'
git push
If there are meaningful changes, these steps will set the git configuration for checkin to the repository with a meaningful commit message that references the GitHub Action's job number.
Actions to Manage Scheduled Jobs Like Cron
There are many ways to trigger an action to execute. One clever thing I observed others do was to have something like a webhook from a third-party service create an issue and then use the creation of an issue to trigger the action to run.
Given my use case is primarily about metadata updates and new blog content, a daily update is sufficient. With GitHub Actions you can also configure scheduled jobs that run like a cron job would at regular intervals.
on:
schedule:
- cron: '55 7 * * 1-5' # Weekdays at 7:55
Every weekday morning since that's when I'm more likely to have updates the workflow will run and update the README with any new changes.
Actions to Manage Merges
This last trigger is pretty straightforward, any time I change the content of the templates used to generate the README, I want that push
and merge to trigger an update right away.
The action can run on
that trigger as well:
on:
push:
Take Action on Your Profile
I hope this overview helps you get acquainted with GitHub Actions, perhaps using your own GitHub Profile as a way to get familiar with a low-stakes project while building your portfolio.
If you missed an earlier part of the series, check them out for specific dives into the various tools used.
- with the Liquid template engine and Shields (Part 1 of 4)
- using GitHub's GraphQL API to query dynamic data about all my repos (Part 2 of 4)
- fetching RSS blog post details from third-party sites (Part 3 of 4)
Let me know if you have any questions or were able to use this on your own profile or similar project.
Top comments (0)