📝 docs: add comprehensive version management and CLI documentation

This commit is contained in:
2026-04-05 11:28:11 +02:00
parent 3e8c50d80a
commit a5344d6ed8
13 changed files with 1864 additions and 1 deletions

View File

@@ -9,6 +9,8 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"DanceLessonsCoach/pkg/version"
)
// Config represents the application configuration
@@ -52,6 +54,21 @@ type APIConfig struct {
V2Enabled bool `mapstructure:"v2_enabled"`
}
// VersionInfo holds application version information
type VersionInfo struct {
Version string `mapstructure:"-"` // Set via ldflags
Commit string `mapstructure:"-"` // Set via ldflags
Date string `mapstructure:"-"` // Set via ldflags
GoVersion string `mapstructure:"-"` // Set at runtime
}
// VersionCommand handles version display
func (c *Config) VersionCommand() string {
// This will be enhanced when we integrate with cobra
return fmt.Sprintf("DanceLessonsCoach %s (commit: %s, built: %s, go: %s)",
version.Version, version.Commit, version.Date, version.GoVersion)
}
// SamplerConfig holds tracing sampler configuration
type SamplerConfig struct {
Type string `mapstructure:"type"`

View File

@@ -5,6 +5,7 @@ package server
import (
"context"
"embed"
"fmt"
"net"
"net/http"
"os/signal"
@@ -20,6 +21,7 @@ import (
"DanceLessonsCoach/pkg/greet"
"DanceLessonsCoach/pkg/telemetry"
"DanceLessonsCoach/pkg/validation"
"DanceLessonsCoach/pkg/version"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
@@ -70,6 +72,9 @@ func (s *Server) setupRoutes() {
// Readiness endpoint at root level
s.router.Get("/api/ready", s.handleReadiness)
// Version endpoint at root level
s.router.Get("/api/version", s.handleVersion)
// API routes
s.router.Route("/api/v1", func(r chi.Router) {
r.Use(s.getAllMiddlewares()...)
@@ -169,6 +174,46 @@ func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) {
}
}
// handleVersion godoc
// @Summary Get API version
// @Description Returns the API version information
// @Tags System/Version
// @Accept plain,json
// @Produce plain,json
// @Param format query string false "Response format (plain, full, json)" Enums(plain, full, json) default(plain)
// @Success 200 {string} string "Version information"
// @Router /version [get]
func (s *Server) handleVersion(w http.ResponseWriter, r *http.Request) {
log.Trace().Msg("Version check requested")
// Get format parameter
format := r.URL.Query().Get("format")
if format == "" {
format = "plain" // default format
}
switch format {
case "plain":
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(version.Short()))
case "full":
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(version.Full()))
case "json":
w.Header().Set("Content-Type", "application/json")
jsonResponse := fmt.Sprintf(`{
"version": "%s",
"commit": "%s",
"built": "%s",
"go": "%s"
}`, version.Version, version.Commit, version.Date, version.GoVersion)
w.Write([]byte(jsonResponse))
default:
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(version.Short()))
}
}
func (s *Server) Router() http.Handler {
return s.router
}

110
pkg/version/version.go Normal file
View File

@@ -0,0 +1,110 @@
// Package version provides version information and management for DanceLessonsCoach
package version
import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
)
// Version information - updated during build or from VERSION file
var (
Version = "1.0.0" // Semantic version (MAJOR.MINOR.PATCH)
Commit = "" // Git commit hash
Date = "" // Build date
GoVersion = runtime.Version() // Go version used to build
)
// init reads version from VERSION file if ldflags not used
func init() {
// Only read from VERSION file if Version is still default value
// This allows ldflags to override during build
if Version == "1.0.0" && Commit == "" && Date == "" {
readVersionFromFile()
}
}
// readVersionFromFile reads version info from VERSION file
func readVersionFromFile() {
data, err := os.ReadFile("VERSION")
if err != nil {
// File not found or can't read - keep default values
return
}
lines := strings.Split(string(data), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "MAJOR=") {
Version = strings.TrimPrefix(line, "MAJOR=")
} else if strings.HasPrefix(line, "MINOR=") {
// Append minor to version
minor := strings.TrimPrefix(line, "MINOR=")
if Version != "1.0.0" {
Version = Version + "." + minor
}
} else if strings.HasPrefix(line, "PATCH=") {
// Append patch to version
patch := strings.TrimPrefix(line, "PATCH=")
if Version != "1.0.0" {
Version = Version + "." + patch
}
} else if strings.HasPrefix(line, "PRERELEASE=") {
pre := strings.TrimPrefix(line, "PRERELEASE=")
pre = strings.Trim(pre, `"`)
if pre != "" && Version != "1.0.0" {
Version = Version + "-" + pre
}
}
}
// Try to get git commit when running in development (go run)
if Commit == "" {
getGitCommit()
}
// Try to get build date when running in development (go run)
if Date == "" {
getBuildDate()
}
}
// getGitCommit tries to get the current git commit hash
func getGitCommit() {
cmd := exec.Command("git", "rev-parse", "--short", "HEAD")
output, err := cmd.Output()
if err == nil {
Commit = strings.TrimSpace(string(output))
}
}
// getBuildDate tries to get the current build date
func getBuildDate() {
cmd := exec.Command("date", "-u", "+%Y-%m-%dT%H:%M:%SZ")
output, err := cmd.Output()
if err == nil {
Date = strings.TrimSpace(string(output))
}
}
// Info returns formatted version information
func Info() string {
return fmt.Sprintf("DanceLessonsCoach %s (commit: %s, built: %s UTC, go: %s)", Version, Commit, Date, GoVersion)
}
// Short returns just the version number
func Short() string {
return Version
}
// Full returns detailed version information
func Full() string {
return fmt.Sprintf(`DanceLessonsCoach Version Information:
Version: %s
Commit: %s
Built: %s (UTC)
Go: %s`,
Version, Commit, Date, GoVersion)
}