1// Command cartograph is the workbench beside the Atlas reader. Where the2// reader serves the newest build of every game and asks no questions,3// cartograph answers for the collection itself: which sources feed each4// game, how mature every build stands along the axes that judge it, and what5// moved between any two builds of one game.6//7// It serves a dashboard on localhost and touches nothing on its own: reading8// the library is free, and every operation that fetches or writes runs only9// when a person submits it from the page.10package main1112import (13 "flag"14 "fmt"15 "net"16 "net/http"17 "os"18 "path/filepath"19 "strings"2021 "github.com/FelineStateMachine/atlas/internal/bundle"22)2324func main() {25 bundles := flag.String("bundles", "",26 "registry directory holding .atlas bundles (default: the application's own library)")27 addr := flag.String("addr", "127.0.0.1:6180", "address the dashboard listens on")28 archive := flag.String("archive", "",29 "fmg-archive root fetches fill and builds read (operations require it)")30 repo := flag.String("repo", "",31 "atlas repository whose tools operations run (default: found from the working directory)")32 flag.Parse()3334 if *bundles == "" {35 library, err := bundle.DefaultDir()36 if err != nil {37 fmt.Fprintln(os.Stderr, "cartograph: resolving the bundle library:", err)38 os.Exit(1)39 }40 *bundles = library41 }42 if *repo == "" {43 *repo = findRepo()44 }4546 server := newServer(&library{dir: *bundles},47 &workshop{repo: *repo, archive: *archive, bundles: *bundles})48 listener, err := net.Listen("tcp", *addr)49 if err != nil {50 fmt.Fprintln(os.Stderr, "cartograph:", err)51 os.Exit(1)52 }53 fmt.Printf("cartograph: reading %s\n", *bundles)54 fmt.Printf("cartograph: http://%s\n", listener.Addr())55 if err := http.Serve(listener, server.routes()); err != nil {56 fmt.Fprintln(os.Stderr, "cartograph:", err)57 os.Exit(1)58 }59}6061// findRepo walks up from the working directory looking for this module's own62// go.mod, so `go run ./cmd/cartograph` from anywhere inside the checkout63// needs no pointing. Anywhere else, -repo says where the tools live; the64// dashboard's views work without one, and only operations demand it.65func findRepo() string {66 dir, err := os.Getwd()67 if err != nil {68 return ""69 }70 for {71 data, err := os.ReadFile(filepath.Join(dir, "go.mod"))72 if err == nil && strings.Contains(string(data), "module github.com/FelineStateMachine/atlas\n") {73 return dir74 }75 parent := filepath.Dir(dir)76 if parent == dir {77 return ""78 }79 dir = parent80 }81}