# 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 ```yaml # 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.3` → `2.0.0` - **MINOR**: New features, backward compatible - New endpoints added - New optional parameters - New response fields - Example: `1.2.3` → `1.3.0` - **PATCH**: Bug fixes only - No API contract changes - Performance improvements - Security patches - Example: `1.2.3` → `1.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.1` → `1.0.0` - **MINOR**: New features - New commands - New flags (backward compatible) - Can use new API features if available - Example: `0.8.1` → `0.9.0` - **PATCH**: Bug fixes and improvements - Error message improvements - Performance fixes - Documentation updates - Example: `0.8.1` → `0.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 ```makefile # 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 ```go // 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 ```go // 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 ```bash #!/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 ```bash #!/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 ```bash # 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) ```bash # 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 ```bash # 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 ```bash # 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.