feat(binary): check zuur/binary package.lua version against constraints

This commit is contained in:
2026-08-08 17:38:21 -04:00
parent c876821cda
commit b367f21d29
3 changed files with 469 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
Warning
Warning ## Warning for package tofu ##
Warning
Warning The following compiler flags have been specified in the package description
Warning file. They are handled by DUB and direct use in packages is discouraged.
Warning Alternatively, you can set the DFLAGS environment variable to pass custom flags
Warning to the compiler, or use one of the suggestions below:
Warning
Warning warningsAsErrors: Use "buildRequirements" to control the warning level
Warning
Starting Performing "debug" build using /usr/bin/dmd for x86_64.
Up-to-date toml 1.0.0: target for configuration [library] is up to date.
Building tofu ~main: building configuration [application]
Linking tofu
Finished To force a rebuild of up-to-date targets, run again with --force
Warning: malformed TOML config at /tmp/tofu-test-config-bad-190867.toml: Invalid table key declaration (2:0)
Warning: invalid TOFU_DEFAULT_JOBS 'not-a-number', using default 1
7 modules passed unittests
 . checking binary firefox ...
- binary firefox-2.1.0 satisfies firefox>=2.0
 . checking binary libfoo ...
- binary libfoo-1.9 does not satisfy libfoo>=2.0
 . checking binary noexist ...
 . checking binary brokenpkg ...
 . checking binary badpkg ...
 . checking binary testpkg ...
- binary testpkg-2.1.0 satisfies testpkg
+52
View File
@@ -183,6 +183,58 @@ _Auto-scaffolded by /start-work. Append new entries below - never overwrite._
---
## Task 10 — `tofu.binary` (zuur/binary package.lua version checking)
### Architecture
- Module `tofu.binary` depends on: `tofu.types` (BinaryCheckResult, DepConstraint), `tofu.http` (get, HttpException), `tofu.config` (Config, binaryManifestUrl), `tofu.vercmp` (satisfies), `tofu.log` (logDetail, logInfo).
- `checkBinaryVersion(name, constraint, cfg)` fetches the package.lua manifest from zuur/binary via HTTP, parses the version, and checks satisfaction.
### Key logic — 404 vs other errors
- HTTP 404 → binary doesn't exist (recipe-only). Return `BinaryCheckResult(false, "", false)`.
- Any other HttpException (500, timeout, connection failure) → **rethrow** — network problems must propagate, not be silently swallowed.
- Detection: `e.msg.canFind("404")` — the HttpException format is `"HTTP %d fetching <url>"`.
### Version extraction from Lua package.lua
- Light parse: scan for `version = "..."` in the manifest body.
- Word-boundary check on "version" keyword (preceding char must be whitespace/`{`/`,`/`;`, following char must be whitespace/`=`).
- If version field not found or malformed → `BinaryException`.
- This avoids a full Lua parser — package.lua manifests are flat key-value tables.
- Empty version string (e.g. `version = ""`) also throws BinaryException.
### Memoization
- Module-level `BinaryCheckResult[string] _versionCache` — keyed by package name.
- On cache hit: recompute satisfaction against caller's constraint (same version, different constraint possible).
- On 404: cache the "not found" result so subsequent calls don't re-fetch.
- Lifetime: one command run. Tests use distinct package names to avoid cross-state contamination.
- The cache stores `exists` and `ver`; `satisfies` is always recomputed per-constraint.
### `version` keyword trap (again)
- Local variable `version` conflicts with D keyword. Used `ver` instead. Same pattern as types.d where struct field is `ver`.
### Test infrastructure
- Reused local one-shot TCP server pattern from http.d:
- `bindAndSpawn(response)` — bind ephemeral port, spawn thread, return URL.
- `oneShotResponder(listener, response)` — accept one connection, send canned HTTP response, exit.
- `httpResponse(code, reason, body)` — build minimal HTTP/1.1 response with Content-Length.
- `testConfig(baseUrl)` — create a Config pointing zuurUrl at the test server.
### Test cases (6/6 pass)
1. Binary exists, version 2.1.0, constraint ge 2.0 → satisfies:true ✓
2. Binary exists, version 1.9, constraint ge 2.0 → satisfies:false ✓
3. 404 → exists:false, ver:"", satisfies:false ✓
4. 500 → HttpException rethrown (not swallowed) ✓
5. Manifest without version field → BinaryException ✓
6. Version 2.1.0 with unconstrained dep (op none) → satisfies:true ✓
### `@safe` annotation consistency
- `checkBinaryVersion` and all helpers marked `@safe`. The function calls `http.get()` (which is `@safe`), `satisfies` (`@safe`), and log functions (`@safe`). No `@trusted` blocks needed in production code.
- Test helpers (`oneShotResponder`, `bindAndSpawn`, `httpResponse`) are `@trusted` since they use `std.socket`.
### Note: pre-existing fetch.d compile issue
- `fetch.d` (from parallel task 7) has a missing `std.algorithm.searching : canFind` import that blocks `dub test`. `dub build` passes (fetch.d is excluded via `.skip` extension renames by the parallel agent). Binary.d tests verified via standalone `dmd -I... -i -main -unittest` compilation — all 7 modules pass.
---
## Task 7 — `tofu.index` (ZUUR index fetch & sandboxed Lua parse)
### Security: whitelist sandbox (NOT blacklist)