Skip to content

Add ability to extract and compile examples both locally and in CI #3

Add ability to extract and compile examples both locally and in CI

Add ability to extract and compile examples both locally and in CI #3

# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors
name: Check Rust Examples
on:
push:
branches:
- "main"
paths:
- 'src/coding-guidelines/**/*.rst'
- 'tests/rust-examples/**/*.rst'
- 'scripts/extract_rust_examples.py'
- 'scripts/rustdoc_utils.py'
- 'src/examples_prelude.rs'
- '.github/workflows/check-rust-examples.yml'
pull_request:
branches:
- "main"
paths:
- 'src/coding-guidelines/**/*.rst'
- 'tests/rust-examples/**/*.rst'
- 'scripts/extract_rust_examples.py'
- 'scripts/rustdoc_utils.py'
- 'src/examples_prelude.rs'
- '.github/workflows/check-rust-examples.yml'
merge_group:
# Allow running this workflow from other workflows
workflow_call:
jobs:
# Test Rust examples across multiple toolchains
test-examples:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
# Test with stable (latest) - all examples except nightly-only should pass
- rust: stable
name: Stable (latest)
# Test with an older version to verify version skipping works correctly
- rust: '1.75.0'
name: '1.75.0'
# Test with nightly for channel-specific examples
- rust: nightly
name: Nightly
name: Test Examples (${{ matrix.name }})
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain (${{ matrix.name }})
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
- name: Display Rust version
run: |
rustc --version
cargo --version
- name: Install uv
uses: astral-sh/setup-uv@v6
- name: Extract and test Rust examples
id: test-examples
run: |
mkdir -p build/examples
# Run combined extract and test
set +e # Don't exit on error, we want to capture results
uv run python scripts/extract_rust_examples.py \
--test \
--src-dir src/coding-guidelines \
--src-dir tests/rust-examples \
--prelude src/examples_prelude.rs \
--json build/examples/results.json \
--verbose \
2>&1 | tee build/examples/test_output.log
EXIT_CODE=$?
# Generate summary
echo "## 🧪 Rust Examples Test Results (${{ matrix.name }})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Rust version:** \`$(rustc --version)\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f build/examples/results.json ]; then
TOTAL=$(jq '.total' build/examples/results.json)
PASSED=$(jq '.passed' build/examples/results.json)
FAILED=$(jq '.failed' build/examples/results.json)
SKIPPED=$(jq '.skipped // 0' build/examples/results.json)
if [ "$FAILED" -eq 0 ]; then
if [ "$SKIPPED" -gt 0 ]; then
echo "✅ **$PASSED of $TOTAL examples passed, $SKIPPED skipped**" >> $GITHUB_STEP_SUMMARY
else
echo "✅ **All $TOTAL examples passed!**" >> $GITHUB_STEP_SUMMARY
fi
else
echo "❌ **$FAILED of $TOTAL examples failed** ($SKIPPED skipped)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Failed Examples" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
jq -r '.results[] | select(.passed == false) | "- **\(.example.source_file):\(.example.line_number)**: \(.error_message)"' \
build/examples/results.json >> $GITHUB_STEP_SUMMARY
fi
# Show skipped examples if any
if [ "$SKIPPED" -gt 0 ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Skipped Examples" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
jq -r '.results[] | select(.skipped == true) | "- **\(.example.source_file):\(.example.line_number)**: \(.skip_reason)"' \
build/examples/results.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
fi
fi
exit $EXIT_CODE
- name: Upload example test artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: rust-examples-results-${{ matrix.rust }}
path: |
build/examples/results.json
build/examples/test_output.log
build/examples/examples.json
retention-days: 7
- name: Annotate failures in PR
if: failure() && github.event_name == 'pull_request'
run: |
if [ -f build/examples/results.json ]; then
jq -r '.results[] | select(.passed == false) |
"::error file=\(.example.source_file),line=\(.example.line_number)::\(.error_message)"' \
build/examples/results.json
fi
# Aggregate job that requires all example tests to pass
test-examples-complete:
runs-on: ubuntu-latest
needs: test-examples
if: always()
steps:
- name: Check all Rust example tests passed
run: |
if [[ "${{ needs.test-examples.result }}" == "success" ]]; then
echo "✅ All Rust example tests passed across all toolchains!"
else
echo "❌ Some Rust example tests failed"
exit 1
fi
# Validate rustdoc attribute consistency
check-rustdoc-format:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check for legacy code blocks
run: |
echo "## 📋 Rustdoc Format Check" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
LEGACY_COUNT=0
LEGACY_FILES=""
for file in $(find src/coding-guidelines -name "*.rst" 2>/dev/null); do
COUNT=$(grep -c "^\s*\.\. code-block:: rust" "$file" 2>/dev/null || echo 0)
if [ "$COUNT" -gt 0 ]; then
LEGACY_FILES="$LEGACY_FILES\n- $file: $COUNT occurrences"
LEGACY_COUNT=$((LEGACY_COUNT + COUNT))
fi
done
if [ "$LEGACY_COUNT" -eq 0 ]; then
echo "✅ All Rust code examples use the rust-example:: directive" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ **Found $LEGACY_COUNT code blocks using legacy format**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Files with legacy code-block:: rust:" >> $GITHUB_STEP_SUMMARY
echo -e "$LEGACY_FILES" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Consider migrating with: \`uv run python scripts/migrate_rust_examples.py\`" >> $GITHUB_STEP_SUMMARY
fi