mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-22 13:22:16 -05:00
The software component existed to give the mod one exec hook. It is not needed for that any more, so a release now carries our installer and our payload and nothing else: 26MB smaller, 21MB of which was FlashForge's own firmwareExe travelling back to the partition it was already on, along with their klippy tree, their wifi module and their configs. WHAT IT GIVES BACK. /usr/prog/PROGRAM/software is no longer rewritten, so the version directory there stays FlashForge's with their genuine firmwareExe inside it -- and app_startup.sh's five-second watchdog, which copies one out of that directory when nothing by that name is running, has something to copy again. A printer whose payload fails to install now comes up on the stock UI instead of a blank screen. That recovery has been broken since we started deleting their binary out of a component we shipped; docs/how-it-works.md claimed it worked throughout. THE ROOT PASSWORD moves with it, because it used to ride in the component's shadow file. bin/pack.sh bakes ROOT_PW_HASH into the installer, single-quoted -- an unquoted $6$... would have the printer's shell expand $6 as a positional parameter and ship a truncated hash -- and the installer writes it to /usr/prog/etc/shadow. The random-password path asks one question instead of four: FlashForge ship the same root hash on every machine, so a live shadow still carrying it means nobody has set a password and it is ours to set. The .prev-root-hash recording, the shipped-vs-live comparison and the restore-it- afterwards step were all machinery for surviving a file that nothing overwrites any more, and are gone. pack.sh checks the hardcoded constant against the stock package it built from, because getting it wrong is silent and total: every printer would read as "already set" and none would ever get a password. bin/patch.sh is bin/payload.sh. It patched nothing any more -- no splice, no shadow edit, no firmwareExe deletion -- and what is left is the payload: the feed installed by the printer's own opkg, the seeded config, the manifest. Its sections are renumbered 1-5 with no gaps, and the five tombstone comments marking where removed sections used to be are gone with them. test_recipe_layout.py loses six of its nine tests. Each compared the tree against a description of the tree -- no fourth directory kind, each kind still used, 3rdparty carries no files of ours, installer/ holds exactly two named files -- and none could go red for a reason a printer would notice, while all six went red the moment the layout legitimately changed. The two that bite are kept: a payload/ path escaping $MODDIR, and a recipe name at both levels, which makes pkg_dir silently build the wrong package. SW_VER is deleted from config.env.example and both workflows: it stamped a component that is not built. Gates: 112 static tests pass. The generated installer is verified after substitution -- all four lines present, and `sh -n` over the result, because a hash with a stray character in it would otherwise reach a printer as a syntax error whose only symptom is a package that installs nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CH7Yb4wuPhnppHAB25UX7e
94 lines
3.8 KiB
Python
94 lines
3.8 KiB
Python
"""A `[ -f $MODDIR/... ]` guard that can only ever be false.
|
|
|
|
THE BUG THIS EXISTS FOR. `installer/run-append.sh` replaces FlashForge's
|
|
`klipperDaemon` -- whose `start` forks a second, unsupervised klippy beside the
|
|
s6-supervised one -- and it guarded that work with
|
|
|
|
if [ -f $MODDIR/bin/klipperDaemon ] && [ -d /usr/prog/klipper ]; then
|
|
|
|
The file ships at `$MODDIR/prog/klipperDaemon`. It moved there in 057a3a1 and
|
|
the guard did not, so the test was false on every printer and the whole block
|
|
was skipped IN SILENCE -- no error, no log line, just a stock `klipperDaemon`
|
|
left in place for release after release.
|
|
|
|
Nothing could have caught it. `sh -n` parses it, shellcheck likes it, and both
|
|
lanes stay green because the branch simply never runs. `test_no_undefined_names`
|
|
is the same idea for Python; this is the shell half.
|
|
|
|
WHY EXISTENCE GUARDS AND NOT EVERY $MODDIR PATH. Most `$MODDIR/...` references
|
|
are things a script CREATES or writes, and demanding those exist in a package
|
|
would be wrong. A `[ -f ]` / `[ -x ]` / `[ -d ]` is different: it is a claim
|
|
that something is already there, and the only two ways it can be true are that
|
|
a package ships it or that an earlier step wrote it. The second is the
|
|
allowlist below, and it is deliberately short so that adding to it is a
|
|
decision somebody makes rather than a way to silence this test.
|
|
"""
|
|
import re
|
|
|
|
import pytest
|
|
|
|
from lib.paths import ROOT
|
|
|
|
pytestmark = pytest.mark.static
|
|
|
|
# Scripts that read $MODDIR expecting the payload to be installed.
|
|
SOURCES = sorted(
|
|
set(ROOT.glob("installer/*.sh"))
|
|
| set(ROOT.glob("pkgs/*/payload/prog/*"))
|
|
| set(ROOT.glob("pkgs/*/payload/bin/*.sh"))
|
|
| set(ROOT.glob("pkgs/*/payload/etc/s6-rc/source/*/run"))
|
|
)
|
|
|
|
# `[ -f $MODDIR/a/b ]`, `[ -x ${MODDIR}/a/b ]`. Only literal paths: anything
|
|
# with a variable or a glob in it cannot be resolved here and is skipped.
|
|
GUARD = re.compile(r"\[\s+-[fxdesr]\s+\$\{?MODDIR\}?/([A-Za-z0-9_.][A-Za-z0-9_./-]*)")
|
|
|
|
# Not shipped by any package, and correctly so.
|
|
ALLOWED = {
|
|
# Templated from config.env by bin/payload.sh and then preserved across
|
|
# updates by run-append.sh -- user state, so a package member would
|
|
# overwrite a printer's settings on the first upgrade.
|
|
"anvil.conf",
|
|
# Written at runtime by the installer to notice a changed root filesystem.
|
|
".prev-root-hash",
|
|
# Inside anvil-helixscreen's upstream tarball, which pkgs/helixscreen
|
|
# unpacks at build time -- so it is shipped, just not committed here.
|
|
"helixscreen/bin/helix-launcher.sh",
|
|
}
|
|
|
|
|
|
def _shipped():
|
|
"""Every path any recipe ships, relative to $MODDIR."""
|
|
out = set()
|
|
for payload in ROOT.glob("pkgs/*/payload"):
|
|
for p in payload.rglob("*"):
|
|
out.add(str(p.relative_to(payload)))
|
|
return out
|
|
|
|
|
|
def test_every_moddir_existence_guard_names_something_that_ships():
|
|
shipped = _shipped()
|
|
missing = []
|
|
for src in SOURCES:
|
|
try:
|
|
text = src.read_text(encoding="utf-8", errors="replace")
|
|
except OSError:
|
|
continue
|
|
for lineno, line in enumerate(text.splitlines(), 1):
|
|
for path in GUARD.findall(line):
|
|
path = path.rstrip("/")
|
|
if path in ALLOWED or path in shipped:
|
|
continue
|
|
missing.append(
|
|
"%s:%d $MODDIR/%s"
|
|
% (src.relative_to(ROOT), lineno, path))
|
|
|
|
assert not missing, (
|
|
"%d existence guard(s) name a $MODDIR path no recipe ships, so the "
|
|
"test is false on every printer and whatever it guards never runs -- "
|
|
"silently, which is how the klipperDaemon replacement was skipped for "
|
|
"several releases:\n %s\n"
|
|
"Either the path is wrong, or the file should be in a package, or it "
|
|
"is written at runtime and belongs in ALLOWED with a reason."
|
|
% (len(missing), "\n ".join(missing)))
|