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 }