From 2e3833dd30fafdc270e8d80a78af9116571dc247 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Thu, 27 Aug 2026 13:49:00 +0200 Subject: Use subcommand args instead of flags for CLI --- README.md | 14 ++++++------- cli/cli.go | 68 ++++++++++++++++++++++++++++++++++++++++++++------------------ main.go | 5 +++-- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 123b021..deaa9fb 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ Climb profile by 100m step. ```bash mise run build mise run dev-watch # Start the web server on http://localhost:8080 -go run . -fetch-osm # Download the France OSM PBF (resumable) -go run . -extract-osm france-latest.osm.pbf # Extract mountain passes from a France OSM PBF -go run . -backfill # Recompute and persist all ride computed values -go run . -backtest # Benchmark computed vs stored ride values -go run . -pprof # Expose pprof endpoints at `/debug/pprof` +go run . fetch-osm # Download the France OSM PBF (resumable) +go run . extract-osm france-latest.osm.pbf # Extract mountain passes from a France OSM PBF +go run . backfill # Recompute and persist all ride computed values +go run . backtest # Benchmark computed vs stored ride values +go run . -- -pprof # Expose pprof endpoints at `/debug/pprof` docker compose up -d pyroscope # Start the local Pyroscope server on http://localhost:4040 ``` @@ -129,7 +129,8 @@ Answer "what slows the server" with function-level CPU profiles grouped by route ```bash # Start the server with pprof (exposes http://localhost:8080/debug/pprof/) -go run . -pprof +go run . -- -pprof +# or: go run . serve --pprof # or: mise run dev -- -pprof ``` @@ -158,7 +159,6 @@ go tool pprof -http=:8081 http://localhost:8080/debug/pprof/heap # allocs ## TODO -- cli args instea dof flags - screaming archi + hexagon - Compute estimated power, speed - For a ride, scatter, one point per 100m segment. Slope vs speed diff --git a/cli/cli.go b/cli/cli.go index 1fdb866..c71fd9e 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -4,7 +4,6 @@ import ( "context" "database/sql" "errors" - "flag" "fmt" "log/slog" "math" @@ -21,23 +20,26 @@ import ( "github.com/schollz/progressbar/v3" ) -var ( - fetchOSM = flag.Bool("fetch-osm", false, "download the France OSM PBF (resumable) into france-latest.osm.pbf") - extractOSM = flag.String("extract-osm", "", "extract mountain passes from an OSM PBF file into the database") - backfill = flag.Bool("backfill", false, "recompute all stored ride values") - backtest = flag.Bool("backtest", false, "run data benchmarks comparing computed values against stored reference values") - enablePprof = flag.Bool("pprof", false, "expose pprof endpoints at /debug/pprof") -) +func Run(db *sql.DB, configPath string, appConfig config.Config, args []string, enablePprof bool) { + if len(args) == 0 { + runServer(db, configPath, appConfig, enablePprof) + return + } -func Run(db *sql.DB, configPath string, appConfig config.Config) { - switch { - case *fetchOSM: + switch args[0] { + case "fetch-osm": err := mountain_pass.FetchFrancePBF("france-latest.osm.pbf") kcore.Expect(err, "failed to download France OSM PBF") - case *extractOSM != "": + case "extract-osm": + if len(args) < 2 { + fmt.Fprintln(os.Stderr, "extract-osm requires a PBF file path argument") + printUsage() + os.Exit(2) + } + pbfPath := args[1] enabled := progressBarEnabled() var bar *progressbar.ProgressBar - _, err := mountain_pass.ExtractMountainPasses(context.Background(), *extractOSM, db, func(current, total int64) { + _, err := mountain_pass.ExtractMountainPasses(context.Background(), pbfPath, db, func(current, total int64) { if bar == nil && enabled { bar = progressbar.DefaultBytes(total, "Extracting mountain passes") } @@ -49,7 +51,7 @@ func Run(db *sql.DB, configPath string, appConfig config.Config) { bar.Finish() } kcore.Expect(err, "failed to extract mountain passes from OSM") - case *backfill: + case "backfill": enabled := progressBarEnabled() var bar *progressbar.ProgressBar count, err := ride.Backfill(db, func(current, total int) { @@ -65,13 +67,41 @@ func Run(db *sql.DB, configPath string, appConfig config.Config) { } kcore.Expect(err, "failed to backfill ride values") slog.Info("Backfilled ride values", "rides", count) - case *backtest: + case "backtest": kcore.Expect(runBacktest(db), "failed to run backtest") + case "serve": + pprof := enablePprof + for _, arg := range args[1:] { + if arg == "--pprof" || arg == "-pprof" || arg == "pprof" { + pprof = true + } + } + runServer(db, configPath, appConfig, pprof) + case "help", "-h", "--help": + printUsage() default: - runServer(db, configPath, appConfig) + fmt.Fprintf(os.Stderr, "unknown command %q\n", args[0]) + printUsage() + os.Exit(2) } } +func printUsage() { + fmt.Fprintf(os.Stderr, `Usage: biking_home [global flags] [args] + +Global flags: + -config path to YAML configuration file (default "config.yaml") + -pprof expose pprof endpoints at /debug/pprof (also: "serve --pprof") + +Commands: + serve start web server (default when no command given) + fetch-osm download the France OSM PBF into france-latest.osm.pbf + extract-osm extract mountain passes from an OSM PBF file + backfill recompute all stored ride values + backtest run data benchmarks comparing computed values against stored reference values +`) +} + // progressBarEnabled reports whether stderr is attached to a terminal, so the // progress bar only renders for interactive runs and stays out of piped or // redirected output. @@ -80,9 +110,9 @@ func progressBarEnabled() bool { return err == nil && fi.Mode()&os.ModeCharDevice != 0 } -func runServer(db *sql.DB, configPath string, appConfig config.Config) { - server := web.NewServer(db, configPath).WithPprof(*enablePprof) - if *enablePprof { +func runServer(db *sql.DB, configPath string, appConfig config.Config, enablePprof bool) { + server := web.NewServer(db, configPath).WithPprof(enablePprof) + if enablePprof { slog.Info("pprof enabled", "endpoint", appConfig.Server.PublicURL+"/debug/pprof/") } slog.Info("Starting web server", "address", appConfig.Server.PublicURL) diff --git a/main.go b/main.go index 8971433..397b3d6 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,8 @@ import ( ) var ( - configFile = flag.String("config", "config.yaml", "path to the YAML configuration file") + configFile = flag.String("config", "config.yaml", "path to the YAML configuration file") + enablePprof = flag.Bool("pprof", false, "expose pprof endpoints at /debug/pprof") ) func main() { @@ -30,7 +31,7 @@ func main() { db, err := sql.Open("sqlite3", appConfig.Database.Path) kcore.Expect(err, "failed to open database") defer db.Close() - cli.Run(db, *configFile, appConfig) + cli.Run(db, *configFile, appConfig, flag.Args(), *enablePprof) } // startPyroscope starts the continuous profiler when enabled in the config and -- cgit v1.3