Add ReleaseKit version reporting

This commit is contained in:
2026-10-07 13:51:52 +00:00
parent 721365e59d
commit 7e7940db9a
5 changed files with 131 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
# Implementation Plan: ReleaseKit Adoption
**Last updated:** 2026-10-07
**Status:** Ready for implementation; no stages completed.
**Status:** Stage 1 complete; Stages 2–5 pending.
**Audience:** Luna coding agent.
## Scope and Execution
@@ -64,7 +64,7 @@ Gitea with delivery credentials, or wait for CI. Deployment observations belong
to the first adopting release and its separate delivery status. No extra release
gates or platform guarantees are introduced by these adoption checks.
## Stage 1 — Add the Dependency and Version Command
## Stage 1 — Add the Dependency and Version Command [x]
**Primary files:** `go.mod`, `go.sum`, `internal/cli/run.go`, and focused tests in
`internal/cli`.
@@ -92,6 +92,12 @@ gates or platform guarantees are introduced by these adoption checks.
module diff, and commit. Real release-version injection is verified in Stage 5;
no test should hardcode an upstream private linker symbol.
**Completed 2026-10-07:** Added ReleaseKit v0.1.0 and the configuration-free
`audita version` command with root/help routing and usage-error handling.
Checks passed: `go test ./internal/cli ./cmd/audita`,
`go build -o ./bin/audita ./cmd/audita`, `./bin/audita version`, and
`git diff --check`.
## Stage 2 — Introduce the Product Profile and Thin Local Wrapper
**Primary files:** new `releasekit.json`, `scripts/release.sh`, and

1
go.mod
View File

@@ -4,6 +4,7 @@ go 1.25.5
require (
gitea.maximumdirect.net/eric/ConfigKit v0.5.0
gitea.maximumdirect.net/eric/ReleaseKit v0.1.0
gitea.maximumdirect.net/eric/promptkit v0.12.0
gopkg.in/yaml.v3 v3.0.1
)

2
go.sum
View File

@@ -1,5 +1,7 @@
gitea.maximumdirect.net/eric/ConfigKit v0.5.0 h1:2KwG10+kApWM3avOoYA+p768xcbHUp7tQ2rqizBNpyg=
gitea.maximumdirect.net/eric/ConfigKit v0.5.0/go.mod h1:tYWpsrbLyu05r8+1MapWp5fN6ZrX9I8hU2xEPedYmYk=
gitea.maximumdirect.net/eric/ReleaseKit v0.1.0 h1:Ejk85+9Of+A8M7Mod91iB3KxrrQYgnJcz9hPA6fL1a4=
gitea.maximumdirect.net/eric/ReleaseKit v0.1.0/go.mod h1:KmH607Vc/RK/zKrCB96cWh0QZYvkbxBFvtqV+BkKaxs=
gitea.maximumdirect.net/eric/promptkit v0.12.0 h1:oIFWEg6GPxtmX3Plqv0euP+FnK1LCk/t4xDy6oKCpP4=
gitea.maximumdirect.net/eric/promptkit v0.12.0/go.mod h1:pFOOy5v1zH17stEi9rmHYmfbuLjnfVMcUhfmk+4VtJY=
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v3 v3.20260930.0 h1:lzfCDbzJQRGzIlRPRprnAV9hVVL5zUQy80VMQ0kbYa0=

View File

