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  }