diff --git a/cli/cmd_test.go b/cli/cmd_test.go index 1d58ac1..7fb7b0f 100644 --- a/cli/cmd_test.go +++ b/cli/cmd_test.go @@ -379,6 +379,31 @@ func TestVersion(t *testing.T) { runNoServer(t, "version").wantCode(t, 0).wantOut(t, "ccrawl") } +// The version line is six facts glued into a sentence, and a CI job that wants +// one of them should not have to write a regular expression for it. Asking for +// a format gets the same six as data. +func TestVersionRendersAsData(t *testing.T) { + r := runNoServer(t, "version", "-o", "jsonl").wantCode(t, 0) + var got map[string]any + if err := json.Unmarshal([]byte(strings.TrimSpace(r.Out)), &got); err != nil { + t.Fatalf("-o jsonl is not JSON: %v\n%s", err, r.Out) + } + for _, k := range []string{"version", "commit", "built", "os", "arch", "go"} { + if _, ok := got[k]; !ok { + t.Errorf("no %q field: %v", k, got) + } + } + runNoServer(t, "version", "-o", "csv").wantCode(t, 0).wantOut(t, "version,commit,built,os,arch,go") + + // auto keeps the sentence, in a pipe as well as on a terminal, because that + // is what ccrawl version has always printed and what anything grepping it + // expects. A test run is a pipe, so this is the case that would break. + runNoServer(t, "version").wantCode(t, 0). + wantOut(t, "ccrawl"). + wantNotOut(t, `"version"`) + runNoServer(t, "version", "--short").wantCode(t, 0).wantNotOut(t, "commit") +} + // An unknown command and an unknown flag exit 1, not the 2 that means a usage // error, because the argument parser rejects them before ccrawl sees anything. // The reference says so under Caveat in docs/content/reference/exit-codes.md, diff --git a/cli/version.go b/cli/version.go index 53a9489..a85fda5 100644 --- a/cli/version.go +++ b/cli/version.go @@ -18,6 +18,11 @@ func newVersionCmd() kit.Command { return kit.Command{ Use: "version", Short: "Print version information", + Long: `Print the version, the commit it was built from, when it was built, and the platform and Go toolchain behind it. + +The default is the one-line form people read. -o renders the same six fields as data, for a CI job that wants one of them without a regular expression: + + ccrawl version -o json | jq -r .commit`, Flags: v.flags, Run: v.run, } @@ -27,12 +32,31 @@ func (v *versionCmd) flags(f *kit.FlagSet) { f.BoolVar(&v.short, "short", false, "print just the version number") } -func (v *versionCmd) run(_ context.Context, _ []string) error { +func (v *versionCmd) run(ctx context.Context, _ []string) error { if v.short { _, _ = fmt.Fprintln(cmdOut, Version) return nil } - _, _ = fmt.Fprintf(cmdOut, "ccrawl %s (commit %s, built %s, %s/%s, %s)\n", - Version, Commit, Date, runtime.GOOS, runtime.GOARCH, runtime.Version()) - return nil + // auto stays the sentence, on a terminal and in a pipe alike. Everywhere + // else auto means "a table for a person, JSON Lines for a pipe", but this + // line is what `ccrawl version` has always printed and what a script that + // greps it expects, and changing that to help a script that does not exist + // yet is the wrong trade. Asking for a format is how you say you want data. + if st := kit.FromContext(ctx); st == nil || st.Output.Format == "" || st.Output.Format == "auto" { + _, _ = fmt.Fprintf(cmdOut, "ccrawl %s (commit %s, built %s, %s/%s, %s)\n", + Version, Commit, Date, runtime.GOOS, runtime.GOARCH, runtime.Version()) + return nil + } + + app := appFromCtx(ctx) + cols := []string{"version", "commit", "built", "os", "arch", "go"} + vals := []string{Version, Commit, Date, runtime.GOOS, runtime.GOARCH, runtime.Version()} + value := map[string]any{} + for i, c := range cols { + value[c] = vals[i] + } + if err := app.Out.Emit(Row{Cols: cols, Vals: vals, Value: value}); err != nil { + return err + } + return app.Out.Flush() } diff --git a/docs/content/reference/cli.md b/docs/content/reference/cli.md index 893f5f1..8dc51af 100644 --- a/docs/content/reference/cli.md +++ b/docs/content/reference/cli.md @@ -1078,6 +1078,22 @@ The global flags apply, so `-c`, `--data-dir`, and `--no-cache` all set the serv --- +## version + +```sh +ccrawl version # the one-line form +ccrawl version --short # just the version number +ccrawl version -o json # the same six fields as data +``` + +The default line is six facts glued into a sentence: `ccrawl 0.10.1 (commit 664360e, built 2026-08-13, darwin/arm64, go1.26.5)`. Pass `-o` and you get them separately, which is what a CI job wants when it needs one of them and would otherwise write a regular expression: + +```bash +ccrawl version -o json | jq -r .commit +``` + +`-o auto`, which is the default, keeps the sentence whether the output is a terminal or a pipe. Every other command switches to JSONL when piped; this one does not, because that line is what `ccrawl version` has always printed and asking for a format is how you say you want data instead. + ## config ```sh