mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-22 05:12:16 -05:00
worktree-timelapse-test-gcode
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b98552386b |
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 |
||
|
|
4850be432c |
Docs: say what is there now, not what the last three commits removed
Deleting test/ and anvil.conf left claims behind in nine files. Every one of
these was present tense about something that no longer exists.
THE CHELPER SYMBOL GATE, in three places that each described it as live:
notes/20-klipper-fork.md said pkgs/klipper gates the .so on "both the ELF
flags and test-chelper.py's symbol check". It
gates on neither now: the ABI is asked once of the
installed filesystem in qa/replica/test_abi.py,
and the single-source rule -- the .so is compiled
from the chelper sources of the tree it ships -- is
the whole defence. The page says that instead.
notes/85-packaging.md described the KLIPPER_FORK fallback the script no
longer had; the script itself is gone too.
qa-migration.md "What survives in test/" listed both the 28 unit
tests and test-chelper.py as surviving. It is
"What survived in test/, and then did not", and it
names what went uncovered rather than leaving that
to be discovered.
ANVIL.CONF, in the two notes that still read it at runtime:
notes/80-s6-migration.md is a dated plan, so it keeps its phases and gains
a "Where this ended up" note: anvil.conf and every
MOD_* switch were removed, so the phases that
source it describe an intermediate state, and the
case-*.sh gates they name are qa/replica/ modules.
notes/85-packaging.md predicted seed/ emptying via a postinst that seeds
anvil.conf.default. It emptied by anvil.conf being
deleted instead. Its payload allowlist said six
entries including anvil.conf and bin/busybox; the
real one in test_ipk.py has five and neither.
THE TWO STANDALONE TREES under tools/, stale before any of this and now fixed:
tools/python/README.md and tools/supervisor/README.md both told the reader to
run their gate with `test/integration/printer-exec.py` over a case-*.sh, and
one offered a `make test-python` target. None of the three has existed for
several commits. Both now point at qa/replica/, which installs the real package
and asks more than hand-feeding a tarball to a case script could. The four
places tools/python/README.md narrates what case-python.sh proves are past
tense, since that is now a record of how the measurements were taken.
versions.env's sqlite3 note pointed at the same dead target.
NAV: every notes/ page was listed except 85-packaging.md, and qa-migration.md
was reachable only by following a link out of Testing, Building or How it
works. Both are in the nav now, which also clears the two "not included in the
nav" lines from every build. .venv/ is gitignored -- docs/requirements.txt
tells you to make one right there in the tree.
Verified: `mkdocs build --strict` is clean and no longer reports pages outside
the nav. qa/static is 123 passed, its 110 errors the same missing
vendor/opkg-utils as before. No .md file is read by any lane, so the check that
matters here is the build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bLGYpVAeVzAX6NeAxcU42
|
||
|
|
5b463b889a |
Five switches that were only ever set to 1
MOD_WEB, MOD_CAM, MOD_UI, MOD_SSH and MOD_WIFI all defaulted to on, and what
each one really offered was a half-installed printer: Mainsail with no
moonraker behind it, a camera that would not stream, a screen dark on purpose,
a root password deliberately not set on a machine whose entire recovery story
is "ssh in". Nobody wants those states. What someone who sets one actually
wants is a stock printer, and that is one FlashForge package away.
What they cost was a second answer to every question. Each init script carried
a gate, each `down` file argued its existence from a flag rather than from the
ordering that was the real reason, and anvil.conf had five lines that told a
user they had a choice.
MOD_SSH was the clearest case: it never turned ssh off. dropbear is the
rootfs's own and listens either way, so all the flag did was decline to set a
password.
Each gate is replaced by the fact it was standing in front of, not by a
constant:
S60nginx, S62moonraker nothing -- the web stack is what this mod is
S65camera there is no video device, which the script already
finds out and reports
S50wifi there is no $WIFI_CONF, likewise
S80ui HelixScreen is not installed, which was already the
other half of the condition
To leave a piece out, leave it out: BUILD_MAINSAIL, BUILD_MOONRAKER and
BUILD_HELIX decide what goes into a build, and `opkg remove` decides what stays
on a printer. What survives in anvil.conf is what has a range rather than a
state -- MOD_CAM_RES/FPS/PORT and the NICE_* priorities -- plus MOD_SPLASH,
MOD_STARTUP, MOD_IMPORT and MOD_S6, which are untouched here.
The `down` files stay. They were argued from MOD_WEB and MOD_CAM, but the
argument never depended on them: s6-svscan starts a service the instant the
directory appears, which is before any init script has checked a prerequisite.
Their text now says so.
Measured: `make build` produces a payload with no live read of any of the five,
and qa-static is 265 passed with shellcheck over the edited init scripts. The
test suite still exercises the removed flags and has not been updated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011gKRUCqj18TVF1Vz1oMpmC
|
||
|
|
6e97f946b0 |
execline was 2.1MB unstripped against musl; it is 1008KB and mandatory
Eight recipes: skalibs, execline, s6, s6-rc, mainsail, moonraker,
helixscreen, anvil-core. bin/patch.sh loses 183 lines and stops being a
build system for any of them.
The supervision stack moves to the Ingenic glibc toolchain and links libc
dynamically, so the musl toolchain -- the only second libc in this tree --
is deleted along with MUSL_TOOLCHAIN_*, S6_STAMP and S6_BUILD. The
measurement that chose musl compared two STATIC builds; dynamic glibc was
never in it. Measured now: s6's shipped tree is 696K against ~930K for the
static musl one, and the whole stack including execline and s6-rc is 2.0M.
Three things that were not guesses:
* s6 and s6-rc do not link against glibc 2.29 without -lpthread. skalibs'
pthread_mutex_tailock reaches pthread_mutex_timedlock, which glibc kept
in libpthread until 2.34. Upstream never sees it and musl never had the
split. PKG_MAKE_ARGS is the lever, because s6's PTHREAD_LIB is wired
only to --enable-nsss and its Makefile appends LDLIBS last.
* s6 ships 21 binaries, not 13. s6-rc's generated scripts exec s6-sudo,
s6-sudoc, the s6-ipcserver chain, s6-sudod and s6-fdholder-daemon --
found by running a real up/down cycle with a restricted PATH, not by
reading the documentation.
* --enable-static-libc has never been a skalibs option. Its configure
warns on unknown flags and patch.sh sent that to /dev/null, so it was a
no-op for as long as section 5b existed.
execline ships whole. A subset picked from today's services fails the day
somebody adds a pipeline, at boot, naming a program nobody chose to leave
out. tools/supervisor/README.md and 80-s6-migration.md said it was not
needed; both now say why that stopped being true.
PY_STAMP was spelled in three places and happened to agree -- the same
shape as the s6 stamp bug, caught before it inverted. It is in common.sh
now, and the test that guarded S6_STAMP by name is rewritten around the
rule so it covers both and dies with neither.
HelixScreen's binaries reach mips_abi_gate for the first time: patch.sh
never gated them. They pass, 3 objects, nan2008/o32/mips32r2.
anvil-core carries a content hash in PKG_STAMP_EXTRA because its version is
a release date: pinned, an edit to payload/ would read as current; unpinned,
it rebuilds daily. The version says which release, the stamp says what is
in it. anvil.conf stays out -- it is user state that run-append.sh
preserves, and a package member is overwritten on every upgrade.
Twelve packages, byte-identical across two cold builds. 152 static tests.
The replica suite has NOT been run: docker is unavailable here, and the s6
change ships without its behavioural gate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
491d0dfe1d |
The payload carries s6, built here from a pinned hash
Nothing starts it yet. This step only puts the supervisor in the payload, so
that the boot change in the next phase is a boot change and not a build
change at the same time.
skalibs and s6 2.15.1.0 are pinned in versions.env beside a musl mipsel
toolchain, and bin/patch.sh cross-compiles them the way it already
cross-compiles c_helper.so: unpack a pinned toolchain into work/, build,
check the result, stage it. Nothing binary is vendored in this repo, so
anything the printer executes is built from a hash we chose.
Three properties of the build are load-bearing, and all three were measured
on the replica rather than reasoned about:
* --prefix=/usr/data/anvil, because s6 bakes its libexecdir in at compile
time. Every waiting verb execs s6-svlisten, which spawns s6-ftrigrd from
that baked-in path. Built for any other prefix, s6-svstat and respawn
keep working and only the waiting verbs fail -- it fails late and
partially, which is the worst way for a supervisor to fail. Moving MODDIR
now means rebuilding s6.
* -D_FILE_OFFSET_BITS=64, because without it readdir() returns EOVERFLOW on
this box: a 32-bit build meeting 64-bit inodes. s6-svscan starts cleanly
and then cannot see its own scandir.
* musl, statically. Static glibc produced a 73MB tree here. musl produces
3.6MB, of which we ship 928K -- the 13 supervision binaries and
libexec/s6-ftrigrd, nothing else.
--disable-execline had to be added: s6 links against execline by default and
the build stops without its headers. Turning it off is what makes "we ship no
execline" true rather than aspirational; our run scripts are plain /bin/sh.
The build is cached on the two version strings, unlike c_helper.so which
rebuilds on mtime. That difference is deliberate: chelper's sources are a
checkout someone may be editing, while these are tarballs that cannot change
without their sha256 changing. Staging into the payload is never cached, so a
stale payload is not a thing that can happen. The ELF check runs outside the
cache branch, on the tree that will actually ship: a cross-build that quietly
produced host, big-endian or 64-bit objects looks like a clean build here and
like ENOEXEC on a printer, with nothing to explain why.
libexec/ joins bin/ as a payload directory. Its name is part of the ABI
rather than a preference, for the compiled-in-prefix reason above. The
install manifest from the previous commit picked it up with no change, which
was verified rather than assumed.
case-supervisor.sh now runs against the tree the build produced instead of a
hand-made tarball, and execs all 13 binaries rather than one. Its runit half
is gone: the comparison is over and recorded in tools/supervisor/README.md,
nothing in the build produces runit any more, and a gate that still wanted it
could only ever be fed by hand. bin/verify.sh checks for s6-svscan and for
libexec/s6-ftrigrd separately, because a payload missing only the latter
supervises perfectly and fails every waiting verb.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
d6c07faa9b |
Measure s6 and runit on the printer before choosing either
anvil-service.sh hand-rolls what a supervisor does: liveness, a stop that
waits, a respawn loop. The question was whether to keep hand-rolling it. This
answers it with measurements from the replica rather than from project pages,
and changes nothing that ships.
There is nothing on the printer to reuse. busybox 1.31.1 is PID 1 and no
supervisor exists anywhere on the rootfs. busybox init does implement respawn,
but only from /etc/inittab, and /etc is inside the read-only squashfs that a
stock flash overwrites -- the one supervisor already on the box cannot be
reached. So both candidates had to be cross-compiled and run.
Two findings cost most of the time, and neither would have surfaced from a
build test:
* Without -D_FILE_OFFSET_BITS=64 both supervisors die identically the moment
they scan their own service directory -- readdir() returns EOVERFLOW,
"Value too large for defined data type", a 32-bit build meeting 64-bit
inodes. They start, they load, and then they cannot see their services.
* s6 resolves its helper programs through a prefix baked in at compile time.
s6-svc -w and s6-svwait exec s6-svlisten, which spawns s6-ftrigrd out of
libexecdir. Installed anywhere other than the configured prefix, status
and respawn keep working and every waiting verb fails -- late, and only
partially. runit's tools find each other on PATH and do not care.
That second one is why build.sh configures --prefix=/usr/data/anvil. The mod's
install root has to behave like a real prefix root -- bin/, lib/, libexec/,
etc/, share/ -- which is also what would let a newer Python and Moonraker
install into the same tree instead of being special-cased.
Both stacks supervise correctly on the printer's own kernel: status answers, a
kill -9 is respawned, and a stop waits for the process to actually be gone.
Either would delete the two bugs anvil-service.sh fixes by hand. The one real
difference is readiness: an s6 service can declare itself ready over a
notification-fd and s6-svwait -U blocks until it does, measured at 5s against a
service that waits 5s. runit's sv start returns when the process is forked,
which says nothing about whether it works. Our boot order is filenames and our
waiting is hand-rolled -- S65camera polls /dev/video0 for 30s, S70klipper
retries an MCU handshake -- so readiness is the part worth having.
Size, built against musl rather than static glibc, which took s6 from 73MB to
3.6MB: the s6 supervision subset is 13 binaries at 813KB plus s6-ftrigrd at
116KB; runit is 5 binaries at 335KB. execline is not needed -- s6 run scripts
can be plain sh.
test/integration/printer/case-supervisor.sh is the gate proving all of the
above on the replica. tools/supervisor/README.md records what neither
supervisor replaces: MOD_* gating, FF_PYTHON, S70klipper's force-start handoff,
and the fact that firmwareExe runs S* in the foreground in filename order --
ordering that adopting s6 would have to re-express as readiness waits. That,
not the supervisor, is the work.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|