Skip to content

Commit bac79ad

Browse files
Merge pull request #6 from StewAlexander-com/add-ci-and-install-scripts
CI workflow, install/run scripts, and idiot-proof quick start
2 parents f9d1be4 + 7eec0f5 commit bac79ad

7 files changed

Lines changed: 666 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
concurrency:
10+
group: ci-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
backend:
15+
name: Backend tests (pytest)
16+
runs-on: ubuntu-latest
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
python-version: ["3.10", "3.11", "3.12"]
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: Set up Python ${{ matrix.python-version }}
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: ${{ matrix.python-version }}
28+
cache: pip
29+
cache-dependency-path: |
30+
backend/requirements.txt
31+
backend/requirements-dev.txt
32+
33+
- name: Install backend dependencies
34+
working-directory: backend
35+
run: |
36+
python -m pip install --upgrade pip
37+
pip install -r requirements-dev.txt
38+
39+
- name: Run pytest
40+
working-directory: backend
41+
run: pytest -q
42+
43+
frontend:
44+
name: Frontend JS syntax check
45+
runs-on: ubuntu-latest
46+
steps:
47+
- uses: actions/checkout@v4
48+
49+
- name: Set up Node
50+
uses: actions/setup-node@v4
51+
with:
52+
node-version: "20"
53+
54+
- name: Syntax-check frontend JS
55+
run: |
56+
set -e
57+
for f in frontend/*.js; do
58+
echo "node --check $f"
59+
node --check "$f"
60+
done
61+
62+
- name: Validate JSON content
63+
run: |
64+
set -e
65+
python3 -c "
66+
import json, pathlib, sys
67+
errors = 0
68+
for p in list(pathlib.Path('frontend').rglob('*.json')) + list(pathlib.Path('curriculum').rglob('*.json')):
69+
try:
70+
json.loads(p.read_text())
71+
print(f'ok {p}')
72+
except Exception as e:
73+
print(f'ERR {p}: {e}', file=sys.stderr)
74+
errors += 1
75+
sys.exit(1 if errors else 0)
76+
"
77+
78+
scripts:
79+
name: Shell script lint + smoke
80+
runs-on: ubuntu-latest
81+
steps:
82+
- uses: actions/checkout@v4
83+
84+
- name: shellcheck install/run scripts
85+
run: |
86+
if [ -f install.sh ] || [ -f run.sh ]; then
87+
sudo apt-get update -y
88+
sudo apt-get install -y shellcheck
89+
for f in install.sh run.sh; do
90+
[ -f "$f" ] && shellcheck -x "$f"
91+
done
92+
else
93+
echo "no install.sh / run.sh yet; skipping"
94+
fi
95+
96+
- name: Set up Python
97+
uses: actions/setup-python@v5
98+
with:
99+
python-version: "3.12"
100+
101+
- name: Smoke-test install.sh (skip model pull, skip Ollama)
102+
env:
103+
TUTOR_SKIP_OLLAMA: "1"
104+
TUTOR_SKIP_MODEL_PULL: "1"
105+
TUTOR_NONINTERACTIVE: "1"
106+
run: |
107+
if [ -f install.sh ]; then
108+
chmod +x install.sh run.sh scripts/smoke_run.sh
109+
./install.sh
110+
test -d backend/.venv
111+
backend/.venv/bin/python -c "import fastapi, uvicorn, httpx, pydantic"
112+
else
113+
echo "install.sh missing; skipping smoke test"
114+
fi
115+
116+
- name: Smoke-test run.sh (no Ollama; check /api/health and /)
117+
env:
118+
TUTOR_SKIP_OLLAMA: "1"
119+
TUTOR_PORT: "8801"
120+
run: |
121+
if [ -f scripts/smoke_run.sh ]; then
122+
chmod +x scripts/smoke_run.sh
123+
./scripts/smoke_run.sh
124+
else
125+
echo "scripts/smoke_run.sh missing; skipping run.sh smoke"
126+
fi

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*.egg-info/
5+
.pytest_cache/
6+
.mypy_cache/
7+
.ruff_cache/
8+
9+
# Virtualenvs
10+
.venv/
11+
backend/.venv/
12+
venv/
13+
14+
# OS / editor
15+
.DS_Store
16+
.idea/
17+
.vscode/
18+
19+
# Logs / runtime
20+
/tmp/
21+
*.log

README.md

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,61 @@ flowchart TD
7373
└── 0001-offline-first-local-llm.md
7474
```
7575

76-
## Running the Frontend
76+
## Quick start (idiot-proof)
77+
78+
Two commands. macOS and Linux. Python 3.10+. Ollama is optional for a
79+
first look at the UI; required for chat replies and code evaluation.
80+
81+
```bash
82+
gh repo clone StewAlexander-com/python-tutor
83+
cd python-tutor
84+
./install.sh # creates venv, installs deps, pulls model if Ollama is up
85+
./run.sh # serves UI + API on http://localhost:8001/
86+
```
87+
88+
Then open <http://localhost:8001/> in your browser. You'll see the
89+
lesson list, the inline code lab (Run / Evaluate), and the floating
90+
"Ask tutor" chat panel.
91+
92+
### Expected behaviour when Ollama is not running
93+
94+
- The web UI loads normally — you can read lessons and run code locally
95+
(`POST /api/run` does not need the LLM).
96+
- `/api/health` reports `status: "degraded"` and `ollama_reachable: false`.
97+
- `Evaluate` and the chat panel return a clear 503 — they don't hang.
98+
- As soon as you start `ollama serve`, everything works without a restart.
99+
100+
### What if Ollama isn't installed?
101+
102+
`install.sh` and `run.sh` **never** install system binaries on your
103+
behalf. If Ollama is missing, they print exactly what to run:
104+
105+
```bash
106+
# macOS
107+
brew install ollama && ollama serve &
108+
109+
# Linux
110+
curl -fsSL https://ollama.com/install.sh | sh && ollama serve &
111+
```
112+
113+
Then `./install.sh` again to pull `gemma3:4b`.
114+
115+
### Troubleshooting
116+
117+
| Symptom | Fix |
118+
| -------------------------------------- | ------------------------------------------------------------------------------------------- |
119+
| `Python 3.10+ is required` | Install Python (see [`docs/install-runtime-workflow.md`](docs/install-runtime-workflow.md)) |
120+
| Chat returns `ollama_reachable: false` | Run `ollama serve` in another terminal |
121+
| Port 8001 already in use | `TUTOR_PORT=9001 ./run.sh` |
122+
| Model missing in chat replies | `ollama pull gemma3:4b` (or set `TUTOR_MODEL` to your model) |
123+
| Service worker shows stale UI | Hard-refresh the browser (Cmd/Ctrl-Shift-R) |
124+
| `install.sh` failed mid-`pip install` | Re-run it — it's idempotent and reuses the venv |
125+
126+
For the design rationale behind the two-script flow (and the five flows
127+
we evaluated), see
128+
[`docs/install-runtime-workflow.md`](docs/install-runtime-workflow.md).
129+
130+
## Running the Frontend (manual)
77131

78132
A static, dependency-free SPA lives in [`frontend/`](frontend/). It was adapted from the [Python Power User](https://github.com/StewAlexander-com/Python-Power-User) project (MIT) and provides the learner-facing UI for this framework.
79133

0 commit comments

Comments
 (0)