mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-23 13:52:16 -05:00
The third and last service of this phase, written as a sibling of nginx and the camera rather than as a new idea: a run script that execs moonraker in the foreground under $FF_PYTHON with the old start-stop-daemon arguments and the -b removed, a shipped `down` file because MOD_WEB is read at runtime while the payload is built once, and S62moonraker kept as a thin wrapper so the verbs and the vocabulary the gate greps for do not change. Readiness is :7125 in TCP_LISTEN, with the port parsed out of the [server] section of moonraker.conf so a user who moved it does not get a healthy printer reporting itself broken. The two alternatives are argued in the file and rejected: "forked" is what the pidfile already measured, badly, and "klippy_state is ready" is wrong -- moonraker reporting klippy_state=error is moonraker doing its job, so that definition would fail to fire on exactly the broken printer whose UI you need to reach. The gate proves the distinction rather than assuming it: s6-svwait -U was still blocking while s6-svstat already said up, and returned at 46s against a 40s stand-in delay, after which /server/info answered. With no scanner moonraker falls back to starting directly, as nginx does and unlike the camera. MOD_WEB is one switch across both scripts, so nginx up with moonraker refusing invents a third state nobody chose and is the worst of the three to debug -- Mainsail loads and blames Klipper. And ff-startup.py drives the whole boot off moonraker's API, so refusing would turn a failed s6 build into a printer that cannot bring itself up or say why. Starting twice is not possible: the `down` file plus the run script's sweep of any leftover moonraker see to that. The deferred config move is why -d /usr/data is still here, marked as the one line phase 7 changes. klipperDaemon is FlashForge's script and hardcodes KLIPPER_CONF=/usr/data/config/printer.cfg, so the directory cannot move until we own the launch; docs/notes/80-s6-migration.md now says so and carries the argument for moving it into phase 7a rather than losing it. Four bugs in the gate itself, found by running it and fixed there, each now commented. s6-svstat -p hands over the pid at FORK, so a check that read /proc/PID/cmdline saw `/bin/sh ./run` and not the interpreter -- it waits for the exec now, and a run script that never execs is a failure rather than a race. The same fork-is-not-ready gap made a supervised nginx look dead. A `start | sed` blocked for 43 seconds because the detached readiness reporter holds the pipe open. And a churning service reports pid -1 at every sample, so "the pid keeps changing" read as "the pid is stable" -- churn is measured by maximum age from s6-svstat now, which is what the negative control needed to mean anything. gates.moonraker() hands over the real s6 when one exists but DEGRADES rather than skipping when one does not, which is the opposite of the nginx and camera gates and deliberate. Those two are about supervision and have nothing to say without a supervisor. This one is the only gate that runs the shipped Moonraker at all, so skipping it in a checkout that has not built s6 would take the component-import checks with it and let a broken pin through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>