@@ -2,9 +2,11 @@ package cli
import (
"context"
"flag"
"fmt"
"io"
"gitea.maximumdirect.net/eric/ReleaseKit/buildinfo"
"gitea.maximumdirect.net/eric/audita/internal/core/schema"
"gitea.maximumdirect.net/eric/audita/internal/framework/llm"
)
@@ -42,6 +44,9 @@ func Run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
case "config":
writeConfigUsage(stdout)
return 0
case "version":
writeVersionUsage(stdout)
return 0
}
}
@@ -56,12 +61,47 @@ func Run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
if args[0] == "config" {
return runConfig(ctx, args[1:], stdout, stderr)
}
if args[0] == "version" {
return runVersion(args[1:], stdout, stderr)
}
fmt.Fprintf(stderr, "audita: unknown command %q\n\n", args[0])
writeRootUsage(stderr)
return 2
}
func runVersion(args []string, stdout, stderr io.Writer) int {
if len(args) == 1 && (args[0] == "-h" || args[0] == "--help") {
writeVersionUsage(stdout)
return 0
}
for _, arg := range args {
if arg == "-h" || arg == "--help" {
fmt.Fprintln(stderr, "audita version: help flags cannot be combined with other arguments")
fmt.Fprintln(stderr)
writeVersionUsage(stderr)
return 2
}
}
flags := flag.NewFlagSet("version", flag.ContinueOnError)
flags.SetOutput(stderr)
flags.Usage = func() { writeVersionUsage(stderr) }
if err := flags.Parse(args); err != nil {
if err == flag.ErrHelp {
writeVersionUsage(stdout)
return 0
}
return 2
}
if positional := flags.Args(); len(positional) > 0 {
fmt.Fprintf(stderr, "audita version: unexpected argument %q\n\n", positional[0])
writeVersionUsage(stderr)
return 2
}
fmt.Fprintf(stdout, "audita %s\n", buildinfo.Version())
return 0
}
func runConfig(ctx context.Context, args []string, stdout, stderr io.Writer) int {
if len(args) == 0 || isHelpCommand(args) || args[0] == "--help" || args[0] == "-h" {
writeConfigUsage(stdout)
@@ -177,11 +217,19 @@ func writeRootUsage(w io.Writer) {
fmt.Fprintln(w, "Commands:")
fmt.Fprintln(w, " process Process a transcript JSON file")
fmt.Fprintln(w, " config Validate and inspect config")
fmt.Fprintln(w, " version Print the Audita version")
fmt.Fprintln(w)
fmt.Fprintln(w, "Example:")
fmt.Fprintln(w, " audita process transcript.json --glossary glossary.yaml --output corrected.json")
}
func writeVersionUsage(w io.Writer) {
fmt.Fprintln(w, "Print the Audita version.")
fmt.Fprintln(w)
fmt.Fprintln(w, "Usage:")
fmt.Fprintln(w, " audita version")
}
func writeConfigUsage(w io.Writer) {
fmt.Fprintln(w, "Validate and inspect Audita config.")
fmt.Fprintln(w)

View File

@@ -0,0 +1,72 @@
package cli
import (
"bytes"
"context"
"fmt"
"strings"
"testing"
"gitea.maximumdirect.net/eric/ReleaseKit/buildinfo"
)
func TestVersionCommandRunsWithoutApplicationSetup(t *testing.T) {
t.Chdir(t.TempDir())
var stdout, stderr bytes.Buffer
if code := Run(context.Background(), []string{"version"}, &stdout, &stderr); code != 0 {
t.Fatalf("exit=%d, stdout=%q, stderr=%q", code, stdout.String(), stderr.String())
}
if got, want := stdout.String(), fmt.Sprintf("audita %s\n", buildinfo.Version()); got != want {
t.Fatalf("stdout=%q, want %q", got, want)
}
if got := stderr.String(); got != "" {
t.Fatalf("stderr=%q, want empty", got)
}
}
func TestVersionCommandHelp(t *testing.T) {
for _, test := range []struct {
name string
args []string
want string
}{
{name: "root help", args: []string{"--help"}, want: "version Print the Audita version"},
{name: "help version", args: []string{"help", "version"}, want: "audita version"},
{name: "short flag", args: []string{"version", "-h"}, want: "audita version"},
{name: "long flag", args: []string{"version", "--help"}, want: "audita version"},
} {
t.Run(test.name, func(t *testing.T) {
var stdout, stderr bytes.Buffer
if code := Run(context.Background(), test.args, &stdout, &stderr); code != 0 {
t.Fatalf("exit=%d, stdout=%q, stderr=%q", code, stdout.String(), stderr.String())
}
if !strings.Contains(stdout.String(), test.want) {
t.Fatalf("stdout=%q, want %q", stdout.String(), test.want)
}
if got := stderr.String(); got != "" {
t.Fatalf("stderr=%q, want empty", got)
}
})
}
}
func TestVersionCommandRejectsUnexpectedArguments(t *testing.T) {
for _, args := range [][]string{
{"version", "extra"},
{"version", "--unknown"},
{"version", "--help", "extra"},
} {
t.Run(strings.Join(args, " "), func(t *testing.T) {
var stdout, stderr bytes.Buffer
if code := Run(context.Background(), args, &stdout, &stderr); code != 2 {
t.Fatalf("exit=%d, stdout=%q, stderr=%q", code, stdout.String(), stderr.String())
}
if got := stdout.String(); got != "" {
t.Fatalf("stdout=%q, want empty", got)
}
if got := stderr.String(); got == "" {
t.Fatal("expected usage error on stderr")
}
})
}
}