diff --git a/.github/instructions/kiwy-build-and-development.instructions.md b/.github/instructions/kiwy-build-and-development.instructions.md new file mode 100644 index 0000000..0e8c4cf --- /dev/null +++ b/.github/instructions/kiwy-build-and-development.instructions.md @@ -0,0 +1,140 @@ +--- +description: "Use when building, compiling, packaging or releasing the Kiwy Signage Player: PyInstaller build, .exe generation, build.spec, hiddenimports, code signing, Smart App Control, Windows development, Raspberry Pi deployment, or adding new modules under src/. Covers the exact build commands, environment constraints, bundling rules and verification steps." +name: "Kiwy Build & Development" +--- + +# Kiwy Signage Player — Build & Development + +Cross-platform Kivy digital signage player. + +- **Raspberry Pi / Linux** — `src/main.py` is the entry point (root `install.sh`, `start.sh`). +- **Windows** — `windows/run_win.py` is the entry point; it sets Windows env vars, imports + `main.py`, then monkey-patches platform differences. Packaged to `.exe` with PyInstaller. + +## Ground Rules + +- **Keep `src/main.py` cross-platform.** Windows-specific behaviour belongs in + `windows/run_win.py` (see `_patch_main()`), Pi-specific behaviour in `main.py` guarded + by capability checks. Do not add Windows-only imports to `main.py`. +- **Rebuild is mandatory.** The `.exe` bundles `src/`, so Python edits are invisible until + you rebuild. There is no hot reload in the packaged app. +- **`dist/` is a runtime folder, not just build output.** It holds `config/`, `media/`, + `playlists/`, `logs/`, `.kiosk-profile/` and `.player_heartbeat`. Never wipe `dist/` + blindly; a `--clean` rebuild preserves it, but a manual delete destroys player state. + +## Environment + +| Item | Value | +|------|-------| +| Interpreter | **Python 3.12.9** (64-bit) — project venv at `windows\venv` | +| Kivy | 2.3.1 | +| PyInstaller | 6.21.0 | +| Linux target | Python 3.13 (`repo/python-wheels/`, `repo/system-packages/`) | + +- **Python 3.13+/3.14 is NOT supported for the Windows build** — Kivy 2.3.1 has no wheels + for them. +- `cefpython3` is **not installed** in the project venv. The embedded-CEF web-link engine is + therefore unavailable and web links fall back to the Chrome/Edge subprocess adapter. + Installing `cefpython3` (uncomment it in `windows/requirements_win.txt`) restores the + CEF engine. + +## Build + +From `windows\`: + +``` +venv\Scripts\python.exe -m PyInstaller build.spec --clean --noconfirm +``` + +Or the full one-click path (creates venv, installs deps, builds, then signs): + +``` +build_win.bat +``` + +Output (folder mode, via `COLLECT`): + +``` +windows\dist\KiwySignagePlayer\KiwySignagePlayer.exe <- the real build output +``` + +**Deployment hazard:** `windows\dist\KiwySignagePlayer.exe` (one level up) is a stale +leftover from an older single-file build of the same name. It is not refreshed by the +current spec, so deploying it ships old code. Always take the executable from the +`KiwySignagePlayer\` subfolder, and delete the stray copy if it reappears. + +### `build.spec` facts + +- Entry point `run_win.py`; `pathex=[BUILD_DIR, SRC_DIR]` so `src/` modules resolve. +- `runtime_hooks=[pyi_runtime_hook.py]` — sets per-monitor DPI awareness **before** SDL/Kivy + initialise. Keep DPI work here; it must run before any window is created. +- `datas += Tree(src)` bundles all of `src/`. `console=True` so startup errors and the Kivy + log remain visible — do not flip this to `False` without a reason. +- `hiddenimports` must list modules that PyInstaller's static analysis **cannot** see: + lazily imported ones (`cef_browser` is imported inside a function) or dynamic imports + (`getattr`, `importlib`). Top-level imports such as `weblink_session` are found + automatically via `pathex`, but listing them is harmless insurance. + **When you add a module under `src/` that is imported lazily or by string, add it to + `hiddenimports` or the packaged exe will fail at runtime with `ModuleNotFoundError`.** +- `excluded_imports` drops Linux-only packages (`evdev`, `gi`, GStreamer) and the + non-matching `cefpython3` `.pyd` variants. + +## Code Signing (production constraint) + +Production PCs run with **Smart App Control enforced** (`VerifiedAndReputablePolicyState=1`, +UMCI enforced) with **no "Run anyway" bypass** — an unsigned exe is blocked at kernel level. + +- A **self-signed certificate does NOT satisfy Smart App Control.** A cert from a reputable + public CA is required. `create_self_signed_cert.ps1` is dev-only. +- Provide a cert as `KIWY_SIGN_PFX` (+ optional `KIWY_SIGN_PFX_PASSWORD`) or drop + `kiwy_signing.pfx` in `windows\`; `build_win.bat` then signs via `sign_exe.ps1` + (signtool with RFC3161 timestamp when available). +- See `documentation/CODE_SIGNING_SMART_APP_CONTROL.md`. + +## Verify Before Committing + +Fast syntax gate (no build, seconds): + +``` +python -m py_compile src/main.py src/weblink_session.py windows/run_win.py +``` + +**Close the running player before rebuilding.** A running +`dist\KiwySignagePlayer\KiwySignagePlayer.exe` locks the output and the build fails with +"Access is denied". Chrome/Edge kiosk processes left over from web-link playback can also +hold locks; if the build fails, check for stray `msedge.exe` / `chrome.exe` before retrying. + +Because `.exe` is a **reserved Windows device name** as well as a real file, use +`Test-Path -LiteralPath` when probing for the executable, otherwise the path silently +resolves to the console device. + +Development run — **use this on a SAC-locked host**, since the unsigned exe cannot launch: + +``` +cd windows +venv\Scripts\activate +python run_win.py +``` + +Diagnose playback transitions via the trace log (`logs\playback_trace.log`), written by +`src/playback_trace.py`. + +## Release Checklist + +1. Bump `PLAYER_VERSION` in `src/main.py` **and** `filevers`/`prodvers`/`FileVersion`/ + `ProductVersion` in `windows\version_info.txt`; keep them in sync. +2. Rebuild (`build_win.bat`). +3. Confirm the exe is signed (`Get-AuthenticodeSignature`); unsigned builds are blocked on + production hosts. +4. Smoke-test a mixed playlist: image → video → weblink → image, verifying durations, audio + (`audio: off` / `muted`), and a clean exit/restart. + +## Commit Hygiene + +- Never commit `windows\build\` or `windows\dist\` (git-ignored). Tracked build output such + as `windows\archive_list.txt` and `windows\build_last.txt` is the exception, not the rule. +- **Do not commit `player_auth.json` or `src/player_auth.json`** — they contain live + credentials (`auth_code`, `player_id`, `server_url`). These are currently tracked; treat any + future change to them as a deliberate, reviewed decision. +- Do not commit `config\app_config.json` values that are host-specific without checking + whether they belong in the repo default.