github.com/rohankumardubey/draft-classic@v0.16.0/pkg/plugin/installer/vcs_installer.go (about) 1 package installer 2 3 import ( 4 "os" 5 "sort" 6 7 "github.com/Masterminds/semver" 8 "github.com/Masterminds/vcs" 9 "k8s.io/helm/pkg/plugin/cache" 10 11 "github.com/Azure/draft/pkg/draft/draftpath" 12 "github.com/Azure/draft/pkg/plugin" 13 ) 14 15 //VCSInstaller installs plugins from a remote repository 16 type VCSInstaller struct { 17 Repo vcs.Repo 18 Version string 19 base 20 } 21 22 // NewVCSInstaller creates a new VCSInstaller. 23 func NewVCSInstaller(source, version string, home draftpath.Home) (*VCSInstaller, error) { 24 // create a system safe cache key 25 key, err := cache.Key(source) 26 if err != nil { 27 return nil, err 28 } 29 cachedpath := home.Path("cache", "plugins", key) 30 repo, err := vcs.NewRepo(source, cachedpath) 31 if err != nil { 32 return nil, err 33 } 34 35 i := &VCSInstaller{ 36 Repo: repo, 37 Version: version, 38 base: newBase(source, home), 39 } 40 return i, err 41 } 42 43 // Install clones a remote repository and creates a symlink to the plugin directory in DRAFT_HOME 44 // 45 // Implements Installer 46 func (i *VCSInstaller) Install() error { 47 if err := i.sync(i.Repo); err != nil { 48 return err 49 } 50 51 ref, err := i.solveVersion(i.Repo) 52 if err != nil { 53 return err 54 } 55 56 if ref != "" { 57 if err := i.setVersion(i.Repo, ref); err != nil { 58 return err 59 } 60 } 61 62 if !isPlugin(i.Repo.LocalPath()) { 63 return plugin.ErrMissingMetadata 64 } 65 66 return i.link(i.Repo.LocalPath()) 67 } 68 69 // Update updates a remote repository 70 func (i *VCSInstaller) Update() error { 71 debug("updating %s", i.Repo.Remote()) 72 if i.Repo.IsDirty() { 73 return plugin.ErrRepoDirty 74 } 75 if err := i.Repo.Update(); err != nil { 76 return err 77 } 78 if !isPlugin(i.Repo.LocalPath()) { 79 return plugin.ErrMissingMetadata 80 } 81 return nil 82 } 83 84 func existingVCSRepo(location string, home draftpath.Home) (Installer, error) { 85 repo, err := vcs.NewRepo("", location) 86 if err != nil { 87 return nil, err 88 } 89 i := &VCSInstaller{ 90 Repo: repo, 91 base: newBase(repo.Remote(), home), 92 } 93 94 return i, err 95 } 96 97 // Filter a list of versions to only included semantic versions. The response 98 // is a mapping of the original version to the semantic version. 99 func getSemVers(refs []string) []*semver.Version { 100 var sv []*semver.Version 101 for _, r := range refs { 102 if v, err := semver.NewVersion(r); err == nil { 103 sv = append(sv, v) 104 } 105 } 106 return sv 107 } 108 109 // setVersion attempts to checkout the version 110 func (i *VCSInstaller) setVersion(repo vcs.Repo, ref string) error { 111 debug("setting version to %q", i.Version) 112 return repo.UpdateVersion(ref) 113 } 114 115 func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) { 116 if i.Version == "" { 117 return "", nil 118 } 119 120 if repo.IsReference(i.Version) { 121 return i.Version, nil 122 } 123 124 // Create the constraint first to make sure it's valid before 125 // working on the repo. 126 constraint, err := semver.NewConstraint(i.Version) 127 if err != nil { 128 return "", err 129 } 130 131 // Get the tags 132 refs, err := repo.Tags() 133 if err != nil { 134 return "", err 135 } 136 debug("found refs: %s", refs) 137 138 // Convert and filter the list to semver.Version instances 139 semvers := getSemVers(refs) 140 141 // Sort semver list 142 sort.Sort(sort.Reverse(semver.Collection(semvers))) 143 for _, v := range semvers { 144 if constraint.Check(v) { 145 // If the constraint passes get the original reference 146 ver := v.Original() 147 debug("setting to %s", ver) 148 return ver, nil 149 } 150 } 151 152 return "", plugin.ErrVersionDoesNotExist 153 } 154 155 // sync will clone or update a remote repo. 156 func (i *VCSInstaller) sync(repo vcs.Repo) error { 157 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 }