📝 docs: add comprehensive version management and CLI documentation
This commit is contained in:
@@ -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"`
|
||||
|
||||
@@ -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
110
pkg/version/version.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user