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