Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Overview

A tiny Rust CLI that bootstraps GitHub Classroom autograding for Rust projects.

πŸš€ Currently deployed in Boston University’s Intro to Rust course (130+ students, 1000+ student repos).

Key Features

  • ⚑ Fast setup β€” go from repo β†’ Classroom-ready assignment in under 60 seconds.
  • πŸ“ Flexible outputs β€” grading tables copied to clipboard or written directly to your README.
  • 🏎️ Optimized CI β€” precompiled YAMLs (no runtime parsing) for faster, cheaper runs.
  • πŸ”§ Instructor-friendly CLI β€” init, build, table, reset cover the full workflow.

How it Works

  • init β€” scans for Rust tests and builds .autograder/autograder.json.
  • build β€” converts that config into a ready-to-run GitHub Actions workflow at .github/workflows/classroom.yaml.
  • table β€” generates a Markdown grading table for READMEs, keeping grading criteria transparent.
  • reset β€” cleans up generated files for a fresh start.

Keeps autograding setup simple for instructors while making grading criteria clear for students.

Releases

Prebuilt binaries

OS / TargetDownload
macOS (x86_64-apple-darwin)See Assets on the latest release
Windows (x86_64-pc-windows-gnu)See Assets on the latest release

Assets are named: autograder-setup-vX.Y.Z-<target>.tar.gz (macOS) or .zip (Windows).

Installation

If you already have Rust installed:

cargo install autograder-setup

Check installation:

autograder-setup --version

Option B β€” Install from release

macOS

# 1) Download the macOS asset from the latest release
# 2) Extract and install:
tar -xzf autograder-setup-vX.Y.Z-x86_64-apple-darwin.tar.gz
sudo install -m 0755 autograder-setup-vX.Y.Z-x86_64-apple-darwin/autograder-setup /usr/local/bin/autograder-setup

# 3) Remove the Quarantine Attribute to disable macOS Gatekeeper and code signing requirement.
sudo xattr -r -d com.apple.quarantine /usr/local/bin/autograder-setup

# 4) Check that you can run it
autograder-setup --version

Windows (PowerShell)

# 1) Download the Windows .zip from the latest release
# 2) Extract and install:
Expand-Archive autograder-setup-vX.Y.Z-x86_64-pc-windows-gnu.zip -DestinationPath .

$dir = Get-ChildItem -Directory "autograder-setup-v*-x86_64-pc-windows-gnu" | Select-Object -First 1
$exe = Join-Path $dir.FullName "autograder-setup.exe"

$UserBin = "$env:USERPROFILE\.local\bin"
New-Item -ItemType Directory -Force -Path $UserBin | Out-Null
Move-Item $exe "$UserBin\autograder-setup.exe" -Force

# Add to PATH for current session (optionally add permanently in System settings)
$env:PATH = "$UserBin;$env:PATH"
autograder-setup --version

Option C β€” Build from source

git clone https://github.com/JoeyRussoniello/rust-autograder-setup
cd rust-autograder-setup
cargo build --release

# binary at target/release/autograder-setup.
# Add to PATH, or move the binary to the assignment repo you're configuring

Quickstart

The goal of autograder-setup is to take a plain Rust project template and make it GitHub Classroom–ready in under a minute.
This quickstart shows the minimal workflow instructors need to get up and running.


Step 0: Explore the CLI

autograder-setup --help

This prints top-level usage and shows all available subcommands. Use --help after any command to see its options.


Step 1: Initialize an autograder configuration

autograder-setup init
  • Scans your project recursively for all #[test] functions.
  • Builds a JSON config at .autograder/autograder.json.
  • Automatically adds optional checks (Clippy linting, commit count) unless disabled.

Why it’s useful: this JSON file acts as a single source of truth for grading. You can tweak points, timeouts, or thresholds before generating CI.


Step 2: Review and adjust points

Open .autograder/autograder.json in your editor:

[
  {
    "meta": { "name": "add_two", "description": "check add_two works", "points": 2, "timeout": 10 },
    "type": "cargo_test",
    "manifest_path": "Cargo.toml"
  }
]

You can increase/decrease point values, set timeouts, or change descriptions here. This makes grading customizable.


Step 3: Build the GitHub Actions workflow

autograder-setup build
  • Reads .autograder/autograder.json.
  • Emits .github/workflows/classroom.yaml with one job per test/check.
  • Uses the official classroom-resources/autograding-* actions.

