Skip to content

Commit 5d94015

Browse files
Merge pull request #17 from StewAlexander-com/Windows-install
feat(windows): add install.ps1 / run.ps1 + Windows CI smoke
2 parents ddd8257 + be0b894 commit 5d94015

7 files changed

Lines changed: 1007 additions & 16 deletions

File tree

.github/workflows/ci.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,98 @@ jobs:
153153
else
154154
echo "scripts/smoke_flags.sh missing; skipping flags smoke"
155155
fi
156+
157+
windows:
158+
name: Windows PowerShell install + run smoke
159+
runs-on: windows-latest
160+
steps:
161+
- uses: actions/checkout@v4
162+
163+
- name: Set up Python
164+
uses: actions/setup-python@v5
165+
with:
166+
python-version: "3.12"
167+
168+
- name: PowerShell syntax check (install.ps1 / run.ps1)
169+
shell: pwsh
170+
run: |
171+
$ErrorActionPreference = 'Stop'
172+
foreach ($f in @('install.ps1','run.ps1')) {
173+
if (-not (Test-Path $f)) { throw "missing $f" }
174+
$tokens = $null; $parseErrors = $null
175+
[System.Management.Automation.Language.Parser]::ParseFile(
176+
(Resolve-Path $f), [ref]$tokens, [ref]$parseErrors) | Out-Null
177+
if ($parseErrors -and $parseErrors.Count -gt 0) {
178+
$parseErrors | ForEach-Object { Write-Host $_ }
179+
throw "parse errors in $f"
180+
}
181+
Write-Host "ok $f"
182+
}
183+
184+
- name: Help text (-Help) for both scripts
185+
shell: pwsh
186+
run: |
187+
$ErrorActionPreference = 'Stop'
188+
.\install.ps1 -Help | Select-Object -First 5
189+
.\run.ps1 -Help | Select-Object -First 5
190+
191+
- name: Smoke-test install.ps1 (noninteractive, skip Ollama, skip pull)
192+
shell: pwsh
193+
env:
194+
TUTOR_SKIP_OLLAMA: "1"
195+
TUTOR_SKIP_MODEL_PULL: "1"
196+
TUTOR_NONINTERACTIVE: "1"
197+
run: |
198+
$ErrorActionPreference = 'Stop'
199+
.\install.ps1 -NoLaunch
200+
if (-not (Test-Path 'backend\.venv\Scripts\python.exe')) {
201+
throw 'venv was not created'
202+
}
203+
& 'backend\.venv\Scripts\python.exe' -c "import fastapi, uvicorn, httpx, pydantic; print('imports ok')"
204+
205+
- name: Smoke-test run.ps1 -NoLaunch (preflight only, skip Ollama)
206+
shell: pwsh
207+
env:
208+
TUTOR_SKIP_OLLAMA: "1"
209+
TUTOR_PORT: "8801"
210+
run: |
211+
$ErrorActionPreference = 'Stop'
212+
.\run.ps1 -NoLaunch -SkipOllama -Port 8801
213+
214+
- name: Verify FastAPI app imports cleanly (no Ollama)
215+
shell: pwsh
216+
env:
217+
TUTOR_SKIP_OLLAMA: "1"
218+
run: |
219+
$ErrorActionPreference = 'Stop'
220+
# Lightweight check: the same app the server would serve must
221+
# import without errors from inside the venv. Avoids actually
222+
# binding a port on the Windows runner (HTTP localhost probes
223+
# under nested pwsh + Start-Process have proven flaky in CI).
224+
$py = Join-Path (Get-Location) 'backend\.venv\Scripts\python.exe'
225+
if (-not (Test-Path $py)) { throw "missing venv python: $py" }
226+
Push-Location backend
227+
try {
228+
& $py -c "import app.main; assert hasattr(app.main, 'app'), 'app.main.app missing'; print('ok app.main imports')"
229+
if ($LASTEXITCODE -ne 0) { throw "app.main import failed (exit $LASTEXITCODE)" }
230+
} finally {
231+
Pop-Location
232+
}
233+
234+
- name: Reject unknown parameter
235+
shell: pwsh
236+
run: |
237+
$ErrorActionPreference = 'Continue'
238+
$err = $null
239+
try {
240+
& .\install.ps1 -DoesNotExist 2>&1 | Out-Null
241+
} catch {
242+
$err = $_
243+
}
244+
# PowerShell raises a ParameterBindingException before the script
245+
# body runs; either $err is set or the call wrote a non-terminating
246+
# error to $Error[0].
247+
if (-not $err -and -not $Error[0]) {
248+
throw 'unknown parameter should have been rejected'
249+
}
250+
Write-Host 'ok unknown parameter rejected'

