Files
cs249r_book/.github/workflows/tinytorch-preview-dev.yml
Vijay Janapa Reddi 11244afb62 fix(tinytorch-preview): mirror manifest at /tinytorch/ for visual-smoke
Live deploy mounts _build/ under /tinytorch/, so the meta tag's
hardcoded /tinytorch/release-manifest.json resolves correctly.
Visual-smoke serves _build/ at the root, so it needs the file at
_build/tinytorch/release-manifest.json to match the same path.

Mirror the manifest there after emit so smoke's HTTP server finds it.
2026-05-14 10:00:06 -04:00

273 lines
10 KiB
YAML

name: '🔥 TinyTorch · 👁️ Preview (Dev)'
# =============================================================================
# TinyTorch — Dev Preview Deploy (Quarto)
# =============================================================================
#
# Builds the TinyTorch Quarto site with PDFs and slide decks, then
# deploys to the dev preview site via SSH. Gated on Validate (Dev) passing.
#
# Flow:
# 1. BUILD_PDFS — Compile guide + paper PDFs (reusable workflow)
# 2. BUILD_SITE — Quarto render + inject PDFs + download slides
# 3. DEPLOY — Push to dev preview repo via SSH
#
# Triggers:
# - workflow_run: after Validate (Dev) succeeds on dev branch
# - workflow_dispatch: manual
#
# Deploys to: harvard-edge.github.io/{DEV_REPO}/{DEV_TINYTORCH_PATH}/
# Secrets: SSH_DEPLOY_KEY
# Vars: TINYTORCH_SITE, BOOK_TOOLS, DEV_REPO, DEV_REPO_URL, DEV_TINYTORCH_PATH
#
# Related:
# - tinytorch-validate-dev.yml — Gates this workflow (must pass first)
# - tinytorch-publish-live.yml — Production deploy to mlsysbook.ai/tinytorch/
# - tinytorch-build-pdfs.yml — Reusable PDF build
#
# =============================================================================
on:
workflow_dispatch:
workflow_run:
workflows: ["🔥 TinyTorch · ✅ Validate (Dev)"]
types: [completed]
branches: [dev]
permissions:
contents: read
actions: read
concurrency:
group: tinytorch-dev-deploy
cancel-in-progress: true
jobs:
# Build PDFs (only if Validate passed or manual trigger)
build-pdfs:
name: '📚 Build PDFs'
if: (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'dev') || github.event_name == 'workflow_dispatch'
uses: ./.github/workflows/tinytorch-build-pdfs.yml
# Build the site (after PDFs complete — site builds even if PDFs fail).
# Split out from deploy so the artifact gates the visual-smoke job before
# any dev preview goes live; deploy only runs if smoke is green.
build-site:
name: '🔥 Build TinyTorch Dev Site'
runs-on: ubuntu-latest
needs: [build-pdfs]
if: |
always() &&
((github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'dev') || github.event_name == 'workflow_dispatch')
permissions:
contents: read
actions: read
steps:
- name: 📥 Checkout
uses: actions/checkout@v6
- name: 🔧 Setup Quarto
uses: quarto-dev/quarto-actions/setup@v2
# Community dashboard is in-tree at tinytorch/quarto/community/
# and published via Quarto's `resources: community/**` entry; no
# pre-render copy step is needed here.
- name: 🔨 Build Quarto Site
working-directory: ${{ vars.TINYTORCH_SITE }}
run: |
quarto render
touch _build/.nojekyll
- name: 📦 Emit dev release manifest
# The footer release-pill.js fetches /tinytorch/release-manifest.json
# at runtime. Preview-dev builds still need this file to exist or
# visual-smoke catches the 404 as a console error on every page.
# We stamp a dev manifest derived from the commit SHA so the pill
# renders without erroring; the publish-live workflow stamps the
# real release manifest on its own.
run: |
set -euo pipefail
SHORT_SHA=$(git rev-parse --short HEAD)
RELEASE_ID="0.0.0-dev+${SHORT_SHA}"
RELEASE_HASH=$(python3 scripts/version/release.py compute-hash \
--paths tinytorch/modules tinytorch/quarto \
tinytorch/pyproject.toml tinytorch/MANIFEST.in \
--exclude '_build' '__pycache__' '*.pyc' '.DS_Store' \
'.ipynb_checkpoints' '*.egg-info')
python3 scripts/version/release.py emit-manifest \
--output ${{ vars.TINYTORCH_SITE }}/_build/release-manifest.json \
--project tinytorch \
--tier A \
--release-id "$RELEASE_ID" \
--release-hash "$RELEASE_HASH"
# Mirror at /tinytorch/release-manifest.json so visual-smoke, which
# serves _build/ at the root, can resolve the absolute path baked
# into the page meta tag. Live deploy mounts _build/ under
# /tinytorch/, so the file ends up in the right place either way.
mkdir -p ${{ vars.TINYTORCH_SITE }}/_build/tinytorch
cp ${{ vars.TINYTORCH_SITE }}/_build/release-manifest.json \
${{ vars.TINYTORCH_SITE }}/_build/tinytorch/release-manifest.json
echo "✅ Dev manifest written"
cat ${{ vars.TINYTORCH_SITE }}/_build/release-manifest.json
- name: 📥 Download PDF Guide
uses: actions/download-artifact@v8
continue-on-error: true
with:
name: TinyTorch-Guide
path: ./pdf-artifacts/guide
- name: 📥 Download PDF Paper
uses: actions/download-artifact@v8
continue-on-error: true
with:
name: TinyTorch-Paper
path: ./pdf-artifacts/paper
- name: 📁 Inject PDFs into site
run: |
echo "📁 Injecting PDFs into built site..."
mkdir -p ${{ vars.TINYTORCH_SITE }}/_build/assets/downloads
if [ -f ./pdf-artifacts/guide/TinyTorch-Guide.pdf ]; then
cp ./pdf-artifacts/guide/TinyTorch-Guide.pdf ${{ vars.TINYTORCH_SITE }}/_build/assets/downloads/TinyTorch-Guide.pdf
echo "✅ Injected TinyTorch-Guide.pdf"
else
echo "⚠️ Guide PDF not found"
fi
if [ -f ./pdf-artifacts/paper/paper.pdf ]; then
cp ./pdf-artifacts/paper/paper.pdf ${{ vars.TINYTORCH_SITE }}/_build/assets/downloads/TinyTorch-Paper.pdf
echo "✅ Injected TinyTorch-Paper.pdf"
else
echo "⚠️ Paper PDF not found"
fi
echo ""
echo "📦 Downloads folder contents:"
ls -la ${{ vars.TINYTORCH_SITE }}/_build/assets/downloads/ || echo "No downloads folder"
- name: 📊 Download slide decks from release
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "📊 Downloading slide decks from GitHub release..."
mkdir -p ${{ vars.TINYTORCH_SITE }}/_build/assets/slides
# Download all PDFs from the slides release
gh release download tinytorch-slides-v0.1.0 \
--repo harvard-edge/cs249r_book \
--pattern "*.pdf" \
--dir ${{ vars.TINYTORCH_SITE }}/_build/assets/slides || echo "⚠️ No slides release found"
echo ""
echo "📦 Slides folder contents:"
ls -la ${{ vars.TINYTORCH_SITE }}/_build/assets/slides/ || echo "No slides folder"
echo "✅ Downloaded $(ls ${{ vars.TINYTORCH_SITE }}/_build/assets/slides/*.pdf 2>/dev/null | wc -l) slide decks"
- name: 🔗 Rewrite URLs for dev site
run: bash .github/scripts/rewrite-dev-urls.sh tinytorch ${{ vars.TINYTORCH_SITE }}/_build
- name: 🔧 Modify announcement for dev preview
run: |
COMMIT_SHORT="${{ github.sha }}"
COMMIT_SHORT="${COMMIT_SHORT:0:8}"
python3 ${{ vars.BOOK_TOOLS }}/scripts/publish/modify_dev_announcement.py \
${{ vars.TINYTORCH_SITE }}/_build \
--verbose \
--commit-hash "${{ github.sha }}" \
--commit-short "$COMMIT_SHORT"
- name: 📤 Upload built site as artifact
uses: actions/upload-artifact@v7
with:
name: tinytorch-dev-build
path: ${{ vars.TINYTORCH_SITE }}/_build
retention-days: 1
include-hidden-files: true
# Visual smoke gate — fails before deploy if Bootstrap/CSS regressed,
# the navbar collapse breakpoint shifted, or pages render blank. Runs
# the shared .github/scripts/visual_smoke.py against the artifact above.
smoke:
name: '👁️ Visual Smoke'
needs: [build-site]
if: needs.build-site.result == 'success'
uses: ./.github/workflows/infra-visual-smoke.yml
with:
site: tinytorch
artifact: tinytorch-dev-build
pages: '/index.html /preface.html'
# Deploy is gated on smoke passing. Re-fetches the artifact rather than
# the raw repo so we deploy the exact bytes that smoke validated.
deploy:
name: '🚀 Deploy TinyTorch Dev'
runs-on: ubuntu-latest
needs: [smoke]
if: needs.smoke.result == 'success'
permissions:
contents: read
steps:
- name: 📦 Download built site
uses: actions/download-artifact@v8
with:
name: tinytorch-dev-build
path: _build
- name: 🚀 Deploy to Dev Site via SSH
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
run: |
echo "🔐 Starting ssh-agent..."
eval "$(ssh-agent -s)"
echo "$SSH_DEPLOY_KEY" | tr -d '\r' | ssh-add - > /dev/null
mkdir -p ~/.ssh
ssh-keyscan github.com >> ~/.ssh/known_hosts
git config --global user.email "actions@github.com"
git config --global user.name "GitHub Actions"
echo "🔄 Cloning dev preview repository (${{ vars.DEV_REPO }})..."
git clone --depth=1 ${{ vars.DEV_REPO_URL }} target-repo
cd target-repo
echo "🧹 Cleaning ${{ vars.DEV_TINYTORCH_PATH }} directory..."
rm -rf ${{ vars.DEV_TINYTORCH_PATH }}
mkdir -p ${{ vars.DEV_TINYTORCH_PATH }}
echo "🚚 Copying TinyTorch site content..."
cp -r "${{ github.workspace }}/_build/." ${{ vars.DEV_TINYTORCH_PATH }}/
echo "🔍 Validating deployment content..."
if [ ! -f "${{ vars.DEV_TINYTORCH_PATH }}/index.html" ]; then
echo "❌ CRITICAL: ${{ vars.DEV_TINYTORCH_PATH }}/index.html is missing. Aborting deployment."
exit 1
fi
echo "📦 Contents of ${{ vars.DEV_TINYTORCH_PATH }}/:"
ls -la ${{ vars.DEV_TINYTORCH_PATH }}/ | head -10
git add .
git commit -m "🔥 Deploy TinyTorch dev (Quarto) to /${{ vars.DEV_TINYTORCH_PATH }}/ from ${{ github.sha }}" --allow-empty || echo "🟡 Nothing to commit"
for i in 1 2 3; do
if git push origin main 2>/dev/null; then
echo "✅ Push succeeded on attempt $i"
break
fi
echo "⚠️ Push failed (attempt $i/3), pulling with rebase..."
git pull --rebase origin main || true
done
echo "✅ TinyTorch deployed to: https://harvard-edge.github.io/${{ vars.DEV_REPO }}/${{ vars.DEV_TINYTORCH_PATH }}/"