Skip to content
Merged

Dev #41

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
ce5e0b4
Update Docker base image
Jul 8, 2025
9f307fe
Update ref-utils submodule
Jul 8, 2025
69f272d
fmt exercise.py
Jul 8, 2025
388688d
typo
Jul 8, 2025
52c8a5a
cascaed db deletions for instances
Jul 8, 2025
a5b7ed0
Switch from pip/requirements.txt to uv for dependency management
Dec 18, 2025
72dcb6e
Add coverage tracking configuration for containers
Dec 18, 2025
43c1cb1
Add unique bridge_id for test isolation
Dec 18, 2025
ca52d2f
Add TestConfig for standalone unit testing
Dec 18, 2025
1529465
Redirect output to stderr for SFTP compatibility
Dec 18, 2025
9ca34c7
Add centralized logging module for Flask-independent usage
Dec 18, 2025
f2d9c21
Fix webapp dependencies and setup
Dec 18, 2025
dbd59e8
Add unit tests for core components
Dec 19, 2025
ff64ed5
Add GitHub Actions CI workflow
Dec 19, 2025
f9def5a
Refactor submission tests to use decorator-based registration
Dec 19, 2025
fa733de
Fix create_submission call in manual submit endpoint
Dec 19, 2025
618149b
Improve error message for test output length exceeded
Dec 19, 2025
df61ee5
Add test output directories to gitignore
Dec 19, 2025
8d8fd63
Add E2E and integration test infrastructure
Dec 19, 2025
0d6fb70
Update submodule
Dec 19, 2025
b3b7d1c
Add unique installation ID to Docker resource names
Dec 19, 2025
7e6eb67
Update ref-utils submodule
Dec 19, 2025
81910fb
Capture and upload Docker build logs in CI
Dec 19, 2025
33f6e42
Exclude ref-linux submodule and skip runtime checks in CI
Dec 19, 2025
dc392d8
Fix ruff linter errors across codebase
Dec 19, 2025
356c726
Add pre-commit hook for CI linting checks
Dec 19, 2025
1246f08
Add pyright and mypy checks to pre-commit hook
Dec 19, 2025
a02e2bf
Add mypy configuration and fix type annotations
Dec 19, 2025
0352604
Remove pyright from linting checks
Dec 19, 2025
c332158
Add pre-push hook to prevent dev/main divergence
Dec 19, 2025
d990dd3
Capture and log docker compose errors in test setup
Dec 19, 2025
8bc27b6
Add missing colorama dependency to webapp
Dec 19, 2025
cc5e060
Fix decorator syntax in test exercise submission_tests
Dec 19, 2025
5ba3cc9
Move submission_tests template to separate file for linting
Dec 19, 2025
109cc45
Fix submission_tests module loading for files without .py extension
Dec 19, 2025
a01e13f
Remove run_tests from public API
Dec 19, 2025
4d77500
Add remote_exec feature for E2E test server-side code execution
Dec 19, 2025
b161fbf
Update test Python version from 3.12 to 3.13
Dec 19, 2025
0d5d14f
Refactor remote_exec to use cloudpickle for closure support
Dec 19, 2025
0acb868
Add UserManager and refactor student registration
Dec 19, 2025
1fc8aaf
Add test conditions helpers for database state verification
Dec 19, 2025
cdfc9f6
Add integration tests for exercise, submission, and user workflows
Dec 19, 2025
8812546
Add API security tests and configurable rate limiting
Dec 19, 2025
c66ae95
Add submodule commit verification to pre-push hook
Dec 19, 2025
2887691
Fix formatting in test_port_forwarding.py
Dec 19, 2025
12debad
Standardize HTTP status code assertions in API tests
Dec 19, 2025
7d546f8
Fix file browser security tests to use valid tokens
Dec 19, 2025
b81c223
Fix task check exit code when tests fail
Dec 19, 2025
9d8fc3e
Fix submission test to check source file instead of binary
Dec 20, 2025
32b9721
Configure uv local cache and fix type errors
Dec 20, 2025
e1b9d87
Update ref-utils submodule (uv cache config)
Dec 20, 2025
cbefeec
Auto-initialize database on first startup
Dec 20, 2025
e3008cd
Use /29 subnets for instance networks to prevent address exhaustion
Dec 20, 2025
5e62f21
Limit peripheral services to 4 per exercise
Dec 20, 2025
6169218
Update testing instructions in project docs
Dec 20, 2025
4cd00d4
Add E2E tests for SSH key types (RSA, Ed25519, ECDSA)
Dec 20, 2025
db167df
Add Ed25519 and ECDSA SSH key support for student registration
Dec 20, 2025
2b59f9c
Extend API tests to support multiple SSH key types
Dec 20, 2025
fae00c7
Improve test infrastructure and configuration
Dec 20, 2025
002a518
Add failure_logs to gitignore
Dec 20, 2025
1746b6b
Add todo.md to gitignore
Dec 20, 2025
e791db3
Add database lock timeout with configurable limits
Dec 20, 2025
f500bae
Add LOG_DIR configuration and persistent file logging
Dec 20, 2025
66fd0f4
Fix build thread deadlock and add detailed build logging
Dec 20, 2025
eccdfdd
Improve test infrastructure for build debugging
Dec 20, 2025
61fde81
Configure pytest to use auto workers capped at 16
Dec 20, 2025
c419a09
Revert "Configure pytest to use auto workers capped at 16"
Dec 20, 2025
b43f2a1
Increase pytest workers from 4 to 10
Dec 20, 2025
7b9ac44
Fix pub_key/priv_key index size limitation
Dec 20, 2025
da58251
Change default SSH key type to Ed25519
Dec 20, 2025
31b238e
Add tests/failure_logs/ to gitignore
Dec 20, 2025
72dfa7a
Remove confirmation prompts from ctrl.sh commands
Dec 20, 2025
78896f7
Fix Docker resource prefix in test infrastructure
Dec 20, 2025
4c1c3cc
Run cgroup checks unconditionally in ctrl.sh
Dec 20, 2025
53874ea
Move docker-compose files to temp work directory
Dec 20, 2025
ecf740b
Fix coverage SQLite race condition with pytest-xdist
Dec 20, 2025
0574495
Update ORM models to use SQLAlchemy 2.0 mapped_column
Dec 21, 2025
fa33740
Replace patched OpenSSH with Rust SSH reverse proxy
Dec 21, 2025
580a4ee
Add ssh-reverse-proxy/target/ and tests/.coverage to gitignore
Dec 21, 2025
dab4083
Add module-scoped test fixtures with bridge counter for parallel isol…
Dec 22, 2025
33b42ff
Support DOCKER_RESSOURCE_PREFIX environment override
Dec 22, 2025
53e6844
Fix container hostname lookup for Docker Compose project isolation
Dec 22, 2025
c9e5217
Improve instance container management
Dec 22, 2025
4630225
Add SSH proxy logging and debugging improvements
Dec 22, 2025
bc12401
Fix exercise factory grading-points validation
Dec 22, 2025
fb82c7c
Add info-level logging for exercise import errors
Dec 22, 2025
67e93ae
Remove port forwarding tests pending SSH feature implementation
Dec 22, 2025
4d7e3cf
Add E2E test for Docker resource prefix verification
Dec 22, 2025
aca0a95
Improve SSH client and Rust proxy tests
Dec 22, 2025
b488f1d
Document race condition fix policy
Dec 22, 2025
7a2c33a
Handle None return from Docker network.containers property
Dec 23, 2025
13740a5
Add CONTEXT.md to important documents list
Dec 23, 2025
e98531a
Remove SSH_PROXY_ARCHITECTURE.md planning document
Dec 23, 2025
4cf8d2d
Fix Docker API race conditions causing CI failures
Dec 23, 2025
3b5e17f
Replace assertions with graceful error handling in InstanceManager
Dec 23, 2025
f902c7a
Add failure log management for E2E tests
Dec 23, 2025
5c66118
Fix IPAM None handling and add failure log summary to CI
Dec 23, 2025
bde0d2b
Increase key refresh interval from 2s to 60s
Mar 4, 2026
eb6b8f5
Log key refresh only when keys actually changed
Mar 4, 2026
0ed1608
Set unlimited memlock ulimit for ssh-reverse-proxy container
Mar 4, 2026
b61837a
Add ssh-proxy data volume mount
Mar 4, 2026
91554a8
Check submodule sync on startup and offer to update
Mar 6, 2026
e21496e
Persist SSH host key, fix channel cleanup, and limit welcome message …
Mar 6, 2026
724af88
Construct SSH welcome message in provision API
Mar 6, 2026
d204365
Fix exercise build DB session handling to prevent lock contention
Mar 6, 2026
0e64a33
Handle database lock timeout errors gracefully
Mar 6, 2026
5b61fa8
Make flash messages dismissible and float above content
Mar 6, 2026
13cc98b
Auto-refresh exercise list when builds complete
Mar 6, 2026
c04f50e
Handle concurrent exercise deletion gracefully
Mar 6, 2026
b44e212
Update ref-utils submodule to export run_tests
Mar 6, 2026
8acdf05
Suppress run_tests() during submission_tests module loading
Mar 18, 2026
e5a6bfb
Return structured JSON from /api/instance/info and expose it to tests
Apr 13, 2026
bd267ad
Show raw test score and pass status in submissions list
Apr 13, 2026
74596d9
Update ref-utils submodule for cached get_instance_info()
Apr 13, 2026
ef478f8
Introduce ExerciseConfig model for shared exercise settings
Apr 13, 2026
9a317be
Auto-generate app secrets on first run via prepare.py
Apr 13, 2026
2b18323
Introduce student groups with predefined name lists and group-aware g…
Apr 13, 2026
72dfe97
Eager-load and expunge Exercise.config in the build thread
Apr 13, 2026
7300ee0
Add public scoreboard with switchable views and ranking strategies
Apr 13, 2026
85fef42
Install ref-utils editable and bind-mount host source into instances
Apr 14, 2026
d710e07
Expand ARCHITECTURE.md with detailed component breakdown
Apr 14, 2026
3c69d39
Remove unused PySocks dependency
Apr 14, 2026
75efeaa
Bind-mount ref-utils source into web container via compose template
Apr 14, 2026
f6a334d
Re-render settings.env and docker-compose.yml from settings.yaml on d…
Apr 14, 2026
ac43286
Rename scoreboard 'wave' to 'assignment' and document optional features
Apr 14, 2026
31179f1
Split student-facing views and api.py into dedicated API packages
Apr 14, 2026
2fbffe2
Add the Vue SPA frontend sources
Apr 14, 2026
39c687f
Wire the spa-frontend service into compose, ctrl.sh, and settings
Apr 14, 2026
e2ca935
Stop tracking webapp/ref_webapp.egg-info/
Apr 14, 2026
11056e0
Replace single scoring policy with per-task scoring policies
Apr 14, 2026
969346b
Update SCOREBOARD.md ranking section for the best_sum strategy
Apr 14, 2026
178238d
Replace Chart.js with Apache ECharts in scoreboard charts
Apr 15, 2026
a82da89
Drop empty teams from the points-over-time chart
Apr 15, 2026
c9617ee
Localize scoreboard chart dates and hide unstarted assignment markers
Apr 15, 2026
8a2ccc4
Front Flask and the Vue SPA with a Caddy frontend-proxy on port 8000
Apr 15, 2026
b395162
Warn about vite dev exposure and reflect the course in the page title
Apr 15, 2026
2561d61
Name downloaded SSH keys after their actual algorithm
Apr 15, 2026
019c6ea
Clean up README to match current architecture
Apr 16, 2026
d03b5e1
Add configurable TLS support to frontend-proxy
Apr 16, 2026
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
244 changes: 244 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Important Documents

