No FlashForge component ships; patch.sh is payload.sh

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
This commit is contained in:
Oleksandr ShyshatskyiandClaude Opus 5 committed 2026-08-30 15:16:52 +03:00
1 parent 0711a39692
commit fae26a85e5
60 files changed
+464 -617

No files matched your search

+2 -2
View File
@@ -215,7 +215,7 @@ build rather than quietly adding a name to PATH.
**Cold build: 142 seconds** (`tools/python/build.sh`, 16 cores), of which about
90 is the x86-64 build-python. In the build lane it is ~100s and it happens
**once**: `bin/patch.sh` caches the cross-built tree in `work/.py313` under a
**once**: `bin/payload.sh` caches the cross-built tree in `work/.py313` under a
stamp of all eight versions, so every later build copies rather than compiles.
The stamp is all eight and not just CPython's, because a bumped OpenSSL under
an unchanged `PY_VERSION` has to rebuild.
@@ -285,7 +285,7 @@ record and for trying a version bump before it becomes a pin. It is not how
the shipped interpreter is built: `pkg/python` compiles it inside the repo's
own build image, from the sha256-pinned tarballs, against the seven static
libraries the feed builds as their own `-dev` packages, and caches it in
`work/pkg/python`. `bin/patch.sh` section 5c runs that recipe and stages what
`work/pkg/python`. `bin/payload.sh` section 5c runs that recipe and stages what
it produced; it no longer contains a compiler invocation of its own.
The gate, against the tree the build actually produced:
+4 -4
View File
@@ -18,7 +18,7 @@
# loader = /lib/ld-linux-mipsn8.so.1 , rootfs glibc 2.33
#
# Toolchain: the repo's Ingenic gcc 7.2 / glibc 2.29 cross compiler -- the SAME
# one bin/patch.sh uses for klippy's c_helper.so. musl is forbidden: a
# one bin/payload.sh uses for klippy's c_helper.so. musl is forbidden: a
# musl-linked interpreter cannot dlopen a glibc c_helper.so, which is exactly
# how klippy loads it. glibc 2.29 -> 2.33 is forward compatible.
#
@@ -55,9 +55,9 @@ XZ_VER=5.4.7
BZIP2_VER=1.0.8
EXPAT_VER=2.6.4
EXPAT_TAG=R_2_6_4
# The mod's prefix root on the printer, and the same --prefix bin/patch.sh
# The mod's prefix root on the printer, and the same --prefix bin/payload.sh
# builds with: bin/python3.13 beside bin/s6-svscan, stdlib in
# lib/python3.13/. The one difference is that patch.sh deletes the `python3`
# lib/python3.13/. The one difference is that payload.sh deletes the `python3`
# symlink (and idle3/pydoc3/*-config) out of bin/ before staging, so that
# putting $MODDIR/bin on PATH cannot quietly change what `python3` means --
# see tools/python/README.md. This harness leaves the tree as `make install`
@@ -68,7 +68,7 @@ PREFIX=/usr/data/anvil
if [ "${1:-}" != "--in-container" ]; then
[ -x "$TOOLCHAIN_HOST/bin/mips-linux-gnu-gcc" ] || {
echo "!! no toolchain at $TOOLCHAIN_HOST" >&2
echo " run ./bin/fetch-assets.sh, then any build (bin/patch.sh unpacks it)" >&2
echo " run ./bin/fetch-assets.sh, then any build (bin/payload.sh unpacks it)" >&2
exit 1; }
exec docker run --rm \
-v "$TOOLCHAIN_HOST":/toolchain:ro \