Skip to content
Merged
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
95 changes: 95 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,98 @@ jobs:
else
echo "scripts/smoke_flags.sh missing; skipping flags smoke"
fi

windows:
name: Windows PowerShell install + run smoke
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: PowerShell syntax check (install.ps1 / run.ps1)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
foreach ($f in @('install.ps1','run.ps1')) {
if (-not (Test-Path $f)) { throw "missing $f" }
$tokens = $null; $parseErrors = $null
[System.Management.Automation.Language.Parser]::ParseFile(
(Resolve-Path $f), [ref]$tokens, [ref]$parseErrors) | Out-Null
if ($parseErrors -and $parseErrors.Count -gt 0) {
$parseErrors | ForEach-Object { Write-Host $_ }
throw "parse errors in $f"
}
Write-Host "ok $f"
}

- name: Help text (-Help) for both scripts
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
.\install.ps1 -Help | Select-Object -First 5
.\run.ps1 -Help | Select-Object -First 5

- name: Smoke-test install.ps1 (noninteractive, skip Ollama, skip pull)
shell: pwsh
env:
TUTOR_SKIP_OLLAMA: "1"
TUTOR_SKIP_MODEL_PULL: "1"
TUTOR_NONINTERACTIVE: "1"
run: |
$ErrorActionPreference = 'Stop'
.\install.ps1 -NoLaunch
if (-not (Test-Path 'backend\.venv\Scripts\python.exe')) {
throw 'venv was not created'
}
& 'backend\.venv\Scripts\python.exe' -c "import fastapi, uvicorn, httpx, pydantic; print('imports ok')"

- name: Smoke-test run.ps1 -NoLaunch (preflight only, skip Ollama)
shell: pwsh
env:
TUTOR_SKIP_OLLAMA: "1"
TUTOR_PORT: "8801"
run: |
$ErrorActionPreference = 'Stop'
.\run.ps1 -NoLaunch -SkipOllama -Port 8801

- name: Verify FastAPI app imports cleanly (no Ollama)
shell: pwsh
env:
TUTOR_SKIP_OLLAMA: "1"
run: |
$ErrorActionPreference = 'Stop'
# Lightweight check: the same app the server would serve must
# import without errors from inside the venv. Avoids actually
# binding a port on the Windows runner (HTTP localhost probes
# under nested pwsh + Start-Process have proven flaky in CI).
$py = Join-Path (Get-Location) 'backend\.venv\Scripts\python.exe'
if (-not (Test-Path $py)) { throw "missing venv python: $py" }
Push-Location backend
try {
& $py -c "import app.main; assert hasattr(app.main, 'app'), 'app.main.app missing'; print('ok app.main imports')"
if ($LASTEXITCODE -ne 0) { throw "app.main import failed (exit $LASTEXITCODE)" }
} finally {
Pop-Location
}

- name: Reject unknown parameter
shell: pwsh
run: |
$ErrorActionPreference = 'Continue'
$err = $null
try {
& .\install.ps1 -DoesNotExist 2>&1 | Out-Null
} catch {
$err = $_
}
# PowerShell raises a ParameterBindingException before the script
# body runs; either $err is set or the call wrote a non-terminating
# error to $Error[0].
if (-not $err -and -not $Error[0]) {
throw 'unknown parameter should have been rejected'
}
Write-Host 'ok unknown parameter rejected'
43 changes: 36 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ never claims code works without running it.

## Quick start

Two commands. macOS or Linux. Python 3.10+.
Two commands. macOS, Linux, or Windows. Python 3.10+.

**macOS / Linux**

```bash
gh repo clone StewAlexander-com/python-tutor
Expand All @@ -172,17 +174,33 @@ cd python-tutor
./run.sh # serves UI + API at http://localhost:8001/
```

**Windows (PowerShell 5.1+ or PowerShell 7)**

```powershell
gh repo clone StewAlexander-com/python-tutor
cd python-tutor
.\install.ps1 # sets up venv, then prompts y/N for any host-level step
.\run.ps1 # serves UI + API at http://localhost:8001/
```

> If PowerShell blocks the script with an execution-policy error, run it once
> with: `powershell -ExecutionPolicy Bypass -File .\install.ps1` (or set the
> per-user policy: `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`).

Open <http://localhost:8001/> — you'll land on the lesson list with the code lab
and floating "Ask tutor" panel.

> `install.sh` only touches the repo on its own. **Installing Ollama, starting
> the daemon, pulling the model, or launching the app are all opt-in y/N
> prompts.** Press Enter and nothing changes on your host.
> `install.sh` / `install.ps1` only touches the repo on its own. **Installing
> Ollama, starting the daemon, pulling the model, or launching the app are all
> opt-in y/N prompts.** Press Enter and nothing changes on your host. On
> Windows the Ollama install path uses `winget` (App Installer) when you say
> yes; otherwise a manual <https://ollama.com/download/windows> link is shown.

Run `./install.sh --help` or `./run.sh --help` for every option. The most
common shapes:
Run `./install.sh --help` or `.\install.ps1 -Help` (and the matching `run`
script) for every option. The most common shapes:

```bash
# macOS / Linux
./install.sh --yes # trusted host: install Ollama, pull model, launch
./install.sh --noninteractive # CI: never prompt, default everything to "no"
./install.sh --skip-ollama # set up Python only; skip every Ollama probe
Expand All @@ -191,6 +209,16 @@ common shapes:
./run.sh --open-browser # open the URL once /api/health is green
```

```powershell
# Windows
.\install.ps1 -Yes # trusted host: install Ollama, pull model, launch
.\install.ps1 -NonInteractive # CI: never prompt, default everything to "no"
.\install.ps1 -SkipOllama # set up Python only; skip every Ollama probe
.\install.ps1 -Model llama3.1:8b # use a different model than gemma3:4b
.\run.ps1 -Port 8042 # choose a different port
.\run.ps1 -OpenBrowser # open the URL once /api/health is green
```

The classic env vars (`TUTOR_NONINTERACTIVE`, `PYTHON_TUTOR_ASSUME_YES`,
`TUTOR_SKIP_OLLAMA`, `TUTOR_MODEL`, `TUTOR_PORT`, …) still work — the flags
are sugar on top of them.
Expand All @@ -207,7 +235,8 @@ Full env-var list and design rationale:

| Symptom | What to do |
| --------------------------------------------- | ----------------------------------------------- |
| "Python 3.10+ is required and was not found" | `brew install python@3.12` / `apt install python3.12` and re-run. |
| "Python 3.10+ is required and was not found" | `brew install python@3.12` / `apt install python3.12` / `winget install -e --id Python.Python.3.12` and re-run. |
| Windows: "running scripts is disabled on this system" | One-shot: `powershell -ExecutionPolicy Bypass -File .\install.ps1`. Persistent (recommended): `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`. |
| `pip install` fails on DNS / proxy / pypi | The script detects this and prints offline/proxy/wheelhouse recipes. See [install-audit.md](docs/install-audit.md#pip-install-fails-on-a-network-you-dont-control). |
| "Port 8001 is already in use" | `./run.sh --port 8002` (probe uses `/dev/tcp`, no `lsof` needed). |
| Ollama installed but daemon down on `:11434` | Answer `y` to "Start `ollama serve` now?" or run it yourself in another Terminal. |
Expand Down
Loading
Loading