Files
wild-cloud-dev/future/independent-versioning.md

15 KiB

Independent Component Versioning (Future Design)

This document describes a future enhancement for Wild Cloud where each component (API, CLI, Web) can have independent versions while still being bundled together in versioned packages.

Status: Design document for future implementation Current Implementation: Unified versioning (single VERSION file) Migration Path: Can be implemented once we reach v1.0.0 and need API stability


Why Independent Versioning?

Current Limitations (Unified Versioning)

  • CLI can't be updated without releasing entire package
  • API bug fix requires new package even if CLI/Web unchanged
  • Can't signal API breaking changes vs CLI enhancements independently
  • Users can't install just CLI on CI machines with specific version

Benefits of Independent Versioning

  • CLI Independence: Download/install CLI separately for CI/automation
  • API Stability Signals: API version communicates breaking changes
  • Flexible Release Cadence: Update components on different schedules
  • Better Compatibility Communication: "CLI v0.9 works with API v1.2-1.3"
  • Reduced Package Churn: Don't republish entire package for small fixes

Architecture Overview

Version Files (4 files)

wild-cloud/
├── VERSION                    # Package/distribution version: 0.1.1
├── api/VERSION               # API version: 1.2.3
├── cli/VERSION               # CLI version: 0.8.1
└── web/VERSION               # Web version: 0.5.2

Compatibility Matrix

# wild-cloud/MANIFEST.yaml
package:
  version: "0.1.1"
  name: "wild-cloud-central"
  description: "Wild Cloud Central Management Service"

components:
  api:
    version: "1.2.3"
    compatibility:
      min_cli_version: "0.7.0"    # Oldest CLI that works
      max_cli_version: "0.9.x"    # Newest CLI tested

  cli:
    version: "0.8.1"
    compatibility:
      min_api_version: "1.1.0"    # Oldest API supported
      max_api_version: "1.2.x"    # Newest API supported

  web:
    version: "0.5.2"
    compatibility:
      requires_api_version: "1.2.3"  # Exact API version required

# What combinations have been tested and certified
tested_combinations:
  - package: "0.1.1"
    api: "1.2.3"
    cli: "0.8.1"
    web: "0.5.2"
    notes: "Stable release, all features tested"

  - package: "0.1.0"
    api: "1.2.2"
    cli: "0.8.0"
    web: "0.5.1"
    notes: "Previous stable release"

Version Semantics

API Version (Server/Backend)

Format: MAJOR.MINOR.PATCH

  • MAJOR: Breaking API changes

    • Endpoints removed or renamed
    • Request/response format changes
    • Authentication mechanism changes
    • Example: 1.2.32.0.0
  • MINOR: New features, backward compatible

    • New endpoints added
    • New optional parameters
    • New response fields
    • Example: 1.2.31.3.0
  • PATCH: Bug fixes only

    • No API contract changes
    • Performance improvements
    • Security patches
    • Example: 1.2.31.2.4

Stability Promise:

  • CLI version 0.8.x guaranteed to work with API 1.2.x
  • CLI version 0.8.x should work with API 1.3.x (newer minor)
  • CLI version 0.8.x will NOT work with API 2.0.x (major bump)

CLI Version (Client Tool)

Format: MAJOR.MINOR.PATCH

  • MAJOR: Breaking CLI changes

    • Command structure changes
    • Flag renames or removal
    • Configuration format changes
    • Example: 0.8.11.0.0
  • MINOR: New features

    • New commands
    • New flags (backward compatible)
    • Can use new API features if available
    • Example: 0.8.10.9.0
  • PATCH: Bug fixes and improvements

    • Error message improvements
    • Performance fixes
    • Documentation updates
    • Example: 0.8.10.8.2

Compatibility Promise:

  • CLI gracefully degrades when API doesn't support new features
  • wild app deploy with new flag works with old API (ignores new features)
  • CLI checks API version and warns about incompatibilities

