mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-12 05:45:27 +00:00
221 lines
No EOL
5.2 KiB
Go
221 lines
No EOL
5.2 KiB
Go
package updates
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Version represents a semantic version
|
|
type Version struct {
|
|
Major int
|
|
Minor int
|
|
Patch int
|
|
Prerelease string
|
|
Build string
|
|
}
|
|
|
|
// VersionInfo contains detailed version information
|
|
type VersionInfo struct {
|
|
Version string `json:"version"`
|
|
Build string `json:"build"`
|
|
Runtime string `json:"runtime"`
|
|
Channel string `json:"channel,omitempty"`
|
|
IsDocker bool `json:"isDocker"`
|
|
IsDevelopment bool `json:"isDevelopment"`
|
|
}
|
|
|
|
// ParseVersion parses a version string into a Version struct
|
|
func ParseVersion(versionStr string) (*Version, error) {
|
|
// Remove 'v' prefix if present
|
|
versionStr = strings.TrimPrefix(versionStr, "v")
|
|
|
|
// Regular expression for semantic versioning
|
|
re := regexp.MustCompile(`^(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?(?:\+(.+))?$`)
|
|
matches := re.FindStringSubmatch(versionStr)
|
|
|
|
if len(matches) == 0 {
|
|
return nil, fmt.Errorf("invalid version format: %s", versionStr)
|
|
}
|
|
|
|
major, _ := strconv.Atoi(matches[1])
|
|
minor, _ := strconv.Atoi(matches[2])
|
|
patch, _ := strconv.Atoi(matches[3])
|
|
|
|
return &Version{
|
|
Major: major,
|
|
Minor: minor,
|
|
Patch: patch,
|
|
Prerelease: matches[4],
|
|
Build: matches[5],
|
|
}, nil
|
|
}
|
|
|
|
// String returns the string representation of the version
|
|
func (v *Version) String() string {
|
|
version := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
|
|
if v.Prerelease != "" {
|
|
version += "-" + v.Prerelease
|
|
}
|
|
if v.Build != "" {
|
|
version += "+" + v.Build
|
|
}
|
|
return version
|
|
}
|
|
|
|
// Compare compares two versions
|
|
// Returns:
|
|
// -1 if v < other
|
|
// 0 if v == other
|
|
// 1 if v > other
|
|
func (v *Version) Compare(other *Version) int {
|
|
if v.Major != other.Major {
|
|
return compareInts(v.Major, other.Major)
|
|
}
|
|
if v.Minor != other.Minor {
|
|
return compareInts(v.Minor, other.Minor)
|
|
}
|
|
if v.Patch != other.Patch {
|
|
return compareInts(v.Patch, other.Patch)
|
|
}
|
|
|
|
// Handle prerelease versions
|
|
if v.Prerelease == "" && other.Prerelease != "" {
|
|
return 1 // v is release, other is prerelease
|
|
}
|
|
if v.Prerelease != "" && other.Prerelease == "" {
|
|
return -1 // v is prerelease, other is release
|
|
}
|
|
if v.Prerelease != other.Prerelease {
|
|
// Try to parse RC numbers for comparison
|
|
vRC := extractRCNumber(v.Prerelease)
|
|
otherRC := extractRCNumber(other.Prerelease)
|
|
if vRC >= 0 && otherRC >= 0 {
|
|
return compareInts(vRC, otherRC)
|
|
}
|
|
return strings.Compare(v.Prerelease, other.Prerelease)
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// IsNewerThan returns true if v is newer than other
|
|
func (v *Version) IsNewerThan(other *Version) bool {
|
|
return v.Compare(other) > 0
|
|
}
|
|
|
|
// IsPrerelease returns true if this is a prerelease version
|
|
func (v *Version) IsPrerelease() bool {
|
|
return v.Prerelease != ""
|
|
}
|
|
|
|
// GetCurrentVersion gets the current running version
|
|
func GetCurrentVersion() (*VersionInfo, error) {
|
|
// Try to get version from git first (development)
|
|
gitVersion, err := getGitVersion()
|
|
if err == nil && gitVersion != "" {
|
|
return &VersionInfo{
|
|
Version: gitVersion,
|
|
Build: "development",
|
|
Runtime: "go",
|
|
IsDevelopment: true,
|
|
IsDocker: isDockerEnvironment(),
|
|
}, nil
|
|
}
|
|
|
|
// Try to read from VERSION file in multiple locations
|
|
versionPaths := []string{
|
|
"VERSION",
|
|
"/opt/pulse/VERSION",
|
|
filepath.Join(filepath.Dir(os.Args[0]), "VERSION"),
|
|
}
|
|
|
|
for _, path := range versionPaths {
|
|
versionBytes, err := os.ReadFile(path)
|
|
if err == nil {
|
|
return &VersionInfo{
|
|
Version: strings.TrimSpace(string(versionBytes)),
|
|
Build: "release",
|
|
Runtime: "go",
|
|
IsDevelopment: false,
|
|
IsDocker: isDockerEnvironment(),
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// Final fallback
|
|
return &VersionInfo{
|
|
Version: "4.2.0",
|
|
Build: "release",
|
|
Runtime: "go",
|
|
IsDevelopment: false,
|
|
IsDocker: isDockerEnvironment(),
|
|
}, nil
|
|
}
|
|
|
|
// getGitVersion gets version information from git
|
|
func getGitVersion() (string, error) {
|
|
// Get the latest tag
|
|
cmd := exec.Command("git", "describe", "--tags", "--always", "--dirty")
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
version := strings.TrimSpace(string(output))
|
|
if version == "" {
|
|
return "", fmt.Errorf("no version found")
|
|
}
|
|
|
|
return version, nil
|
|
}
|
|
|
|
// isDockerEnvironment checks if running in Docker
|
|
func isDockerEnvironment() bool {
|
|
// Check for Docker-specific files
|
|
if fileExists("/.dockerenv") {
|
|
return true
|
|
}
|
|
|
|
// Check cgroup for Docker
|
|
data, err := exec.Command("cat", "/proc/1/cgroup").Output()
|
|
if err == nil && strings.Contains(string(data), "docker") {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// fileExists checks if a file exists
|
|
func fileExists(path string) bool {
|
|
cmd := exec.Command("test", "-f", path)
|
|
return cmd.Run() == nil
|
|
}
|
|
|
|
// compareInts compares two integers
|
|
func compareInts(a, b int) int {
|
|
if a < b {
|
|
return -1
|
|
}
|
|
if a > b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// extractRCNumber extracts the RC number from a prerelease string like "rc.9" or "rc9"
|
|
func extractRCNumber(prerelease string) int {
|
|
re := regexp.MustCompile(`rc\.?(\d+)`)
|
|
matches := re.FindStringSubmatch(strings.ToLower(prerelease))
|
|
if len(matches) > 1 {
|
|
num, err := strconv.Atoi(matches[1])
|
|
if err == nil {
|
|
return num
|
|
}
|
|
}
|
|
return -1
|
|
} |