Artifact 019ae0fba5d88788e749d5a5354f6aa8ac23a60258db51f876401b2e4de11f0d:
- File
cmd/quarry/main.go
— part of check-in
[d0e80a9156]
at
2026-08-15 12:05:39
on branch trunk
— checkin: detail op + page, hash resolution, zit ls on demand
CheckinByPrefix (unique-only); file set via zit subprocess at request time; timeline links hashes; harness snapshots every check-in page. (user: dmestas, size: 3627) [annotate] [blame] [check-ins using]
// quarry is a multi-repository forge for zit. See docs/rfcs/. package main import ( "encoding/json" "flag" "fmt" "log" "net/http" "os" "path/filepath" "time" "craftdesign.group/quarry/internal/api" "craftdesign.group/quarry/internal/events" "craftdesign.group/quarry/internal/index" "craftdesign.group/quarry/internal/ingest" "craftdesign.group/quarry/internal/ops" "craftdesign.group/quarry/internal/web" "craftdesign.group/quarry/internal/zitcli" ) // version is stamped at build time; every page footer and the API report it // (0001 Principle 1). var version = "0.0.0-dev" func main() { if len(os.Args) > 1 { switch os.Args[1] { case "version": fmt.Println("quarry", version) return case "spec": if err := printSpec(os.Stdout); err != nil { fmt.Fprintln(os.Stderr, "quarry:", err) os.Exit(1) } return case "serve": if err := serveCmd(os.Args[2:]); err != nil { fmt.Fprintln(os.Stderr, "quarry:", err) os.Exit(1) } return } } fmt.Fprintln(os.Stderr, "usage: quarry version|spec|serve") os.Exit(2) } // serveCmd runs the HTTP surface: the op-generated API plus the HTML // pages, both consumers of one registry. With --repo, a watcher ingests // the store into a derived index (0003 phase 1: one store, one node). func serveCmd(args []string) error { fs := flag.NewFlagSet("serve", flag.ContinueOnError) addr := fs.String("addr", "127.0.0.1:8080", "listen address") repo := fs.String("repo", "", "zit repository to serve (checkout dir or .zit file)") data := fs.String("data", "", "derived-index directory (default: <repo>/.quarry)") interval := fs.Duration("sync-interval", 2*time.Second, "store poll interval") if err := fs.Parse(args); err != nil { return err } deps := api.Deps{Version: version} if *repo != "" { dir := *data if dir == "" { dir = filepath.Join(*repo, ".quarry") } if err := os.MkdirAll(dir, 0o755); err != nil { return err } ev, err := events.Start(filepath.Join(dir, "nats")) if err != nil { return err } defer ev.Close() ix, err := index.Open(filepath.Join(dir, "index.db")) if err != nil { return err } defer ix.Close() deps.Index = ix cli := zitcli.New(*repo) deps.Zit = cli pipe := ingest.NewPipeline(cli, ev, ix, "store") if n, err := pipe.Sync(); err != nil { return fmt.Errorf("initial ingest: %w", err) } else { fmt.Printf("ingested %d artifacts from %s\n", n, *repo) } go func() { for range time.Tick(*interval) { if _, err := pipe.Sync(); err != nil { log.Printf("ingest: %v", err) } } }() } mux, err := buildHandler(deps) if err != nil { return err } fmt.Println("quarry", version, "listening on", *addr) return http.ListenAndServe(*addr, mux) } // buildHandler wires the two registry consumers onto one mux. func buildHandler(deps api.Deps) (http.Handler, error) { r := ops.NewRegistry() if err := api.RegisterAll(r, deps); err != nil { return nil, err } apiRouter, err := api.Router(r, deps.Version) if err != nil { return nil, err } mux := http.NewServeMux() mux.Handle("/api/", apiRouter) mux.Handle("/", web.Handler(r)) return mux, nil } // printSpec emits the OpenAPI document generated from the full registry — // the same registry serve will run, so the spec cannot drift (0001 §9). func printSpec(w *os.File) error { r := ops.NewRegistry() if err := api.RegisterAll(r, api.Deps{Version: version}); err != nil { return err } spec, err := ops.Spec(r, "quarry", version) if err != nil { return err } enc := json.NewEncoder(w) enc.SetIndent("", " ") return enc.Encode(spec) }