github.com/henvic/wedeploycli@v1.7.6-0.20200319005353-3630f582f284/deployment/internal/repodiscovery/repodiscovery.go (about)

     1  package repodiscovery
     2  
     3  import (
     4  	"path/filepath"
     5  	"sort"
     6  
     7  	"github.com/hashicorp/errwrap"
     8  	"github.com/henvic/wedeploycli/services"
     9  	git "gopkg.in/src-d/go-git.v4"
    10  	"gopkg.in/src-d/go-git.v4/config"
    11  	"gopkg.in/src-d/go-git.v4/plumbing"
    12  )
    13  
    14  // Repository found.
    15  type Repository struct {
    16  	Services          []string `json:"repos,omitempty"`
    17  	Path              string   `json:"path,omitempty"`
    18  	Origin            string   `json:"origins,omitempty"`
    19  	Commit            string   `json:"commit,omitempty"`
    20  	CommitAuthor      string   `json:"commitAuthor,omitempty"`
    21  	CommitAuthorEmail string   `json:"commitAuthorEmail,omitempty"`
    22  	CommitMessage     string   `json:"commitMessage,omitempty"`
    23  	CommitDate        string   `json:"commitDate,omitempty"`
    24  	Branch            string   `json:"branch,omitempty"`
    25  	CleanWorkingTree  bool     `json:"cleanWorkingTree,omitempty"`
    26  }
    27  
    28  // Discover repositories
    29  type Discover struct {
    30  	Path     string
    31  	Services services.ServiceInfoList
    32  
    33  	config *config.Config
    34  	head   *plumbing.Reference
    35  }
    36  
    37  // Run discovery process
    38  func (d *Discover) Run() (repos []Repository, repoless []string, err error) {
    39  	d.Path, err = filepath.Abs(d.Path)
    40  
    41  	if err != nil {
    42  		return nil, nil, err
    43  	}
    44  
    45  	repoOnRoot, err := d.walkFn(d.Path, d.Services.GetIDs())
    46  
    47  	if err != nil && err != git.ErrRepositoryNotExists {
    48  		return nil, nil, err
    49  	}
    50  
    51  	if repoOnRoot != nil {
    52  		repos = append(repos, *repoOnRoot)
    53  		return repos, repoless, nil
    54  	}
    55  
    56  	for _, serviceInfo := range d.Services {
    57  		repo, err := d.walkFn(serviceInfo.Location, []string{serviceInfo.ServiceID})
    58  
    59  		if err == git.ErrRepositoryNotExists {
    60  			repoless = append(repoless, serviceInfo.ServiceID)
    61  			continue
    62  		}
    63  
    64  		if err != nil {
    65  			return nil, nil, errwrap.Wrapf("error discovering git repos for "+serviceInfo.ServiceID+": {{err}}", err)
    66  		}
    67  
    68  		repos = append(repos, *repo)
    69  	}
    70  
    71  	return repos, repoless, nil
    72  }
    73  
    74  func (d *Discover) walkFn(path string, services []string) (*Repository, error) {
    75  	repo, err := git.PlainOpen(path)
    76  
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	worktree, err := repo.Worktree()
    82  
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	status, err := worktree.Status()
    88  
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	d.head, err = repo.Head()
    94  
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	d.config, err = repo.Config()
   100  
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	var branch, remote = d.maybeGetBranchAndRemote()
   106  
   107  	commitHash := d.head.Hash()
   108  	commit, err := repo.CommitObject(commitHash)
   109  
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	rel, err := filepath.Rel(d.Path, path)
   115  
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	if rel == "." {
   121  		rel = ""
   122  	}
   123  
   124  	return &Repository{
   125  		Services:          services,
   126  		Path:              rel,
   127  		Origin:            d.maybeGetOriginURL(remote),
   128  		Commit:            commitHash.String(),
   129  		CommitAuthor:      commit.Author.Name,
   130  		CommitAuthorEmail: commit.Author.Email,
   131  		CommitMessage:     commit.Message,
   132  		CommitDate:        commit.Author.When.String(),
   133  		Branch:            branch,
   134  		CleanWorkingTree:  isWorkingTreeClean(status),
   135  	}, nil
   136  }
   137  
   138  func (d *Discover) maybeGetBranchAndRemote() (branch, remote string) {
   139  	if !d.head.Name().IsBranch() {
   140  		return
   141  	}
   142  
   143  	branch = d.head.Name().Short()
   144  
   145  	// try first to use whatever remote that is in a given branch
   146  	if b, ok := d.config.Branches[branch]; ok {
   147  		remote = b.Remote
   148  		return
   149  	}
   150  
   151  	// fallback to whatever remote is left using the first found in increasing order
   152  	remotes := []string{}
   153  
   154  	for r := range d.config.Remotes {
   155  		remotes = append(remotes, r)
   156  	}
   157  
   158  	sort.Strings(remotes)
   159  
   160  	if len(remotes) > 0 {
   161  		remote = remotes[0]
   162  	}
   163  
   164  	return
   165  }
   166  
   167  func (d *Discover) maybeGetOriginURL(remote string) string {
   168  	r, ok := d.config.Remotes[remote]
   169  
   170  	if !ok {
   171  		return ""
   172  	}
   173  
   174  	var candidates = r.URLs
   175  
   176  	for _, eu := range candidates {
   177  		if gitRepoURL, err := ExtractRepoURL(eu); err == nil {
   178  			return gitRepoURL
   179  		}
   180  	}
   181  
   182  	return ""
   183  }
   184  
   185  func isWorkingTreeClean(s git.Status) bool {
   186  	for _, status := range s {
   187  		if status.Staging == git.Unmodified && status.Worktree == git.Unmodified {
   188  			continue
   189  		}
   190  
   191  		return false
   192  	}
   193  
   194  	return true
   195  }