crucible depot: Increment 1 core (status/push/verify/get/pull + local/cdn/bunny backends) #30
@@ -5,8 +5,10 @@
|
|||||||
// Cutover state (Phase 5 → 6): the internal pipeline/topdata/erf/wiki
|
// Cutover state (Phase 5 → 6): the internal pipeline/topdata/erf/wiki
|
||||||
// packages migrated from gitea/sow-tools now live in this tree, so wired
|
// packages migrated from gitea/sow-tools now live in this tree, so wired
|
||||||
// builders delegate to internal/app's legacy command surface (mapped per
|
// builders delegate to internal/app's legacy command surface (mapped per
|
||||||
// docs/command-surface.md). A builder with no migrated logic yet (depot) keeps
|
// docs/command-surface.md). depot is wired but bypasses that legacy surface
|
||||||
// the Wired=false fail-closed path: exit 70, never a faked artifact.
|
// entirely, delegating straight to internal/depot.Run. A builder with no
|
||||||
|
// migrated logic yet keeps the Wired=false fail-closed path: exit 70, never a
|
||||||
|
// faked artifact.
|
||||||
package dispatch
|
package dispatch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -91,8 +93,15 @@ var Registry = []Builder{
|
|||||||
{
|
{
|
||||||
Name: "depot",
|
Name: "depot",
|
||||||
Bin: "crucible-depot",
|
Bin: "crucible-depot",
|
||||||
Summary: "verify/move content-addressed depot blobs (SeaweedFS)",
|
Summary: "content-addressed asset depot (local/cdn/bunny)",
|
||||||
Wired: false, // no migrated logic yet; fails closed (exit 70)
|
Commands: []Command{
|
||||||
|
{Name: "status", Summary: "report referenced-vs-present drift against a backend", Usage: "crucible depot status [--manifests DIR] [--source DIR] --target bunny|cdn|local"},
|
||||||
|
{Name: "push", Summary: "upload referenced-but-absent blobs from a local depot", Usage: "crucible depot push [--manifests DIR] --source DIR --target bunny"},
|
||||||
|
{Name: "verify", Summary: "existence sweep plus sampled download re-hash", Usage: "crucible depot verify [--manifests DIR] --target bunny|cdn [--sample N]"},
|
||||||
|
{Name: "get", Summary: "fetch one blob with sha re-verify", Usage: "crucible depot get <sha> <dest> --target cdn|bunny|local"},
|
||||||
|
{Name: "pull", Summary: "incremental verified pull of every referenced blob", Usage: "crucible depot pull [--manifests DIR] --dest DIR --target cdn|bunny"},
|
||||||
|
},
|
||||||
|
Wired: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "hak",
|
Name: "hak",
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ func TestBuilderHelpIsOK(t *testing.T) {
|
|||||||
|
|
||||||
func TestCanonicalCommandSurface(t *testing.T) {
|
func TestCanonicalCommandSurface(t *testing.T) {
|
||||||
want := map[string][]string{
|
want := map[string][]string{
|
||||||
"depot": nil,
|
"depot": {"status", "push", "verify", "get", "pull"},
|
||||||
"hak": {"build", "manifest"},
|
"hak": {"build", "manifest"},
|
||||||
"module": {"build", "extract", "validate", "compare", "manifest"},
|
"module": {"build", "extract", "validate", "compare", "manifest"},
|
||||||
"topdata": {"validate", "build", "package", "compare", "convert"},
|
"topdata": {"validate", "build", "package", "compare", "convert"},
|
||||||
@@ -172,7 +172,11 @@ func TestRegistryCommandNamesAndAliasesAreUnambiguous(t *testing.T) {
|
|||||||
for _, builder := range Registry {
|
for _, builder := range Registry {
|
||||||
seen := map[string]bool{}
|
seen := map[string]bool{}
|
||||||
for _, command := range builder.Commands {
|
for _, command := range builder.Commands {
|
||||||
if command.Name == "" || command.Summary == "" || command.AppCommand == "" || command.Usage == "" {
|
// depot parses its own subcommands and bypasses AppCommand routing
|
||||||
|
// entirely (see the depot special-case in runBuilder), so its
|
||||||
|
// Commands carry no AppCommand.
|
||||||
|
requireAppCommand := builder.Name != "depot"
|
||||||
|
if command.Name == "" || command.Summary == "" || command.Usage == "" || (requireAppCommand && command.AppCommand == "") {
|
||||||
t.Errorf("%s has incomplete command metadata: %#v", builder.Name, command)
|
t.Errorf("%s has incomplete command metadata: %#v", builder.Name, command)
|
||||||
}
|
}
|
||||||
if seen[command.Name] {
|
if seen[command.Name] {
|
||||||
@@ -289,15 +293,35 @@ func TestMenuItemsSkipsUnwiredIncludesWired(t *testing.T) {
|
|||||||
t.Fatal("expected menu items")
|
t.Fatal("expected menu items")
|
||||||
}
|
}
|
||||||
sawModuleBuild := false
|
sawModuleBuild := false
|
||||||
|
sawDepotStatus := false
|
||||||
for _, it := range items {
|
for _, it := range items {
|
||||||
if it.Args[0] == "depot" {
|
|
||||||
t.Errorf("unwired builder %q must not appear in the menu", it.Args[0])
|
|
||||||
}
|
|
||||||
if len(it.Args) == 2 && it.Args[0] == "module" && it.Args[1] == "build" {
|
if len(it.Args) == 2 && it.Args[0] == "module" && it.Args[1] == "build" {
|
||||||
sawModuleBuild = true
|
sawModuleBuild = true
|
||||||
}
|
}
|
||||||
|
if len(it.Args) == 2 && it.Args[0] == "depot" && it.Args[1] == "status" {
|
||||||
|
sawDepotStatus = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !sawModuleBuild {
|
if !sawModuleBuild {
|
||||||
t.Error("expected 'module build' in the menu")
|
t.Error("expected 'module build' in the menu")
|
||||||
}
|
}
|
||||||
|
if !sawDepotStatus {
|
||||||
|
t.Error("expected wired builder 'depot status' in the menu")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDepotRoutesToDepotRun asserts the depot special-case in runBuilder
|
||||||
|
// reaches depot.Run rather than the generic Commands/delegateLegacy path or
|
||||||
|
// the unwired fail-closed path. With no manifests dir present, depot.Run's
|
||||||
|
// status command fails resolving the backend/manifest (exit 70), but the
|
||||||
|
// stderr text must come from depot, never the dispatcher's unwiredMsg.
|
||||||
|
func TestDepotRoutesToDepotRun(t *testing.T) {
|
||||||
|
var out, errw bytes.Buffer
|
||||||
|
code := runBuilder("depot", []string{"status", "--target", "local"}, &out, &errw)
|
||||||
|
if errw.Len() == 0 {
|
||||||
|
t.Fatalf("depot status with no manifests dir: expected an error from depot.Run, got no stderr (exit=%d)", code)
|
||||||
|
}
|
||||||
|
if strings.Contains(errw.String(), "not wired yet") {
|
||||||
|
t.Fatalf("depot status: stderr shows the dispatcher's unwired message, want depot.Run's own error:\n%s", errw.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user