fix(nwsync): pin emitter version, ship the shim, harden manifest reads
ci / ci (pull_request) Successful in 3m28s

Review follow-ups on the same work:

- emitter_version is its own sidecar field, not the build revision. Keying
  the merge check on created_with invalidated every published index on every
  unrelated crucible commit, forcing a re-emit of the whole corpus.
- flake.nix builds cmd/crucible-nwsync, so Nix consumers get the binary the
  docs promise.
- readManifest uses io.ReadFull: bytes.Reader.Read can return a short count
  with no error, so a truncated manifest parsed into zero-padded garbage.
- emit refuses a .mod outright and names why, rather than failing on an
  unknown extension.
- SOURCE_DATE_EPOCH pins the sidecar timestamp; the manifest was already
  deterministic.
- Entry.identity replaces the resref/restype key struct duplicated in emit
  and assemble.

Refs #53.
This commit is contained in:
2026-07-29 12:17:08 +02:00
parent 0de1c1e280
commit 978e2913f6
6 changed files with 96 additions and 30 deletions
+40 -1
View File
@@ -361,7 +361,7 @@ func TestAssembleRefusesMismatchedEmitterVersions(t *testing.T) {
if err := json.Unmarshal(body, &sidecar); err != nil {
t.Fatal(err)
}
sidecar.CreatedWith = "crucible some-other-build"
sidecar.EmitterVersion = "0"
patched, err := marshalSidecar(sidecar)
if err != nil {
t.Fatal(err)
@@ -380,6 +380,45 @@ func TestAssembleRefusesMismatchedEmitterVersions(t *testing.T) {
}
}
func TestEmitHonoursSourceDateEpoch(t *testing.T) {
t.Setenv("SOURCE_DATE_EPOCH", "1700000000")
dir := t.TempDir()
hak := filepath.Join(dir, "pinned.hak")
writeHak(t, hak, map[string][]byte{"one1.tga": []byte("body")})
result, err := Emit(hak, filepath.Join(dir, "out"))
if err != nil {
t.Fatalf("emit: %v", err)
}
body, err := os.ReadFile(result.ManifestPath + ".json")
if err != nil {
t.Fatal(err)
}
var sidecar Sidecar
if err := json.Unmarshal(body, &sidecar); err != nil {
t.Fatal(err)
}
if sidecar.Created != 1700000000 {
t.Errorf("created = %d, want the pinned SOURCE_DATE_EPOCH", sidecar.Created)
}
}
func TestEmitRejectsAModule(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "sow.mod")
var out bytes.Buffer
if err := erf.Write(&out, erf.New("MOD", []erf.Resource{
{Name: "module", Type: restype(t, "ifo"), Data: []byte("ifo"), Size: 3},
})); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, out.Bytes(), 0o644); err != nil {
t.Fatal(err)
}
if _, err := Emit(path, filepath.Join(dir, "out")); err == nil {
t.Fatal("emit accepted a .mod; a manifest never carries module contents")
}
}
func TestAssembleFailsClosedOnMissingIndex(t *testing.T) {
entriesDir, _, _ := emitFixture(t)
_, err := Assemble(AssembleOptions{