mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
* 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
255 lines
6.9 KiB
Go
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
|
|
}
|