README.md

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,9 @@ never claims code works without running it.
163163

164164
## Quick start
165165

166-
Two commands. macOS or Linux. Python 3.10+.
166+
Two commands. macOS, Linux, or Windows. Python 3.10+.
167+
168+
**macOS / Linux**
167169

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

177+
**Windows (PowerShell 5.1+ or PowerShell 7)**
178+
179+
```powershell
180+
gh repo clone StewAlexander-com/python-tutor
181+
cd python-tutor
182+
.\install.ps1 # sets up venv, then prompts y/N for any host-level step
183+
.\run.ps1 # serves UI + API at http://localhost:8001/
184+
```
185+
186+
> If PowerShell blocks the script with an execution-policy error, run it once
187+
> with: `powershell -ExecutionPolicy Bypass -File .\install.ps1` (or set the
188+
> per-user policy: `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`).
189+
175190
Open <http://localhost:8001/> — you'll land on the lesson list with the code lab
176191
and floating "Ask tutor" panel.
177192

178-
> `install.sh` only touches the repo on its own. **Installing Ollama, starting
179-
> the daemon, pulling the model, or launching the app are all opt-in y/N
180-
> prompts.** Press Enter and nothing changes on your host.
193+
> `install.sh` / `install.ps1` only touches the repo on its own. **Installing
194+
> Ollama, starting the daemon, pulling the model, or launching the app are all
195+
> opt-in y/N prompts.** Press Enter and nothing changes on your host. On
196+
> Windows the Ollama install path uses `winget` (App Installer) when you say
197+
> yes; otherwise a manual <https://ollama.com/download/windows> link is shown.
181198
182-
Run `./install.sh --help` or `./run.sh --help` for every option. The most
183-
common shapes:
199+
Run `./install.sh --help` or `.\install.ps1 -Help` (and the matching `run`
200+
script) for every option. The most common shapes:
184201

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

212+
```powershell
213+
# Windows
214+
.\install.ps1 -Yes # trusted host: install Ollama, pull model, launch
215+
.\install.ps1 -NonInteractive # CI: never prompt, default everything to "no"
216+
.\install.ps1 -SkipOllama # set up Python only; skip every Ollama probe
217+
.\install.ps1 -Model llama3.1:8b # use a different model than gemma3:4b
218+
.\run.ps1 -Port 8042 # choose a different port
219+
.\run.ps1 -OpenBrowser # open the URL once /api/health is green
220+
```
221+
194222
The classic env vars (`TUTOR_NONINTERACTIVE`, `PYTHON_TUTOR_ASSUME_YES`,
195223
`TUTOR_SKIP_OLLAMA`, `TUTOR_MODEL`, `TUTOR_PORT`, …) still work — the flags
196224
are sugar on top of them.
@@ -207,7 +235,8 @@ Full env-var list and design rationale:
207235

208236
| Symptom | What to do |
209237
| --------------------------------------------- | ----------------------------------------------- |
210-
| "Python 3.10+ is required and was not found" | `brew install python@3.12` / `apt install python3.12` and re-run. |
238+
| "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. |
239+
| Windows: "running scripts is disabled on this system" | One-shot: `powershell -ExecutionPolicy Bypass -File .\install.ps1`. Persistent (recommended): `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`. |
211240
| `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). |
212241
| "Port 8001 is already in use" | `./run.sh --port 8002` (probe uses `/dev/tcp`, no `lsof` needed). |
213242
| Ollama installed but daemon down on `:11434` | Answer `y` to "Start `ollama serve` now?" or run it yourself in another Terminal. |

0 commit comments

Comments
 (0)