Why it’s useful: this workflow is what Classroom actually runs when grading. It ensures consistency between local tests and CI.


(Optional) Step 4: Generate a grading table

autograder-setup table --to-readme
  • Produces a Markdown table of test names, descriptions, and points.
  • Appends to your README.md, or copy to clipboard by default.

Why it’s useful: students can see exactly how they’ll be graded up front.


Step 5: Reset if needed

autograder-setup reset

Deletes .autograder/ and the generated workflow, so you can start fresh.


Summary

  • init = discover tests and set up config
  • build = turn config into a ready-to-run workflow
  • table = generate a transparent grading table for students
  • reset = undo everything

Together, these steps let you go from zero β†’ reproducible autograder in under a minute, while keeping grading criteria explicit for both instructors and students.

Command: init

Scans the project (recursively), finds test functions, and writes .autograder/autograder.json. Supports nested Rust directories.

Options

-r, --root <ROOT>
        Root of the Rust project (defaults to current directory)

        [default: .]

-t, --tests-dir <TESTS_DIR>
        Location of all test cases (defaults to <root>)

        [default: .]

    --default-points <DEFAULT_POINTS>
        Default number of points per test

        [default: 1]

    --no-style-check
        Disable the Clippy style check (enabled by default)

    --require-commits <REQUIRE_COMMITS>...
        Require specific commit thresholds (e.g. --require-commits 5 10 15 20)

        [default: 1]

    --require-branches <REQUIRE_BRANCHES>...
        Require specific branch tresholds (e.g --require-branches 2 4 6)

    --require-tests <REQUIRE_TESTS>...
        Require specific student-written test thresholds (e.g --require-tests 2 4 6)

-h, --help
        Print help (see a summary with '-h')

Examples

# Initialize for a sibling repo
autograder-setup init --root ../student-assignment

# Only search ./tests recursively
autograder-setup init --tests-dir tests

# Default 5 points per test
autograder-setup init --default-points 5

# Omit style or commit checks
autograder-setup init --no-style-check
autograder-setup init --no-commit-count

# Require at least 5 tests
autograder-setup init --require-tests 5

# Award 1 point for reaching 5, 10, and 20 commits
autograder-setup init --require-commits 5 10 20

# Award points for 10 and 20 commits, and for having 2 and 3 branches
autograder-setup init --require-commits 10 20 --require-branches 2 3

Counting checks (commits, branches, tests)

The init command can emit simple threshold checks that award 1 point each when a submission meets a given threshold. The three related flags behave the same way: each value supplied becomes an independent 1‑point check.

  • --require-commits <N>...

    • Each value produces a check that the submission has at least N commits.
    • Example: --require-commits 5 10 20 β†’ three checks (5, 10, 20 commits).
  • --require-branches <N>...

    • Each value produces a check that the repository has at least N distinct branches.
    • Example: --require-branches 2 4 β†’ two checks (2 branches, 4 branches).
  • --require-tests <N>...

    • Each value produces a check that the student-written test count for a crate reaches at least N tests.
    • IMPORTANT: --require-tests applies per manifest path. For a workspace, a threshold value produces a separate check for the root crate and for each member crate (i.e., each manifest path gets its own check).
    • Example: in a workspace with member/ and a root crate, --require-tests 3 yields a TEST_COUNT check for the root (if present) and a TEST_COUNT check for member (each requiring 3 tests).
      • This behavior can be refined by changing/removing the entries in .autograder.json

Examples

# Award 1 point for reaching 5, 10, and 20 commits
autograder-setup init --require-commits 5 10 20

# Award 1 point for having 2 and 4 branches
autograder-setup init --require-branches 2 4

# Require at least 3 tests for each manifest (root and each workspace member)
autograder-setup init --require-tests 3

Notes

  • Each supplied value becomes an independent 1‑point check (not cumulative).
  • Deprecated: --num-commit-checks N expands to thresholds 1..=N (e.g., --num-commit-checks 3 β†’ 1 2 3). Prefer --require-commits for explicit thresholds.

Command: build

Generates .github/workflows/classroom.yaml from .autograder/autograder.json (and a commit-count script if needed).

Options

