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.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.xguaranteed to work with API1.2.x - CLI version
0.8.xshould work with API1.3.x(newer minor) - CLI version
0.8.xwill NOT work with API2.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 deploywith 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/versionendpoint - Add
wild versioncommand 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
-
CLI Distribution: Should CLI be in separate GitHub releases or same repo with different tags?
- Recommendation: Same repo, different tags (
cli-v0.9.0)
- Recommendation: Same repo, different tags (
-
Version Discovery: Should CLI auto-update check?
- Recommendation: No auto-update, but check for new versions on
--version
- Recommendation: No auto-update, but check for new versions on
-
API Version in URL: Should we support
/api/v2/for major versions?- Recommendation: Not initially, version in header is sufficient
-
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.