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