-r, --root <ROOT>
        Root directory of the Rust project (defaults to current directory)
        [default: .]

    --triggers <TRIGGERS>...
        Add one or more event triggers (always includes `repository_dispatch`)

        Examples:
          --triggers push pull-request

        Possible values:
          - workflow-dispatch: Manual run via the Actions UI
          - push:              Run on git push
          - pull-request:      Run on PR events

    --preset <PRESET>
        Quick config for common classroom setups

        Possible values:
          - instructor:   Instructor-driven grading; manual-friendly
          - student-push: Students get feedback on push
          - student-pr:   Students get feedback on PRs

    --branches <BRANCHES>...
        Restrict pushes and PRs to these branches

        Example:
          --branches main feature

    --paths <PATHS>...
        Only grade when changes affect these files or paths

        Example:
          --paths src/** Cargo.toml

    --paths-ignore <PATHS_IGNORE>...
        Ignore pushes and PRs that *only* affect these files or paths

        Example:
          --paths-ignore README.md docs/**

-h, --help
        Print help (see a summary with '-h')

Examples

autograder-setup build
autograder-setup build --root ../student-assignment

# Run on every push and pull request
autograder-setup build --triggers push pull-request

# Restrict to main branch only
autograder-setup build --triggers push pull-request --branches main

# Ignore documentation-only changes
autograder-setup build --triggers push --paths-ignore README.md docs/**

Workflow details

  • Fixed preamble (permissions, checkout, Rust toolchain).
  • One autograding step per entry in autograder.json.
  • Final reporter step wiring ${{ steps.<id>.outputs.result }} into the report.

Name/ID rules

  • Step name / test-name: verbatim for cargo test entries; ALL_CAPS for other steps (e.g., CLIPPY_STYLE_CHECK).
  • Step id: slugified name (lowercase; spaces & non-alnum β†’ -).
  • Command: cargo test <name>.

Workflow triggers (on:)

By default, the generated workflow uses: on: [repository_dispatch]. This allows instructors to trigger grading manually from the Classroom UI.

Running with flags modifies the triggers:

  • --triggers push β€” run the autograder on every push.
  • --triggers push pull-request β€” run on every push and pull request.
  • --branches β€” restrict which branches are eligible for push/PR grading.
  • --paths and --paths-ignore β€” limit workflow runs to specific file changes.

Notes about choosing triggers:

  • repository_dispatch is the safe default for instructor-initiated grading (avoids CI load on every push).
  • push or pull-request triggers give students instant feedback but increase compute usage.
  • Combine --branches, --paths, and --paths-ignore to control when grading runs.

Example Workflow YAML

name: Autograding Tests
on: [repository_dispatch, push]

permissions:
  checks: write
  actions: read
  contents: read

jobs:
  run-autograding-tests:
    runs-on: ubuntu-latest
    if: github.actor != 'github-classroom[bot]'
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy,rustfmt

      - name: basic_add_small_numbers
        id: basic-add-small-numbers
        uses: classroom-resources/autograding-command-grader@v1
        with:
          test-name: "basic_add_small_numbers"
          setup-command: ""
          command: "cargo test basic_add_small_numbers"
          timeout: 10
          max-score: 1

      - name: CLIPPY_STYLE_CHECK
        id: clippy-style-check
        uses: classroom-resources/autograding-command-grader@v1
        with:
          test-name: "CLIPPY_STYLE_CHECK"
          setup-command: ""
          command: "cargo clippy -- -D warnings"
          timeout: 10
          max-score: 1

      - name: Autograding Reporter
        uses: classroom-resources/autograding-grading-reporter@v1
        env:
          BASIC-ADD-SMALL-NUMBERS_RESULTS: "${{steps.basic-add-small-numbers.outputs.result}}"
          CLIPPY-STYLE-CHECK_RESULTS: "${{steps.clippy-style-check.outputs.result}}"
        with:
          runners: basic-add-small-numbers,clippy-style-check

build usage matrix

This matrix maps common scenarios to the flags you pass, what triggers resolve to, and what the generated on: block looks like.

ScenarioCLI flagsFilters appliedResulting on: block
Instructor-only (manual)(none)β€”on: [repository_dispatch]
Instructor preset--preset instructorβ€”on: [repository_dispatch, workflow_dispatch]
Push only--triggers pushβ€”on: [repository_dispatch, push]
Pull request only--triggers pull-requestβ€”on: [repository_dispatch, pull_request]
Push + PR (student feedback)--triggers push pull-requestβ€”on: [repository_dispatch, push, pull_request]
Student push preset--preset student-pushβ€”on: [repository_dispatch, push]
Student PR preset--preset student-prβ€”on: [repository_dispatch, pull_request]
Branch-limited (main only)--triggers push pull-request --branches mainbranches: [main]See Example A
Branch-limited (main & feature)--triggers push pull-request --branches main featurebranches: [main, feature]See Example B
Run only when certain files change--triggers push --paths src/** Cargo.tomlpaths: [src/**, Cargo.toml]See Example C
Ignore doc-only changes--triggers push --paths-ignore README.md docs/**paths-ignore: [README.md, docs/**]See Example D
Combine paths + branches--triggers pull-request --branches main --paths src/**branches: [main], paths: [src/**]See Example E
Everything together--triggers push pull-request --branches main release --paths src/** Cargo.toml --paths-ignore README.mdbranches, paths, paths-ignoreSee Example F

Advanced Build YAML Examples

Example A

autograder-setup build --triggers push pull-request --branches main

Runs the autograder on both pushes and pull requests, but only for the main branch.

on:
  repository_dispatch:
  push:
    branches: [main]
  pull_request:
    branches: [main]

Example B

autograder-setup build --triggers push pull-request --branches main feature

Same as Example A, but allow grading on multiple branches β€” here, both main and feature.

on:
  repository_dispatch:
  push:
    branches: [main, feature]
  pull_request:
    branches: [main, feature]

Example C

autograder-setup build --triggers push --paths src/** Cargo.toml

Run the autograder only when commits modify source files or the manifest (Cargo.toml).

on:
  repository_dispatch:
  push:
    paths: [src/**, Cargo.toml]

Example D

autograder-setup build --triggers push --paths-ignore README.md docs/**

Ignore pushes that only change documentation or the README.

on:
  repository_dispatch:
  push:
    paths-ignore: [README.md, docs/**]

Example E

autograder-setup build --triggers pull-request --branches main --paths src/**`

Run the autograder for pull requests into main, but only when code files are modified.

on:
  repository_dispatch:
  pull_request:
    branches: [main]
    paths: [src/**]

Example F: Putting It All Together

Using the command

autograder-setup build --triggers push pull-request \
    --branches main release --paths src/** Cargo.toml \
    --paths-ignore README.md

We can combine all filters for fine-grained control: grade only on code changes to main or release branches, ignoring documentation-only updates.

on:
  repository_dispatch:
  push:
    branches: [main, release]
    paths: [src/**, Cargo.toml]
    paths-ignore: [README.md]
  pull_request:
    branches: [main, release]
    paths: [src/**, Cargo.toml]
    paths-ignore: [README.md]

All options can be further tweaked in the YAML for additional refinement, such as grading different branches for pushes and pull requests

Do these calls feel verbose? Feel free to open an issue for commonly used build patterns to improve our presets options!

Command: table

Reads .autograder/autograder.json and generates a Markdown table of test names, descriptions, and points.

Options

-r, --root <ROOT>   Root of the Rust project [default: .]
    --no-clipboard  Print to stdout instead of copying to clipboard
    --to-readme     Append the table to README.md
-h, --help          Print help

Examples

autograder-setup table            # copy to clipboard
autograder-setup table --no-clipboard
autograder-setup table --root ../student-assignment --to-readme

Example Output

Test nameDescriptionPoints
add_coreAdd function works in the core case10
add_small_numbersAdd function works with small numbers5
add_with_negativesAdd function handles negative inputs3
clippy_style_checkClippy linting check2

Command: reset

Deletes generated files: the .autograder/ directory and .github/workflows/classroom.yml.

Options

-r, --root <ROOT>  Root of the Rust project [default: .]
-h, --help         Print help

Example

autograder-setup reset

.autograder/autograder.json Schema

FieldTypeReqDescription
meta.namestringyesDisplay name in the workflow and test filter
meta.descriptionstringyesStudent-facing description (supports ## placeholder for counts)
meta.pointsnumberyesMax score for this test (default 1)
meta.timeoutnumberyesSeconds for the autograder step (default 10)
typestringyesOne of: cargo_test, clippy, commit_count, test_count
manifest_pathstringnoPath to Cargo.toml (for cargo_test, clippy, test_count)
min_commitsnumbernoRequired commits (only for commit_count)
min_testsnumbernoRequired tests (only for test_count)

Example

[
  {
     "meta": { "name": "test_func_1", "description": "a test function", "points": 1, "timeout": 10 },
     "type": "cargo_test",
     "manifest_path": "Cargo.toml"
  },
  {
    "meta": { "name": "COMMIT_COUNT_1", "description": "Ensure at least ## commits.", "points": 1, "timeout": 10 },
    "type": "commit_count",
    "min_commits": 5
  },
  {
    "meta": { "name": "TEST_COUNT", "description": "Ensure at least ## tests exist.", "points": 1, "timeout": 10 },
    "type": "test_count",
    "min_tests": 3
  }
]

Repository Structure

.
β”œβ”€β”€ Cargo.lock                           # Cargo dependency lockfile (generated; checked in for reproducible builds)
β”œβ”€β”€ Cargo.toml                           # Crate metadata and dependencies
β”œβ”€β”€ LICENSE                              # Project license
β”œβ”€β”€ README.md                            # Top-level overview (link to docs)
└── src
    β”œβ”€β”€ cli                              # CLI subcommands and orchestration
    β”‚   β”œβ”€β”€ build                        # `autograder-setup build` β€” render workflow YAML from autograder.json
    β”‚   β”‚   β”œβ”€β”€ build_functions.rs       # Preamble, YAML helpers, commit-count script writer, small utilities
    β”‚   β”‚   β”œβ”€β”€ mod.rs                   # Subcommand entry + YAMLAutograder builder (ties everything together)
    β”‚   β”‚   β”œβ”€β”€ steps.rs                 # Hand-assembled YAML step emitters (CommandStep / ReporterStep)
    β”‚   β”‚   └── tests.rs                 # Unit tests for YAML rendering and build behavior
    β”‚   β”œβ”€β”€ init                         # `autograder-setup init` β€” scan tests and write `.autograder/autograder.json`
    β”‚   β”‚   β”œβ”€β”€ functions.rs             # High-level constructors for AutoTests (clippy/commit count/test count)
    β”‚   β”‚   β”œβ”€β”€ mod.rs                   # Subcommand entry and pipeline glue
    β”‚   β”‚   β”œβ”€β”€ scan.rs                  # Rust source scanner (finds #[test]/#[..::test], docs, manifests)
    β”‚   β”‚   └── tests.rs                 # Parser/scan tests and manifest-path logic tests
    β”‚   β”œβ”€β”€ mod.rs                       # Top-level CLI wiring (arg parsing, subcommand dispatch)
    β”‚   β”œβ”€β”€ reset                        # `autograder-setup reset` β€” remove generated files
    β”‚   β”‚   β”œβ”€β”€ mod.rs                   # Subcommand entry
    β”‚   β”‚   └── tests.rs                 # Safety checks for deleting generated artifacts
    β”‚   β”œβ”€β”€ table                        # `autograder-setup table` β€” generate student-facing Markdown table
    β”‚   β”‚   └── mod.rs                   # Subcommand entry and table rendering
    β”‚   └── tests.rs                     # Cross-subcommand/integration-style tests for the CLI layer
    β”œβ”€β”€ main.rs                          # Binary entrypoint; delegates to `cli`
    β”œβ”€β”€ types                            # Core data model for the autograder
    β”‚   β”œβ”€β”€ command_makers.rs            # Per-variant command builders (cargo test/clippy/test-count/commit-count)
    β”‚   └── mod.rs                       # `AutoTest { meta, kind }`, `TestMeta`, `TestKind` + Markdown row impl
    └── utils
        β”œβ”€β”€ mod.rs                       # Shared helpers: path walking, slug/id, yaml_quote, replace_double_hashtag, etc.
        └── tests.rs                     # Unit tests for utilities

FAQ

Does it run on every push?
Only if you use --grade-on-push. The default trigger is repository_dispatch to keep CI usage low.

How do I show a grading table in the README?
Run autograder-setup table (copy to clipboard) or autograder-setup table --to-readme.

Can I award partial credit for commit frequency?
Yes – use multiple thresholds with --require-commits, e.g., 5 10 20.

How do I require students to write additional tests?
Use --require-tests N. The autograder expects N + the number of tests generated from cargo test entries.