mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-19 03:49:50 -05:00
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>
80 lines
3.7 KiB
Bash
80 lines
3.7 KiB
Bash
#!/usr/bin/env bash
|
|
# 1/3 -- decrypt the stock package and open the software component.
|
|
#
|
|
# ./unpack.sh uses $STOCK_TGZ from config.env
|
|
# ./unpack.sh <file> unpack some other package
|
|
#
|
|
# Result:
|
|
# work/outer/ the 8 top-level files (runFirmwareExe.sh, *.tar.xz, imgs)
|
|
# work/software/ the extracted software-<ver> tree -- EDIT THIS
|
|
set -euo pipefail
|
|
. "$(dirname "$0")/common.sh"
|
|
|
|
SRC="${1:-$STOCK_TGZ}"
|
|
[ -f "$SRC" ] || { echo "no such package: $SRC" >&2; exit 1; }
|
|
|
|
rm -rf work/outer work/software
|
|
mkdir -p work/outer work/software
|
|
|
|
echo ">> decrypting $(basename "$SRC")"
|
|
# The printer's /usr/prog/bin/unTar runs exactly this, minus -md md5 (its
|
|
# OpenSSL 1.0.2 defaulted to MD5 key derivation; modern OpenSSL needs it said).
|
|
openssl des3 -d -k "$FF_KEY" -salt -md md5 -in "$SRC" 2>/dev/null \
|
|
| tar -xf - -C work/outer
|
|
|
|
echo ">> outer contents:"
|
|
ls -la work/outer | sed 's/^/ /'
|
|
|
|
SW_TARBALL=$(ls -1 work/outer/software-*.tar.xz 2>/dev/null | head -n1 || true)
|
|
[ -n "$SW_TARBALL" ] || { echo "no software-*.tar.xz in package" >&2; exit 1; }
|
|
|
|
STOCK_SW_VER=$(basename "$SW_TARBALL" | sed 's/^software-//; s/\.tar\.xz$//')
|
|
echo ">> extracting software component $STOCK_SW_VER"
|
|
tar -xf "$SW_TARBALL" -C work/software
|
|
|
|
# Every package carries a model gate. runFirmwareExe.sh compares the MACHINE
|
|
# and PID it was built for against the ones app_startup.sh passes in (which
|
|
# come from the firmware already on the printer) and REFUSES to install on a
|
|
# mismatch. Record it so verify.sh can check it against your printer.
|
|
PKG_MACHINE=$(sed -n 's/^MACHINE=//p' work/outer/runFirmwareExe.sh | head -n1)
|
|
PKG_PID=$(sed -n 's/^PID=//p' work/outer/runFirmwareExe.sh | head -n1)
|
|
echo "${PKG_MACHINE:-unknown}" > work/.pkg_machine
|
|
echo "${PKG_PID:-unknown}" > work/.pkg_pid
|
|
echo ">> package installs on: ${PKG_MACHINE:-unknown} (PID ${PKG_PID:-unknown})"
|
|
|
|
echo "$STOCK_SW_VER" > work/.stock_sw_ver
|
|
echo "$SRC" > work/.source_pkg
|
|
|
|
# ---- has FlashForge changed printer.base.cfg under us? ---------------------
|
|
# Our payload/klipper/config/printer.base.cfg is FlashForge's file with the
|
|
# chamber block replaced by an include, so a change on their side means a pin
|
|
# map, stepper current or endstop we are shipping stale.
|
|
#
|
|
# This lives HERE, and not in a test, because here is the only moment a
|
|
# pristine stock tree exists: bin/patch.sh copies our file straight over
|
|
# work/software/klipper/config/printer.base.cfg a few steps later. test-base-cfg.py
|
|
# read it afterwards and so spent its life diffing our file against itself --
|
|
# green on a cold tree, red on a second run, and blind to the drift either way.
|
|
#
|
|
# A warning, not a gate: their file changing is news, not a broken build, and
|
|
# it can only happen when you point the build at a new stock package.
|
|
STOCK_BASE=work/software/klipper/config/printer.base.cfg
|
|
OURS=payload/klipper/config/printer.base.cfg
|
|
if [ -f "$STOCK_BASE" ] && [ -f "$OURS" ]; then
|
|
# Compare only the section/option lines: comments and blank lines differ by
|
|
# design, and our chamber include replaces their heater block.
|
|
strip() { grep -vE '^\s*(#|$)' "$1" | grep -vE '^\[(heater_generic|verify_heater) chamber_heater\]|^\[include printer\.chamber\.cfg\]'; }
|
|
if strip "$STOCK_BASE" | diff -q - <(strip "$OURS") >/dev/null 2>&1; then
|
|
echo ">> printer.base.cfg matches the stock file"
|
|
else
|
|
echo " !! printer.base.cfg DIFFERS from this package's stock file." >&2
|
|
echo " FlashForge may have changed pins/currents/limits. Review:" >&2
|
|
echo " diff $STOCK_BASE $OURS" >&2
|
|
fi
|
|
fi
|
|
|
|
echo
|
|
echo "Unpacked. Edit work/software/ then run ./pack.sh"
|
|
echo " stock software version: $STOCK_SW_VER"
|
|
echo " files: $(find work/software -type f | wc -l)"
|