- `README.md` - Project overview and setup instructions
- `EXERCISES.md` - Exercise creation and submission testing
- `docs/ARCHITECTURE.md` - System architecture and components
- `.claude/CONTEXT.md` - Ongoing work and recent changes (create if missing)

## Build and Run Commands

**Note:** In sandboxed environments where `~/.docker` may be read-only, set `DOCKER_CONFIG` to a writable directory before running Docker commands:

```bash
export DOCKER_CONFIG=/path/to/repo/.docker-cache
```

The test infrastructure (`tests/helpers/ref_instance.py`) automatically sets this to `.docker-cache/` in the repo root.

```bash
# Build all Docker images
./ctrl.sh build

# Start services
# For development, always use --debug and --hot-reloading:
# --debug enables Flask debug mode and verbose logging
# --hot-reloading enables Flask auto-reload and runs the spa-frontend
# under `vite dev` (Vite HMR) instead of a static build
./ctrl.sh up --debug --hot-reloading
./ctrl.sh up # production-style start, no HMR

# Stop services
./ctrl.sh stop # Keep containers
./ctrl.sh down # Remove containers

# Database migrations
./ctrl.sh db-upgrade

# View logs
./ctrl.sh logs -f
```

## Code Quality

Python code must pass the same checks as CI. **Always run these checks on new or modified code.**

