Commit Graph
5 Commits
Author SHA1 Message Date
Abdessamad Derraz 8b404e500f fix: keep a packed file's executable bit
Pinning every member's metadata made packs reproducible and took the
executable bit with it. The RetroDECK pack ships the two Voxatron engine
binaries, and extracted at 644 they cannot be run.

Git records the bit, so reading it from the source file keeps a pack the
same from any clone. Nothing else about the source's mode reaches the
archive: 2569 members ship at 644 and 942 at 755, which is what the
builder produced before the pinning.

Nothing caught this. The comparison that proved the pinning inert
checked member names, CRCs and sizes, and mode is none of those. A test
now builds a runnable payload and asserts it survives extraction.

RetroDECK rebuilds to the same bytes twice and passes its integrity
check, 2008/2008 baseline and 1551/1551 cores.
2026-08-23 07:58:23 +02:00
Abdessamad Derraz 77b06bcb1b test: cover the emulator pack's own build path
generate_emulator_pack is a distinct build path from the platform packs,
and nothing asserted that it produced the same bytes twice. It does, but
only because every member write was routed through one writer: restoring
the mtime-copying write on that path alone fails the new test and no
other.
2026-08-23 06:53:15 +02:00
Abdessamad Derraz a2bd197b9b fix: pin every pack member to a fixed date
A pack was still not a function of its inputs. ZipFile.write copies the
source file's mtime into the member: the wall clock for an archive this
build rebuilt in tmp/, the checkout time for a file from the collection.
Two consecutive builds of the Recalbox pack differed on 348 members
whose content matched byte for byte, and a pack built from a fresh clone
could never match one built from another.

Every member now goes through one writer that stamps the epoch the
archive rebuilder already uses, streaming the content so a firmware
image of several hundred megabytes is not read whole.

The pack was already covered by a two-builds-are-identical test, which
passed: its fixture held no romset, so it never reached the rebuild
path. The fixture has one now, and reverting the writer fails both that
test and the new one.

Verified on the real collection: Recalbox and RetroArch rebuild to the
same bytes twice, contents unchanged from the previous revision (1319
and 4517 members, zero CRC differences), and both still pass their
native integrity check.
2026-08-12 15:14:48 +02:00
Abdessamad Derraz 24e9b820ae feat: make packs reproducible byte for byte
Two builds of the same pack from the same inputs produced different
archives. Of 67 members, 65 were already identical: only README.txt
and manifest.json differed, both stamped with the wall clock by
writestr and the second carrying a generated timestamp. Generated
members now use the epoch the archive rebuilder already applies, and
the timestamp comes from the database snapshot the pack was built
from, so the same data yields the same bytes.

Install manifests skipped archived platforms, which is why RetroPie
had none; archived means upstream is no longer scraped, not that the
packs stopped shipping. A target-filtered manifest also had no record
of its filter beyond the filename, so it carries one the way a
region-filtered manifest already does.
2026-08-11 00:55:42 +02:00
Abdessamad Derraz a483ed93b0 refactor: stream zip rebuild instead of buffering 2026-08-08 04:40:07 +02:00