mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-23 05:42:17 -05:00
The replica gates -- the only ones that decide whether a package bricks a
printer -- had never run in CI. They were gated on a STOCK_URL_CREATOR5PRO
secret that was never set, so printer-sim rendered as a grey dash beside
two green ticks and read as "CI passed".
The packages it needs are already public. ghzserg/FF publishes both stock
packages and the factory image, and this repo already points at that
release in docs/printer-replica.md. All three are now pinned in ci.yml by
sha256 and verified byte-for-byte against the copies used locally:
Creator5Pro-1.9.7-1.2.9-20260810.tgz 1f3252fe... 93MB
Creator5-1.9.7-1.2.9-20260810.tgz 72009be1... 93MB
Creator5Pro-factory.tar.xz 45657924... 190MB
Nothing proprietary is redistributed from here; the workflow fetches what
is published elsewhere, cached on their checksums so an unchanged pin
costs that archive nothing. Secrets still override for a fork with its own
firmware -- but a URL override without a matching checksum now refuses to
run rather than downloading something nobody verified. have-firmware is
gone with the `if:` it fed.
Rehearsing that job locally found the reason it would have failed anyway.
The replica gates call ./bin/patch.sh directly, and patch.sh expects
vendor/ already populated -- bin/build.sh is what fetches Mainsail and
HelixScreen, and the gates do not go through build.sh. No local run ever
noticed, because vendor/ was always already there. Both workflows now
fetch assets before the suite; in release.yml that step has to precede the
gates, since `make release` would otherwise fetch them one step too late.
Second half: a skip must be legible.
ALLOW_SKIP=1 silenced the explanation as well as the exit code -- the
summary printed "2 skipped" and then nothing about what or why. That is
the disease this harness exists to treat, reintroduced by the flag meant
to manage it. Now:
- Every skip is listed again in the summary with its reason and whether
it was accepted, whatever ALLOW_SKIP says, and becomes a GitHub
annotation. Nobody reads a green job's log.
- ALLOW_SKIP takes names: ALLOW_SKIP="pytest,the printer replica". `1`
still accepts everything, but it is a standing promise never to notice
a skip again, so CI uses the list and a third skip fails there.
- The pytest gate names the tests that skipped instead of counting them.
That immediately showed the exec-bit test skipping wherever .git is
absent -- a bare "6 skipped" would not have.
VERIFIED:
- printer-sim rehearsed end to end on the actual published bytes, real
git checkout, REQUIRE_PRINTER_SIM=1 and no ALLOW_SKIP: 12 passed,
0 failed, 0 skipped, exit 0, in about four minutes. Before the vendor
step it failed at patch.sh, which is what CI would have done.
- The four ALLOW_SKIP cases, in the build image with no firmware: none
set -> exit 1 naming both gaps; "pytest" only -> exit 1, replica still
NOT ACCEPTED; both named -> exit 0, both still printed; and under
GITHUB_ACTIONS the ::warning:: annotations appear.
- pytest 30 passed. Both workflows parse.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
159 lines
6.4 KiB
YAML
159 lines
6.4 KiB
YAML
name: release
|
|
|
|
# A release is blocked on the printer replica passing.
|
|
#
|
|
# The replica is the printer's real rootfs.squashfs, chrooted under
|
|
# qemu-mipsel, with /usr/prog installed by FlashForge's own updater. The
|
|
# installer under test runs on the printer's busybox, tar, md5sum and unTar --
|
|
# not on Debian stand-ins. If that fails, nothing ships.
|
|
#
|
|
# The stock FlashForge packages are proprietary and ~93MB each, so they are
|
|
# never in the repo. Point these repository secrets at them (pre-signed URLs,
|
|
# a private release asset, an S3 object -- anything curl can fetch):
|
|
#
|
|
# STOCK_URL_CREATOR5PRO required
|
|
# STOCK_URL_CREATOR5 required
|
|
# FACTORY_URL required -- the factory image, which is the
|
|
# replica's real /usr/prog and /usr/data
|
|
# STOCK_SHA256_CREATOR5PRO optional but recommended
|
|
# STOCK_SHA256_CREATOR5 optional but recommended
|
|
#
|
|
# Without them the job FAILS. It does not skip: a release that skipped the
|
|
# only test that can catch a brick is worse than no release.
|
|
|
|
on:
|
|
push:
|
|
tags: [ 'v*' ]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Check the stock packages are configured
|
|
env:
|
|
PRO: ${{ secrets.STOCK_URL_CREATOR5PRO }}
|
|
STD: ${{ secrets.STOCK_URL_CREATOR5 }}
|
|
FACTORY: ${{ secrets.FACTORY_URL }}
|
|
run: |
|
|
missing=
|
|
[ -n "$PRO" ] || missing="$missing STOCK_URL_CREATOR5PRO"
|
|
[ -n "$STD" ] || missing="$missing STOCK_URL_CREATOR5"
|
|
[ -n "$FACTORY" ] || missing="$missing FACTORY_URL"
|
|
if [ -n "$missing" ]; then
|
|
echo "::error::missing secrets:$missing"
|
|
echo "The printer replica cannot run without the stock packages, and"
|
|
echo "a release is not allowed to skip it. See the comment at the top"
|
|
echo "of .github/workflows/release.yml."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Fetch the stock firmware
|
|
env:
|
|
PRO: ${{ secrets.STOCK_URL_CREATOR5PRO }}
|
|
STD: ${{ secrets.STOCK_URL_CREATOR5 }}
|
|
PRO_SHA: ${{ secrets.STOCK_SHA256_CREATOR5PRO }}
|
|
STD_SHA: ${{ secrets.STOCK_SHA256_CREATOR5 }}
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p stock
|
|
curl -fsSL "$PRO" -o stock/Creator5Pro-stock.tgz
|
|
curl -fsSL "$STD" -o stock/Creator5-stock.tgz
|
|
[ -z "$PRO_SHA" ] || echo "$PRO_SHA stock/Creator5Pro-stock.tgz" | sha256sum -c -
|
|
[ -z "$STD_SHA" ] || echo "$STD_SHA stock/Creator5-stock.tgz" | sha256sum -c -
|
|
ls -lh stock
|
|
|
|
- name: Write test.env (the replica's /usr/prog)
|
|
env:
|
|
FACTORY: ${{ secrets.FACTORY_URL }}
|
|
# Optional repository variable: a prebuilt replica image, e.g.
|
|
# monstrofil/creator5-printer:latest. It carries the factory
|
|
# partitions AND the stock package already installed, which is about
|
|
# a minute of setup saved on every replica case. The end-to-end test
|
|
# reinstalls the baseline itself when the package under test is for
|
|
# the other model, so this only ever changes how long it takes.
|
|
PRINTER_IMAGE: ${{ vars.PRINTER_IMAGE }}
|
|
run: |
|
|
set -euo pipefail
|
|
curl -fsSL "$FACTORY" -o stock/factory.tar.xz
|
|
echo "PROG_DUMP=\"$PWD/stock/factory.tar.xz\"" > test.env
|
|
[ -z "${PRINTER_IMAGE:-}" ] || echo "PRINTER_IMAGE=\"$PRINTER_IMAGE\"" >> test.env
|
|
cat test.env
|
|
|
|
- name: Write config.env
|
|
run: |
|
|
cat > config.env <<CFG
|
|
MOD_NAME=anvil
|
|
# Tags are release dates: v20260823. A manual run has no tag -- and
|
|
# GITHUB_REF_NAME would be the branch there -- so leave it empty and
|
|
# let the build stamp today's UTC date itself.
|
|
MOD_VER=$(case "$GITHUB_REF" in refs/tags/*) echo "${GITHUB_REF_NAME#v}";; *) echo "";; esac)
|
|
SW_VER=""
|
|
TARGET_MACHINE=Creator5Pro
|
|
STOCK_TGZ_CREATOR5PRO="$PWD/stock/Creator5Pro-stock.tgz"
|
|
STOCK_TGZ_CREATOR5="$PWD/stock/Creator5-stock.tgz"
|
|
STOCK_TGZ=""
|
|
KLIPPER_FORK=""
|
|
ROOT_PW_HASH='${{ secrets.ROOT_PW_HASH }}'
|
|
FF_KEY='FFP0331&*%root'
|
|
CFG
|
|
|
|
# Before the suite, not after: the replica gates run ./bin/patch.sh
|
|
# directly and it expects vendor/ already populated. `make release`
|
|
# further down would have fetched them, but that runs after the gates.
|
|
- name: Fetch the pinned Mainsail and HelixScreen assets
|
|
run: make vendor ASSET_ROOT=$PWD
|
|
|
|
- name: Extract the printer rootfs
|
|
run: make rootfs ASSET_ROOT=$PWD
|
|
|
|
- name: Brick-safety suite, replica required
|
|
run: |
|
|
docker build -q -t creator5-fw-build -f docker/Dockerfile.build docker
|
|
docker run --rm \
|
|
-v "$PWD":"$PWD" -w "$PWD" \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
-e REQUIRE_PRINTER_SIM=1 \
|
|
creator5-fw-build ./test/run-tests.py
|
|
|
|
- name: Build both models
|
|
run: make release ASSET_ROOT=$PWD
|
|
|
|
- name: End-to-end update test on the packages that will actually ship
|
|
# The suite above tested a package built from the same tree; this
|
|
# tests the exact files that get attached to the release.
|
|
env:
|
|
REQUIRE_PRINTER_SIM: 1 # a skip here must fail the release
|
|
run: |
|
|
for p in dist/*.tgz; do
|
|
echo "=== $p ==="
|
|
./test/integration/sim-install.py "$p" || exit 1
|
|
done
|
|
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: firmware-packages
|
|
path: dist/*.tgz
|
|
retention-days: 30
|
|
|
|
- name: Publish
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: dist/*.tgz
|
|
body: |
|
|
Copy the file matching YOUR printer to the root of a FAT32 USB
|
|
stick and power the machine on. A `Creator5Pro-` package will not
|
|
install on a Creator 5 and vice versa.
|
|
|
|
Every package here was installed into a replica of the printer --
|
|
the real rootfs running under qemu-mipsel, with the package on a
|
|
real FAT filesystem that the machine's own app_startup.sh found
|
|
and installed -- and the machine was booted again afterwards and
|
|
checked to come up.
|