1
15
16 package installer
17
18 import (
19 "os"
20 "sort"
21
22 "github.com/Masterminds/semver/v3"
23 "github.com/Masterminds/vcs"
24 "github.com/pkg/errors"
25
26 "helm.sh/helm/v3/internal/third_party/dep/fs"
27 "helm.sh/helm/v3/pkg/helmpath"
28 "helm.sh/helm/v3/pkg/plugin/cache"
29 )
30
31
32 type VCSInstaller struct {
33 Repo vcs.Repo
34 Version string
35 base
36 }
37
38 func existingVCSRepo(location string) (Installer, error) {
39 repo, err := vcs.NewRepo("", location)
40 if err != nil {
41 return nil, err
42 }
43 i := &VCSInstaller{
44 Repo: repo,
45 base: newBase(repo.Remote()),
46 }
47 return i, nil
48 }
49
50
51 func NewVCSInstaller(source, version string) (*VCSInstaller, error) {
52 key, err := cache.Key(source)
53 if err != nil {
54 return nil, err
55 }
56 cachedpath := helmpath.CachePath("plugins", key)
57 repo, err := vcs.NewRepo(source, cachedpath)
58 if err != nil {
59 return nil, err
60 }
61 i := &VCSInstaller{
62 Repo: repo,
63 Version: version,
64 base: newBase(source),
65 }
66 return i, err
67 }
68
69
70
71
72 func (i *VCSInstaller) Install() error {
73 if err := i.sync(i.Repo); err != nil {
74 return err
75 }
76
77 ref, err := i.solveVersion(i.Repo)
78 if err != nil {
79 return err
80 }
81 if ref != "" {
82 if err := i.setVersion(i.Repo, ref); err != nil {
83 return err
84 }
85 }
86
87 if !isPlugin(i.Repo.LocalPath()) {
88 return ErrMissingMetadata
89 }
90
91 debug("copying %s to %s", i.Repo.LocalPath(), i.Path())
92 return fs.CopyDir(i.Repo.LocalPath(), i.Path())
93 }
94
95
96 func (i *VCSInstaller) Update() error {
97 debug("updating %s", i.Repo.Remote())
98 if i.Repo.IsDirty() {
99 return errors.New("plugin repo was modified")
100 }
101 if err := i.Repo.Update(); err != nil {
102 return err
103 }
104 if !isPlugin(i.Repo.LocalPath()) {
105 return ErrMissingMetadata
106 }
107 return nil
108 }
109
110 func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) {
111 if i.Version == "" {
112 return "", nil
113 }
114
115 if repo.IsReference(i.Version) {
116 return i.Version, nil
117 }
118
119
120
121 constraint, err := semver.NewConstraint(i.Version)
122 if err != nil {
123 return "", err
124 }
125
126
127 refs, err := repo.Tags()
128 if err != nil {
129 return "", err
130 }
131 debug("found refs: %s", refs)
132
133
134 semvers := getSemVers(refs)
135
136
137 sort.Sort(sort.Reverse(semver.Collection(semvers)))
138 for _, v := range semvers {
139 if constraint.Check(v) {
140
141 ver := v.Original()
142 debug("setting to %s", ver)
143 return ver, nil
144 }
145 }
146
147 return "", errors.Errorf("requested version %q does not exist for plugin %q", i.Version, i.Repo.Remote())
148 }
149
150
151 func (i *VCSInstaller) setVersion(repo vcs.Repo, ref string) error {
152 debug("setting version to %q", i.Version)
153 return repo.UpdateVersion(ref)
154 }
155
156
157 func (i *VCSInstaller) sync(repo vcs.Repo) error {
158 if _, err := os.Stat(repo.LocalPath()); os.IsNotExist(err) {
159 debug("cloning %s to %s", repo.Remote(), repo.LocalPath())
160 return repo.Get()
161 }
162 debug("updating %s", repo.Remote())
163 return repo.Update()
164 }
165
166
167
168 func getSemVers(refs []string) []*semver.Version {
169 var sv []*semver.Version
170 for _, r := range refs {
171 if v, err := semver.NewVersion(r); err == nil {
172 sv = append(sv, v)
173 }
174 }
175 return sv
176 }
177
View as plain text