Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 84 additions & 6 deletions .github/workflows/safety-gate.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# HOOK PARITY: This workflow mirrors .pre-commit-config.yaml.
# HOOK PARITY: This workflow mirrors .pre-commit-config.yaml (safety checks)
# and the local dev workflow (Rust checks).
# When editing this file, update the pre-commit config to match.

name: Safety Gate
Expand All @@ -13,6 +14,9 @@ permissions:
contents: read

jobs:
# ---------------------------------------------------------------
# Safety: secrets and forbidden patterns
# ---------------------------------------------------------------
secrets-scan:
name: Scan for secrets
runs-on: ubuntu-latest
Expand Down Expand Up @@ -58,8 +62,11 @@ jobs:
fi
echo "No forbidden patterns found."

rust-checks:
name: Rust lint and test
# ---------------------------------------------------------------
# Rust: lint the entire workspace (examples + exercises)
# ---------------------------------------------------------------
lint:
name: Lint (fmt + clippy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -68,11 +75,82 @@ jobs:
with:
components: rustfmt, clippy

- uses: Swatinem/rust-cache@v2

- name: Format check
run: cargo fmt --all -- --check

- name: Clippy
- name: Clippy (full workspace)
run: cargo clippy --workspace --all-targets -- -D warnings

- name: Tests
run: cargo test --workspace
# ---------------------------------------------------------------
# Rust: test examples (chapter crates, NOT exercise crates)
#
# Exercise crates contain todo!() stubs that panic at runtime.
# That's by design — students fill them in. We verify exercises
# COMPILE (via clippy above) but only RUN tests on the example
# crates where all tests should pass.
# ---------------------------------------------------------------
test-examples:
name: Test examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2

- name: Discover and test example crates
run: |
# Find all chapter crates that are NOT exercise crates.
# Convention: exercise crates live in ch*/exercises/
EXAMPLE_CRATES=$(
cargo metadata --no-deps --format-version 1 \
| python3 -c "
import json, sys
meta = json.load(sys.stdin)
for pkg in meta['packages']:
if 'exercises' not in pkg['manifest_path']:
print(f'-p {pkg[\"name\"]}', end=' ')
"
)

echo "Testing: $EXAMPLE_CRATES"
# shellcheck disable=SC2086
cargo test $EXAMPLE_CRATES

# ---------------------------------------------------------------
# Rust: verify exercise crates BUILD (compile + link)
#
# This catches broken test code, missing imports, or type errors
# in the exercise scaffolding — without running the todo!() stubs.
# ---------------------------------------------------------------
build-exercises:
name: Build exercises (no run)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2

- name: Build exercise test binaries
run: |
# Build test binaries for exercise crates without running them.
# This verifies the test code compiles against the todo!() stubs.
EXERCISE_CRATES=$(
cargo metadata --no-deps --format-version 1 \
| python3 -c "
import json, sys
meta = json.load(sys.stdin)
for pkg in meta['packages']:
if 'exercises' in pkg['manifest_path']:
print(f'-p {pkg[\"name\"]}', end=' ')
"
)

echo "Building (no run): $EXERCISE_CRATES"
# shellcheck disable=SC2086
cargo test --no-run $EXERCISE_CRATES
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
[workspace]
resolver = "2"
members = [
"ch00-getting-started",
"ch00-getting-started/exercises",
"ch01-ownership",
"ch01-ownership/exercises",
"ch02-error-handling",
"ch02-error-handling/exercises",
]

[workspace.package]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ demanding the user carry it for them.

| # | Title | Python Concept | Rust Concept |
|---|-------|---------------|--------------|
| 0 | **Getting Started** | `pip`, `venv`, `python hello.py` | `cargo`, `rustup`, `cargo run` |
| 1 | **Ownership** | Reference counting, `del`, `with` blocks | Ownership, borrowing, lifetimes, RAII |
| 2 | **Error Handling** | `try/except`, `None`, `LBYL vs EAFP` | `Result<T,E>`, `Option<T>`, `?` operator |
| 3 | **Traits & Generics** | ABCs, Protocols, duck typing | Traits, generics, trait bounds |
Expand Down
6 changes: 6 additions & 0 deletions ch00-getting-started/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "ch00-getting-started"
version = "0.1.0"
edition.workspace = true
license.workspace = true
description = "Chapter 0: Getting Started — toolchain setup and hello world"
Loading