```bash
# Install tools (if needed)
uv tool install ruff
uv tool install mypy

# Install test dependencies (required for mypy)
cd tests && uv sync

# Linting and formatting (run from repo root)
ruff check .
ruff format --check . # Verify formatting (use 'ruff format .' to fix)

# Type checking (run from tests/ directory)
cd tests && uv run mypy .
```

These checks must pass before committing. CI will reject PRs that fail any of these checks.

### Git Hooks

A pre-commit hook is available that automatically runs linting checks before each commit:

```bash
# Install git hooks
./hooks/install.sh
```

The hook runs `ruff check`, `ruff format --check`, and `mypy`, rejecting commits that fail.

## Testing

**Important:** Never manually start a REF instance for running automated Python tests. The test infrastructure handles instance lifecycle automatically. Starting instances manually for interactive testing/debugging is fine.

```bash
# Install test dependencies
cd tests && uv sync

# Run all tests (test infrastructure manages REF instance)
cd tests && pytest

# Run only unit tests
cd tests && pytest unit/

# Run only integration tests
cd tests && pytest integration/

# Run only E2E tests
cd tests && pytest e2e/

# Skip slow tests
cd tests && pytest -m "not slow"

# Run a single test file
cd tests && pytest unit/test_ssh_client.py

# Run a specific test
cd tests && pytest unit/test_ssh_client.py::test_function_name
```

