Automating Monthly Band Checks with GitHub Actions and GitHub Pages
So…sometimes you’d like to know if one of your favourite bands has rolled out a new album. A long time ago I just crafted a small JSON file as a small database, a list of objects where each one represents a band and contains its latest album and MusicBrainz ID (plus other info as underlined by its schema).
Then, once in a while, I run the script to see if there’s something new.
Today I took the chance of some free time to unleash Claude a bit and pump up the original script:
- better terminal interface
- backoff retry strategy when interacting with MusicBrainz
- partial runs save their results in the JSON file
One of the last things missing was automatise this process on a scheduled basis. So our friend GitHub came to the rescue even this time.
What we’re building
- A GitHub Actions workflow that runs on the 1st of every month
- The workflow commits updated state (checks.json) back to main branch
- It generates a static HTML results page and deploys it to GitHub Pages
Prerequisites
- A Node.js project in a GitHub repo
- Node 22+ locally and in CI
- The repo can be public or private (GitHub Actions free tier gives 2,000 minutes/month for private repos — one monthly 10-minute run costs nothing)
Step 1 — Emit structured results from your script #
Basically, this simple block of code:
// before this, looping objects and querying MusicBrainz...
try {
const payload = {
date: TODAY,
elapsed_seconds: (Date.now() - startTime) / 1000,
bands_total: entries.length,
findings: results,
};
await writeFile(RESULTS_FILE, JSON.stringify(payload, null, 2), 'utf8');
} catch (err) {
console.error(red('Warning: failed to write results.json:'), err.message);
}
where results.txt contains the records that will be displayed on GitHub Pages.
Step 2 — Create the HTML page generator #
This is basically this file reading our results, and composing an HTML file inside a site folder. This file will be run by our scheduled action.
Keeping HTML generation out of the main script separates concerns: index.mjs fetches and persists data, generate-page.mjs renders it. You can regenerate the page locally from any saved results.json without re-running the API calls.
Step 3 — Create the workflow file and push it #
Create .github/workflows/monthly-check.yml:
name: Monthly bands check
on:
schedule:
- cron: '0 8 1 * *' # 1st of every month at 08:00 UTC
workflow_dispatch: # manual trigger for testing
permissions:
contents: write
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: Run bands check
run: node index.mjs
- name: Commit updated state
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add checks.json
git diff --staged --quiet || git commit -m "chore: monthly bands check $(date -u +%Y-%m-%d)"
git push
- name: Generate results page
run: node generate-page.mjs
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
publish_branch: gh-pages
commit_message: "Update results page ${{ github.run_id }}"
force_orphan: false
A few things worth noting:
permissions: contents: write— the defaultGITHUB_TOKENis read-only. This line grants it write access so it can commitchecks.jsonback and push to thegh-pagesbranch.workflow_dispatch:adds a “Run workflow” button in the Actions UI. Essential for testing without waiting for the 1st of the month.git diff --staged --quiet || git commit:skips creating an empty commit if nothing changed (e.g. all bands were already checked today).timeout-minutes: 20:caps runaway runs. The default is 6 hours; a 20-minute ceiling is safer for a script that should finish in ~10 minutes.peaceiris/actions-gh-pages@v4:the de facto standard action forgh-pagesbranch deployments. It handles orphan branch creation on first run, so you don’t need to create the branch manually.
💬 Note: pushes to main do not trigger this workflow. The only triggers are the monthly cron and the manual button. No push: trigger is defined.
Step 4 — Trigger the first run manually #
The gh-pages branch doesn’t exist yet, the action creates it. You need to run the workflow once before you can configure Pages.
- Go to your repo → Actions tab
- Click “Monthly bands check” in the left sidebar
- Click “Run workflow” → leave “Use workflow from” on
main→ click the green “Run workflow” button
Wait for the job to complete (green checkmark). This first run creates the gh-pages branch and pushes index.html to it. “Use workflow from: main” means “run the copy of the workflow file that lives on main.” It has nothing to do with where the results are deployed.
Step 5 — Enable GitHub Pages #
Now that gh-pages exists, configure Pages to serve from it:
- Go to Settings → Pages
- Under Build and deployment → Source, select “Deploy from a branch”
- Under Branch, select
gh-pagesand/ (root) - Click Save
If you already have a site at username.github.io, there’s no conflict. GitHub serves that from your username/username.github.io repo. This project gets its own path: username.github.io/bands_update/. Each project repo gets its own sub-path automatically.
⚠️ One gotcha: GitHub silently disables scheduled workflows on repos with no activity for 60 days. If you don’t push anything for two months, the schedule stops firing and you get an email warning. Re-enabling it requires going to Actions → the workflow → “Enable workflow”. Since you’re running it monthly and committing checks.json back, this won’t be an issue — each run creates a commit, which counts as activity.
Conclusions #
Next step could be an integration with Whatsapp or Telegram to notify me there are new albums :-)