diff --git a/.gitignore b/.gitignore index 3c449e44..43ee92a1 100644 --- a/.gitignore +++ b/.gitignore @@ -486,3 +486,6 @@ build/ # Generated fresh by the runner on each CI run. Committing a local copy # would label this machine capabilities as the runner is, which is a lie. gates/runnability-ci.tsv + +# Per-course virtualenvs created by ./run.sh run. +.venv/ diff --git a/DOCS-runnability.md b/DOCS-runnability.md index 15ee9fad..0ebbda11 100644 --- a/DOCS-runnability.md +++ b/DOCS-runnability.md @@ -39,7 +39,7 @@ run cannot clobber the local column. | `Programming/ECOAR` | runnable | - | make | | `Programming/ECOTE` | runnable | - | python3 /home/kuhy/WUT_Computer_Science/Programming/ECOTE/program/translator/main.py | | `Programming/ECRYPT` | no-code | - | test1.py is a 0-byte stub; this course produced no submitted code | -| `Programming/ECRYPT_PROJECT` | runnable | - | python3 /home/kuhy/WUT_Computer_Science/Programming/ECRYPT_PROJECT/code | +| `Programming/ECRYPT_PROJECT` | runnable | - | python3 the entry script | | `Programming/EGUI` | runnable | - | dotnet build | | `Programming/EOOP` | runnable | - | compiles each standalone .cpp | | `Programming/EOPSY` | runnable | - | make | @@ -47,12 +47,12 @@ run cannot clobber the local column. | `Programming/E-PSYOPS` | runnable | - | opens in the Unity editor | | `Programming/ERSMS-project` | runnable | - | docker compose up | | `Programming/NLP` | runnable | - | python3 the entry script | -| `Programming/PBAD` | runnable | - | jupyter nbconvert --execute | -| `Programming/PORR` | runnable | - | python3 /home/kuhy/WUT_Computer_Science/Programming/PORR/code | +| `Programming/PBAD` | blocked | - | jupyter is on PATH but broken (bad interpreter) | +| `Programming/PORR` | runnable | - | python3 /home/kuhy/WUT_Computer_Science/Programming/PORR/code/main.py | | `Programming/PSD` | runnable | - | starts the Kafka/Flink stack (installs packages with sudo) | | `Programming/psd_project` | runnable | - | mvn package | | `Programming/TRAK` | runnable | - | python3 the entry script | -| `Programming/twm_4` | runnable | - | jupyter nbconvert --execute | +| `Programming/twm_4` | blocked | - | jupyter is on PATH but broken (bad interpreter) | | `Programming/USD` | no-code | - | README describes a MetaDrive RL assignment; no implementation was submitted | | `Programming/WDWR` | runnable | - | python3 the entry script | | `Programming/ZPOB` | runnable | - | builds the report with latexmk | diff --git a/NotProgramming/EDABA-LAB/textual-description/description.tex b/NotProgramming/EDABA-LAB/textual-description/description.tex index 48afe888..e16a59d0 100644 --- a/NotProgramming/EDABA-LAB/textual-description/description.tex +++ b/NotProgramming/EDABA-LAB/textual-description/description.tex @@ -1,4 +1,8 @@ \documentclass{report} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{graphicx} \usepackage{hyperref} \usepackage{pdfpages} diff --git a/NotProgramming/SDM/presentations/my_thesis/main.tex b/NotProgramming/SDM/presentations/my_thesis/main.tex index ee535b12..9bb3066c 100644 --- a/NotProgramming/SDM/presentations/my_thesis/main.tex +++ b/NotProgramming/SDM/presentations/my_thesis/main.tex @@ -300,7 +300,7 @@ \item Intuicyjność \item Materiały \item Zasoby (Assety) - \item Dostępne funkcje} + \item Dostępne funkcje \item ChatGPT \end{itemize} \end{frame} diff --git a/NotProgramming/SDM2/presentations/my_thesis/main.tex b/NotProgramming/SDM2/presentations/my_thesis/main.tex index 324fdc4c..04ec5a27 100644 --- a/NotProgramming/SDM2/presentations/my_thesis/main.tex +++ b/NotProgramming/SDM2/presentations/my_thesis/main.tex @@ -1,4 +1,8 @@ \documentclass{beamer} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage[fontsize=20pt]{fontsize} \usepackage{graphicx} diff --git a/NotProgramming/SPD/lab1/main.tex b/NotProgramming/SPD/lab1/main.tex index f98644cc..e15d715a 100644 --- a/NotProgramming/SPD/lab1/main.tex +++ b/NotProgramming/SPD/lab1/main.tex @@ -1,6 +1,9 @@ \documentclass[12pt]{article} \usepackage[polish]{babel} \usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{array} \usepackage{float} \usepackage{graphicx} % Add this line to include graphics diff --git a/NotProgramming/STUP/report.tex b/NotProgramming/STUP/report.tex index dcd3043b..b57efd84 100644 --- a/NotProgramming/STUP/report.tex +++ b/NotProgramming/STUP/report.tex @@ -1,4 +1,8 @@ \documentclass[12pt]{extreport} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \begin{document} diff --git a/Programming/AIS/report/final/ver1/main.tex b/Programming/AIS/report/final/ver1/main.tex index b3be20c6..4abccb2c 100644 --- a/Programming/AIS/report/final/ver1/main.tex +++ b/Programming/AIS/report/final/ver1/main.tex @@ -1,4 +1,8 @@ \documentclass[a4paper,12pt]{article} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} % Packages \usepackage{amsmath, amssymb, amsthm} % For math symbols and theorems diff --git a/Programming/AIS/report/final/ver2/etoll.tex b/Programming/AIS/report/final/ver2/etoll.tex index 2924d53d..c0e8ae93 100644 --- a/Programming/AIS/report/final/ver2/etoll.tex +++ b/Programming/AIS/report/final/ver2/etoll.tex @@ -2,6 +2,9 @@ \usepackage[left=0.75in,right=0.75in,bottom=0.75in]{geometry} \usepackage[polish]{babel} \usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{hyperref} \usepackage{graphicx} \usepackage{enumitem} diff --git a/Programming/BSS/lab1/report_1.tex b/Programming/BSS/lab1/report_1.tex index edde3448..7899cdbc 100644 --- a/Programming/BSS/lab1/report_1.tex +++ b/Programming/BSS/lab1/report_1.tex @@ -1,4 +1,10 @@ -\documentclass{article}[12pt] +% Options belong BEFORE the class name; this read +% \documentclass{article}[12pt], where [12pt] is not a class option at all. +\documentclass[12pt]{article} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{graphicx} \title{[BSS]-Lab1-śr16-KrzysztofRudnicki} diff --git a/Programming/E-PSYOPS/projectConcept/main.tex b/Programming/E-PSYOPS/projectConcept/main.tex index 72121d22..15995e84 100644 --- a/Programming/E-PSYOPS/projectConcept/main.tex +++ b/Programming/E-PSYOPS/projectConcept/main.tex @@ -1,5 +1,8 @@ \documentclass{article} \usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{listings} \usepackage{xcolor} \usepackage{graphicx} diff --git a/Programming/EARIN/project/preliminary/report.tex b/Programming/EARIN/project/preliminary/report.tex index dca03d66..9b8f1d0d 100644 --- a/Programming/EARIN/project/preliminary/report.tex +++ b/Programming/EARIN/project/preliminary/report.tex @@ -1,4 +1,8 @@ \documentclass[12pt]{article} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{graphicx} % Required for inserting images \usepackage{hyperref} diff --git a/Programming/ECOTE/run.sh b/Programming/ECOTE/run.sh index 9252dc47..d7df3ad1 100755 --- a/Programming/ECOTE/run.sh +++ b/Programming/ECOTE/run.sh @@ -9,4 +9,5 @@ set -euo pipefail course_main --kind python \ --entry "$(dirname "$(readlink -f "$0")")/program/translator/main.py" \ + --test-dir "$(dirname "$(readlink -f "$0")")/program" \ -- "$@" diff --git a/Programming/ECRYPT_PROJECT/report/report.tex b/Programming/ECRYPT_PROJECT/report/report.tex index e649bea4..85bef6e2 100644 --- a/Programming/ECRYPT_PROJECT/report/report.tex +++ b/Programming/ECRYPT_PROJECT/report/report.tex @@ -1,4 +1,8 @@ \documentclass[12pt]{article} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{hyperref} \usepackage{graphicx} \usepackage{float} diff --git a/Programming/ECRYPT_PROJECT/run.sh b/Programming/ECRYPT_PROJECT/run.sh index f441b9a5..9f3fa5a5 100755 --- a/Programming/ECRYPT_PROJECT/run.sh +++ b/Programming/ECRYPT_PROJECT/run.sh @@ -7,6 +7,8 @@ set -euo pipefail # shellcheck source=../../scripts/course_lib.sh . "$(dirname "$(readlink -f "$0")")/../../scripts/course_lib.sh" +# No --entry on purpose: code/project.py prompts on stdin, so it cannot be run +# headlessly. This course ships a real pytest suite, and that is the check. course_main --kind python \ - --entry "$(dirname "$(readlink -f "$0")")/code" \ + --test-dir "$(dirname "$(readlink -f "$0")")/sieve" \ -- "$@" diff --git a/Programming/ECRYPT_PROJECT/code/C-100000.csv b/Programming/ECRYPT_PROJECT/sieve/C-100000.csv similarity index 100% rename from Programming/ECRYPT_PROJECT/code/C-100000.csv rename to Programming/ECRYPT_PROJECT/sieve/C-100000.csv diff --git a/Programming/ECRYPT_PROJECT/code/P-100000.csv b/Programming/ECRYPT_PROJECT/sieve/P-100000.csv similarity index 100% rename from Programming/ECRYPT_PROJECT/code/P-100000.csv rename to Programming/ECRYPT_PROJECT/sieve/P-100000.csv diff --git a/Programming/ECRYPT_PROJECT/code/__init__.py b/Programming/ECRYPT_PROJECT/sieve/__init__.py similarity index 100% rename from Programming/ECRYPT_PROJECT/code/__init__.py rename to Programming/ECRYPT_PROJECT/sieve/__init__.py diff --git a/Programming/ECRYPT_PROJECT/code/conftest.py b/Programming/ECRYPT_PROJECT/sieve/conftest.py similarity index 100% rename from Programming/ECRYPT_PROJECT/code/conftest.py rename to Programming/ECRYPT_PROJECT/sieve/conftest.py diff --git a/Programming/ECRYPT_PROJECT/code/project.py b/Programming/ECRYPT_PROJECT/sieve/project.py similarity index 100% rename from Programming/ECRYPT_PROJECT/code/project.py rename to Programming/ECRYPT_PROJECT/sieve/project.py diff --git a/Programming/ECRYPT_PROJECT/code/test_main.py b/Programming/ECRYPT_PROJECT/sieve/test_main.py similarity index 100% rename from Programming/ECRYPT_PROJECT/code/test_main.py rename to Programming/ECRYPT_PROJECT/sieve/test_main.py diff --git a/Programming/EOOP/Various/currency/currency.cpp b/Programming/EOOP/Various/currency/currency.cpp index e81d6f02..d83af483 100755 --- a/Programming/EOOP/Various/currency/currency.cpp +++ b/Programming/EOOP/Various/currency/currency.cpp @@ -15,7 +15,7 @@ g++ -Wall -Wextra -Werror -pedantic lab_2.cpp currency.hpp */ -#include "currency2.hpp" +#include "currency.hpp" #include int main() { diff --git a/Programming/ERSMS-project/docs/final_documentation/main.tex b/Programming/ERSMS-project/docs/final_documentation/main.tex index b059a8f8..868ea8b6 100644 --- a/Programming/ERSMS-project/docs/final_documentation/main.tex +++ b/Programming/ERSMS-project/docs/final_documentation/main.tex @@ -1,4 +1,8 @@ -\documentclass{article}[11pt] +\documentclass{article} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc}[11pt] \usepackage{graphicx} \usepackage{float} diff --git a/Programming/PORR/report/first_part/main.tex b/Programming/PORR/report/first_part/main.tex index 443ca457..b4222dd0 100644 --- a/Programming/PORR/report/first_part/main.tex +++ b/Programming/PORR/report/first_part/main.tex @@ -1,4 +1,8 @@ \documentclass[12pt]{article} +\usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{pgfplots} \usepgfplotslibrary{groupplots} \pgfplotsset{compat=1.17} diff --git a/Programming/PORR/run.sh b/Programming/PORR/run.sh index eb6f060e..7b687781 100755 --- a/Programming/PORR/run.sh +++ b/Programming/PORR/run.sh @@ -8,5 +8,5 @@ set -euo pipefail . "$(dirname "$(readlink -f "$0")")/../../scripts/course_lib.sh" course_main --kind python \ - --entry "$(dirname "$(readlink -f "$0")")/code" \ + --entry "$(dirname "$(readlink -f "$0")")/code/main.py" \ -- "$@" diff --git a/Programming/PSD/zin1/zin1.tex b/Programming/PSD/zin1/zin1.tex index ad045bf8..f92ba3a6 100644 --- a/Programming/PSD/zin1/zin1.tex +++ b/Programming/PSD/zin1/zin1.tex @@ -1,6 +1,9 @@ \documentclass[a4paper, 12pt]{article} \usepackage{graphicx} \usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage[polish]{babel} % Add Polish language support % Default fixed font does not support bold face diff --git a/Programming/WDWR/project/report/report.tex b/Programming/WDWR/project/report/report.tex index 0fced065..c5fdd1f5 100644 --- a/Programming/WDWR/project/report/report.tex +++ b/Programming/WDWR/project/report/report.tex @@ -1,6 +1,9 @@ \documentclass[12pt]{article} \usepackage[polish]{babel} \usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{amsmath} \usepackage{amsfonts} \usepackage{amssymb} diff --git a/Programming/WDWR/projekt/doc/main.tex b/Programming/WDWR/projekt/doc/main.tex index 76ffec82..e261645b 100644 --- a/Programming/WDWR/projekt/doc/main.tex +++ b/Programming/WDWR/projekt/doc/main.tex @@ -1,5 +1,8 @@ \documentclass{article} \usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \usepackage{polski} \usepackage{amsmath} \usepackage{relsize,amsfonts} diff --git a/Programming/ZPOB/esej/esej.tex b/Programming/ZPOB/esej/esej.tex index ad919c8a..1ab6ec69 100644 --- a/Programming/ZPOB/esej/esej.tex +++ b/Programming/ZPOB/esej/esej.tex @@ -2,6 +2,9 @@ \usepackage[polish]{babel} \usepackage[polish]{babel} \usepackage[utf8]{inputenc} +% T1 is required for Polish ogonek characters; without it pdflatex +% fails with "Command \k unavailable in encoding OT1". +\usepackage[T1]{fontenc} \author{Krzysztof Rudnicki, 307585} \title{ZBOP esej \\ Organizacja systemów produkcyjnych} \subtitle{ze względu na sposób spłenienia zapotrzebowania} diff --git a/gates/known-incomplete.txt b/gates/known-incomplete.txt new file mode 100644 index 00000000..3260775a --- /dev/null +++ b/gates/known-incomplete.txt @@ -0,0 +1,15 @@ +# Source files that cannot build because something they need was never +# submitted. One repo-relative path per line, with the reason and how it was +# checked above it. +# +# This is NOT a lint allowlist. Nothing here is a style exception; each entry +# is a factual statement that a dependency is absent from the archive, and the +# check is `git log --all -- ` coming back empty. Fabricating the +# missing file would be inventing coursework that was never handed in. +# +# Read by scripts/course_lib.sh, which skips these and says why. + +# Includes "set.hpp", which does not exist here and never has: +# git log --all --name-only -- '*set.hpp' -> no results +# The header was supplied by the course and only the .cpp was submitted. +Programming/EOOP/Various/set/set.cpp diff --git a/gates/runnability-local.tsv b/gates/runnability-local.tsv index 98cc7133..d61d5f21 100644 --- a/gates/runnability-local.tsv +++ b/gates/runnability-local.tsv @@ -25,7 +25,7 @@ Programming/EARIN runnable python3 the entry script Programming/ECOAR runnable make Programming/ECOTE runnable python3 /home/kuhy/WUT_Computer_Science/Programming/ECOTE/program/translator/main.py Programming/ECRYPT no-code test1.py is a 0-byte stub; this course produced no submitted code -Programming/ECRYPT_PROJECT runnable python3 /home/kuhy/WUT_Computer_Science/Programming/ECRYPT_PROJECT/code +Programming/ECRYPT_PROJECT runnable python3 the entry script Programming/EGUI runnable dotnet build Programming/EOOP runnable compiles each standalone .cpp Programming/EOPSY runnable make @@ -33,12 +33,12 @@ Programming/EPFU runnable make Programming/E-PSYOPS runnable opens in the Unity editor Programming/ERSMS-project runnable docker compose up Programming/NLP runnable python3 the entry script -Programming/PBAD runnable jupyter nbconvert --execute -Programming/PORR runnable python3 /home/kuhy/WUT_Computer_Science/Programming/PORR/code +Programming/PBAD blocked jupyter is on PATH but broken (bad interpreter) +Programming/PORR runnable python3 /home/kuhy/WUT_Computer_Science/Programming/PORR/code/main.py Programming/PSD runnable starts the Kafka/Flink stack (installs packages with sudo) Programming/psd_project runnable mvn package Programming/TRAK runnable python3 the entry script -Programming/twm_4 runnable jupyter nbconvert --execute +Programming/twm_4 blocked jupyter is on PATH but broken (bad interpreter) Programming/USD no-code README describes a MetaDrive RL assignment; no implementation was submitted Programming/WDWR runnable python3 the entry script Programming/ZPOB runnable builds the report with latexmk diff --git a/scripts/course_lib.sh b/scripts/course_lib.sh index 29e46eec..52050655 100644 --- a/scripts/course_lib.sh +++ b/scripts/course_lib.sh @@ -20,6 +20,9 @@ readonly COURSE_EXIT_NO_CODE=79 COURSE_ROOT="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[1]}")")" && pwd)" readonly COURSE_ROOT +# Repo root is two levels up: every course dir is //. +COURSE_GATES="$(cd "${COURSE_ROOT}/../.." && pwd)/gates" +readonly COURSE_GATES # --- helpers ----------------------------------------------------------------- @@ -65,6 +68,14 @@ _probe_any() { _blocked "needs $label (none of: $*)" } +# True if the path is recorded in gates/known-incomplete.txt. +_is_known_incomplete() { + local manifest="${COURSE_GATES:-}/known-incomplete.txt" + [[ -f "$manifest" ]] || return 1 + local rel="$1" + grep -vE '^\s*#|^\s*$' "$manifest" | grep -qxF "$rel" +} + _first_file() { find "$COURSE_ROOT" -name "$1" -not -path '*/node_modules/*' -print -quit 2>/dev/null } @@ -118,6 +129,10 @@ _probe() { ;; notebook) _have jupyter || _blocked "needs jupyter" + # Being on PATH is not the same as working: this machine has a + # jupyter shim whose pipx venv interpreter is gone, so it fails + # with "bad interpreter". Probe by actually invoking it. + jupyter --version > /dev/null 2>&1 || _blocked "jupyter is on PATH but broken (bad interpreter)" _runnable "jupyter nbconvert --execute" ;; compose) @@ -129,6 +144,63 @@ _probe() { esac } +# Use the course venv when one exists, otherwise the system interpreter. +_python_bin() { + if [[ -x "$COURSE_ROOT/.venv/bin/python" ]]; then + printf '%s' "$COURSE_ROOT/.venv/bin/python" + else + printf 'python3' + fi +} + +# Dependencies are the runner's job, not the reader's. Without this, EARIN +# dies on ModuleNotFoundError for gymnasium and the "runnable" status is a lie. +_python_install_requirements() { + local reqs=() + while IFS= read -r r; do reqs+=("$r"); done < <( + find "$COURSE_ROOT" -name 'requirements*.txt' -not -path '*/.venv/*' | sort) + [[ ${#reqs[@]} -gt 0 ]] || return 0 + + if [[ ! -x "$COURSE_ROOT/.venv/bin/python" ]]; then + printf 'creating .venv\n' + python3 -m venv "$COURSE_ROOT/.venv" + fi + "$COURSE_ROOT/.venv/bin/python" -m pip install --quiet --upgrade pip + + # EVERY requirements file, not just the first: EARIN is six independent + # labs plus a project, and installing only one of them left the lab it + # then tried to run missing gymnasium. + local req + for req in "${reqs[@]}"; do + printf 'installing %s\n' "${req#"$COURSE_ROOT"/}" + "$COURSE_ROOT/.venv/bin/python" -m pip install --quiet -r "$req" || { + printf 'warning: some requirements in %s failed\n' "${req#"$COURSE_ROOT"/}" >&2 + } + done +} + +# Where a course ships real tests, running them is a stronger check than +# running the program -- and for an interactive program (ECRYPT_PROJECT prompts +# on stdin) it is the ONLY check that works without a person at a keyboard. +_python_run_tests() { + local suite + suite="$(find "$COURSE_ROOT" -name 'test_*.py' -not -path '*/.venv/*' -print -quit 2>/dev/null)" + [[ -n "$suite" ]] || return 0 + local py + py="$(_python_bin)" + "$py" -c 'import pytest' 2> /dev/null || { + printf 'tests present but pytest is not installed; skipping\n' + return 0 + } + # Which directory pytest runs FROM is course-specific and cannot be + # guessed: ECOTE needs program/ so `from translator.main import` resolves, + # ECRYPT_PROJECT needs sieve/ because its fixtures open P-100000.csv by a + # relative path. So the stub says, and COURSE_ROOT is only the default. + local from_dir="${COURSE_TEST_DIR:-$COURSE_ROOT}" + printf 'running the test suite in %s\n' "${from_dir#"$COURSE_ROOT"/}" + ( cd "$from_dir" && "$py" -m pytest -q ) +} + # --- runners ----------------------------------------------------------------- _run() { @@ -156,9 +228,41 @@ _run() { ;; python) local entry="${COURSE_ENTRY:-}" - [[ -n "$entry" ]] || entry="$(_first_file 'main.py')" - [[ -n "$entry" ]] || _no_code "no entry point; see this directory's README" - ( cd "$(dirname "$entry")" && python3 "$(basename "$entry")" ) + # An entry may be given as a directory; resolve it to the module + # inside. Passing the directory itself makes python look for + # __main__.py and fail with a message about the wrong thing. + if [[ -d "$entry" ]]; then + local cand + for cand in main.py project.py __main__.py; do + if [[ -f "$entry/$cand" ]]; then + entry="$entry/$cand" + break + fi + done + fi + # Only auto-pick a main.py when there is exactly ONE. A course + # with six labs has six, and choosing whichever find returns first + # is not "running the project", it is running an arbitrary lab. + if [[ ! -f "$entry" ]]; then + local mains + mains="$(find "$COURSE_ROOT" -name 'main.py' -not -path '*/.venv/*' | wc -l)" + [[ "$mains" -eq 1 ]] && entry="$(_first_file 'main.py')" + fi + + _python_install_requirements + + if [[ -f "$entry" ]]; then + ( cd "$(dirname "$entry")" && "$(_python_bin)" "$(basename "$entry")" ) + else + # A pile of one-shot scripts with no entry contract (NLP is + # the case here). Syntax-check them all rather than picking one + # arbitrarily and calling that "running the project". + printf 'no single entry point; syntax-checking every module instead\n' + find "$COURSE_ROOT" -name '*.py' -not -path '*/.venv/*' -print0 \ + | xargs -0 -r "$(_python_bin)" -m py_compile + printf 'all modules compile\n' + fi + _python_run_tests ;; node) ( cd "${COURSE_ENTRY:-$COURSE_ROOT}" && npm install && npm start ) ;; maven) ( cd "${COURSE_ENTRY:-$COURSE_ROOT}" && mvn -q package ) ;; @@ -177,6 +281,10 @@ _run() { mkdir -p "$COURSE_ROOT/build" while IFS= read -r src; do rel="${src#"$COURSE_ROOT"/}" + if _is_known_incomplete "${src#"${COURSE_GATES%/gates}"/}"; then + printf 'skipping %s (see gates/known-incomplete.txt)\n' "$rel" + continue + fi out="$COURSE_ROOT/build/$(basename "${rel%.cpp}")" # Some files here are exam-answer fragments with no main(), so # they cannot be linked. Compile those to an object instead -- @@ -211,11 +319,13 @@ course_main() { COURSE_KIND="" COURSE_ENTRY="" COURSE_NOTE="" + COURSE_TEST_DIR="" while [[ $# -gt 0 ]]; do case "$1" in --kind) COURSE_KIND="$2"; shift 2 ;; --entry) COURSE_ENTRY="$2"; shift 2 ;; --note) COURSE_NOTE="$2"; shift 2 ;; + --test-dir) COURSE_TEST_DIR="$2"; shift 2 ;; --) shift; break ;; *) break ;; esac