Skip to content

Commit a6a31d5

Browse files
committed
feat: expose dryrun version in MCP; add schema version
1 parent 1174409 commit a6a31d5

6 files changed

Lines changed: 99 additions & 25 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
ext=""
4848
[ "${{ runner.os }}" = "Windows" ] && ext=".exe"
4949
CGO_ENABLED=1 go build \
50-
-ldflags "-X main.version=${GITHUB_REF_NAME}" \
50+
-ldflags "-X github.com/boringsql/dryrun/internal/buildinfo.Version=${GITHUB_REF_NAME}" \
5151
-o "dryrun${ext}" \
5252
./cmd/dryrun
5353
- name: Archive (tar.xz)

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.PHONY: build clean
2+
3+
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null)
4+
5+
build:
6+
go build -ldflags "-X github.com/boringsql/dryrun/internal/buildinfo.Version=$(VERSION)" -o bin/dryrun ./cmd/dryrun
7+
8+
clean:
9+
rm -rf bin/

cmd/dryrun/main.go

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ import (
77
"log/slog"
88
"os"
99
"path/filepath"
10-
"runtime/debug"
1110
"sort"
12-
"strings"
1311

1412
"github.com/spf13/cobra"
1513

1614
mcpserver "github.com/mark3labs/mcp-go/server"
1715

16+
"github.com/boringsql/dryrun/internal/buildinfo"
1817
"github.com/boringsql/dryrun/internal/config"
1918
"github.com/boringsql/dryrun/internal/diff"
2019
"github.com/boringsql/dryrun/internal/history"
@@ -23,22 +22,6 @@ import (
2322
"github.com/boringsql/dryrun/internal/schema"
2423
)
2524

26-
// version is set via ldflags: -X main.version=v0.1.0
27-
var version string
28-
29-
func getVersion() string {
30-
if version != "" {
31-
return version
32-
}
33-
if info, ok := debug.ReadBuildInfo(); ok {
34-
v := info.Main.Version
35-
if v != "" && v != "(devel)" && !strings.Contains(v, "0.0.0-") {
36-
return v
37-
}
38-
}
39-
return "dev"
40-
}
41-
4225
var (
4326
flagDB string
4427
flagProfile string
@@ -50,7 +33,7 @@ func main() {
5033
root := &cobra.Command{
5134
Use: "dryrun",
5235
Short: "PostgreSQL schema intelligence",
53-
Version: getVersion(),
36+
Version: buildinfo.Get(),
5437
}
5538

5639
pf := root.PersistentFlags()
@@ -62,14 +45,26 @@ func main() {
6245
root.AddCommand(
6346
probeCmd(), initCmd(), importCmd(), dumpSchemaCmd(),
6447
lintCmd(), driftCmd(), snapshotCmd(), profileCmd(),
65-
mcpServeCmd(), statsCmd(),
48+
mcpServeCmd(), statsCmd(), versionCmd(),
6649
)
6750

6851
if err := root.Execute(); err != nil {
6952
os.Exit(1)
7053
}
7154
}
7255

56+
func versionCmd() *cobra.Command {
57+
return &cobra.Command{
58+
Use: "version",
59+
Short: "Print dryrun and history schema versions",
60+
RunE: func(cmd *cobra.Command, args []string) error {
61+
fmt.Printf("dryrun %s\n", buildinfo.Get())
62+
fmt.Printf("history schema: v%d\n", history.HistorySchemaVersion)
63+
return nil
64+
},
65+
}
66+
}
67+
7368
func probeCmd() *cobra.Command {
7469
return &cobra.Command{
7570
Use: "probe",
@@ -893,7 +888,7 @@ func mcpServeCmd() *cobra.Command {
893888
server.SetUninitialized(candidates)
894889
}
895890

896-
mcpSrv := mcpserver.NewMCPServer("dryrun", getVersion(),
891+
mcpSrv := mcpserver.NewMCPServer("dryrun", buildinfo.Get(),
897892
mcpserver.WithInstructions(server.Instructions()),
898893
)
899894
server.Register(mcpSrv)

internal/buildinfo/buildinfo.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package buildinfo
2+
3+
import (
4+
"regexp"
5+
"runtime/debug"
6+
)
7+
8+
// set via ldflags: -X github.com/boringsql/dryrun/internal/buildinfo.Version=v0.9.0
9+
var Version string
10+
11+
// pseudo-version forms v0.0.0-<ts>-<sha> and vX.Y.Z-0.<ts>-<sha>
12+
var pseudoVersion = regexp.MustCompile(`-(0\.)?\d{14}-[0-9a-f]{12}(\+incompatible)?$`)
13+
14+
func Get() string {
15+
if Version != "" {
16+
return Version
17+
}
18+
// go install records the module version; pseudo-versions and "(devel)" are dev builds
19+
if info, ok := debug.ReadBuildInfo(); ok {
20+
v := info.Main.Version
21+
if v != "" && v != "(devel)" && !pseudoVersion.MatchString(v) {
22+
return v
23+
}
24+
}
25+
return "dev"
26+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package buildinfo
2+
3+
import "testing"
4+
5+
func TestGetReturnsLdflagsVersion(t *testing.T) {
6+
// Get() should hand back whatever ldflags injected, untouched.
7+
old := Version
8+
t.Cleanup(func() { Version = old })
9+
10+
Version = "v0.9.0"
11+
if got := Get(); got != "v0.9.0" {
12+
t.Fatalf("Get() = %q, want v0.9.0", got)
13+
}
14+
}
15+
16+
func TestPseudoVersionMatches(t *testing.T) {
17+
// these are the build-info Main.Version strings we must NOT report as a real
18+
// version; matching them makes Get() fall through to "dev".
19+
pseudo := []string{
20+
"v0.0.0-20260530120000-abc123456789", // untagged repo
21+
"v0.9.0-0.20260530120000-abc123456789", // tagged ancestor, newer commit
22+
"v1.2.3-0.20260530120000-abc123456789+incompatible",
23+
}
24+
for _, v := range pseudo {
25+
if !pseudoVersion.MatchString(v) {
26+
t.Errorf("expected %q to be detected as a pseudo-version", v)
27+
}
28+
}
29+
30+
real := []string{
31+
"v0.9.0",
32+
"v0.8.2",
33+
"v1.2.3-rc1",
34+
"v2.0.0+incompatible",
35+
}
36+
for _, v := range real {
37+
if pseudoVersion.MatchString(v) {
38+
t.Errorf("did not expect %q to match the pseudo-version pattern", v)
39+
}
40+
}
41+
}

internal/mcp/server.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/jackc/pgx/v5/pgxpool"
1010

11+
"github.com/boringsql/dryrun/internal/buildinfo"
1112
"github.com/boringsql/dryrun/internal/dryrun"
1213
"github.com/boringsql/dryrun/internal/history"
1314
"github.com/boringsql/dryrun/internal/lint"
@@ -183,15 +184,17 @@ func (s *Server) Instructions() string {
183184
metaNote += "\n\nWarning: " + *note + "."
184185
}
185186

187+
dv := buildinfo.Get()
188+
186189
snap, err := s.getSchema()
187190
if err != nil || snap.PgVersion == "" {
188-
return "dryrun PostgreSQL schema advisor. No schema loaded yet.\n\n" + metaNote
191+
return fmt.Sprintf("dryrun %s, PostgreSQL schema advisor. No schema loaded yet.\n\n%s", dv, metaNote)
189192
}
190193

191194
ver, err := dryrun.ParsePgVersion(snap.PgVersion)
192195
if err != nil {
193-
return fmt.Sprintf("dryrun PostgreSQL schema advisor. Database: %s\n\n%s", snap.Database, metaNote)
196+
return fmt.Sprintf("dryrun %s, PostgreSQL schema advisor. Database: %s\n\n%s", dv, snap.Database, metaNote)
194197
}
195198

196-
return fmt.Sprintf("dryrun PostgreSQL schema advisor. PostgreSQL %s; database: %s\n\n%s", ver, snap.Database, metaNote)
199+
return fmt.Sprintf("dryrun %s, PostgreSQL schema advisor. PostgreSQL %s; database: %s\n\n%s", dv, ver, snap.Database, metaNote)
197200
}

0 commit comments

Comments
 (0)