mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-22 21:32:16 -05:00
2b3772664aa9aef488b349615218e9c0c07b9fa7
17
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
68afe65485 |
Fix two features that silently did nothing, and give Moonraker a user seam
Both bugs were found by auditing |
||
|
|
d05565284d |
Sections E, F, G: wrong values, stale claims, overclaims
E -- values and counts. The START_PRINT prologue has no M400 and an idle timeout of 864000, not 1800000. The nozzle-clean temperature comes from the material table FIRST, so NOZZLE=240 with no per-tool temp cleans at 220 -- documented backwards before. TOOLS is the paired 0:220,2:240 form. The wipe Z is the raw eddy frame, not the homed one. The runout M117 strings are "T<n> clog" / "T<n> out of filament", not E-codes. Grab-verify raises E0051+tool, not E0143. Docks are at X~297; 250/280 are staging waypoints. The grab sequence is documented in the order _grab actually emits it. ff_print exposes seven status fields, not nine, and never emits TOOLS=. Package sizes reconciled against the real artifacts (62MB built, ~93MB stock, ~370MB of CI assets, ~60s not 90s). testing.md's sample output block matched no header the code prints, so it is replaced with the real ones; its pytest row listed two of six suites. F -- stale claims. _FF_PREFLIGHT is not "missing". SDCARD_SET_CHANNEL is not still issued ( |
||
|
|
b2c651b11f |
Fix the runout insert path and TOOL=ALL staging; drop SAFE-MODE for MOD_UI
A2: _FF_INSERT's body was entirely inside {% if kind == 'switch' %}, yet
insert_gcode is wired on all four fm_ex* with KIND=motion. A motion insert --
which is what clearing a clog looks like, filament moving again -- produced no
output at all, on exactly the path where the operator most needs the next
step. Adds the motion branch the comment already described.
A3: TOOL_OFFSET_CALIBRATE staged each tool with set_nozzle() inside the loop,
so a gap-check failure on a later tool left earlier tools pending for
SAVE_CONFIG -- while raising an error that says "nothing saved". True for
TOOL=<n>, false for TOOL=ALL, and told to the operator at the moment they
decide whether to save. Staging now happens after every tool has passed.
A8: SAFE-MODE is removed rather than repaired. It never worked: the settle
check cleared the counter 90s after every boot on a condition
(`[ -s $CHOICE ]`) that cannot be false, since $CHOICE is written two lines
earlier and "none" is not empty. Nothing checked whether helix-screen was
alive, so the latch only fired on three reboots inside 90 seconds -- not the
crash-loop it was written for.
In its place, MOD_UI in anvil.conf, alongside MOD_WEB/MOD_CAM/MOD_SSH/MOD_WIFI
and templated by patch.sh the same way. It stops the UI being started; it does
not pretend to repair one. The docs said `touch SAFE-MODE` was how you recover
a blank screen. Nothing recovers a blank screen on the machine: HelixScreen is
the only UI, and getting one back means flashing a package. Said plainly now,
in the README, the recovery section and the troubleshooting table -- along with
what is genuinely unaffected, which is ssh, Mainsail, Klipper and printing.
48 passed, 5 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
401c2ecf35 |
The ff-*.cfg are ours: overwrite them, and say so at the top
Editing a shipped config was the wrong seam, and the installer was
letting people do it. An edited ff-*.cfg was KEPT and the new version
left beside it as .mod-new, so a machine could sit for releases on a
macro set nobody else was running -- the failure mode being a bug report
about code that had been replaced twice since.
Klipper already offers the better seam. It parses with
RawConfigParser(strict=False): same-named sections MERGE, the last value
of an option wins, and a redefined [gcode_macro] replaces the original.
So an override in printer.cfg restates only what it changes, keeps every
shipped value it does not mention, and survives every flash. printer.cfg
is the one config nothing ever writes.
So run-append.sh now overwrites ff-*.cfg unconditionally, logging a line
when it clobbered something so the log explains where an edit went, and
each of those files opens with a header saying it will be overwritten
and where to put changes instead.
printer.base.cfg needed no installer rule: FlashForge's own run.sh:135
does `cp $WORK_DIR/klipper/config/* /usr/data/config/ -rf` on every
flash, no comparison and no .mod-new, so it has always worked this way.
It gets the header anyway, because "why did my edit vanish" is the same
question there -- and the stock-drift comparison in bin/unpack.sh strips
comment lines, so the banner does not trip that warning.
moonraker.conf deliberately keeps the .mod-new dance. It is not a
Klipper config and has no include-and-override seam, so the argument
above does not apply to it, and a printer reached through a tuned
trusted_clients or cors_domains block would lose that access on an
update.
test_config_ownership.py holds the parts together: every mod-owned
config must carry the banner near the top, the banner must name
printer.cfg, moonraker.conf must NOT carry it, and run-append.sh must
still contain the overwrite branch the banner promises. A banner that
outlives the behaviour is worse than none -- the user trusts it, puts
nothing in printer.cfg, and their edit quietly wins.
Rebased onto
|
||
|
|
424ef059a7 |
Stop telling the operator to persist prepare=0
The feature print's header and docs/hardware-testing.md both said to set FF_BEFORE_PRINT_START.prepare to 0 and offered "persist it by editing variable_prepare" as the way to make it stick. Following that breaks every other print on this machine. The Orca profile here deliberately carries no START_PRINT: [ff_print] wraps SDCARD_PRINT_FILE and the implicit prepare is what homes and heats the bed. FlashForge's start block has no G28 and no M190 of its own, so with prepare persisted at 0 an ordinary file's first move -- G1 Z5 F2400 -- runs on unhomed axes, and nothing has heated the bed. The feature print is the one exception, because it carries its own START_PRINT with the TOOLS= list the automatic path cannot derive (nothing in a sliced file names the tool set without reading the slicer's config block). So it is a per-print toggle, and both files now say to put it back afterwards -- or just RESTART, which does it anyway. Comments and docs only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
30b21262ed |
Ship the two verification files, not the thing that wrote one
The generator was a build step for an artifact that is written once and then read by a person standing at a printer. Keeping it meant the file you send to the machine did not exist in the repo, and the header of the file you did get told you to re-run a script instead of telling you what to watch. gcode/ now holds the G-code itself and bin/gen-test-gcode.py is gone. Added alongside it: creator5-safe-moves.gcode, the feature print with the heat and the filament taken out. Once homed, nothing in it goes below Z50, nothing extrudes, no heater is ever given a target. It homes, laps the bed slow then fast, runs the diagonals a failing corexy motor turns into an L, changes tools, sweeps the fans and checks the chamber gate -- so that a dock that does not latch or a Z offset that is wrong is found 50 mm up rather than in the plate. It names its tools with SELECT_TOOL rather than a bare `Tn`, and carries no M104 or M140. ff_print scans a file's head for exactly those to decide what to prepare, so this one leaves it nothing to find and is safe whether FF_BEFORE_PRINT_START.prepare is 0 or 1. The feature print still wants it at 0. test_gcode.py reads the committed files now instead of running a generator, and covers both. The safe file gets the two checks that are its whole reason to exist -- never below Z50, and never a heater target or an E word -- because those are exactly the properties a later edit would break while leaving a file that still runs and still looks safe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
959fbd356a |
Generate the print that proves the macros, not just the machine
The on-hardware checklist ended at "a single-tool test print" and "a multi-tool test print" without saying which print. Any cube proves the motion system; none of them prove START_PRINT's preflight gate, the per-tool purge, the print Z offset, the M106 fan map or the M141 model gate, which are the parts of this firmware that have never run on a printer. bin/gen-test-gcode.py writes one file that drives all of them in the order a real print drives them, and lays down nested squares -- one ring per tool, a fixed gap apart -- so the tool offsets can be measured with calipers afterwards instead of eyeballed. It calibrates nothing: TOOL_OFFSET_CALIBRATE and STATION_CALIBRATE need the build plate off, which a print does not have, so the file only reads the geometry into the log. PAUSE is left out for the same kind of reason -- a resume that fails should not be buried inside the print the offsets are being measured from, so the header tells the operator to press Pause in Mainsail instead. Four things the first draft got wrong on the machine and not on paper: T<n> ships with restore_axis unset, so a change restores no position at all and every move after one has to re-state itself; descending to layer height before the cross-bed travel dragged the nozzle back over the work from the dock; START_PRINT's clean leaves each tool retracted by purge_retract and the start block primes only the tool it starts on, so a tool's first ring was the one recovering those 5 mm; and parked tools sat at full temperature oozing into their docks for the whole print. test_gcode.py is the gate that keeps the file honest: every command in it must be one the shipped configs or extras define, TOOLS= must list every tool the file uses, and every coordinate must be inside the configured axis limits. Renaming a macro now breaks the suite rather than the print. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
91604c2dfb |
Cut the suite to four gates, and rewrite the Python half as pytest
Twelve gates, most of them the weaker copy of a gate that survives. What is
left: pytest for the Klipper config, and three replica gates -- the end-to-end
install, MCU bring-up, and stock recovery.
Fix first, because the cut depends on it: a skip counted as a pass. Every
launcher exits 0 when it decides it cannot run, and sub() reported that as
`ok`. That is how test-abi.sh sat in the suite printing green while checking
nothing -- run-tests.sh deleted work/modpayload immediately before it ran and
CI set KLIPPER_FORK="", so it had no targets on any run, ever. Skips are now
counted apart, the summary says so, and the suite exits non-zero unless
ALLOW_SKIP=1 says the gap is deliberate. With four gates instead of twelve, a
gate that silently does not run is the failure mode that matters most.
Dropped, each because something else already does the job properly:
test-abi.sh bin/patch.sh refuses to build a non-nan2008
c_helper.so, bin/verify.sh re-checks the packaged
one, and case-install.sh reads the ELF header of
the file that actually landed on the machine.
test-ash-conformance.sh case-install.sh runs `sh -n` over every installed
script with the same qemu'd busybox and then greps
the boot log for "Syntax error".
test-model-gate.sh verify.sh SS8b/SS9 checks the same invariant and
pack.sh exits on MODEL MISMATCH before a package
exists. Its header claimed it proved the two models
ship different files; no such check was in it.
test-macros.py It used jinja2.Environment() -- default '{{ }}'.
The configs have no '{{' at all, only single-brace
expressions, so it checked '{% %}' block structure
and never looked inside an expression. Folded into
the chamber tests, which had the delimiters right.
test-base-cfg.py It read work/software/klipper/config/printer.base.cfg
as its stock reference -- the file bin/patch.sh
overwrites with ours. It was diffing our file
against itself: green on a cold tree, red on a
second run, blind to real drift either way. The
comparison moves to bin/unpack.sh, where a pristine
tree exists, as a warning rather than a gate.
lint-danger.sh Its structural greps were satisfiable by comments:
firmwareExe:44 and S80ui:16 *mention* helix and
SAFE-MODE, so they passed with the logic deleted.
case-ui.sh Goes with the helix-only decision.
applets.allow Ten entries, none of which the extractor had asked
about in a long time.
The command-word half of test-applets went too. It extracted only the first
word of a simple unprefixed command, so `if timeout 5 foo; then` yielded
['if','echo','fi'] -- the one failure its docstring named was invisible to it.
The absolute-path half survives: S50wifi's binaries sit behind a
[ -d /sys/class/net/wlan0 ] guard no simulation ever reaches, so nothing else
checks they exist.
The Python half is pytest now. Real assertions instead of hand-rolled
PASS/FAIL counters, underscored filenames so the modules import normally
(ffcfg keeps only the parser; the dash in test-macros.py was why one test used
to reach the other's parser through importlib), and a `rootfs` marker that
splits the tests needing the proprietary package from those that do not --
deselected in the PR lane, run in the replica half, so neither lane reports
skips it did not expect. test_default_delimiters_are_blind pins the jinja2 bug
so it cannot come back.
The config gate also moves out of the replica half into the half that runs on
a plain pull request. It needs python3, jinja2 and files in the repo, and it
is the only safety-relevant check in the suite -- a chamber heater declared on
a Creator 5 that has no element for it -- so it had no business being gated
behind the proprietary firmware.
Verified: pytest 21 passed / 5 rootfs-marked; the fixture pipeline
(unpack, patch, pack, verify) passes end to end; injecting a broken
single-brace expression fails test_every_macro_parses with the exact line, and
parses clean under the old defaults; the skip path reports SKIP and exits 1,
and ALLOW_SKIP=1 accepts it.
Also drops the CI `abi` job, which ran on a bare checkout with no binaries and
so was permanently green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
12bbd1e7dc |
Retire the probe profile: there is one package now
The probe profile was a bring-up aid. It set BUILD_APPLY=0, reinstalled the stock component byte-for-byte and wrote a report to the stick, so you could prove the update chain worked on your own machine -- the filename glob, the DES3 key, the md5 gate, run.sh execution -- before flashing anything that changed a file. That rehearsal has served its purpose. With it gone there was exactly one profile left, so the whole PROFILE indirection goes too: profiles/ is deleted and its six BUILD_* flags are plain defaults in bin/common.sh, which also means config.env now overrides them instead of being silently overridden. make default folds into make build; all-profiles and RECOVERY_PROFILE are gone with it. What is NOT gone is the undo button: flashing the stock FlashForge package still restores every file the mod touches, and case-recovery.sh still proves it. What is gone is the dry run. Note the loss: PROG_MB/DATA_MB used to come out of the probe report. The docs now point at df -h over ssh, which you only have after a successful flash. |
||
|
|
5bdf78109a |
Retire firmwareExe: own the web stack, and say what goes with it
The mod replaces FlashForge's application outright, so the reasoning that still framed the web stack as coexisting with it was wrong on its own terms. * nginx and moonraker are documented as the only web stack on the printer. MOD_WEB=0 is a debugging switch that leaves ssh and the touchscreen, not a way back to stock -- that is a stock FlashForge package. The own-prefix rule stays, for the reason that is actually true: /usr/prog is the firmware partition and every FlashForge package reinstalls it, so a config written there is undone by the next stock flash. * Configs the mod ships now update when you have not edited them. A live file still byte-identical to the one the last package wrote is replaced; one you changed is kept and the new version lands beside it as .mod-new. $MODDIR/config-installed holds that last-written copy. Without this a config we own could only ever be written once: it exists on the second flash, so it would land as .mod-new forever and updates would never reach the printer. * Dropped the three dead /server/files/ alias blocks from nginx.conf. The regex location above them matches /server/ too and nginx gives regex locations precedence, so they never fired -- and had they fired, serving those paths from disk would 405 the DELETE and POST that Mainsail sends to the very same URLs. * Recorded what dies with firmwareExe, in docs/notes/25 and in the README: the :8898 REST API (FlashPrint, Orca's FlashForge profile, the mobile app) and the :8080 camera stream. The camera is the one that looks restorable and is not -- the MJPEG server is compiled into firmwareExe (its own accept loop, `GET /?action=stream`, boundarydonotcross) rather than being mjpg-streamer, so there is no daemon left to point a [webcam] at. assets/moonraker.conf carries the block, commented, for when a streamer is built for this board. Also in this change: the MOD_UI switch and the stock-UI path are gone, the chamber-heater model gate arrives with ff-model.cfg/ff-chamber.cfg, and the tests follow. make test: 32 passed, 1 failed. The failure is the probe end-to-end, which dies at "mount: failed to setup loop device for /stick.img" -- leaked loop devices on this build host, not the package. Probe installs nothing, and the default profile's install -> re-install -> boot passes in the same run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a9299397f4 |
Build the toolchanger from this repo instead of a TOOLCHANGE= path
bin/patch.sh now copies the ff_*.py extras, the ff-*.cfg configs and the HelixScreen printer-database entry out of payload/, the way it already handled report.sh and the nginx/moonraker assets. Both call sites also lose a silent-skip guard. They tested for the files under $TOOLCHANGE and quietly shipped a package without the toolchanger if the path was wrong or unset -- worth having when the source was a checkout that might not be there, wrong now that the files are committed next to the script. A missing file here is a broken checkout, and cp should say so. CI and the test fixture had TOOLCHANGE="" set precisely to trigger that skip, so drop the line; both build the probe profile, which never reaches this step anyway. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GgNhSa1Qvwvxb2nQXT6b1Y |
||
|
|
22d16f984e |
Run the printer's own boot script for the update test, and cut the suite to 5m
The install test used to re-implement app_startup.sh: it pulled the glob,
MACHINE and PID out of it with sed, then called the installer itself. A
mistake in our reading of the boot script could never be caught that way.
The package now sits on a real FAT filesystem on a loop device exposed as
/dev/sda1, and app_startup.sh runs verbatim through three boots -- stick in,
stick still in, stick pulled. The last one asserts what actually matters:
nginx and moonraker up, klipper launched, the UI in the foreground where
app_startup.sh's own 5-second ps watchdog can see it.
One substitution, declared in .SIMULATED: the printer's kernel has nls_cp936
and a container kernel does not, so /bin/mount retries without codepage= if
the real options are rejected.
Speed. The image carried the partitions but not the stock install, and
test-env.sh sourced the config files after the environment -- so PRINTER_IMAGE
on the command line was overwritten by the empty value in test.env and the
published image was never used. Measured, per replica case: 22s to unpack the
factory image, 37s to install the stock package, 0.7s with the image. The
stock install is now baked in at image build time (bake.sh -- it needs binfmt
and chroot, so it runs privileged and the result is committed) and recorded in
/usr/prog/.BASELINE; a run asking for a different package reinstalls.
make test: ~20m -> 5m26s. Every section header now carries elapsed seconds.
Dropped four checks that could not fail:
* test-abi's execution half was `qemu "$f" -h || [ $? -lt 126 ]`, which
accepts nearly every exit status
* test-ash's md5sum -s probe printed "inconclusive" on every path
* run-tests.sh's per-file `sh -n` "POSIX" loop ran bash, and its bashism
grep flagged `local`, which busybox ash supports
* CI's shellcheck ended in `|| true`
And one that mattered more: the release workflow's final sim-install loop
ignored every exit status, and sim-install.sh exits 0 on a skip -- so the last
gate before publishing could not fail. It now stops on the first failure and
runs with REQUIRE_PRINTER_SIM=1, which turns skips into failures throughout.
Also: the profile lists in run-tests.sh, RECOVERY_PROFILE and release.yml
still said ssh/web/full/helix and would have failed the release job; work/.sim
was shared, so two suites at once truncated each other's packages; and the
fixture half left a 380KB package in work/out where make test-install picks it
up.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgNhSa1Qvwvxb2nQXT6b1Y
|
||
|
|
a5300bde12 |
Split the repo into ship / build / test lanes, and add the printer replica
Two strands, both landing here because they had grown into each other in the
working tree.
The printer replica: the printer's real rootfs.squashfs chrooted under
qemu-mipsel, with /usr/prog and /usr/data from the factory image, so a package
is installed by FlashForge's own updater running on the printer's own busybox,
tar, md5sum and unTar. There are no stubs -- if the real prog partition is
missing the harness refuses to run rather than test against mocks. A release
workflow is blocked on it.
The lanes: a file's directory now says whether it can ever reach a printer.
payload/, assets/ ship
bin/, profiles/ build the package
test/, docs/ neither, ever
* the replica's own infrastructure moved out of bin/ and docker/ and in
beside the harness it belongs to (test/extract-rootfs.sh,
test/build-printer-image.sh, test/printer/Dockerfile{,.full})
* the Makefile has two runners: only RUNSIM gets the docker socket and the
replica's variables, so a build cannot reach the docker daemon at all
* replica settings moved to test.env, which nothing in a package reads
* build-time flags renamed BUILD_* (APPLY, REPORT, KLIPPER, TOOLCHANGE,
MAINSAIL, HELIX). MOD_* now means only the runtime switches written into
anvil.conf and re-read by the printer at every boot
* verify.sh fails if any file in a package is byte-identical to one in
bin/, test/, docker/ or profiles/ -- by content, because a name blacklist
would trip over whatever Mainsail happens to call common.sh
Fixed while mapping it:
* BUILD_REPORT was read only inside the BUILD_APPLY=0 branch, so four
profiles declared it and got nothing. It is honoured on both paths now,
and off outside probe: report.sh copies /etc, passwd and shadow onto the
USB stick, which is a diagnostic, not something to hand out.
* three checks in the brick lint had been quietly dead. Two were guarded on
a payload/boot.sh replaced by the firmwareExe wrapper long ago; the third
was the file glob itself, which matched neither firmwareExe nor the
init.d/S* scripts -- the UI wrapper and every service went unscanned. A
missing file is now a failure rather than a silent pass.
* run-tests.sh overwrote config.env and copied it back, leaving the config
you edited one crashed run away from being a fixture. It uses CONFIG_ENV.
* bin/make-uninstall.sh does not exist and never did; README, run-pre.sh and
full.env all pointed at it. Recovery is the stock package, which is what
test-recovery actually proves.
README is now for someone who wants to flash their printer: what it is, which
file, how to flash, what to do when it goes wrong. The rest moved to
docs/how-it-works.md, docs/building.md and docs/testing.md.
make test: 89 passed, 0 failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
855bb52573 |
Build a separate package per model; add the model-gate test
The Creator5Pro stock package turned up, and comparing it with the non-Pro one settled the question: they are different files carrying DIFFERENT firmwareExe binaries (f666ff60... vs bf505905...), each with its own MACHINE=/PID= gate. That exposed a real bug: pack.sh emitted BOTH filenames from a single build, copying one file to the other model's name. It was modelled on FlashForge's factory package, where doing so is legitimate because that package is an installer-only wrapper with no model-specific binaries. For a full firmware package it would hand one model the other's firmware. pack.sh now names the output from the gate inside the package it was built from, and refuses if that disagrees with TARGET_MACHINE. config.env takes one stock package per model (STOCK_TGZ_CREATOR5 / STOCK_TGZ_CREATOR5PRO) and MODEL=/TARGET_MACHINE selects between them. `make release` builds both into dist/. test/test-model-gate.sh locks it in: filename prefix matches the gate, the PID matches the model, and the two packages do not share a firmwareExe. Also fixed: the CI fixture now carries a real model gate (and is generated into work/ rather than /tmp, because sibling-container mounts are resolved by the host daemon, so a build-container /tmp path mounts empty). 77 checks pass. Both models build and verify from the real firmware. |
||
|
|
3a70b678f4 |
Recovery is the stock package; add the model gate check
Drop bin/make-uninstall.sh. Diffing its output against FlashForge's own
software component showed it differed in exactly two files -- run.sh (22
appended lines) and the md5sum.list that changed because of it. Everything
doing the actual reverting was FlashForge's. So recovery is simply "flash the
stock package you already have"; the leftover /usr/data/mod payload is inert
once firmwareExe is the genuine binary again.
test/sim-roundtrip.sh now proves that path instead: install the mod, flash the
stock package, assert firmwareExe matches the stock binary byte-for-byte, the
boot scripts are restored, and no stock script references the payload.
That test immediately surfaced a real problem. Packages carry a model gate --
runFirmwareExe.sh has MACHINE=/PID= baked in and refuses to install when they
do not match what app_startup.sh passes from the installed firmware:
Creator5 / 0028
Creator5Pro / 0029
The stock package in use here is the non-Pro build (MACHINE=Creator5), while
the target printer reports "Creator 5 Pro" in general.json. Anything built
from it would be refused by the printer, and it is not a valid recovery image
for a Pro either.
unpack.sh now reports the gate, verify.sh fails on a mismatch against
TARGET_MACHINE in config.env, and the ladder doc leads with checking it.
|
||
|
|
ff7204f7d5 |
Run everything in Docker; test against the printer's real busybox
The stock kernel-*.tar.xz turned out to contain rootfs.squashfs -- the printer's genuine root filesystem. Unpacking it answered several questions that were previously guesswork: - init is busybox init -> /etc/init.d/rcS -> S99factory_test_shell -> /usr/prog/app_startup.sh. There is no systemd, and rcS runs from the read-only squashfs before /usr/prog/etc is bind-mounted over /etc, so a new S-script cannot be injected into the stock boot sequence. - /usr/sbin/dropbear and an ENABLED /etc/init.d/S50dropbear already ship. Port 22 is open on a stock printer; only the root password is missing. The dropbear cross-build is therefore deleted -- it was never needed. New: test/test-ash-conformance.sh parses every on-printer script with the printer's own busybox 1.31.1 ash under qemu-mipsel. A parse error in the firmwareExe wrapper would mean a blank screen, and neither bash -n nor dash -n proves busybox accepts it. Makefile now runs every target inside the pinned build image, with the docker socket mounted so test targets can start sibling containers. The repo is mounted at its real host path rather than /src, because sibling mounts are resolved by the host daemon. 78 checks pass. |
||
|
|
4415905fb3 |
Initial commit: USB-installable custom firmware builder for Creator 5
Builds a modified FlashForge update package that the printer installs by itself from a USB stick, reusing FlashForge's own updater rather than a parallel one. Integration is a single file: /usr/prog/PROGRAM/software/firmwareExe is replaced with a shell script. Everything funnels through that binary, so app_startup.sh, rcS and the init chain stay completely stock. The genuine binary is kept as firmwareExe.stock and remains the default UI. Key findings from the stock firmware, each enforced by a test: - components named *.tar.xz are PLAIN tar; the installer runs bare `tar -xvf` - the .tgz is DES3-encrypted plain tar, not gzip - nothing in init starts Klipper -- firmwareExe runs start.sh, so replacing the UI without S70klipper leaves a printer that cannot move - the installer wipes the software dir before run.sh, so an uninstall package must ship the genuine firmwareExe rather than back one up - ssh already works: stock ships dropbear + an enabled S50dropbear, only the root password is missing Brick-safety is designed in, not bolted on: a five-rung profile ladder starting with a probe that changes nothing, a container-based simulation that actually runs the installer and asserts the printer would still boot, an uninstall round-trip test, and a SAFE-MODE latch for UI crash loops. 75 checks pass. Not yet run on hardware. |