[object Object]

← back to Cli Printing Press

fix(cli): default empty version to 1.0.0 and normalize to semver

f07a795de84c2321989880400c6d60f4e0c9e8a5 · 2026-04-11 12:42:46 -0700 · Trevin Chow

The spec Validate() function now defaults empty versions to "1.0.0"
and normalizes non-semver versions: "4" → "4.0.0", "4.4" → "4.4.0".

Previously, a spec with no version field passed validation and the
template rendered `var version = ""`, making --version print nothing.
Specs from OpenAPI that only had major.minor (e.g., "4.4") produced
non-semver CLI versions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit f07a795de84c2321989880400c6d60f4e0c9e8a5
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Sat Apr 11 12:42:46 2026 -0700

    fix(cli): default empty version to 1.0.0 and normalize to semver
    
    The spec Validate() function now defaults empty versions to "1.0.0"
    and normalizes non-semver versions: "4" → "4.0.0", "4.4" → "4.4.0".
    
    Previously, a spec with no version field passed validation and the
    template rendered `var version = ""`, making --version print nothing.
    Specs from OpenAPI that only had major.minor (e.g., "4.4") produced
    non-semver CLI versions.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
 internal/spec/spec.go      | 10 ++++++++++
 internal/spec/spec_test.go | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/internal/spec/spec.go b/internal/spec/spec.go
index 4963652d..e7abde8b 100644
--- a/internal/spec/spec.go
+++ b/internal/spec/spec.go
@@ -274,6 +274,16 @@ func (s *APISpec) Validate() error {
 	if s.Name == "" {
 		return fmt.Errorf("name is required")
 	}
+	// Default version to 1.0.0 if missing, and normalize to semver.
+	if s.Version == "" {
+		s.Version = "1.0.0"
+	} else if !strings.Contains(s.Version, ".") {
+		// Bare major: "4" → "4.0.0"
+		s.Version = s.Version + ".0.0"
+	} else if strings.Count(s.Version, ".") == 1 {
+		// Major.minor only: "4.4" → "4.4.0"
+		s.Version = s.Version + ".0"
+	}
 	// Parser fallback may supply a placeholder base_url when the source spec omits servers.
 	if s.BaseURL == "" && s.BasePath == "" {
 		return fmt.Errorf("base_url is required")
diff --git a/internal/spec/spec_test.go b/internal/spec/spec_test.go
index 619ea582..693320de 100644
--- a/internal/spec/spec_test.go
+++ b/internal/spec/spec_test.go
@@ -87,6 +87,38 @@ func TestValidation(t *testing.T) {
 	}
 }
 
+func TestVersionDefaultAndNormalize(t *testing.T) {
+	base := func(v string) APISpec {
+		return APISpec{
+			Name:      "x",
+			Version:   v,
+			BaseURL:   "http://x",
+			Resources: map[string]Resource{"a": {Endpoints: map[string]Endpoint{"b": {Method: "GET", Path: "/"}}}},
+		}
+	}
+
+	tests := []struct {
+		input    string
+		expected string
+	}{
+		{"", "1.0.0"},        // empty → default
+		{"1.0.0", "1.0.0"},   // already semver
+		{"4", "4.0.0"},       // bare major
+		{"4.4", "4.4.0"},     // major.minor only
+		{"4.17.1", "4.17.1"}, // already semver
+		{"0.1.0", "0.1.0"},   // already semver
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.input, func(t *testing.T) {
+			s := base(tt.input)
+			err := s.Validate()
+			require.NoError(t, err)
+			assert.Equal(t, tt.expected, s.Version)
+		})
+	}
+}
+
 func TestNewFields(t *testing.T) {
 	s := APISpec{
 		Name:    "x",

← ad9e4aee fix(cli): movie-goat retro — generator param handling, write  ·  back to Cli Printing Press  ·  fix(cli): decouple CLI version from API version 68bc76f3 →