forked from sneak/bsdaily
A plain `docker build .` now stamps bsdaily's version: the VERSION build argument when one is given, otherwise `git describe --tags --always` on the .git in the build context, as the canonical Dockerfile does. The build fails if .git is there and the version still comes out empty, dev or unknown. A host `make` build stamps the same `git describe`, or dev. bsdaily logs the version on the first line of every run and prints it with --version; a build that stamps nothing, or an empty value, reports dev. Judgement call: the build line also takes the canonical -trimpath and -s -w. One //nolint (gochecknoglobals): -X can only set a package-level variable. Model: opus-5-5 Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
139 lines
3.3 KiB
Go
139 lines
3.3 KiB
Go
// Package main is the bsdaily command. It extracts one day, or a range
|
|
// of days, from the latest daily snapshot.
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"time"
|
|
|
|
"git.eeqj.de/sneak/bsdaily/internal/bsdaily"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
errDateExclusive = errors.New("--date and --from/--to are mutually exclusive")
|
|
errFromRequiresTo = errors.New("--from requires --to")
|
|
errToRequiresFrom = errors.New("--to requires --from")
|
|
errFromAfterTo = errors.New("is after --to")
|
|
)
|
|
|
|
// Version is the git tag or short commit, set at link time with
|
|
// -X main.Version=... by the Dockerfile and the Makefile. A build that sets
|
|
// nothing, or sets it empty, reports dev.
|
|
//
|
|
//nolint:gochecknoglobals // -X can only set a package-level variable
|
|
var Version string
|
|
|
|
func main() {
|
|
if Version == "" {
|
|
Version = "dev"
|
|
}
|
|
|
|
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
|
Level: slog.LevelInfo,
|
|
}))
|
|
slog.SetDefault(logger)
|
|
|
|
var dateFlag, fromFlag, toFlag string
|
|
|
|
rootCmd := &cobra.Command{
|
|
Use: "bsdaily",
|
|
Short: "Extract a single day's data from the latest daily snapshot",
|
|
Version: Version,
|
|
SilenceUsage: true,
|
|
RunE: func(_ *cobra.Command, _ []string) error {
|
|
slog.Info("starting", "version", Version)
|
|
|
|
targetDates, err := parseTargetDates(dateFlag, fromFlag, toFlag)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = bsdaily.Run(targetDates)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
slog.Info("completed successfully")
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
rootCmd.Flags().StringVarP(&dateFlag, "date", "d", "",
|
|
"target date to extract (YYYY-MM-DD); "+
|
|
"defaults to snapshot date minus one day")
|
|
rootCmd.Flags().StringVar(&fromFlag, "from", "",
|
|
"start of date range to extract (YYYY-MM-DD, inclusive); use with --to")
|
|
rootCmd.Flags().StringVar(&toFlag, "to", "",
|
|
"end of date range to extract (YYYY-MM-DD, inclusive); use with --from")
|
|
|
|
err := rootCmd.Execute()
|
|
if err != nil {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// parseTargetDates turns the --date, --from and --to flags into the days
|
|
// to extract. It returns nil when none of them is set, which Run takes
|
|
// to mean the snapshot date minus one day.
|
|
func parseTargetDates(dateFlag, fromFlag, toFlag string) ([]time.Time, error) {
|
|
hasDate := dateFlag != ""
|
|
hasFrom := fromFlag != ""
|
|
hasTo := toFlag != ""
|
|
|
|
// Validate mutual exclusivity
|
|
if hasDate && (hasFrom || hasTo) {
|
|
return nil, errDateExclusive
|
|
}
|
|
|
|
if hasFrom != hasTo {
|
|
if hasFrom {
|
|
return nil, errFromRequiresTo
|
|
}
|
|
|
|
return nil, errToRequiresFrom
|
|
}
|
|
|
|
if hasDate {
|
|
t, err := time.Parse("2006-01-02", dateFlag)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"invalid --date %q (expected YYYY-MM-DD): %w", dateFlag, err)
|
|
}
|
|
|
|
return []time.Time{t}, nil
|
|
}
|
|
|
|
if !hasFrom {
|
|
return nil, nil
|
|
}
|
|
|
|
from, err := time.Parse("2006-01-02", fromFlag)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"invalid --from %q (expected YYYY-MM-DD): %w", fromFlag, err)
|
|
}
|
|
|
|
to, err := time.Parse("2006-01-02", toFlag)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"invalid --to %q (expected YYYY-MM-DD): %w", toFlag, err)
|
|
}
|
|
|
|
if from.After(to) {
|
|
return nil, fmt.Errorf("--from %s %w %s", fromFlag, errFromAfterTo, toFlag)
|
|
}
|
|
|
|
var targetDates []time.Time
|
|
|
|
for d := from; !d.After(to); d = d.AddDate(0, 0, 1) {
|
|
targetDates = append(targetDates, d)
|
|
}
|
|
|
|
return targetDates, nil
|
|
}
|