feat(depot): select a depot tree on disk with --out DIR
ci / ci (pull_request) Successful in 3m20s

`depot status` and `depot get` chose a local depot tree with `--target
local`, resolving the root from `DEPOT_DIR`. That split one decision over
two flags and left `--target local` valid with no directory anywhere.

Adopt the nwsync convention instead: `--out DIR` names the tree on disk in
the same breath as choosing it, and `--target` now names remote backends
only. The two are mutually exclusive. `--target local` stays as a
deprecated alias for `--out $DEPOT_DIR` so existing callers keep working,
and `status --source` is now ignored with a warning instead of silently
accepted.

Also split positionals off by hand in `depot get`, so the documented
`depot get <sha> <dest> --out DIR` form parses its flags at all — Go's
flag package stops at the first positional.

Closes #66
This commit is contained in:
2026-07-28 10:15:14 +02:00
parent ed6945d308
commit 6bbc2c3a83
4 changed files with 154 additions and 33 deletions
+96
View File
@@ -54,6 +54,91 @@ func TestRunUsage(t *testing.T) {
})
}
func TestOutFlag(t *testing.T) {
t.Run("status --out reads the given tree", func(t *testing.T) {
sha := shaOf("out-blob")
manifests := t.TempDir()
writeManifest(t, manifests, sha, 8)
depotDir := t.TempDir()
writeBlob(t, depotDir, sha, "out-blob")
var out, errb bytes.Buffer
code := Run([]string{"status", "--manifests", manifests, "--out", depotDir}, &out, &errb, testGetenv(nil))
if code != 0 {
t.Fatalf("expected 0, got %d (stdout=%s stderr=%s)", code, out.String(), errb.String())
}
if !bytesContains(out.String(), "present=1") {
t.Fatalf("expected present=1, got %s", out.String())
}
})
t.Run("get --out fetches from the given tree", func(t *testing.T) {
sha := shaOf("get-blob")
depotDir := t.TempDir()
writeBlob(t, depotDir, sha, "get-blob")
dest := filepath.Join(t.TempDir(), "fetched")
var out, errb bytes.Buffer
code := Run([]string{"get", sha, dest, "--out", depotDir}, &out, &errb, testGetenv(nil))
if code != 0 {
t.Fatalf("expected 0, got %d (stderr=%s)", code, errb.String())
}
got, err := os.ReadFile(dest)
if err != nil {
t.Fatal(err)
}
if string(got) != "get-blob" {
t.Fatalf("got %q", got)
}
})
t.Run("--out with any --target is rejected", func(t *testing.T) {
for _, target := range []string{"bunny", "cdn", "local"} {
var out, errb bytes.Buffer
code := Run([]string{"status", "--manifests", t.TempDir(), "--out", t.TempDir(), "--target", target}, &out, &errb, testGetenv(nil))
if code != 64 {
t.Fatalf("--target %s: expected 64, got %d (stderr=%s)", target, code, errb.String())
}
}
})
t.Run("get accepts flags before the positionals", func(t *testing.T) {
sha := shaOf("flags-first-blob")
depotDir := t.TempDir()
writeBlob(t, depotDir, sha, "flags-first-blob")
dest := filepath.Join(t.TempDir(), "fetched")
var out, errb bytes.Buffer
code := Run([]string{"get", "--out", depotDir, sha, dest}, &out, &errb, testGetenv(nil))
if code != 0 {
t.Fatalf("expected 0, got %d (stderr=%s)", code, errb.String())
}
})
t.Run("neither --out nor --target is rejected", func(t *testing.T) {
var out, errb bytes.Buffer
code := Run([]string{"status", "--manifests", t.TempDir()}, &out, &errb, testGetenv(nil))
if code != 64 {
t.Fatalf("expected 64, got %d (stderr=%s)", code, errb.String())
}
})
t.Run("--target local still works as a hidden alias", func(t *testing.T) {
sha := shaOf("alias-blob")
manifests := t.TempDir()
writeManifest(t, manifests, sha, 11)
depotDir := t.TempDir()
writeBlob(t, depotDir, sha, "alias-blob")
var out, errb bytes.Buffer
code := Run([]string{"status", "--manifests", manifests, "--target", "local"}, &out, &errb,
testGetenv(map[string]string{"DEPOT_DIR": depotDir}))
if code != 0 {
t.Fatalf("expected 0, got %d (stdout=%s stderr=%s)", code, out.String(), errb.String())
}
})
}
func TestGetInvalidSHA(t *testing.T) {
var out, errb bytes.Buffer
dir := t.TempDir()
@@ -172,6 +257,17 @@ func bytesContains(s, substr string) bool {
return bytes.Contains([]byte(s), []byte(substr))
}
func writeBlob(t *testing.T, root, sha, content string) {
t.Helper()
path := filepath.Join(root, BlobKey(sha))
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
t.Fatal(err)
}
}
func writeManifest(t *testing.T, dir, sha string, size int64) {
t.Helper()
content := fmt.Sprintf("assets:\n - path: foo\n sha256: %s\n size: %d\n", sha, size)