aboutsummaryrefslogtreecommitdiff
path: root/internal/yae/source.go
blob: 185aba79fa4ce692c271e625d6db93910264b401 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package yae

import (
	"fmt"
	"strings"

	"github.com/charmbracelet/log"
)

type Source struct {
	URL           string `json:"url"`
	SHA256        string `json:"sha256"`
	Unpack        bool   `json:"unpack"`
	Type          string `json:"type"`
	Version       string `json:"version,omitempty"`
	URLTemplate   string `json:"url_template,omitempty"`
	TagPredicate  string `json:"tag_predicate,omitempty"`
	TrimTagPrefix string `json:"trim_tag_prefix,omitempty"`
	Pinned        bool   `json:"pinned,omitempty"`
	Force         bool   `json:"force,omitempty"`
}

func (source *Source) Update(sources *Sources, name string, force bool, forcePinned bool) (bool, error) {
	log.Infof("checking %s", name)

	updated := false

	if !sources.Exists(name) {
		log.Warnf("skipped %s: source does not exist", name)

		return updated, nil
	}

	if source.Pinned && !forcePinned {
		log.Infof("skipped %s: source is pinned", name)

		return updated, nil
	}

	if source.Type == "git" {
		log.Debugf("checking %s: remote git tag", name)

		tag, err := source.fetchLatestGitTag()

		if err != nil {
			return updated, err
		}

		if tag != source.Version || force || source.Force {
			if tag != source.Version {
				log.Infof("bumped %s: %s -> %s", name, source.Version, tag)
			}

			if tag != source.Version {
				updated = true
			}

			source.Version = tag

			if strings.Contains(source.URLTemplate, "{version}") {
				source.URL = strings.ReplaceAll(source.URLTemplate, "{version}", source.Version)

				log.Debugf("patched %s: substituted url template", name)
			}
		} else {
			log.Infof("skipped %s: version remains unchanged", name)

			return updated, nil
		}
	}

	log.Debugf("checking %s: sha256", name)

	sha256, err := FetchSHA256(source.URL, source.Unpack)

	if err != nil {
		return updated, err
	}

	if sha256 != source.SHA256 {
		log.Infof("rehashed %s: %s -> %s", name, source.SHA256, sha256)

		source.SHA256 = sha256
		updated = true
	}

	(*sources)[name] = *source

	return updated, nil
}

func (source *Source) fetchLatestGitTag() (string, error) {
	if source.Type == "git" {
		repository := "https://github.com/" + strings.Split(source.URL, "/")[3] + "/" + strings.Split(source.URL, "/")[4]
		remotes, err := command("bash", false, "-c", fmt.Sprintf("git ls-remote %s | awk -F'/' '{print $NF}' | sort -V", repository))

		if err != nil {
			return "", err
		}

		refs := strings.Split(remotes, "\n")
		var latest string

		if source.TagPredicate == "" {
			latest = refs[len(refs)-2]
		} else {
			for i := len(refs) - 2; i >= 0; i-- {
				if strings.Contains(refs[i], source.TagPredicate) {
					latest = refs[i]

					break
				}
			}
		}

		if source.TrimTagPrefix != "" {
			latest = strings.TrimPrefix(latest, source.TrimTagPrefix)
		}

		return latest, nil
	}

	return "", fmt.Errorf("source is not a git repository")
}