1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00
Files
DankMaterialShell/core/internal/version/version.go
Saurabh de8f2e6a68 feat/matugen3 (#771)
* added matugen 3 terminal templates and logic

fixed version check and light terminal check

refactored json generation

fixed syntax

keep tmp debug

fixed file outputs

fixed syntax issues and implicit passing

added debug stderr output

* moved calls to matugen after template is built correctly

added --json hex

disabled debug message

cleaned up code into modular functions, re-added second full matugen call

fixed args

added shift

commented vs code section

debug changes

* arg format fixes

fixed json import flag

fixed string quotation

fix arg order

* cleaned up

fix cfg naming

* removed mt2.0 templates and refactored worker

removed/replaced matugen 2 templates

fix formatter diffs + consistent styling

* fixed last json output

* fixed syntax error

* vs code templates

* matugen: inject all stock/custom theme colors as overrides
- also some general architectural changes

* dank16: remove vscode enrich option

---------

Co-authored-by: bbedward
2025-11-26 16:34:53 -05:00

255 lines
6.9 KiB
Go

package version
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
type VersionInfo struct {
Current string
Latest string
IsGit bool
IsBranch bool
IsTag bool
HasUpdate bool
}
// VersionFetcher is an interface for fetching version information
type VersionFetcher interface {
GetCurrentVersion(dmsPath string) (string, error)
GetLatestVersion(dmsPath string) (string, error)
}
// DefaultVersionFetcher is the default implementation that uses git/curl
type DefaultVersionFetcher struct{}
func (d *DefaultVersionFetcher) GetCurrentVersion(dmsPath string) (string, error) {
if _, err := os.Stat(filepath.Join(dmsPath, ".git")); err == nil {
originalDir, err := os.Getwd()
if err != nil {
return "", err
}
defer os.Chdir(originalDir)
if err := os.Chdir(dmsPath); err != nil {
return "", fmt.Errorf("failed to change to DMS directory: %w", err)
}
tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD")
if tagOutput, err := tagCmd.Output(); err == nil {
return strings.TrimSpace(string(tagOutput)), nil
}
branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
if branchOutput, err := branchCmd.Output(); err == nil {
branch := strings.TrimSpace(string(branchOutput))
revCmd := exec.Command("git", "rev-parse", "--short", "HEAD")
if revOutput, err := revCmd.Output(); err == nil {
rev := strings.TrimSpace(string(revOutput))
return fmt.Sprintf("%s@%s", branch, rev), nil
}
return branch, nil
}
}
cmd := exec.Command("dms", "--version")
if output, err := cmd.Output(); err == nil {
return strings.TrimSpace(string(output)), nil
}
return "unknown", nil
}
func (d *DefaultVersionFetcher) GetLatestVersion(dmsPath string) (string, error) {
if _, err := os.Stat(filepath.Join(dmsPath, ".git")); err == nil {
originalDir, err := os.Getwd()
if err != nil {
return "", err
}
defer os.Chdir(originalDir)
if err := os.Chdir(dmsPath); err != nil {
return "", fmt.Errorf("failed to change to DMS directory: %w", err)
}
currentRefCmd := exec.Command("git", "symbolic-ref", "-q", "HEAD")
currentRefOutput, _ := currentRefCmd.Output()
onBranch := len(currentRefOutput) > 0
if !onBranch {
tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD")
if _, err := tagCmd.Output(); err == nil {
// Add timeout to git fetch to prevent hanging
fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", "--tags", "--quiet")
fetchCmd.Run()
latestTagCmd := exec.Command("git", "tag", "-l", "v*", "--sort=-version:refname")
latestTagOutput, err := latestTagCmd.Output()
if err != nil {
return "", fmt.Errorf("failed to get latest tag: %w", err)
}
tags := strings.Split(strings.TrimSpace(string(latestTagOutput)), "\n")
if len(tags) == 0 || tags[0] == "" {
return "", fmt.Errorf("no version tags found")
}
return tags[0], nil
}
} else {
branchCmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
branchOutput, err := branchCmd.Output()
if err != nil {
return "", fmt.Errorf("failed to get current branch: %w", err)
}
currentBranch := strings.TrimSpace(string(branchOutput))
// Add timeout to git fetch to prevent hanging
fetchCmd := exec.Command("timeout", "5s", "git", "fetch", "origin", currentBranch, "--quiet")
fetchCmd.Run()
remoteRevCmd := exec.Command("git", "rev-parse", "--short", fmt.Sprintf("origin/%s", currentBranch))
remoteRevOutput, err := remoteRevCmd.Output()
if err != nil {
return "", fmt.Errorf("failed to get remote revision: %w", err)
}
remoteRev := strings.TrimSpace(string(remoteRevOutput))
return fmt.Sprintf("%s@%s", currentBranch, remoteRev), nil
}
}
// Add timeout to prevent hanging when GitHub is down
cmd := exec.Command("curl", "-s", "--max-time", "5", "https://api.github.com/repos/AvengeMedia/DankMaterialShell/releases/latest")
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to fetch latest release: %w", err)
}
var result struct {
TagName string `json:"tag_name"`
}
if err := json.Unmarshal(output, &result); err != nil {
for _, line := range strings.Split(string(output), "\n") {
if strings.Contains(line, "\"tag_name\"") {
parts := strings.Split(line, "\"")
if len(parts) >= 4 {
return parts[3], nil
}
}
}
return "", fmt.Errorf("failed to parse latest version: %w", err)
}
return result.TagName, nil
}
// defaultFetcher is used by the public functions
var defaultFetcher VersionFetcher = &DefaultVersionFetcher{}
func GetCurrentDMSVersion() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("failed to get home directory: %w", err)
}
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
if _, err := os.Stat(dmsPath); os.IsNotExist(err) {
return "", fmt.Errorf("DMS not installed")
}
return defaultFetcher.GetCurrentVersion(dmsPath)
}
func GetLatestDMSVersion() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("failed to get home directory: %w", err)
}
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
return defaultFetcher.GetLatestVersion(dmsPath)
}
func GetDMSVersionInfo() (*VersionInfo, error) {
return GetDMSVersionInfoWithFetcher(defaultFetcher)
}
func GetDMSVersionInfoWithFetcher(fetcher VersionFetcher) (*VersionInfo, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("failed to get home directory: %w", err)
}
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
if _, err := os.Stat(dmsPath); os.IsNotExist(err) {
return nil, fmt.Errorf("DMS not installed")
}
current, err := fetcher.GetCurrentVersion(dmsPath)
if err != nil {
return nil, err
}
latest, err := fetcher.GetLatestVersion(dmsPath)
if err != nil {
return nil, fmt.Errorf("failed to get latest version: %w", err)
}
info := &VersionInfo{
Current: current,
Latest: latest,
IsGit: strings.Contains(current, "@"),
IsBranch: strings.Contains(current, "@"),
IsTag: !strings.Contains(current, "@") && strings.HasPrefix(current, "v"),
}
if info.IsBranch {
parts := strings.Split(current, "@")
latestParts := strings.Split(latest, "@")
if len(parts) == 2 && len(latestParts) == 2 {
info.HasUpdate = parts[1] != latestParts[1]
}
} else if info.IsTag {
info.HasUpdate = current != latest
} else {
info.HasUpdate = false
}
return info, nil
}
func CompareVersions(v1, v2 string) int {
v1 = strings.TrimPrefix(v1, "v")
v2 = strings.TrimPrefix(v2, "v")
parts1 := strings.Split(v1, ".")
parts2 := strings.Split(v2, ".")
maxLen := len(parts1)
if len(parts2) > maxLen {
maxLen = len(parts2)
}
for i := 0; i < maxLen; i++ {
var p1, p2 int
if i < len(parts1) {
fmt.Sscanf(parts1[i], "%d", &p1)
}
if i < len(parts2) {
fmt.Sscanf(parts2[i], "%d", &p2)
}
if p1 < p2 {
return -1
}
if p1 > p2 {
return 1
}
}
return 0
}