Tests must fail if dependencies are missing. Only skip tests if explicitly requested.

**Do not write tests that check CLI help commands.** Testing `--help` output is low value.

**Do not use hardcoded values in assertions.** Tests should verify behavior and relationships, not specific magic numbers or strings that may change.

### Test Architecture and Abstractions

Tests outside of `tests/unit/` (e.g., integration tests, E2E tests) must **never directly manipulate database objects**. Instead, they should:

1. **Use manager classes** - `ExerciseManager`, `InstanceManager`, `ExerciseImageManager` provide the business logic layer
2. **Follow view function patterns** - Replicate the same logic that view functions in `ref/view/` use
3. **Use `tests/helpers/method_exec.py`** - Pre-built functions that call managers via `remote_exec`

This ensures tests exercise the same code paths as the real application, catching integration issues that unit tests might miss.

**Example - Correct approach:**
```python
# Use InstanceManager.remove() like the view does
mgr = InstanceManager(instance)
mgr.remove()
```

**Example - Incorrect approach:**
```python
# Don't directly delete DB objects
db.session.delete(instance)
db.session.commit()
```

The abstraction layers are:
- `ref/view/` - HTTP request handlers (views)
- `ref/core/` - Business logic managers (ExerciseManager, InstanceManager, etc.)
- `ref/model/` - SQLAlchemy models (data layer)

Tests should interact with `ref/core/` managers or replicate `ref/view/` logic, not bypass them to manipulate `ref/model/` directly.

## Dependency Management

Use `uv` for all Python dependency management. Each component has its own `pyproject.toml`:
- `webapp/pyproject.toml` - Web application
- `ref-docker-base/pyproject.toml` - Container base image
- `tests/pyproject.toml` - Test suite

## Architecture Overview

REF is a containerized platform for hosting programming exercises with isolated student environments. See `docs/ARCHITECTURE.md` for full details.

### Components

1. **Web Application** (`webapp/`) - Flask app served by uWSGI on internal port 8000 (not published; reached via `frontend-proxy`)
- `ref/view/` - HTML route handlers (exercises, grading, instances, file browser, visualization, admin student management, system settings, etc.)
- `ref/services_api/` - JSON endpoints called by services (SSH reverse proxy hooks in `ssh.py`, student container callbacks in `instance.py`)
- `ref/frontend_api/` - JSON endpoints consumed by the Vue SPA (registration/restore-key in `students.py`, public scoreboard in `scoreboard.py`; mounted under `/api/v2/*` + `/api/scoreboard/*`)
- `ref/model/` - SQLAlchemy models (users, groups, exercises, instances, submissions, grades, system settings)
- `ref/core/` - Business logic managers (`ExerciseManager`, `InstanceManager`, `ExerciseImageManager`, `UserManager`, `DockerClient`, etc.)

Student-facing pages (registration, restore-key, public scoreboard) are served by the Vue SPA under `/spa/*` and talk to `ref/frontend_api/`. Admin pages live under `ref/view/` as Jinja-rendered HTML.

