Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Builds the site and publishes it.
#
# Astro writes the page that sits behind every permalink — one file per song,
# one per episode — out of tracks/playlist.json and the mirrored feed. That
# used to be tools/build-routes.py, and the pages it wrote were committed:
# adding one song meant a pull request carrying the thirty-odd files that song
# changed. Now the build runs here and nothing generated lives in the repo.
#
# Contributors still send an MP3 and three lines of JSON. They no longer send
# a diff of thirty-five generated pages with it.
#
# Pages has to be set to deploy from GitHub Actions rather than from a branch
# for this to be what is served: Settings → Pages → Source → GitHub Actions,
# or
#
# gh api -X PUT repos/omacom/radio.omarchy.org/pages -f build_type=workflow
#
# That is a one-time repository setting and not something a workflow can do for
# itself: actions/configure-pages only creates a Pages site that does not exist
# yet, and leaves an existing one on whatever source it already has. So the
# build checks it first and says so, rather than spending four minutes and a
# 160 MB upload to fail at the last step with "Not Found".

name: deploy

on:
push:
branches: [main]
# The same build and the same checks, without the publish: a pull request
# that would not build is worth knowing about before it is merged, and after
# a merge is too late here because the merge is the deploy.
pull_request:
workflow_dispatch:
# stories.yml calls this after mirroring an episode in. It has to: a push
# made with GITHUB_TOKEN does not set off another workflow, so the hourly
# commit would otherwise sit there unpublished until somebody else pushed.
workflow_call:
inputs:
ref:
description: What to build. The caller's own commit by default, which
is not what a caller that has just pushed one wants.
type: string
required: false
default: ''

# A deploy in flight and a second one behind it are two halves of a race for
# the same site, so they queue rather than overlap. Not shared with the feed
# mirror: this workflow is what that one calls, and a group held by the caller
# is a group the callee waits on forever.
#
# A pull request is not in that race. It gets a group of its own so a check
# never queues behind a deploy, and a second push to a branch calls off the
# check still running on the first.
concurrency:
group: >-
${{ github.event_name == 'pull_request'
&& format('deploy-pr-{0}', github.ref) || 'pages' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read
pages: write
id-token: write

jobs:
build:
runs-on: ubuntu-latest
steps:
# Before anything expensive. A repository still serving from a branch
# will not serve what this run uploads, and the failure it gives at the
# end names neither the cause nor the fix.
#
# Not on a pull request: nothing is being published, and a check that
# went red over a setting the branch cannot change would be red on every
# pull request until somebody changed it.
- name: Check that Pages is set to deploy from Actions
if: github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
run: |
if ! kind=$(gh api "repos/$GITHUB_REPOSITORY/pages" --jq .build_type 2>/dev/null); then
echo "::warning::could not read the Pages configuration; letting the deploy decide"
exit 0
fi
echo "Pages build type: $kind"
if [ "$kind" != "workflow" ]; then
echo "::error::Pages is set to deploy from a branch ($kind), so it will not serve this build."
echo "Set Settings -> Pages -> Source to GitHub Actions, or run:"
echo " gh api -X PUT repos/$GITHUB_REPOSITORY/pages -f build_type=workflow"
exit 1
fi

- uses: actions/checkout@v4
with:
# Empty means the commit this run is for, which is what a push
# wants. A caller passes a branch, so it gets its own new commit.
ref: ${{ inputs.ref }}

- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm

- run: npm ci

# The pages are only as good as the types behind them.
- name: Check the types
run: npm run check

- name: Build
run: npm run build

# Every route is asked for over a server that resolves paths the way
# this host does, and every link in every page is followed. This is what
# catches a permalink that would answer 404 before it is deployed.
- name: Check that every address serves the page it should
run: node tools/test-routes.mjs --no-build

# 160 MB of it is the songs, so this is skipped on a pull request: the
# build and the checks are what a branch needs, and there is nothing to
# publish it to.
- uses: actions/upload-pages-artifact@v3
if: github.event_name != 'pull_request'
with:
path: dist

deploy:
needs: build
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
63 changes: 0 additions & 63 deletions .github/workflows/routes.yml

This file was deleted.

45 changes: 32 additions & 13 deletions .github/workflows/stories.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Keeps stories/feed.rss level with the show.
# Keeps public/stories/feed.rss level with the show.
#
# The player reads the episode list out of this repo rather than from the
# show's host, so this is what makes a new episode appear on the deck. The
Expand All @@ -9,6 +9,12 @@
# mirror that compared the whole file would write 24 commits a day. The
# stamp is dropped before the comparison, in tools/fetch-stories.py.
#
# An episode has an address of its own, /podcast/<episode>, and the page behind
# it is written by the build out of this file — so mirroring the feed is what
# makes that page exist. The deploy is called here rather than left to the
# push: a commit made with GITHUB_TOKEN does not set off another workflow, so
# an episode would sit unpublished until somebody else pushed something.
#
# Note that GitHub turns off a scheduled workflow in a repository nobody has
# pushed to for 60 days, and emails to say so. Any commit turns it back on.

Expand All @@ -21,17 +27,21 @@ on:
- cron: '17 * * * *'
workflow_dispatch:

# Two runs writing the same file would race each other to push it, and
# routes.yml pushes the same pages when the playlist moves.
# Two runs writing the same file would race each other to push it. Its own
# group, not the deploy's: this workflow calls that one, and a group held here
# is a group the deploy would wait on until this job ended — which it cannot,
# because it is waiting for the deploy.
concurrency:
group: pages
group: feed-mirror

permissions:
contents: write

jobs:
refresh:
runs-on: ubuntu-latest
outputs:
published: ${{ steps.mirror.outputs.published }}
steps:
- uses: actions/checkout@v4

Expand All @@ -40,19 +50,12 @@ jobs:
- name: Read the feed
run: python3 tools/fetch-stories.py

# An episode has an address of its own, /podcast/<episode>, and on a
# static host that means a page of its own. This is where a new one gets
# written, along with the sitemap entry that points at it.
- name: Write the page behind every episode
run: python3 tools/build-routes.py

- name: Check that the deck and the pages agree about every address
run: python3 tools/test-routes.py

- name: Commit it, if the show published something
id: mirror
run: |
if git diff --quiet; then
echo "nothing new"
echo "published=no" >> "$GITHUB_OUTPUT"
exit 0
fi
git config user.name 'github-actions[bot]'
Expand All @@ -61,3 +64,19 @@ jobs:
git commit -m 'Bring the podcast feed up to date'
git pull --rebase --autostash
git push
echo "published=yes" >> "$GITHUB_OUTPUT"

# Only when there is something new to publish. Building the same tree
# twenty-four times a day would deploy nothing and say it had.
publish:
needs: refresh
if: needs.refresh.outputs.published == 'yes'
permissions:
contents: read
pages: write
id-token: write
uses: ./.github/workflows/deploy.yml
with:
# The commit this run started at is the one before the mirror's, so the
# build has to be told to take the branch as it now stands.
ref: main
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.DS_Store
*.log
__pycache__/
node_modules/
dist/
.astro/
Loading
Loading