refactor(ci): rename TinyTorch CI to Validate (Dev) pattern

Follow book pattern:
- Rename tinytorch-ci.yml → tinytorch-validate-dev.yml
- Rename workflow to "🔥 TinyTorch Validate (Dev)"
- Update tinytorch-publish-dev.yml to trigger after Validate passes
- Build PDFs after validation, then deploy

Flow: Push → Validate → (if passes) → Build PDFs → Deploy
This commit is contained in:
Vijay Janapa Reddi
2026-01-25 17:01:09 -05:00
parent 1a41a85232
commit e8e0c89c73
2 changed files with 17 additions and 24 deletions

View File

@@ -0,0 +1,557 @@
name: 🔥 TinyTorch Validate (Dev)
# =============================================================================
# TinyTorch Validate (Dev) - Comprehensive Testing Workflow
# =============================================================================
#
# Test Progression (sequential, fail-fast):
#
# Stage 1: INLINE BUILD
# └─ Build package from src/ modules (if this fails, nothing works)
#
# Stage 2-4: PARALLEL TESTS (all depend on Stage 1 only)
# ├─ Stage 2: UNIT TESTS - Test individual module functions
# ├─ Stage 3: INTEGRATION TESTS - Cross-module validation
# └─ Stage 4: CLI TESTS - Verify CLI commands work correctly
#
# Stage 5: E2E TESTS (depends on Stages 2-4 all passing)
# └─ Full user journey validation
#
# Stage 6: RELEASE VALIDATION (runs after all stages pass)
# └─ Destructive full journey test (resets modules, full student simulation)
#
# Triggers:
# - Push to dev: ALL tests (dev must be rock solid)
# - Push to main: ALL tests (production ready)
# - PR: Standard tests (stages 1-5)
# - Feature branches: Quick tests (unit + cli only)
# - Manual: Any test type
# - Called by publish: all (pre-release gate)
#
# =============================================================================
on:
push:
branches: [main, dev]
paths:
- 'tinytorch/**'
- '.github/workflows/tinytorch-*.yml'
pull_request:
branches: [main, dev]
paths:
- 'tinytorch/**'
- '.github/workflows/tinytorch-*.yml'
workflow_call:
inputs:
test_type:
description: 'Test type to run'
required: false
default: 'all'
type: string
workflow_dispatch:
inputs:
test_type:
description: 'Test type to run'
required: false
default: 'quick'
type: choice
options:
- quick # unit + cli (fast feedback, no build)
- standard # stages 1-5 (no milestones)
- all # stages 1-6 (everything except release)
- release # destructive full validation
permissions:
contents: read
actions: read
concurrency:
group: tinytorch-ci-${{ github.ref }}
cancel-in-progress: true
env:
TITO_ALLOW_SYSTEM: "1"
TINYTORCH_QUIET: "1"
jobs:
# ===========================================================================
# Configuration: Determine what tests to run
# ===========================================================================
configure:
name: 📋 Configure
runs-on: ubuntu-latest
outputs:
test_type: ${{ steps.config.outputs.test_type }}
run_inline: ${{ steps.config.outputs.run_inline }}
run_unit: ${{ steps.config.outputs.run_unit }}
run_integration: ${{ steps.config.outputs.run_integration }}
run_cli: ${{ steps.config.outputs.run_cli }}
run_e2e: ${{ steps.config.outputs.run_e2e }}
run_release: ${{ steps.config.outputs.run_release }}
steps:
- name: Determine test configuration
id: config
run: |
# Determine test type based on trigger
if [ "${{ github.event_name }}" = "workflow_call" ]; then
TEST_TYPE="${{ inputs.test_type }}"
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TEST_TYPE="${{ github.event.inputs.test_type }}"
elif [ "${{ github.event_name }}" = "pull_request" ]; then
TEST_TYPE="standard"
elif [ "${{ github.ref }}" = "refs/heads/main" ]; then
TEST_TYPE="all"
elif [ "${{ github.ref }}" = "refs/heads/dev" ]; then
# dev must be rock solid - run everything
TEST_TYPE="all"
else
# feature branches get quick feedback
TEST_TYPE="quick"
fi
echo "test_type=$TEST_TYPE" >> $GITHUB_OUTPUT
# Set flags based on test type
# Sequential order: inline → unit → integration → cli → e2e → release
case "$TEST_TYPE" in
quick)
# Fast feedback: skip inline build, just unit + cli
echo "run_inline=false" >> $GITHUB_OUTPUT
echo "run_unit=true" >> $GITHUB_OUTPUT
echo "run_integration=false" >> $GITHUB_OUTPUT
echo "run_cli=true" >> $GITHUB_OUTPUT
echo "run_e2e=false" >> $GITHUB_OUTPUT
echo "run_release=false" >> $GITHUB_OUTPUT
;;
standard)
# Stages 1-5
echo "run_inline=true" >> $GITHUB_OUTPUT
echo "run_unit=true" >> $GITHUB_OUTPUT
echo "run_integration=true" >> $GITHUB_OUTPUT
echo "run_cli=true" >> $GITHUB_OUTPUT
echo "run_e2e=true" >> $GITHUB_OUTPUT
echo "run_release=false" >> $GITHUB_OUTPUT
;;
all)
# Stages 1-5 (everything except release)
echo "run_inline=true" >> $GITHUB_OUTPUT
echo "run_unit=true" >> $GITHUB_OUTPUT
echo "run_integration=true" >> $GITHUB_OUTPUT
echo "run_cli=true" >> $GITHUB_OUTPUT
echo "run_e2e=true" >> $GITHUB_OUTPUT
echo "run_release=false" >> $GITHUB_OUTPUT
;;
release)
# Full validation: all stages + destructive release at the end
echo "run_inline=true" >> $GITHUB_OUTPUT
echo "run_unit=true" >> $GITHUB_OUTPUT
echo "run_integration=true" >> $GITHUB_OUTPUT
echo "run_cli=true" >> $GITHUB_OUTPUT
echo "run_e2e=true" >> $GITHUB_OUTPUT
echo "run_release=true" >> $GITHUB_OUTPUT
;;
esac
echo "📋 Test configuration: $TEST_TYPE"
# ===========================================================================
# STAGE 1: INLINE BUILD - Build package from src/
# ===========================================================================
stage-1-inline:
name: 📝 Stage 1 · Inline Build
runs-on: ubuntu-latest
needs: configure
if: needs.configure.outputs.run_inline == 'true'
timeout-minutes: 30
defaults:
run:
working-directory: tinytorch
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: 'tinytorch/requirements.txt'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Inline Tests (Progressive Build)
run: |
echo "📝 Building package from src/ modules..."
echo " Tests each module exports correctly in sequence."
./bin/tito dev test --inline --ci
# ===========================================================================
# STAGE 2: UNIT TESTS - Test individual module functions
# Runs in PARALLEL with Stages 3 and 4 (all depend only on Stage 1)
# ===========================================================================
stage-2-unit:
name: 🧪 Stage 2 · Unit Tests
runs-on: ubuntu-latest
needs: [configure, stage-1-inline]
if: |
always() &&
needs.configure.outputs.run_unit == 'true' &&
(needs.stage-1-inline.result == 'success' || needs.stage-1-inline.result == 'skipped')
timeout-minutes: 30
defaults:
run:
working-directory: tinytorch
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: 'tinytorch/requirements.txt'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Unit Tests
run: |
./bin/tito dev test --unit --ci
# ===========================================================================
# STAGE 3: INTEGRATION TESTS - Cross-module validation
# Runs in PARALLEL with Stages 2 and 4 (all depend only on Stage 1)
# ===========================================================================
stage-3-integration:
name: 🔗 Stage 3 · Integration
runs-on: ubuntu-latest
needs: [configure, stage-1-inline]
if: |
always() &&
needs.configure.outputs.run_integration == 'true' &&
(needs.stage-1-inline.result == 'success' || needs.stage-1-inline.result == 'skipped')
timeout-minutes: 15
defaults:
run:
working-directory: tinytorch
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: 'tinytorch/requirements.txt'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Integration Tests
run: |
./bin/tito dev test --integration --ci
# ===========================================================================
# STAGE 4: CLI TESTS - Verify CLI commands work
# Runs in PARALLEL with Stages 2 and 3 (all depend only on Stage 1)
# ===========================================================================
stage-4-cli:
name: 💻 Stage 4 · CLI Tests
runs-on: ubuntu-latest
needs: [configure, stage-1-inline]
if: |
always() &&
needs.configure.outputs.run_cli == 'true' &&
(needs.stage-1-inline.result == 'success' || needs.stage-1-inline.result == 'skipped')
timeout-minutes: 10
defaults:
run:
working-directory: tinytorch
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: 'tinytorch/requirements.txt'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run CLI Tests
run: |
./bin/tito dev test --cli --ci
# ===========================================================================
# STAGE 5: E2E TESTS - Full user journey validation
# Depends on Stages 2, 3, and 4 ALL passing (the integration gate)
# ===========================================================================
stage-5-e2e:
name: 🌐 Stage 5 · E2E Tests
runs-on: ubuntu-latest
needs: [configure, stage-2-unit, stage-3-integration, stage-4-cli]
if: |
always() &&
needs.configure.outputs.run_e2e == 'true' &&
(needs.stage-2-unit.result == 'success' || needs.stage-2-unit.result == 'skipped') &&
(needs.stage-3-integration.result == 'success' || needs.stage-3-integration.result == 'skipped') &&
(needs.stage-4-cli.result == 'success' || needs.stage-4-cli.result == 'skipped')
timeout-minutes: 15
defaults:
run:
working-directory: tinytorch
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: 'tinytorch/requirements.txt'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run E2E Tests
run: |
./bin/tito dev test --e2e --ci
# ===========================================================================
# STAGE 6: RELEASE VALIDATION - Destructive full journey (after all stages)
# ===========================================================================
stage-6-release:
name: 🚀 Stage 6 · Release
runs-on: ubuntu-latest
needs: [configure, stage-5-e2e]
# Only run if: release enabled AND all previous stages passed
if: |
always() &&
needs.configure.outputs.run_release == 'true' &&
(needs.stage-5-e2e.result == 'success' || needs.stage-5-e2e.result == 'skipped')
timeout-minutes: 45
defaults:
run:
working-directory: tinytorch
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: 'tinytorch/requirements.txt'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Release Validation
run: |
echo "🚀 Running DESTRUCTIVE release validation..."
echo " This resets all modules and runs the full student journey."
./bin/tito dev test --release --ci
# ===========================================================================
# STAGE 7: FRESH INSTALL - Simulates real student experience
# ===========================================================================
stage-7-fresh-install:
name: 📦 Stage 7 · Fresh Install
runs-on: ubuntu-latest
needs: [configure, stage-5-e2e]
# Run on standard/all/release test types, after stage 5 passes
if: |
always() &&
(needs.configure.outputs.run_e2e == 'true' || needs.configure.outputs.run_release == 'true') &&
(needs.stage-5-e2e.result == 'success' || needs.stage-5-e2e.result == 'skipped')
timeout-minutes: 30
steps:
- name: Checkout (for test script only)
uses: actions/checkout@v4
with:
sparse-checkout: |
tinytorch/scripts/test-fresh-install.sh
- name: Run Fresh Install Test (Docker)
run: |
chmod +x tinytorch/scripts/test-fresh-install.sh
./tinytorch/scripts/test-fresh-install.sh --branch ${{ github.ref_name }}
# ===========================================================================
# Summary: Collect and report all results
# ===========================================================================
summary:
name: 📊 Summary
runs-on: ubuntu-latest
needs: [configure, stage-1-inline, stage-2-unit, stage-3-integration, stage-4-cli, stage-5-e2e, stage-6-release, stage-7-fresh-install]
if: always()
steps:
- name: Generate Summary
run: |
echo "## 🔥 TinyTorch CI Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Test Type** | \`${{ needs.configure.outputs.test_type }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Trigger** | ${{ github.event_name }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Branch** | \`${{ github.ref_name }}\` |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Test Progression (Sequential)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Stage | Test | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-------|------|--------|" >> $GITHUB_STEP_SUMMARY
# Stage 1: Inline
if [ "${{ needs.configure.outputs.run_inline }}" = "true" ]; then
case "${{ needs.stage-1-inline.result }}" in
success) echo "| 1 | 📝 Inline Build | ✅ Passed |" >> $GITHUB_STEP_SUMMARY ;;
failure) echo "| 1 | 📝 Inline Build | ❌ Failed |" >> $GITHUB_STEP_SUMMARY ;;
*) echo "| 1 | 📝 Inline Build | ⏭️ ${{ needs.stage-1-inline.result }} |" >> $GITHUB_STEP_SUMMARY ;;
esac
else
echo "| 1 | 📝 Inline Build | ⏭️ Skipped |" >> $GITHUB_STEP_SUMMARY
fi
# Stage 2: Unit
if [ "${{ needs.configure.outputs.run_unit }}" = "true" ]; then
case "${{ needs.stage-2-unit.result }}" in
success) echo "| 2 | 🧪 Unit Tests | ✅ Passed |" >> $GITHUB_STEP_SUMMARY ;;
failure) echo "| 2 | 🧪 Unit Tests | ❌ Failed |" >> $GITHUB_STEP_SUMMARY ;;
*) echo "| 2 | 🧪 Unit Tests | ⏭️ ${{ needs.stage-2-unit.result }} |" >> $GITHUB_STEP_SUMMARY ;;
esac
else
echo "| 2 | 🧪 Unit Tests | ⏭️ Skipped |" >> $GITHUB_STEP_SUMMARY
fi
# Stage 3: Integration
if [ "${{ needs.configure.outputs.run_integration }}" = "true" ]; then
case "${{ needs.stage-3-integration.result }}" in
success) echo "| 3 | 🔗 Integration | ✅ Passed |" >> $GITHUB_STEP_SUMMARY ;;
failure) echo "| 3 | 🔗 Integration | ❌ Failed |" >> $GITHUB_STEP_SUMMARY ;;
*) echo "| 3 | 🔗 Integration | ⏭️ ${{ needs.stage-3-integration.result }} |" >> $GITHUB_STEP_SUMMARY ;;
esac
else
echo "| 3 | 🔗 Integration | ⏭️ Skipped |" >> $GITHUB_STEP_SUMMARY
fi
# Stage 4: CLI
if [ "${{ needs.configure.outputs.run_cli }}" = "true" ]; then
case "${{ needs.stage-4-cli.result }}" in
success) echo "| 4 | 💻 CLI Tests | ✅ Passed |" >> $GITHUB_STEP_SUMMARY ;;
failure) echo "| 4 | 💻 CLI Tests | ❌ Failed |" >> $GITHUB_STEP_SUMMARY ;;
*) echo "| 4 | 💻 CLI Tests | ⏭️ ${{ needs.stage-4-cli.result }} |" >> $GITHUB_STEP_SUMMARY ;;
esac
else
echo "| 4 | 💻 CLI Tests | ⏭️ Skipped |" >> $GITHUB_STEP_SUMMARY
fi
# Stage 5: E2E
if [ "${{ needs.configure.outputs.run_e2e }}" = "true" ]; then
case "${{ needs.stage-5-e2e.result }}" in
success) echo "| 5 | 🌐 E2E Tests | ✅ Passed |" >> $GITHUB_STEP_SUMMARY ;;
failure) echo "| 5 | 🌐 E2E Tests | ❌ Failed |" >> $GITHUB_STEP_SUMMARY ;;
*) echo "| 5 | 🌐 E2E Tests | ⏭️ ${{ needs.stage-5-e2e.result }} |" >> $GITHUB_STEP_SUMMARY ;;
esac
else
echo "| 5 | 🌐 E2E Tests | ⏭️ Skipped |" >> $GITHUB_STEP_SUMMARY
fi
# Stage 6: Release
if [ "${{ needs.configure.outputs.run_release }}" = "true" ]; then
case "${{ needs.stage-6-release.result }}" in
success) echo "| 6 | 🚀 Release | ✅ Passed |" >> $GITHUB_STEP_SUMMARY ;;
failure) echo "| 6 | 🚀 Release | ❌ Failed |" >> $GITHUB_STEP_SUMMARY ;;
*) echo "| 6 | 🚀 Release | ⏭️ ${{ needs.stage-6-release.result }} |" >> $GITHUB_STEP_SUMMARY ;;
esac
else
echo "| 6 | 🚀 Release | ⏭️ Skipped |" >> $GITHUB_STEP_SUMMARY
fi
# Stage 7: Fresh Install
if [ "${{ needs.configure.outputs.run_e2e }}" = "true" ] || [ "${{ needs.configure.outputs.run_release }}" = "true" ]; then
case "${{ needs.stage-7-fresh-install.result }}" in
success) echo "| 7 | 📦 Fresh Install | ✅ Passed |" >> $GITHUB_STEP_SUMMARY ;;
failure) echo "| 7 | 📦 Fresh Install | ❌ Failed |" >> $GITHUB_STEP_SUMMARY ;;
*) echo "| 7 | 📦 Fresh Install | ⏭️ ${{ needs.stage-7-fresh-install.result }} |" >> $GITHUB_STEP_SUMMARY ;;
esac
else
echo "| 7 | 📦 Fresh Install | ⏭️ Skipped |" >> $GITHUB_STEP_SUMMARY
fi
- name: Check for failures
run: |
FAILED=false
# Check each enabled test
if [ "${{ needs.configure.outputs.run_inline }}" = "true" ] && [ "${{ needs.stage-1-inline.result }}" = "failure" ]; then
echo "❌ Stage 1 (Inline Build) failed"
FAILED=true
fi
if [ "${{ needs.configure.outputs.run_unit }}" = "true" ] && [ "${{ needs.stage-2-unit.result }}" = "failure" ]; then
echo "❌ Stage 2 (Unit Tests) failed"
FAILED=true
fi
if [ "${{ needs.configure.outputs.run_integration }}" = "true" ] && [ "${{ needs.stage-3-integration.result }}" = "failure" ]; then
echo "❌ Stage 3 (Integration) failed"
FAILED=true
fi
if [ "${{ needs.configure.outputs.run_cli }}" = "true" ] && [ "${{ needs.stage-4-cli.result }}" = "failure" ]; then
echo "❌ Stage 4 (CLI Tests) failed"
FAILED=true
fi
if [ "${{ needs.configure.outputs.run_e2e }}" = "true" ] && [ "${{ needs.stage-5-e2e.result }}" = "failure" ]; then
echo "❌ Stage 5 (E2E Tests) failed"
FAILED=true
fi
if [ "${{ needs.configure.outputs.run_release }}" = "true" ] && [ "${{ needs.stage-6-release.result }}" = "failure" ]; then
echo "❌ Stage 6 (Release) failed"
FAILED=true
fi
if { [ "${{ needs.configure.outputs.run_e2e }}" = "true" ] || [ "${{ needs.configure.outputs.run_release }}" = "true" ]; } && [ "${{ needs.stage-7-fresh-install.result }}" = "failure" ]; then
echo "❌ Stage 7 (Fresh Install) failed"
FAILED=true
fi
if [ "$FAILED" = "true" ]; then
echo ""
echo "❌ CI FAILED - See above for details"
exit 1
else
echo "✅ All enabled tests passed"
fi