Compare commits
2
Commits
main
..
9b7be2c76e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b7be2c76e | ||
|
|
56d4054118 |
@@ -97,23 +97,6 @@ broken — is skipped by every later run forever and no backfill repairs it. Wit
|
||||
and replaced when it does not match. It costs a full GET per existing blob, so
|
||||
it is a repair pass, not the default.
|
||||
|
||||
**After a repair, `verify` is what tells you which keys to purge.** A repair is
|
||||
the one thing that makes a key serve different bytes than it did before, and the
|
||||
edge caches these objects for 30 days precisely because that normally cannot
|
||||
happen. The two commands look at different copies on purpose: `emit --verify`
|
||||
repairs the **origin**, `verify` reads the **edge**. So a `verify` run straight
|
||||
after a repair is not a verdict — it is a survey, and every blob it still calls
|
||||
bad is one the edge is serving stale. Purge exactly those, then re-run it; only
|
||||
that second run is the verdict.
|
||||
|
||||
Purging the keys `verify` names beats purging the zone, because the edge only
|
||||
ever cached what somebody actually fetched: the 2026-08-01 repair rewrote 2,603
|
||||
blobs at the origin and left 8 stale at the edge. The purge belongs in the
|
||||
repair procedure rather than in `emit`, which reports how many blobs it wrote
|
||||
and never which ones — so it could not target one even with a CDN credential,
|
||||
which it deliberately does not hold (#89; the procedure itself is in
|
||||
sow-platform's NWSync runbook).
|
||||
|
||||
`emit` uploads blobs first and the index last, so the presence of an index is
|
||||
the publication marker: an artifact whose emit died halfway leaves real blobs in
|
||||
the zone and no index. Blob names are content hashes, so re-running skips
|
||||
|
||||
@@ -67,9 +67,6 @@ check on a published blob upstream of a player's client.
|
||||
--verify makes emit hash what it would otherwise skip. emit normally treats a
|
||||
blob's presence as proof of its contents, so without this an object written
|
||||
truncated, or written by an emitter since found broken, is skipped forever.
|
||||
--verify repairs the storage zone, while verify reads the edge in front of it.
|
||||
So a verify run right after a repair is a survey, not a verdict: it names the
|
||||
keys the edge still serves stale. Purge those, then run it again.
|
||||
|
||||
--out DIR writes to a local repository tree instead of uploading, which is the
|
||||
conformance path against upstream nwn_nwsync_write. Without it, the zone comes
|
||||
|
||||
@@ -560,18 +560,8 @@ func (p *Project) ValidateLayout() error {
|
||||
if strings.TrimSpace(p.Config.Module.ResRef) == "" {
|
||||
failures = append(failures, errors.New("module.resref is required"))
|
||||
}
|
||||
// module.resref names the built .mod FILE, so the 16-byte resref limit does not
|
||||
// apply to it — NWN:EE module file names are routinely longer. It is validated as
|
||||
// a file name instead. The limit still binds when the same value has to be a real
|
||||
// resref: with no haks configured, an asset project names its single generated HAK
|
||||
// after it, and a HAK name is a resref the engine loads.
|
||||
if err := validateOutputFileName("module.resref", p.Config.Module.ResRef+".mod", ".mod"); err != nil {
|
||||
failures = append(failures, err)
|
||||
}
|
||||
if len(p.Config.Module.ResRef) > 16 && strings.TrimSpace(p.Config.Paths.Assets) != "" && len(p.Config.HAKs) == 0 {
|
||||
failures = append(failures, fmt.Errorf(
|
||||
"module.resref %q exceeds 16 characters and would name this project's generated HAK; configure haks[] with a shorter name",
|
||||
p.Config.Module.ResRef))
|
||||
if len(p.Config.Module.ResRef) > 16 {
|
||||
failures = append(failures, fmt.Errorf("module.resref %q exceeds 16 characters", p.Config.Module.ResRef))
|
||||
}
|
||||
if strings.TrimSpace(p.Config.Paths.Source) == "" && strings.TrimSpace(p.Config.Paths.Assets) == "" && !p.HasTopData() {
|
||||
failures = append(failures, errors.New("at least one of paths.source, paths.assets, or topdata.source is required"))
|
||||
|
||||
@@ -1093,78 +1093,6 @@ func TestValidateLayoutAllowsMissingAssetsDir(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// module.resref names the built .mod FILE, not a resource inside an archive, so the
|
||||
// 16-byte resref limit does not apply to it. NWN:EE module file names are commonly
|
||||
// longer (ShadowsOverWestgate.mod is 19). The limit still binds everywhere a resref
|
||||
// really is a resref — see TestValidateLayoutRejectsLongResRefWhenItNamesAHAK.
|
||||
func TestValidateLayoutAllowsLongModuleResRef(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mkdirAll(t, filepath.Join(root, "src"))
|
||||
mkdirAll(t, filepath.Join(root, "build"))
|
||||
|
||||
proj := &Project{
|
||||
Root: root,
|
||||
Config: Config{
|
||||
Module: ModuleConfig{Name: "Shadows Over Westgate", ResRef: "ShadowsOverWestgate"},
|
||||
Paths: PathConfig{Source: "src", Build: "build"},
|
||||
},
|
||||
}
|
||||
|
||||
if err := proj.ValidateLayout(); err != nil {
|
||||
t.Fatalf("ValidateLayout rejected a 19-character module file name: %v", err)
|
||||
}
|
||||
if got, want := filepath.Base(proj.ModuleArchivePath()), "ShadowsOverWestgate.mod"; got != want {
|
||||
t.Fatalf("ModuleArchivePath() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// A module.resref that is not a usable file name is still rejected.
|
||||
func TestValidateLayoutRejectsModuleResRefThatIsAPath(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mkdirAll(t, filepath.Join(root, "src"))
|
||||
|
||||
proj := &Project{
|
||||
Root: root,
|
||||
Config: Config{
|
||||
Module: ModuleConfig{Name: "Test", ResRef: "../escape/mod"},
|
||||
Paths: PathConfig{Source: "src", Build: "build"},
|
||||
},
|
||||
}
|
||||
|
||||
err := proj.ValidateLayout()
|
||||
if err == nil {
|
||||
t.Fatal("ValidateLayout accepted a module.resref containing a path")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "module.resref") {
|
||||
t.Fatalf("error does not name the offending field: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// When a project declares no haks, the module resref becomes the name of the single
|
||||
// generated HAK — and a HAK name IS a resref the engine loads. The limit applies
|
||||
// there, so a long name is only allowed for projects that build no HAKs.
|
||||
func TestValidateLayoutRejectsLongResRefWhenItNamesAHAK(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mkdirAll(t, filepath.Join(root, "src"))
|
||||
mkdirAll(t, filepath.Join(root, "assets"))
|
||||
|
||||
proj := &Project{
|
||||
Root: root,
|
||||
Config: Config{
|
||||
Module: ModuleConfig{Name: "Shadows Over Westgate", ResRef: "ShadowsOverWestgate"},
|
||||
Paths: PathConfig{Source: "src", Assets: "assets", Build: "build"},
|
||||
},
|
||||
}
|
||||
|
||||
err := proj.ValidateLayout()
|
||||
if err == nil {
|
||||
t.Fatal("ValidateLayout accepted a 19-character name for a generated HAK")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "16") {
|
||||
t.Fatalf("error does not explain the resref limit: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// paths.build is an OUTPUT dir the builder creates (MkdirAll) before writing, so
|
||||
// a bare clone with no build dir yet must still validate/build with no pre-step
|
||||
// (R2/parity). Only a build path that exists but is not a directory is an error.
|
||||
|
||||
Reference in New Issue
Block a user