Web Version (Frontend UI)

Format: MAJOR.MINOR.PATCH

  • MAJOR: Major UI redesign or breaking changes
  • MINOR: New features, new pages
  • PATCH: Bug fixes, UX improvements

Note: Web is typically tightly coupled to API and bundled together. Rarely released independently.

Package Version (Distribution)

Format: MAJOR.MINOR.PATCH

  • MAJOR: Major milestone releases (v1.0.0, v2.0.0)
  • MINOR: New features or significant updates
  • PATCH: Bug fixes, component version bumps

Purpose: Tracks the tested, certified combination of components.


Implementation Details

1. Build System Changes

Makefile Reads All Versions

# Read all version files
PACKAGE_VERSION := $(shell cat ../VERSION 2>/dev/null || echo "0.0.0-dev")
API_VERSION := $(shell cat ../api/VERSION 2>/dev/null || echo "0.0.0-dev")
CLI_VERSION := $(shell cat ../cli/VERSION 2>/dev/null || echo "0.0.0-dev")
WEB_VERSION := $(shell cat ../web/VERSION 2>/dev/null || echo "0.0.0-dev")

# Version info target
version:
	@echo "Package: v$(PACKAGE_VERSION)"
	@echo "  API:   v$(API_VERSION)"
	@echo "  CLI:   v$(CLI_VERSION)"
	@echo "  Web:   v$(WEB_VERSION)"

# Build with component versions injected
build-api:
	cd $(API_SOURCE) && GOOS=linux GOARCH=amd64 \
		go build -ldflags="-X main.Version=$(API_VERSION) -X main.PackageVersion=$(PACKAGE_VERSION)" \
		-o ../dist/$(AMD64_BINARY) .

build-cli:
	cd ../cli && GOOS=linux GOARCH=amd64 \
		go build -ldflags="-X main.Version=$(CLI_VERSION)" \
		-o ../dist/build/wild-cli-$(CLI_VERSION)-amd64 .

2. CLI Version Command with Compatibility Check

// cli/cmd/version.go
package cmd

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/spf13/cobra"
)

var Version = "dev" // Injected at build time

type APIVersion struct {
	Version       string `json:"version"`
	MinCLIVersion string `json:"minCliVersion"`
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Show version information and API compatibility",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("Wild CLI v%s\n", Version)

		// Try to get API version if connected
		apiURL := getAPIURL() // From config
		if apiURL != "" {
			apiVersion, err := getAPIVersion(apiURL)
			if err == nil {
				compatible := checkCompatibility(Version, apiVersion)
				status := "✓"
				if !compatible {
					status = "⚠️"
				}
				fmt.Printf("Connected to API v%s %s\n", apiVersion.Version, status)

				if !compatible {
					fmt.Printf("  Warning: CLI v%s may not be fully compatible with API v%s\n",
						Version, apiVersion.Version)
					fmt.Printf("  Recommended CLI version: >= v%s\n", apiVersion.MinCLIVersion)
				}
			}
		}
	},
}

func getAPIVersion(apiURL string) (*APIVersion, error) {
	resp, err := http.Get(apiURL + "/api/v1/version")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var version APIVersion
	if err := json.NewDecoder(resp.Body).Decode(&version); err != nil {
		return nil, err
	}
	return &version, nil
}

func checkCompatibility(cliVersion string, apiVersion *APIVersion) bool {
	// Simple semantic version comparison
	// Full implementation would use proper semver library
	cliMajor := strings.Split(cliVersion, ".")[0]
	minMajor := strings.Split(apiVersion.MinCLIVersion, ".")[0]

	return cliMajor >= minMajor
}

3. API Version Endpoint

// api/internal/api/v1/handlers_version.go
package v1

import (
	"net/http"
)

var Version = "dev"        // Injected at build
var PackageVersion = "dev" // Injected at build

