Conform to podcasting 2.0 #2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Jekyll site CI | |
on: | |
push: | |
branches: | |
- main | |
pull_request: | |
types: [opened, synchronize] | |
jobs: | |
episode: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Dates are valid | |
run: | | |
pip install pytz ciso8601 | |
for episode in _episodes/*/*.md; do | |
date=$(grep 'date:' "$episode" | head -n1 | sed 's/^date: //') | |
if ! python -c "import ciso8601; ciso8601.parse_rfc3339('$date');"; then | |
echo "$episode: bad date '$date'" | |
exit 1 | |
fi | |
done | |
- name: No smart symbols | |
run: | | |
for episode in _episodes/*/*.md; do | |
# this isn't a normal dash, and doesn't get turned into a list | |
if grep -qF '⁃' "$episode"; then | |
echo "$(basename "$episode"): abnormal dash won't make a list" | |
exit 1 | |
fi | |
if grep -P '[“‘’”]' "$episode"; then | |
echo "$(basename "$episode"): found smart quotes" | |
exit 1 | |
fi | |
done | |
- name: Timecode lists are correctly formatted | |
run: | | |
for episode in _episodes/*/*.md; do | |
# timecodes should never start a line (should be in header or list) | |
if grep -qP '^\[@' "$episode"; then | |
echo "$(basename "$episode"): timecode not in list or header" | |
exit 1 | |
fi | |
# timecode listings need to not have empty lines, or we'll get | |
# | |
# <li><p>[@HH:MM:SS] | |
# | |
# which doesn't render right. it happens to work for timecode | |
# listings that have sub-listings, but easiest to check that there | |
# just aren't any gaps. | |
if ! awk '/^\s*$/ { empty = 1; next; } /^\s*-\s*\[@[0-9]/ { if (in_list == 1 && empty == 1) { exit 1; } else { in_list = 1; empty = 0; next; } } /^\s*-/ { empty = 0; next; } { in_list = 0; empty = 0; }' "$episode"; then | |
echo "$(basename "$episode"): empty lines between list items" | |
exit 1 | |
fi | |
done | |
- name: No duplicate URLs | |
run: | | |
for episode in _episodes/*/*.md; do | |
file=$(grep 'file:' "$episode" | head -n1 | sed -e 's/^file: //' -e 's/"//g') | |
n=$(grep -F "$file" -l _episodes/*/*.md | wc -l) | |
if [[ $n -gt 1 ]]; then | |
echo "$episode: shares file with other episodes:" | |
grep -F "$file" -l _episodes/*/*.md | grep -vF "$episode" | |
exit 1 | |
fi | |
done | |
- name: No duplicate slugs | |
# For collections, jekyll _only_ uses the basename (without date) of each | |
# post for the slug, and doesn't error on duplicates. So we must check. | |
run: | | |
for episode in _episodes/*/*.md; do | |
slug="$(basename "$episode" | sed 's/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-//')" | |
files=(_episodes/*/????-??-??-$slug) | |
if [[ "${#files[@]}" -gt 1 ]]; then | |
echo "Duplicate slugs found: ${files[*]}" | |
exit 1 | |
fi | |
done | |
- name: Get all changed episode files | |
id: changed-markdown-files | |
uses: tj-actions/changed-files@v44 | |
with: | |
# Avoid using single or double quotes for multiline patterns | |
files: | | |
_episodes/*/*.md | |
- name: File size specifications are correct | |
# note: we only guard this one since it's slow to download all the | |
# episode files; the others are fine to run for all files since they run | |
# locally. | |
if: steps.changed-markdown-files.outputs.any_changed == 'true' | |
env: | |
ALL_CHANGED_FILES: ${{ steps.changed-markdown-files.outputs.all_changed_files }} | |
run: | | |
for episode in ${ALL_CHANGED_FILES}; do | |
# skip deleted episodes (if any) | |
[ -e "$episode" ] || continue; | |
length=$(grep 'length:' "$episode" | head -n1 | awk '{print $2}' | sed 's/"//g') | |
file=$(grep 'file:' "$episode" | head -n1 | sed -e 's/^file: //' -e 's/"//g') | |
size=$(curl --head -f -s "$file" | grep content-length | awk '{print $2}' | sed 's/\r//') | |
if [ -z $size ]; then | |
echo "couldn't get content length of \"$file\"" | |
exit 1 | |
fi | |
if [ $size -ne $length ]; then | |
echo "$(basename "$episode"): ${length}b (reported) != ${size}b (actual)" | |
exit 1 | |
fi | |
done | |
transcript: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Transcripts match episodes | |
run: | | |
for transcript in _transcripts/*/*.md; do | |
episode="_episodes/$(basename "$(dirname "$transcript")")/$(basename "$transcript")" | |
if [[ ! -e $episode ]]; then | |
echo "$transcript: no matching episode" | |
exit 1 | |
fi | |
done | |
feed: | |
# fails for now due to https://github.com/w3c/feedvalidator/issues/116 | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: ruby/setup-ruby@v1 | |
with: | |
ruby-version: '3.2' | |
bundler-cache: true | |
- name: Install dependencies | |
run: bundle install | |
- name: Build site and feed | |
run: bundle exec jekyll build | |
- name: Grab feed validator | |
run: | | |
git clone https://github.com/w3c/feedvalidator.git | |
- uses: actions/setup-python@v4 | |
with: | |
python-version: '3.x' | |
cache: 'pip' | |
- name: Validate feed | |
run: | | |
# Fix Ubuntu MIME type for RSS | |
sudo sed -i 's@application/x-rss+xml@application/rss+xml@' /etc/mime.types | |
cp _site/podcast.rss validate.rss | |
# https://github.com/rubys/feedvalidator/issues/16 | |
sed -i -e 's/https:/http:/g' \ | |
-e '/rel="self"/ s@href="[^"]*"@href="file://'"$(pwd)"'/validate.rss"@' \ | |
-e '/xmlns:content/a \ | |
xml:base="https://rustacean-station.org"' \ | |
validate.rss | |
cd feedvalidator | |
pip install -r requirements.txt | |
if ! python src/demo.py ../validate.rss; then | |
cat ../validate.rss | |
exit 1 | |
fi |