-
Notifications
You must be signed in to change notification settings - Fork 0
346 lines (302 loc) · 14.2 KB
/
Copy pathbuild.yml
File metadata and controls
346 lines (302 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
name: Build and Deploy Telar Site
#
# There are two equally valid ways to build a Telar site: locally on the user's
# own computer (using scripts/build_local_site.py), or through this GitHub
# Actions workflow which builds and deploys to GitHub Pages automatically.
#
# This workflow runs the full Telar build pipeline:
#
# 1. Set up the three runtime environments (Ruby for Jekyll, Python for the
# build scripts, Node.js for the JavaScript bundler)
# 2. Fetch content from Google Sheets (if enabled in _config.yml) — this
# downloads the spreadsheet tabs as CSV files into telar-content/spreadsheets/
# 3. Run csv_to_json.py to process the CSV data into the JSON files that
# Jekyll uses to render story pages
# 4. Run generate_collections.py to turn the markdown texts in
# telar-content/texts/ into Jekyll collection files
# 5. Run search.py to generate search-data.json with facet counts for the
# browse-and-search UI on the objects page
# 6. Process audio objects — generate peak data and clip files for audio
# cards (with smart caching — audio is only regenerated when audio files
# or objects.csv change)
# 7. Bundle the JavaScript source modules (assets/js/telar-story/) into a
# single file using esbuild
# 8. Build the Jekyll site
# 9. Generate IIIF deep-zoom image tiles for self-hosted exhibition objects
# (with smart caching — tiles are only regenerated when source images or
# objects.csv change)
# 10. Deploy the built site to GitHub Pages
#
# Both the IIIF tile generation and the audio processing include change-detection
# steps that compare the current commit against the previous one. If only content
# files changed (not images/audio or objects.csv), the build restores data from
# the GitHub Actions cache instead of regenerating, which saves significant
# build time. Sites without audio objects skip audio tool installation and
# processing entirely — zero friction for image-only sites.
#
# Version: v1.0.0-beta
on:
push:
branches: [main]
workflow_dispatch:
inputs:
force_iiif:
description: 'Force IIIF tile regeneration'
type: boolean
default: true
force_audio:
description: 'Force audio regeneration'
type: boolean
default: true
permissions:
contents: write
pages: write
id-token: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install libvips (for fast IIIF tile generation)
run: sudo apt-get install -y libvips-tools
- name: Install audio processing tools (audiowaveform + ffmpeg)
run: |
# Only install if audio objects exist (zero friction for image-only sites)
if ls telar-content/objects/*.mp3 telar-content/objects/*.ogg telar-content/objects/*.m4a 2>/dev/null | head -1 | grep -q .; then
echo "Audio objects detected - installing audiowaveform and ffmpeg"
sudo add-apt-repository -y ppa:chris-needham/ppa
sudo apt-get update -q
sudo apt-get install -y audiowaveform ffmpeg
else
echo "No audio objects found - skipping audio tool installation"
fi
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
- name: Build JavaScript bundle
run: |
npm install
npm run build:js
- name: Fetch data from Google Sheets (if enabled)
run: |
# Check if Google Sheets integration is enabled in _config.yml
ENABLED=$(python3 -c "import yaml; config=yaml.safe_load(open('_config.yml')); print(config.get('google_sheets', {}).get('enabled', False))")
if [ "$ENABLED" = "True" ]; then
echo "✓ Google Sheets integration enabled - fetching CSVs..."
python3 scripts/fetch_google_sheets.py
else
echo "✓ Google Sheets integration disabled - using existing CSV files"
fi
- name: Convert CSV to JSON
run: |
python scripts/csv_to_json.py
- name: Generate Jekyll collections
run: |
python scripts/generate_collections.py
- name: Generate search data
run: |
python scripts/telar/search.py
- name: Restore audio data from cache
id: cache-audio
uses: actions/cache/restore@v5
with:
path: cached-audio/
key: audio-data-${{ hashFiles('telar-content/objects/*.mp3', 'telar-content/objects/*.ogg', 'telar-content/objects/*.m4a', 'telar-content/spreadsheets/objects.csv') }}
- name: Detect if audio regeneration is needed
id: detect-audio-changes
run: |
NEEDS_AUDIO="true"
# Check if any audio objects exist first
if ! ls telar-content/objects/*.mp3 telar-content/objects/*.ogg telar-content/objects/*.m4a 2>/dev/null | head -1 | grep -q .; then
echo "No audio objects found - skipping audio processing"
NEEDS_AUDIO="false"
elif [ "${{ github.event_name }}" = "push" ]; then
if git rev-parse HEAD~1 >/dev/null 2>&1; then
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD)
if echo "$CHANGED_FILES" | grep -qE '^telar-content/spreadsheets/objects\.csv$'; then
echo "objects.csv changed - audio regeneration needed"
NEEDS_AUDIO="true"
elif echo "$CHANGED_FILES" | grep -qE '^telar-content/objects/.*\.(mp3|ogg|m4a)$'; then
echo "Audio files changed - regeneration needed"
NEEDS_AUDIO="true"
else
echo "No audio-related changes - skipping audio regeneration"
NEEDS_AUDIO="false"
fi
else
echo "First commit - running full audio processing"
NEEDS_AUDIO="true"
fi
else
echo "Manual trigger - using input parameter (force_audio=${{ inputs.force_audio }})"
NEEDS_AUDIO="${{ inputs.force_audio }}"
fi
echo "needs_audio=$NEEDS_AUDIO" >> $GITHUB_OUTPUT
echo "Audio regeneration: $NEEDS_AUDIO"
- name: Process audio objects (peaks + clips)
if: steps.detect-audio-changes.outputs.needs_audio == 'true'
run: |
python scripts/process_audio.py \
--objects-dir telar-content/objects \
--data-dir _data \
--output-dir assets/audio
- name: Copy audio data to cache directory
if: steps.detect-audio-changes.outputs.needs_audio == 'true'
run: |
if [ -d "assets/audio" ] && [ "$(ls -A assets/audio/peaks 2>/dev/null)" ]; then
echo "Copying audio data to cache directory for future builds"
mkdir -p cached-audio
cp -r assets/audio/* cached-audio/
else
echo "No audio data to cache"
fi
- name: Save audio data to cache
if: steps.detect-audio-changes.outputs.needs_audio == 'true'
uses: actions/cache/save@v5
with:
path: cached-audio/
key: audio-data-${{ hashFiles('telar-content/objects/*.mp3', 'telar-content/objects/*.ogg', 'telar-content/objects/*.m4a', 'telar-content/spreadsheets/objects.csv') }}
- name: Restore audio data from cache (when skipping regeneration)
if: steps.detect-audio-changes.outputs.needs_audio == 'false'
run: |
if [ -d "cached-audio" ] && [ "$(ls -A cached-audio)" ]; then
echo "Restoring audio data from cache"
mkdir -p assets/audio
cp -r cached-audio/* assets/audio/
echo "Audio data restored successfully"
else
echo "No cached audio data found (expected for sites without audio objects)"
fi
- name: Build Jekyll site
run: |
bundle exec jekyll build
- name: Restore IIIF tiles from cache
id: cache-iiif
uses: actions/cache/restore@v5
with:
path: cached-iiif/
# v0.5.0: Flattened image directory structure - now all images in telar-content/objects/
key: iiif-tiles-${{ hashFiles('telar-content/objects/**') }}
- name: Detect if IIIF regeneration is needed
id: detect-changes
run: |
# Failsafe: Default to regenerating IIIF (safe default)
NEEDS_IIIF="true"
# Only attempt detection for push events (not manual triggers)
if [ "${{ github.event_name }}" = "push" ]; then
# Check if this is the first commit (no previous commit to compare)
if git rev-parse HEAD~1 >/dev/null 2>&1; then
# Get list of changed files
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD)
# Check if any images, objects.csv, or _config.yml changed
# Note: _config.yml added in v0.4.3 - IIIF tiles embed base URL from config (thanks Tara)
# v0.5.0: CSV-aware image monitoring - only triggers for images matching object_ids
# Always regenerate if objects.csv or _config.yml changed
if echo "$CHANGED_FILES" | grep -qE '^telar-content/spreadsheets/objects\.csv$|^_config\.yml$'; then
echo "✓ objects.csv or _config.yml changed - IIIF regeneration needed"
NEEDS_IIIF="true"
# Check if any images changed
elif echo "$CHANGED_FILES" | grep -q '^telar-content/objects/'; then
echo "✓ Image files changed - checking if they match objects in CSV..."
# Get list of object IDs from objects.csv (skip header row)
OBJECT_IDS=$(tail -n +2 telar-content/spreadsheets/objects.csv | cut -d',' -f1)
# Get list of changed images (extract filenames, remove path and extension)
CHANGED_IMAGES=$(echo "$CHANGED_FILES" | grep '^telar-content/objects/' | sed 's|.*/||' | sed 's/\.[^.]*$//')
# Check if any changed image matches an object ID
MATCH_FOUND="false"
for img in $CHANGED_IMAGES; do
if echo "$OBJECT_IDS" | grep -qx "$img"; then
echo " - Image '$img' matches object ID in objects.csv"
MATCH_FOUND="true"
fi
done
if [ "$MATCH_FOUND" = "true" ]; then
echo "✓ Changed images match objects in CSV - IIIF regeneration needed"
NEEDS_IIIF="true"
else
echo "✓ Changed images don't match any objects in CSV - skipping IIIF regeneration"
NEEDS_IIIF="false"
fi
else
echo "✓ Only content files changed - skipping IIIF regeneration"
NEEDS_IIIF="false"
fi
else
echo "✓ First commit - running full build with IIIF"
NEEDS_IIIF="true"
fi
else
echo "✓ Manual trigger - using input parameter (force_iiif=${{ inputs.force_iiif }})"
NEEDS_IIIF="${{ inputs.force_iiif }}"
fi
echo "needs_iiif=$NEEDS_IIIF" >> $GITHUB_OUTPUT
echo "IIIF regeneration: $NEEDS_IIIF"
- name: Generate IIIF tiles into _site
if: steps.detect-changes.outputs.needs_iiif == 'true'
run: |
# v0.5.0: Check if source images directory exists (flattened structure)
if [ -d "telar-content/objects" ]; then
# Extract URL and baseurl from _config.yml
SITE_URL=$(python3 -c "import yaml; config=yaml.safe_load(open('_config.yml')); print(config.get('url', ''))")
BASE_URL=$(python3 -c "import yaml; config=yaml.safe_load(open('_config.yml')); print(config.get('baseurl', ''))")
FULL_URL="${SITE_URL}${BASE_URL}"
echo "Generating IIIF tiles with base URL: $FULL_URL"
# v0.5.0: Removed --source-dir parameter (script now defaults to telar-content/objects/)
# v0.5.0: Script is now CSV-driven - only processes objects listed in objects.json
python scripts/generate_iiif.py \
--output-dir _site/iiif/objects \
--base-url "$FULL_URL"
else
echo "No telar-content/objects directory found. Skipping IIIF generation."
fi
- name: Copy generated IIIF tiles to cache directory
if: steps.detect-changes.outputs.needs_iiif == 'true'
run: |
if [ -d "_site/iiif/objects" ] && [ "$(ls -A _site/iiif/objects 2>/dev/null)" ]; then
echo "✓ Copying IIIF tiles to cache directory for future builds"
mkdir -p cached-iiif
cp -r _site/iiif/objects/* cached-iiif/
else
echo "No IIIF tiles to cache (directory empty or missing)"
fi
- name: Save IIIF tiles to cache
if: steps.detect-changes.outputs.needs_iiif == 'true'
uses: actions/cache/save@v5
with:
path: cached-iiif/
# v0.5.0: Flattened image directory structure
key: iiif-tiles-${{ hashFiles('telar-content/objects/**') }}
- name: Restore IIIF tiles from cache to _site (when skipping regeneration)
if: steps.detect-changes.outputs.needs_iiif == 'false'
run: |
if [ -d "cached-iiif" ] && [ "$(ls -A cached-iiif)" ]; then
echo "✓ Restoring IIIF tiles from cache"
mkdir -p _site/iiif/objects
cp -r cached-iiif/* _site/iiif/objects/
echo "✓ IIIF tiles restored successfully"
else
echo "⚠️ Warning: No cached IIIF tiles found. Site may have missing images."
echo "⚠️ Run a full build with IIIF regeneration to create tiles."
fi
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
path: _site
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5