depot: replace --target local with --out DIR (#67)

Closes #66.

`depot status` and `depot get` chose a depot tree on disk with `--target local`, taking the root from `DEPOT_DIR`. One decision, two flags — and `--target local` with no directory anywhere was valid but meaningless.

Now they take `--out DIR`, matching the crucible nwsync surface. `--target` names remote backends only (`bunny|cdn`). Passing both exits 64.

- `--target local` stays as a deprecated alias for `--out $DEPOT_DIR`, listed in the hidden-alias table in `docs/command-surface.md`. Nothing in `wrappers/` calls depot, but the three repos in `wrappers/consumers.txt` are out of reach from here, so the alias stays.
- `status --source` was already unused; it now warns "ignored; use --out DIR" instead of accepting silently.
- Fixes a real parse bug found on the way: the documented `depot get <sha> <dest> --target cdn` form never parsed its flags, because Go's `flag` package stops at the first positional. Positionals are split off by hand now; both orders are tested.
- Registry usage strings and `docs/command-surface.md` synced — that doc still said depot was unwired.

Not converted: `push --source` and `pull --dest`. Neither is `--target local`; one names a read source, the other a remote pull's destination. Say the word if they should become `--out` too.

`go test ./...` green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #67

Co-authored-by: vickydotbat <vickydotbat@tutamail.com>
This commit was merged in pull request #67.
This commit is contained in:
2026-07-28 11:07:58 +00:00
committed by archvillainette
parent ed6945d308
commit 22eecd1a41
4 changed files with 154 additions and 33 deletions
+46 -28
View File
@@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
)
@@ -51,13 +52,15 @@ func Run(args []string, stdout, stderr io.Writer, getenv func(string) string) in
func printRunUsage(w io.Writer) {
fmt.Fprint(w, `usage:
depot status [--manifests DIR] [--source LOCAL_DEPOT] --target bunny|cdn|local
depot push [--manifests DIR] --source LOCAL_DEPOT --target bunny
depot verify [--manifests DIR] --target bunny|cdn [--sample N]
depot get <sha> <dest> --target cdn|bunny|local
depot pull [--manifests DIR] --dest DIR --target cdn|bunny
depot status [--manifests DIR] --target bunny|cdn | --out DIR
depot push [--manifests DIR] --source LOCAL_DEPOT --target bunny
depot verify [--manifests DIR] --target bunny|cdn [--sample N]
depot get <sha> <dest> --target cdn|bunny | --out DIR
depot pull [--manifests DIR] --dest DIR --target cdn|bunny
--target local uses the DEPOT_DIR environment variable as the local root.
--out DIR reads and writes a depot tree on disk at DIR instead of a remote
backend. --target names a remote backend only; the two are mutually exclusive.
--target local is a deprecated alias for --out $DEPOT_DIR.
`)
}
@@ -65,16 +68,27 @@ func printRunUsage(w io.Writer) {
// than internal/backend failures (exit 70).
var errUsage = errors.New("usage error")
// resolveBackend builds the named backend, resolving "local" against DEPOT_DIR.
func resolveBackend(target string, getenv func(string) string, cfg Config) (Backend, error) {
root := ""
if target == "local" {
root = getenv("DEPOT_DIR")
// resolveBackend picks the backend for a command that can work either against a
// depot tree on disk (--out DIR) or a remote backend (--target bunny|cdn).
// "--target local" stays as a deprecated alias for --out $DEPOT_DIR so older
// callers keep working.
func resolveBackend(out, target string, getenv func(string) string, cfg Config) (Backend, error) {
switch {
case out != "" && target != "":
return nil, fmt.Errorf("--out and --target are mutually exclusive: %w", errUsage)
case out != "":
return NewBackend("local", out, cfg)
case target == "local":
root := getenv("DEPOT_DIR")
if root == "" {
return nil, fmt.Errorf("--target local requires DEPOT_DIR to be set: %w", errUsage)
return nil, fmt.Errorf("--target local requires DEPOT_DIR to be set (prefer --out DIR): %w", errUsage)
}
return NewBackend("local", root, cfg)
case target == "":
return nil, fmt.Errorf("--out DIR or --target bunny|cdn is required: %w", errUsage)
default:
return NewBackend(target, "", cfg)
}
return NewBackend(target, root, cfg)
}
// backendErrExit maps a resolveBackend error onto the exit contract.
@@ -119,18 +133,18 @@ func runStatus(args []string, stdout, stderr io.Writer, getenv func(string) stri
fs := flag.NewFlagSet("status", flag.ContinueOnError)
fs.SetOutput(stderr)
manifests := fs.String("manifests", "assets", "directory of *.yml manifests")
_ = fs.String("source", "", "local depot root (unused for status target=local; see DEPOT_DIR)")
target := fs.String("target", "", "bunny|cdn|local")
source := fs.String("source", "", "deprecated and ignored (use --out DIR for a depot tree on disk)")
out := fs.String("out", "", "depot tree on disk to read instead of a remote backend")
target := fs.String("target", "", "bunny|cdn")
if err := fs.Parse(args); err != nil {
return exitUsage
}
if *target == "" {
fmt.Fprintln(stderr, "depot status: --target is required")
return exitUsage
if *source != "" {
fmt.Fprintln(stderr, "depot status: --source is ignored; use --out DIR")
}
cfg := LoadConfig(getenv)
backend, err := resolveBackend(*target, getenv, cfg)
backend, err := resolveBackend(*out, *target, getenv, cfg)
if err != nil {
fmt.Fprintln(stderr, "depot status:", err)
return backendErrExit(err)
@@ -309,13 +323,21 @@ func runVerify(args []string, stdout, stderr io.Writer, getenv func(string) stri
func runGet(args []string, stdout, stderr io.Writer, getenv func(string) string) int {
fs := flag.NewFlagSet("get", flag.ContinueOnError)
fs.SetOutput(stderr)
target := fs.String("target", "", "cdn|bunny|local")
if err := fs.Parse(args); err != nil {
out := fs.String("out", "", "depot tree on disk to read instead of a remote backend")
target := fs.String("target", "", "cdn|bunny")
// Positionals come first in the documented usage, and Go's flag package
// stops parsing at the first one, so split them off by hand.
split := 0
for split < len(args) && !strings.HasPrefix(args[split], "-") {
split++
}
positional := args[:split]
if err := fs.Parse(args[split:]); err != nil {
return exitUsage
}
positional := fs.Args()
positional = append(positional, fs.Args()...)
if len(positional) != 2 {
fmt.Fprintln(stderr, "depot get: usage: depot get <sha> <dest> --target cdn|bunny|local")
fmt.Fprintln(stderr, "depot get: usage: depot get <sha> <dest> --target cdn|bunny | --out DIR")
return exitUsage
}
sha, dest := positional[0], positional[1]
@@ -323,13 +345,9 @@ func runGet(args []string, stdout, stderr io.Writer, getenv func(string) string)
fmt.Fprintf(stderr, "depot get: invalid sha %q\n", sha)
return exitUsage
}
if *target == "" {
fmt.Fprintln(stderr, "depot get: --target is required")
return exitUsage
}
cfg := LoadConfig(getenv)
backend, err := resolveBackend(*target, getenv, cfg)
backend, err := resolveBackend(*out, *target, getenv, cfg)
if err != nil {
fmt.Fprintln(stderr, "depot get:", err)
return backendErrExit(err)