Contributing
git clone https://github.com/masonschafercodes/zpackcd zpackzig build testYou need Zig 0.16.0 and nothing else. zpack has no dependencies.
The commands
Section titled “The commands”| Command | Does |
|---|---|
zig build |
Debug build, into zig-out/bin/ |
zig build test |
Every test, plus builds every example |
zig build examples |
Build the four examples without running them |
zig build example-01 |
Build and run one example |
zig build run -- list game.zpak |
Run the CLI without installing |
zig fmt build.zig src tests examples |
Format. CI checks this with --check |
zig build -Dtarget=aarch64-macos |
Cross-compile; no extra toolchain needed |
zig build test depends on the examples step, so a stale example fails the test
run rather than rotting quietly.
Source layout
Section titled “Source layout”| File | Holds |
|---|---|
src/root.zig |
The module’s public surface, buffer size constants, Stats |
src/format.zig |
The wire format: constants, serialization, assetId, validatePath |
src/Archive.zig |
Opening, validating, looking up, reading, verifying, extracting |
src/pack.zig |
Scanning a tree and streaming it into an archive |
src/EntryReader.zig |
One entry’s original bytes, decompressing |
src/StoredReader.zig |
One entry’s stored bytes, bounded to the entry |
src/ignore.zig |
.zpackignore parsing and glob matching |
src/main.zig |
Argument handling and output formatting. Nothing else |
The split between main.zig and the rest is load-bearing: everything the CLI
does, the library does, so a change to behaviour belongs in the library and a
change to presentation belongs in main.zig.
Each file under tests/ is its own test binary, listed in build.zig:
| File | Covers |
|---|---|
round_trip.zig |
Pack then extract, byte for byte |
reading.zig |
find, read, readAlloc, entryReader |
compression.zig |
The store-versus-deflate decision |
integrity.zig |
Hash checking |
corruption.zig |
Damaged archives failing cleanly |
paths.zig |
Path validation and normalization |
asset_ids.zig |
assetId stability and collision detection |
manifest.zig |
Manifest output |
output.zig |
CLI formatting |
ignore.zig |
.zpackignore patterns |
support.zig |
Shared helpers - not a test file itself |
Adding a test file means adding it to the test_files array in
build.zig.
Tests inside src/ run through the mod_tests step, wired up by the test
block at the bottom of root.zig.
Conventions
Section titled “Conventions”Comments say why, not what. The code says what it does. A comment earns its place by explaining a decision, a constraint, or a trap - the colon-in-path rule, the saturating multiply, the rewind that costs a second read.
Doc comments on public declarations, including the non-obvious constraints: which buffer sizes are required, who owns returned memory, what is not checked.
Errors are named for their cause, not their symptom. AssetIdCollision
rather than BadArchive. Every one is listed in
the error reference; adding a new one means adding a row there.
Nothing is written to disk as a struct. The format is serialized field by field so struct layout can never change the format by accident.
Adding a feature
Section titled “Adding a feature”Some things to keep in mind:
Does it change the archive format? If a v1 reader can no longer read what you produce, the format version has to move. See compatibility.
Does it allocate per file? The design holds that packing a 40 GB tree costs the same working set as a 40 MB one. A per-file allocation breaks that.
Does it read attacker-controlled sizes? Bound them against something real - usually the file size - before allocating or looping. See the security model.
Does it break reproducibility? Anything that lets walk order, timestamps, or environment reach the output does. See reproducible archives.
Documentation
Section titled “Documentation”This site is an Astro Starlight project in docs/:
cd docsbun installbun run dev # http://localhost:4321bun run build # also validates every internal linkThe site uses Bun. bun.lock is committed, so
bun install --frozen-lockfile is what CI runs.
CI builds it on every PR, and starlight-links-validator fails the build on a
link to a page that was renamed or never written - so a broken link is a failed
check rather than a 404 someone finds later.
Pages live in docs/src/content/docs/; the sidebar is in
docs/astro.config.mjs. Adding a page means adding it to both.
Reporting
Section titled “Reporting”Bugs and features: issues.
Security: a security advisory rather than a public issue. A crash, a read or write outside the intended bounds, or an allocation disproportionate to the input all count.