type VersionResponse struct {
	Version        string `json:"version"`
	PackageVersion string `json:"packageVersion"`
	MinCLIVersion  string `json:"minCliVersion"`
	MaxCLIVersion  string `json:"maxCliVersion"`
}

func (api *API) GetVersion(w http.ResponseWriter, r *http.Request) {
	respondJSON(w, http.StatusOK, VersionResponse{
		Version:        Version,
		PackageVersion: PackageVersion,
		MinCLIVersion:  "0.7.0",  // Could read from MANIFEST.yaml
		MaxCLIVersion:  "0.9.x",
	})
}

// Register route in handlers.go
// r.HandleFunc("/api/v1/version", api.GetVersion).Methods("GET")

4. Release Script Support for Component Releases

#!/bin/bash
# scripts/create-release.sh

PACKAGE_VERSION=$(cat ../VERSION)
API_VERSION=$(cat ../api/VERSION)
CLI_VERSION=$(cat ../cli/VERSION)
WEB_VERSION=$(cat ../web/VERSION)

RELEASE_TYPE="$1"  # "package", "cli", "api"

case "$RELEASE_TYPE" in
  "package")
    # Full package release
    TAG="v${PACKAGE_VERSION}"
    TITLE="Wild Cloud Central v${PACKAGE_VERSION}"
    BODY="Package includes: API v${API_VERSION}, CLI v${CLI_VERSION}, Web v${WEB_VERSION}"

    create_release "$TAG" "$TITLE" "$BODY"
    upload_debs
    ;;

  "cli")
    # CLI-only release
    TAG="cli-v${CLI_VERSION}"
    TITLE="Wild CLI v${CLI_VERSION}"
    BODY="Standalone CLI release. Compatible with API v${API_VERSION}"

    create_release "$TAG" "$TITLE" "$BODY"
    upload_cli_binaries
    ;;

  "api")
    # API update within existing package
    TAG="v${PACKAGE_VERSION}"

    if release_exists "$TAG"; then
      update_release_assets "$TAG"
    else
      echo "Package version not changed, updating existing release"
      update_release_assets "$(get_latest_release_tag)"
    fi
    ;;
esac

5. Compatibility Validation Script

#!/bin/bash
# scripts/check-compatibility.sh

MANIFEST="wild-cloud/MANIFEST.yaml"

API_VERSION=$(cat wild-cloud/api/VERSION)
CLI_VERSION=$(cat wild-cloud/cli/VERSION)
WEB_VERSION=$(cat wild-cloud/web/VERSION)

echo "Checking version compatibility..."

# Check if this combination is in tested_combinations
TESTED=$(yq eval ".tested_combinations[] | select(.api == \"$API_VERSION\" and .cli == \"$CLI_VERSION\" and .web == \"$WEB_VERSION\")" "$MANIFEST")

if [ -n "$TESTED" ]; then
  echo "✓ Component versions have been tested together"
else
  echo "⚠️  Warning: This combination has not been tested"
  echo "  API: $API_VERSION"
  echo "  CLI: $CLI_VERSION"
  echo "  Web: $WEB_VERSION"
  echo ""
  echo "Run full integration tests before release"
  exit 1
fi

# Check CLI compatibility with API
MIN_CLI=$(yq eval ".components.api.compatibility.min_cli_version" "$MANIFEST")
if version_less_than "$CLI_VERSION" "$MIN_CLI"; then
  echo "✗ CLI version $CLI_VERSION is older than minimum required $MIN_CLI"
  exit 1
fi

echo "✓ All compatibility checks passed"

Migration Path

Phase 1: Preparation (Current: v0.x.x)

  • Continue using unified VERSION file
  • Build foundation for independent versioning
  • Document API surface and breaking changes

Phase 2: Soft Migration (v0.9.x)

  • Create component VERSION files
  • Keep them in sync with package VERSION
  • Add compatibility checking code (unused)
  • Test infrastructure

