Skip to content

fix(vol4): clean inline math delimiters in 01-boundary, 08-perception… #712

fix(vol4): clean inline math delimiters in 01-boundary, 08-perception…

fix(vol4): clean inline math delimiters in 01-boundary, 08-perception… #712

name: 'πŸ“š Book Β· βœ… Validate (Dev)'
# =============================================================================
# βœ… Validate (Dev) β€” Lint, build, and test the book on every dev push
# =============================================================================
# Re-run trigger: 2026-05-18 β€” refresh after projects.json design-grammar fix
# =============================================================================
#
# Runs pre-commit checks, then builds both volumes in all formats (HTML, PDF,
# EPUB) on Linux and Windows using containerized Quarto. Uploads artifacts
# for the Preview workflow to deploy.
#
# Flow:
# 1. PRE_COMMIT β€” Run pre-commit hooks on all files
# 2. BUILD_CONFIG β€” Determine formats, OS targets, and container settings
# 3. CONTAINER_BUILD β€” Matrix build via book-build-container.yml (up to 12 jobs)
# 4. COLLECT_RESULTS β€” Aggregate per-platform success and artifact names
# 5. VALIDATION_SUMMARY β€” Report pass/fail and trigger preview deployment
#
# Triggers:
# - pull_request into dev, paths under book/ and workflow files
# - push: dev branch, paths under book/ and workflow files
# - workflow_dispatch: manual with format/OS/container options
#
# The pull_request trigger exists because book/** changes previously merged
# with no render gate. PR #2002 landed with the vol1 chapter list commented
# out (the PDF built the foreword alone) and with \newif\ifmlsyscoverpage
# declared in a vol1-only header while the shared before-body.tex set it,
# which broke the Volume II PDF build outright. Neither was visible until
# someone built the PDFs by hand days later.
#
# Deploys to: N/A (artifacts consumed by book-preview-dev.yml)
#
# Related:
# - book-build-container.yml β€” Reusable build matrix called by this workflow
# - book-preview-dev.yml β€” Deploys artifacts produced here to staging
# - book-publish-live.yml β€” Production pipeline (builds from main)
#
# =============================================================================
on:
pull_request:
branches: [dev] # Render gate: catch book breakage before it lands
paths:
- 'book/**'
- 'books/**'
- 'publishing/**'
- '.github/workflows/book-validate-dev.yml'
- '.github/workflows/book-build-container.yml'
- '.github/workflows/book-preview-dev.yml'
- '.github/workflows/update-contributors.yml'
push:
branches: [dev] # Auto-trigger for dev branch
paths:
- 'book/**'
- 'books/**'
- 'publishing/**'
- '.github/workflows/book-validate-dev.yml'
- '.github/workflows/book-build-container.yml'
- '.github/workflows/book-preview-dev.yml'
- '.github/workflows/update-contributors.yml'
workflow_dispatch: # Manual trigger for testing
inputs:
test_branch:
description: 'Branch to test (for feature branch testing)'
required: false
default: ''
type: string
target:
description: 'Target branch for build artifacts (dev/main)'
required: false
type: choice
default: 'dev'
options:
- dev
- main
# Target selection
build_target:
description: 'πŸ“¦ Build target (vol1, vol2, vol3, vol4, all)'
required: true
default: 'all'
type: choice
options:
- all
- vol1
- vol2
- vol3
- vol4
# Format & OS selection
build_formats:
description: 'πŸ“¦ Build formats (all, html, pdf, epub)'
required: true
default: 'all'
type: choice
options:
- all
- html
- pdf
- epub
build_os:
description: 'βš™οΈ Build OS (linux, both, windows) β€” Windows disabled by default'
required: true
default: 'linux'
type: choice
options:
- linux
- both
- windows
# Container validation
run_health_check:
description: 'πŸ’Š Run container health check'
required: false
default: false
type: boolean
# Container configuration (consistent with other workflows)
container_registry:
description: 'Container registry URL'
required: false
default: 'ghcr.io'
type: string
container_tag:
description: 'Container tag to use'
required: false
default: 'latest'
type: string
permissions:
contents: write
pages: write
packages: read
actions: read
concurrency:
# Group includes event_name so a manual workflow_dispatch doesn't cancel
# the auto push run (or vice versa) on the same SHA.
group: book-validate-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
# Step 1: Pre-commit validation
pre-commit:
name: 'πŸ” Pre-commit Checks'
runs-on: ubuntu-latest
steps:
- name: πŸ“₯ Checkout repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.test_branch || github.ref }}
fetch-depth: 0
- name: 🐍 Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: πŸ” Run pre-commit checks
run: |
echo "πŸ” Installing and running pre-commit..."
python -m pip install --upgrade pip
pip install pre-commit
echo "πŸ“¦ Installing project dependencies for custom hooks..."
pip install -r requirements.txt || echo "⚠️ Could not install all requirements, continuing..."
echo "πŸ“¦ Installing fallback hook dependencies..."
pip install rich requests pillow pint pandas pytest || echo "⚠️ Could not install one or more fallback deps, continuing..."
echo "πŸ”§ Installing pre-commit hooks..."
pre-commit install --install-hooks
echo "Python version: $(python --version)"
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "πŸ”„ Running pre-commit on PR changes (origin/${{ github.base_ref }}..HEAD)..."
CMD="pre-commit run --from-ref origin/${{ github.base_ref }} --to-ref HEAD --verbose"
elif [ "${{ github.event_name }}" = "push" ] && git rev-parse --verify HEAD~1 >/dev/null 2>&1; then
echo "πŸ”„ Running pre-commit on push changes (HEAD~1..HEAD)..."
CMD="pre-commit run --from-ref HEAD~1 --to-ref HEAD --verbose"
else
echo "πŸ”„ Running pre-commit on all files..."
CMD="pre-commit run --all-files --verbose"
fi
if eval "$CMD"; then
echo "βœ… Pre-commit checks: PASSED"
else
echo "❌ Pre-commit checks: FAILED"
exit 1
fi
# Step 2: Container Health Check (validate containers before builds)
container-health-check:
name: 'πŸ’Š Container Health Check'
needs: [build-config]
if: inputs.run_health_check == true
uses: ./.github/workflows/infra-health-check.yml # Shared workflow - no book- prefix
with:
test_linux: true
test_windows: true
container_registry: 'ghcr.io'
container_tag: 'latest'
# Step 3: Update contributors (runs in parallel with builds after pre-commit passes)
update-contributors:
name: 'πŸ‘₯ Update Contributors'
needs: [pre-commit]
if: needs.pre-commit.result == 'success' && github.event_name == 'push'
uses: ./.github/workflows/update-contributors.yml
# Step 4: Determine build configuration (shared logic)
build-config:
name: '🎯 Build Configuration'
needs: [pre-commit]
if: always() && needs.pre-commit.result == 'success'
runs-on: ubuntu-latest
outputs:
build_html: ${{ steps.config.outputs.build_html }}
build_pdf: ${{ steps.config.outputs.build_pdf }}
build_epub: ${{ steps.config.outputs.build_epub }}
build_linux: ${{ steps.config.outputs.build_linux }}
build_windows: ${{ steps.config.outputs.build_windows }}
build_target: ${{ steps.config.outputs.build_target }}
container_registry: ${{ steps.config.outputs.container_registry }}
container_tag: ${{ steps.config.outputs.container_tag }}
target: ${{ steps.config.outputs.target }}
steps:
- name: πŸ“₯ Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 2
- name: 🎯 Determine build configuration
id: config
run: |
echo "🎯 === BUILD CONFIGURATION ==="
# Determine what to build based on inputs (individual format flags)
if [ "${{ github.event_name }}" = "push" ] || [ "${{ github.event_name }}" = "pull_request" ]; then
BUILD_HTML="true"
BUILD_PDF="true"
BUILD_EPUB="true"
BUILD_LINUX="true"
BUILD_WINDOWS="false"
TARGET="dev"
# Selective auto-detection of modified volume paths
if [ "${{ github.event_name }}" = "pull_request" ]; then
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }} HEAD 2>/dev/null || echo "")
else
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
fi
echo "πŸ“ Changed files: $CHANGED_FILES"
HAS_VOL1=false
HAS_VOL2=false
HAS_VOL3=false
HAS_VOL4=false
HAS_SHARED=false
while IFS= read -r file; do
[ -z "$file" ] && continue
if [[ "$file" =~ ^books/vol1/ ]] || [[ "$file" =~ ^publishing/quarto/contents/vol1/ ]]; then HAS_VOL1=true; fi
if [[ "$file" =~ ^books/vol2/ ]] || [[ "$file" =~ ^publishing/quarto/contents/vol2/ ]]; then HAS_VOL2=true; fi
if [[ "$file" =~ ^books/vol3/ ]] || [[ "$file" =~ ^publishing/quarto/contents/vol3/ ]]; then HAS_VOL3=true; fi
if [[ "$file" =~ ^books/vol4/ ]] || [[ "$file" =~ ^publishing/quarto/contents/vol4/ ]]; then HAS_VOL4=true; fi
if [[ "$file" =~ ^publishing/binder ]] || [[ "$file" =~ ^shared/ ]] || [[ "$file" =~ ^\.github/ ]]; then HAS_SHARED=true; fi
done <<< "$CHANGED_FILES"
if [ "$HAS_SHARED" = true ]; then
BUILD_TARGET="all"
elif [ "$HAS_VOL1" = true ] && [ "$HAS_VOL2" = false ] && [ "$HAS_VOL3" = false ] && [ "$HAS_VOL4" = false ]; then
BUILD_TARGET="vol1"
elif [ "$HAS_VOL2" = true ] && [ "$HAS_VOL1" = false ] && [ "$HAS_VOL3" = false ] && [ "$HAS_VOL4" = false ]; then
BUILD_TARGET="vol2"
elif [ "$HAS_VOL3" = true ] && [ "$HAS_VOL1" = false ] && [ "$HAS_VOL2" = false ] && [ "$HAS_VOL4" = false ]; then
BUILD_TARGET="vol3"
elif [ "$HAS_VOL4" = true ] && [ "$HAS_VOL1" = false ] && [ "$HAS_VOL2" = false ] && [ "$HAS_VOL3" = false ]; then
BUILD_TARGET="vol4"
else
BUILD_TARGET="all"
fi
echo "πŸ“Š Trigger: Automatic ($BUILD_TARGET detected)"
else
# Manual trigger: respect choices
BUILD_TARGET="${{ inputs.build_target || 'all' }}"
echo "πŸ“Š Trigger: Manual workflow dispatch ($BUILD_TARGET)"
BUILD_HTML="false"
if [[ "${{ inputs.build_formats }}" == "all" || "${{ inputs.build_formats }}" == "html" ]]; then
BUILD_HTML="true"
fi
BUILD_PDF="false"
if [[ "${{ inputs.build_formats }}" == "all" || "${{ inputs.build_formats }}" == "pdf" ]]; then
BUILD_PDF="true"
fi
BUILD_EPUB="false"
if [[ "${{ inputs.build_formats }}" == "all" || "${{ inputs.build_formats }}" == "epub" ]]; then
BUILD_EPUB="true"
fi
BUILD_LINUX="false"
if [[ "${{ inputs.build_os }}" == "both" || "${{ inputs.build_os }}" == "linux" ]]; then
BUILD_LINUX="true"
fi
BUILD_WINDOWS="false"
if [[ "${{ inputs.build_os }}" == "both" || "${{ inputs.build_os }}" == "windows" ]]; then
BUILD_WINDOWS="true"
fi
TARGET="${{ inputs.target }}"
fi
# Container configuration
CONTAINER_REGISTRY="${{ inputs.container_registry || 'ghcr.io' }}"
CONTAINER_TAG="${{ inputs.container_tag || 'latest' }}"
echo "πŸ“Š Final Configuration:"
echo " Target Volume: $BUILD_TARGET"
echo " HTML: $BUILD_HTML"
echo " PDF: $BUILD_PDF"
echo " EPUB: $BUILD_EPUB"
echo " Linux: $BUILD_LINUX"
echo " Windows: $BUILD_WINDOWS"
echo " Registry: $CONTAINER_REGISTRY"
echo " Tag: $CONTAINER_TAG"
echo " Target: $TARGET"
# Set outputs
echo "build_target=$BUILD_TARGET" >> $GITHUB_OUTPUT
echo "build_html=$BUILD_HTML" >> $GITHUB_OUTPUT
echo "build_pdf=$BUILD_PDF" >> $GITHUB_OUTPUT
echo "build_epub=$BUILD_EPUB" >> $GITHUB_OUTPUT
echo "build_linux=$BUILD_LINUX" >> $GITHUB_OUTPUT
echo "build_windows=$BUILD_WINDOWS" >> $GITHUB_OUTPUT
echo "container_registry=$CONTAINER_REGISTRY" >> $GITHUB_OUTPUT
echo "container_tag=$CONTAINER_TAG" >> $GITHUB_OUTPUT
echo "target=$TARGET" >> $GITHUB_OUTPUT
# Step 4: Matrix-driven build calls (clean and simple!)
build-container:
name: '🐳 Container Build Matrix'
needs: [build-config]
if: |
always() &&
(needs.build-config.result == 'success')
uses: ./.github/workflows/book-build-container.yml
with:
build_linux: ${{ needs.build-config.outputs.build_linux == 'true' }}
build_windows: ${{ needs.build-config.outputs.build_windows == 'true' }}
build_html: ${{ needs.build-config.outputs.build_html == 'true' }}
build_pdf: ${{ needs.build-config.outputs.build_pdf == 'true' }}
build_epub: ${{ needs.build-config.outputs.build_epub == 'true' }}
build_target: ${{ needs.build-config.outputs.build_target }}
target: ${{ needs.build-config.outputs.target }}
container_registry: ${{ needs.build-config.outputs.container_registry }}
container_tag: ${{ needs.build-config.outputs.container_tag }}
# Step 4: Collect build results (needed for outputs)
collect-results:
name: 'πŸ“Š Collect Results'
needs: [build-config, build-container]
runs-on: ubuntu-latest
if: always()
outputs:
linux_success: ${{ steps.collect.outputs.linux_success }}
windows_success: ${{ steps.collect.outputs.windows_success }}
linux_html_vol1_artifact: ${{ steps.collect.outputs.linux_html_vol1_artifact }}
linux_html_vol2_artifact: ${{ steps.collect.outputs.linux_html_vol2_artifact }}
linux_pdf_vol1_artifact: ${{ steps.collect.outputs.linux_pdf_vol1_artifact }}
linux_pdf_vol2_artifact: ${{ steps.collect.outputs.linux_pdf_vol2_artifact }}
linux_epub_vol1_artifact: ${{ steps.collect.outputs.linux_epub_vol1_artifact }}
linux_epub_vol2_artifact: ${{ steps.collect.outputs.linux_epub_vol2_artifact }}
steps:
- name: πŸ“Š Collect build results
id: collect
run: |
echo "πŸ“Š === COLLECTING BUILD RESULTS ==="
# These will hold the final results, preferring container builds
OVERALL_SUCCESS="false"
LINUX_HTML_VOL1=""
LINUX_HTML_VOL2=""
LINUX_PDF_VOL1=""
LINUX_PDF_VOL2=""
LINUX_EPUB_VOL1=""
LINUX_EPUB_VOL2=""
# Container-only results
if [[ "${{ needs.build-container.result }}" == "success" ]]; then
echo "βœ… Container builds completed successfully."
OVERALL_SUCCESS="${{ needs.build-container.outputs.build_success }}"
LINUX_HTML_VOL1="${{ needs.build-container.outputs.linux_html_vol1_artifact }}"
LINUX_HTML_VOL2="${{ needs.build-container.outputs.linux_html_vol2_artifact }}"
LINUX_PDF_VOL1="${{ needs.build-container.outputs.linux_pdf_vol1_artifact }}"
LINUX_PDF_VOL2="${{ needs.build-container.outputs.linux_pdf_vol2_artifact }}"
LINUX_EPUB_VOL1="${{ needs.build-container.outputs.linux_epub_vol1_artifact }}"
LINUX_EPUB_VOL2="${{ needs.build-container.outputs.linux_epub_vol2_artifact }}"
else
echo "❌ Container builds failed or were skipped."
OVERALL_SUCCESS="false"
fi
# Determine per-platform success based on overall success and which platforms were built
LINUX_SUCCESS="false"
if [[ "${{ needs.build-config.outputs.build_linux }}" == "true" && "$OVERALL_SUCCESS" == "true" ]]; then
LINUX_SUCCESS="true"
fi
WINDOWS_SUCCESS="false"
if [[ "${{ needs.build-config.outputs.build_windows }}" == "true" && "$OVERALL_SUCCESS" == "true" ]]; then
WINDOWS_SUCCESS="true"
fi
echo "Final Success Status: Linux -> $LINUX_SUCCESS, Windows -> $WINDOWS_SUCCESS"
# Set outputs
echo "linux_success=$LINUX_SUCCESS" >> $GITHUB_OUTPUT
echo "windows_success=$WINDOWS_SUCCESS" >> $GITHUB_OUTPUT
echo "linux_html_vol1_artifact=$LINUX_HTML_VOL1" >> $GITHUB_OUTPUT
echo "linux_html_vol2_artifact=$LINUX_HTML_VOL2" >> $GITHUB_OUTPUT
echo "linux_pdf_vol1_artifact=$LINUX_PDF_VOL1" >> $GITHUB_OUTPUT
echo "linux_pdf_vol2_artifact=$LINUX_PDF_VOL2" >> $GITHUB_OUTPUT
echo "linux_epub_vol1_artifact=$LINUX_EPUB_VOL1" >> $GITHUB_OUTPUT
echo "linux_epub_vol2_artifact=$LINUX_EPUB_VOL2" >> $GITHUB_OUTPUT
# Step 4a: Epubcheck validation (Tier 1 β€” blocks on FATAL)
# The pre-commit hook `book-epub-hygiene` catches source-level issues
# that historically caused EPUB rejection (SVG control chars, dupe
# marker ids, BibTeX URL escapes). Epubcheck is the full structural
# validator and catches the rest: OPF manifest shape, XHTML XML
# validity, cross-file references, URI syntax.
#
# Failure policy: FATAL > 0 fails the job (Kindle and ClearView
# reject any EPUB with FATAL, so this is a ship-stopper). ERROR > 0
# is reported as a warning but does NOT fail the job, to let
# remaining legacy errors be cleaned up incrementally without
# blocking feature work. Tighten this by setting MAX_ERRORS=0 once
# the baseline is clean in both volumes.
epub-validate:
name: 'πŸ“˜ EPUB validate (epubcheck via binder)'
needs: [collect-results]
if: |
always() &&
needs.collect-results.outputs.linux_success == 'true' &&
needs.collect-results.outputs.linux_epub_vol1_artifact != '' &&
needs.collect-results.outputs.linux_epub_vol2_artifact != ''
runs-on: ubuntu-latest
env:
# Ratchet baseline β€” fails if epubcheck counts exceed recorded
# per-volume ceilings. Update with
# ./binder check epub --scope epubcheck \
# --baseline book/tools/audit/epubcheck-baseline.json \
# --update-baseline
# after a cleanup lands, and commit the changed file in the
# same PR as the cleanup. This replaces the older flat
# MAX_FATAL/MAX_ERRORS approach: a ratchet prevents regression
# without cliff-failing during incremental cleanup.
#
# Path is relative to book/ because the run step does `cd book`
# before invoking the binder CLI.
BASELINE_PATH: 'tools/audit/epubcheck-baseline.json'
steps:
- name: πŸ“₯ Checkout repository
uses: actions/checkout@v6
- name: 🐍 Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: πŸ“₯ Download vol1 EPUB artifact
uses: actions/download-artifact@v8
with:
name: ${{ needs.collect-results.outputs.linux_epub_vol1_artifact }}
# Materialize into the canonical location the binder CLI
# discovers (_discover_built_epubs walks
# book/quarto/_build/epub-vol*/).
path: book/quarto/_build/epub-vol1
- name: πŸ“₯ Download vol2 EPUB artifact
uses: actions/download-artifact@v8
with:
name: ${{ needs.collect-results.outputs.linux_epub_vol2_artifact }}
path: book/quarto/_build/epub-vol2
- name: β˜• Set up Java (for the epubcheck jar)
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
- name: ⬇️ Install book Python dependencies
# Installs the `epubcheck` PyPI wrapper (declared in
# book/tools/dependencies/requirements.txt) alongside rich,
# pyyaml, and the rest of the binder runtime. The wrapper
# bundles the epubcheck jar and exposes it on PATH.
run: |
python -m pip install --upgrade pip
pip install -r book/tools/dependencies/requirements.txt
- name: πŸ“˜ Run `binder check epub --scope epubcheck` with baseline ratchet
# Both source-level hygiene and post-build epubcheck validation
# live inside the binder CLI. This step invokes the same
# command a developer runs locally, so CI and local behaviour
# stay in lockstep.
#
# The --baseline ratchet means CI fails only when the per-
# volume FATAL/ERROR/WARNING count *exceeds* what was
# recorded last time the baseline was updated. Prevents
# regression without forcing a cliff-cleanup of every
# pre-existing issue on every PR.
env:
BASELINE_PATH: ${{ env.BASELINE_PATH }}
run: |
set -euo pipefail
cd book
# JSON pass: machine-readable artifact (uploaded below).
# GITHUB_ACTIONS=true is set automatically, so _epub_checks
# emits `::error` annotations inline on the PR diff.
./binder check epub --scope epubcheck \
--baseline "${BASELINE_PATH}" \
--json > epubcheck-report.json || true
# Human pass: exit code gates the CI job via the ratchet.
./binder check epub --scope epubcheck --baseline "${BASELINE_PATH}"
- name: πŸ“€ Upload epubcheck report
if: always()
uses: actions/upload-artifact@v7
with:
name: epubcheck-report
path: book/epubcheck-report.json
retention-days: 14
# Step 4b: Link integrity (Tier 2 β€” non-blocking baseline)
# Tier 1 pre-commit (shared/scripts/check-internal-links.py) already blocks
# broken internal links + anchors. This Lychee pass adds external reachability
# as a warning. Flip fail_on_broken=true once the baseline is clean.
check-links:
name: 'πŸ”— Check Links'
needs: [pre-commit]
if: always() && needs.pre-commit.result == 'success'
uses: ./.github/workflows/infra-link-check.yml
with:
path_pattern: './book/quarto/contents/**/*.qmd'
lycheeignore_path: 'book/config/linting/.lycheeignore'
fail_on_broken: false
max_concurrency: 10
# Step 5: Final results and summary
validation-summary:
name: '🎯 Validation Summary'
if: always()
needs: [pre-commit, build-config, collect-results, epub-validate, check-links]
runs-on: ubuntu-latest
steps:
- name: πŸ“₯ Checkout repository
uses: actions/checkout@v4
- name: 🎯 Generate validation summary
run: |
echo "🎯 === VALIDATION SUMMARY ==="
echo "=============================="
echo ""
# Visual status indicators
echo "πŸ” RESULTS:"
echo "----------"
if [ "${{ needs.pre-commit.result }}" = "success" ]; then
echo "βœ… Pre-commit checks: PASSED"
else
echo "❌ Pre-commit checks: FAILED"
fi
if [ "${{ needs.build-config.outputs.build_linux }}" = "true" ]; then
if [ "${{ needs.collect-results.outputs.linux_success }}" = "true" ]; then
echo "βœ… Linux build: PASSED"
else
echo "❌ Linux build: FAILED"
fi
else
echo "⏭️ Linux build: SKIPPED"
fi
if [ "${{ needs.build-config.outputs.build_windows }}" = "true" ]; then
if [ "${{ needs.collect-results.outputs.windows_success }}" = "true" ]; then
echo "βœ… Windows build: PASSED"
else
echo "❌ Windows build: FAILED"
fi
else
echo "⏭️ Windows build: SKIPPED"
fi
case "${{ needs.epub-validate.result }}" in
success) echo "βœ… EPUB validate (epubcheck): PASSED" ;;
failure) echo "❌ EPUB validate (epubcheck): FAILED" ;;
skipped) echo "⏭️ EPUB validate (epubcheck): SKIPPED" ;;
*) echo "⚠️ EPUB validate (epubcheck): ${{ needs.epub-validate.result }}" ;;
esac
echo ""
echo "🎯 OVERALL STATUS:"
echo "-----------------"
# Determine overall success
PRECOMMIT_OK="${{ needs.pre-commit.result == 'success' }}"
LINUX_OK="${{ needs.collect-results.outputs.linux_success == 'true' || needs.build-config.outputs.build_linux == 'false' }}"
WINDOWS_OK="${{ needs.collect-results.outputs.windows_success == 'true' || needs.build-config.outputs.build_windows == 'false' }}"
if [ "$PRECOMMIT_OK" = "true" ] && [ "$LINUX_OK" = "true" ] && [ "$WINDOWS_OK" = "true" ]; then
echo "🟒 VALIDATION: FULLY OPERATIONAL βœ…"
echo " All enabled checks passed successfully!"
echo "πŸš€ PREVIEW: DEPLOYMENT TRIGGERED βœ…"
echo " A separate workflow will deploy the preview to the staging site."
else
echo "πŸ”΄ VALIDATION: ISSUES DETECTED ❌"
echo " Check the logs above for details"
fi
echo "=============================="
- name: πŸ“‹ Generate GitHub summary
run: |
echo "# 🎯 Enhanced Dev Validation Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "## πŸ“Š Build Results" >> $GITHUB_STEP_SUMMARY
echo "- **Pre-commit**: ${{ needs.pre-commit.result }}" >> $GITHUB_STEP_SUMMARY
echo "- **Linux Build**: ${{ needs.collect-results.outputs.linux_success == 'true' && 'βœ… success' || '❌ failed' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Windows Build**: ${{ needs.collect-results.outputs.windows_success == 'true' && 'βœ… success' || '❌ failed' }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "## πŸ”§ Build Configuration" >> $GITHUB_STEP_SUMMARY
if [ "${{ github.event_name }}" = "push" ]; then
echo "- **Trigger**: Automatic (dev branch push)" >> $GITHUB_STEP_SUMMARY
echo "- **Format**: HTML + PDF + EPUB (mandatory)" >> $GITHUB_STEP_SUMMARY
echo "- **OS**: Linux only (Windows temporarily disabled on push)" >> $GITHUB_STEP_SUMMARY
echo "- **Method**: βœ… Container builds only" >> $GITHUB_STEP_SUMMARY
else
echo "- **Trigger**: Manual workflow dispatch" >> $GITHUB_STEP_SUMMARY
echo "- **Formats**: ${{ inputs.build_formats }}" >> $GITHUB_STEP_SUMMARY
echo "- **OS**: ${{ inputs.build_os }}" >> $GITHUB_STEP_SUMMARY
echo "- **Method**: βœ… Container builds only" >> $GITHUB_STEP_SUMMARY
echo "- **Registry**: ${{ needs.build-config.outputs.container_registry }}" >> $GITHUB_STEP_SUMMARY
echo "- **Tag**: ${{ needs.build-config.outputs.container_tag }}" >> $GITHUB_STEP_SUMMARY
echo "- **Target**: ${{ needs.build-config.outputs.target }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "## πŸš€ Preview Deployment" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.collect-results.outputs.linux_success }}" = "true" ]; then
echo "Deployment will be handled by a separate workflow." >> $GITHUB_STEP_SUMMARY
echo "Check the 'Actions' tab for the 'πŸš€ Deploy Preview' workflow run." >> $GITHUB_STEP_SUMMARY
else
echo "Deployment skipped due to build failure." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "## 🎯 Summary" >> $GITHUB_STEP_SUMMARY
LINUX_OK="${{ needs.collect-results.outputs.linux_success == 'true' || needs.build-config.outputs.build_linux == 'false' }}"
WINDOWS_OK="${{ needs.collect-results.outputs.windows_success == 'true' || needs.build-config.outputs.build_windows == 'false' }}"
if [ "${{ needs.pre-commit.result }}" = "success" ] && [ "$LINUX_OK" = "true" ] && [ "$WINDOWS_OK" = "true" ]; then
echo "βœ… **Validation Passed** - All enabled builds successful" >> $GITHUB_STEP_SUMMARY
echo "πŸš€ **Preview Deployment Triggered** - A separate workflow is handling the deployment." >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Validation Failed** - Check the logs above" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "## πŸ”— Link Check (non-blocking)" >> $GITHUB_STEP_SUMMARY
echo "- Result: \`${{ needs.check-links.result }}\` (see job for detail)" >> $GITHUB_STEP_SUMMARY
- name: 🏷️ Update and Publish per-volume status badges
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_RUN_ID: ${{ github.run_id }}
run: |
python3 site/scripts/update_volume_status.py --publish-gh-pages