2. **Frontend Proxy** (`frontend-proxy/`) - Caddy 2 container that fronts the Flask `web` service and the Vue SPA on a **single host port** (`HTTP_HOST_PORT`, default 8000). Multi-stage Dockerfile: stage 1 builds the SPA with Node; stage 2 is `caddy:2-alpine` with `dist/` baked in at `/srv/spa-dist`. Routes:
- `/spa/*` → `spa-frontend:5173` (dev) or baked `/srv/spa-dist` via `file_server` (prod)
- `/static/*` → bind-mount of `webapp/ref/static` served directly
- `/admin`, `/admin/` → 302 to `/admin/exercise/view`
- `/spa` → 308 `/spa/`
- everything else → `reverse_proxy web:8000` with `header_up X-Tinyproxy {remote_host}` so Flask's rate limiter keys on the real client IP
Dev/prod is selected at container start by `entrypoint.sh` via `$HOT_RELOADING`. The `spa-frontend` service is gated behind the `dev` compose profile, and `ctrl.sh` exports `COMPOSE_PROFILES=dev` when `--hot-reloading` is active. **Never run `--hot-reloading` on a publicly reachable host** — `vite dev` is not a production server. The SPA renders a hazard-striped warning banner when `import.meta.env.DEV` is true.

3. **SSH Reverse Proxy** (`ssh-reverse-proxy/`) - Rust-based SSH proxy on port 2222
- Routes student SSH connections to exercise containers
- Uses web API with HMAC-signed requests for authentication and provisioning
- Supports shell, exec, SFTP, local/remote port forwarding, and X11 forwarding

4. **Instance Container** (`ref-docker-base/`) - Ubuntu 24.04 with dev tools
- Isolated per student/exercise under `ref-instances.slice` cgroup
- SSH server on port 13370
- Contains `ref-utils` for submission testing
- `task`/`_task` scripts for submission testing, `reset-env` for container reset

5. **Database** - PostgreSQL 17.2 storing users, groups, exercises, instances, submissions, grades, system settings

### Connection Flow

```
Client (ssh exercise@host -p 2222)
-> ssh-reverse-proxy validates via /api/getkeys
-> ssh-reverse-proxy provisions via /api/provision
-> Traffic proxied to container SSH (port 13370)
```

### Docker Networks

- `web-host` - Web ↔ Host (HTTP access)
- `web-and-ssh` - Web ↔ SSH reverse proxy API (internal)
- `web-and-db` - Web ↔ PostgreSQL (internal)
- `ssh-and-host` - SSH reverse proxy ↔ Host

### Data Persistence

- `/data/postgresql-db/` - Database files
- `/data/data/imported_exercises/` - Exercise definitions
- `/data/data/persistance/` - User submissions and instance data
- `/data/ssh-proxy/` - SSH proxy state
- `/data/log/` - Application logs

## Code Comments

- Do not reference line numbers in comments (e.g., "see ssh.py lines 397-404"). Line numbers change frequently and become outdated. Reference functions, classes, or use direct code references instead.

## Pending Tasks

Pending tasks in the codebase are marked with `FIXME(claude)` and `TODO(claude)`. When the user requests to process todos or fixmes, search for these markers and address them.

## Fixing Race Conditions

**Never fix race conditions by:**
- Adding timeouts or delays (e.g., `time.sleep()`)
- Reducing the number of threads or parallel processes
- Reducing test parallelism (e.g., changing `-n 10` to `-n 4`)

These approaches hide the underlying problem rather than fixing it. Race conditions must be fixed by addressing the root cause: proper synchronization, locking, atomic operations, or architectural changes.

## Commit Messages

- Do not include Claude as author or co-author in commit messages.
- Do not include historical context like "this fixes the failing test" or "this addresses the previous issue". Describe what the change does, not why it was needed.

## Test Log Summary

After test failures, a summary is automatically generated at `tests/failure_logs/SUMMARY.txt`. To regenerate manually:

```bash
cd tests && python3 summarize_logs.py
```

**Maintaining the pattern list:** The `ERROR_PATTERNS` dict in `tests/summarize_logs.py` defines which errors are detected. Keep this list accurate:
- **Add patterns** for error types that appear in logs but are missing from the summary
- **Remove patterns** that trigger false positives (matching non-error text)
27 changes: 27 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
**/.git
**/node_modules
**/__pycache__
**/*.pyc
**/.venv
**/.mypy_cache
**/.ruff_cache
**/.pytest_cache

ref-linux/
data/
backup_exercises/
ref-exercises/
.docker-cache/

tests/
tests/failure_logs/

webapp/ref_webapp.egg-info/

.codex/
.claude/

docs/
*.md

spa-frontend/dist/
Loading
Loading