Phase 3: Independent Releases (v1.0.0+)

  • Start versioning components independently
  • First CLI-only release
  • Update MANIFEST.yaml with compatibility matrix
  • Enable compatibility warnings

Phase 4: Mature State (v1.1.0+)

  • Components release on different cadences
  • API maintains backward compatibility
  • CLI works with multiple API versions
  • Well-tested compatibility matrix

Release Workflows

Workflow 1: Bug Fix in API Only

# Bump only API version
echo "1.2.4" > api/VERSION

# Optionally bump package patch version
echo "0.1.2" > VERSION

# Build and release
make package-all
make release-package  # Updates/creates v0.1.2 with API v1.2.4

Workflow 2: New CLI Feature (Standalone)

# Bump CLI version
echo "0.9.0" > cli/VERSION

# Build CLI
cd cli && make build

# Release CLI independently
make release-cli  # Creates cli-v0.9.0 tag with standalone binary

# Users can: wget .../cli-v0.9.0/wild-amd64

Workflow 3: Major Package Release

# Bump all versions
echo "2.0.0" > api/VERSION      # Breaking API changes
echo "1.0.0" > cli/VERSION      # CLI redesign
echo "1.0.0" > web/VERSION      # New UI
echo "1.0.0" > VERSION          # Major package milestone

# Update MANIFEST.yaml with new compatibility rules
vim wild-cloud/MANIFEST.yaml

# Run compatibility checks
./scripts/check-compatibility.sh

# Build and release
make package-all
make release-package  # Creates v1.0.0

Workflow 4: Hotfix in Testing

# Fix bug, no version bump
git commit -m "Fix: cluster deletion bug"

# Rebuild and update current release
make package-all
make release-package  # Updates existing release assets

Benefits Summary

For Users

  • Download just CLI for automation/CI (smaller, faster)
  • Know when API changes might break scripts (version signals)
  • Get bug fixes without reinstalling entire package
  • Clear compatibility information (wild --version)

For Developers

  • Release components independently (faster iteration)
  • Signal breaking changes appropriately (API major version)
  • Test combinations in compatibility matrix
  • Reduce package release churn

For DevOps

  • Pin CLI version in CI/CD pipelines
  • Upgrade API without updating all CLI installations
  • Clear upgrade paths and compatibility docs
  • Automated compatibility checking

Implementation Checklist

Phase 1: Foundation

  • Create component VERSION files (keep in sync initially)
  • Add version injection to all components
  • Create MANIFEST.yaml structure
  • Add GET /api/v1/version endpoint
  • Add wild version command with compatibility check

Phase 2: Build System

  • Update Makefile to read all VERSION files
  • Add compatibility validation script
  • Create release script for different release types
  • Test version injection in builds
  • Document version management workflow

Phase 3: Release Infrastructure

  • Support package releases (current behavior)
  • Support CLI-only releases (new tag structure)
  • Support updating existing release assets
  • Create GitHub/Gitea release templates
  • Add CI/CD integration for automated releases

Phase 4: Documentation

  • Document versioning strategy
  • Create compatibility matrix documentation
  • Write upgrade guides
  • Add troubleshooting for version mismatches
  • Create developer guide for version bumping

Open Questions

  1. CLI Distribution: Should CLI be in separate GitHub releases or same repo with different tags?

    • Recommendation: Same repo, different tags (cli-v0.9.0)
  2. Version Discovery: Should CLI auto-update check?

    • Recommendation: No auto-update, but check for new versions on --version
  3. API Version in URL: Should we support /api/v2/ for major versions?

    • Recommendation: Not initially, version in header is sufficient
  4. Deprecation Policy: How long to support old API versions?

    • Recommendation: N-1 major version (API v2.x supports v1.x clients)

References

  • Kubernetes: kubectl ±1 minor version skew policy
  • Docker: CLI forward compatibility with engine
  • Terraform: Provider versioning model
  • HashiCorp Consul: API versioning strategy

This design provides maximum flexibility for component independence while maintaining package simplicity during development.