Files
Kiwy-Signage/.github/instructions/kiwy-build-and-development.instructions.md
T
ske087 eb8e66e427 Add build and development instructions for the workspace
Document how to build, package and release the player so the knowledge is not
rediscovered on each machine or session.

New file: .github/instructions/kiwy-build-and-development.instructions.md

Contents:
- Entry points: src/main.py on Linux/Pi, windows/run_win.py on Windows (which
  patches platform differences before running main.py).
- Verified toolchain: Python 3.12.9 x64 in windows\venv, Kivy 2.3.1,
  PyInstaller 6.21.0. Python 3.13+/3.14 is unsupported for the Windows build
  because Kivy 2.3.1 has no wheels for them.
- Build commands (pyinstaller build.spec, build_win.bat) and the real output
  path, including the note that cefpython3 is not installed in this venv, so
  the embedded-CEF engine is inactive and web links use the subprocess adapter.
- build.spec expectations: runtime hook for DPI awareness, console=True,
  excludes, and the rule that lazily/dynamically imported modules must be listed
  in hiddenimports or the packaged exe fails with ModuleNotFoundError.
- Code signing constraint: production hosts enforce Smart App Control, where an
  unsigned (or self-signed) exe is blocked at kernel level, so a public CA cert
  is required.
- Verification: py_compile gate, the need to close the running player before a
  rebuild (output lock / "Access is denied"), the .exe reserved-device-name
  pitfall that makes Test-Path report a false positive without -LiteralPath, and
  the stale duplicate one-file exe at windows\dist\KiwySignagePlayer.exe that
  must not be deployed.
- Release checklist and commit hygiene (do not track dist/build output or the
  player_auth.json credential files).
2026-09-10 16:52:41 +03:00

141 lines
6.3 KiB
Markdown

---
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.