github.com/jenkins-x/jx/v2@v2.1.155/pkg/users/git.go (about) 1 package users 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/jenkins-x/jx/v2/pkg/kube/naming" 8 k8serrors "k8s.io/apimachinery/pkg/api/errors" 9 10 "github.com/pkg/errors" 11 12 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 14 "gopkg.in/src-d/go-git.v4/plumbing/object" 15 16 jenkinsv1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1" 17 "github.com/jenkins-x/jx-logging/pkg/log" 18 19 jenkninsv1client "github.com/jenkins-x/jx-api/pkg/client/clientset/versioned" 20 21 "github.com/jenkins-x/jx/v2/pkg/gits" 22 ) 23 24 // GitUserResolver allows git users to be converted to Jenkins X users 25 type GitUserResolver struct { 26 GitProvider gits.GitProvider 27 JXClient jenkninsv1client.Interface 28 Namespace string 29 } 30 31 // GitSignatureAsUser resolves the signature to a Jenkins X User 32 func (r *GitUserResolver) GitSignatureAsUser(signature *object.Signature) (*jenkinsv1.User, error) { 33 // We can't resolve no info so shortcircuit 34 if signature.Name == "" && signature.Email == "" { 35 return nil, errors.Errorf("both name and email are empty") 36 } 37 gitUser := &gits.GitUser{ 38 Email: signature.Email, 39 Name: signature.Name, 40 } 41 return r.Resolve(gitUser) 42 } 43 44 // GitUserSliceAsUserDetailsSlice resolves a slice of git users to a slice of Jenkins X User Details 45 func (r *GitUserResolver) GitUserSliceAsUserDetailsSlice(users []gits.GitUser) ([]jenkinsv1.UserDetails, error) { 46 answer := []jenkinsv1.UserDetails{} 47 for _, user := range users { 48 us := user 49 u, err := r.Resolve(&us) 50 if err != nil { 51 return nil, err 52 } 53 if u != nil { 54 answer = append(answer, u.Spec) 55 } 56 } 57 return answer, nil 58 } 59 60 // Resolve will convert the GitUser to a Jenkins X user and attempt to complete the user info by: 61 // * checking the user custom resources to see if the user is present there 62 // * making a call to the gitProvider 63 // as often user info is not complete in a git response 64 func (r *GitUserResolver) Resolve(user *gits.GitUser) (*jenkinsv1.User, error) { 65 if r == nil || user == nil { 66 return nil, nil 67 } 68 selectUsers := func(id string, users []jenkinsv1.User) (string, []jenkinsv1.User, 69 *jenkinsv1.User, error) { 70 var gitUser *gits.GitUser 71 if user.Login != "" { 72 gitUser = r.GitProvider.UserInfo(user.Login) 73 } 74 if gitUser == nil { 75 gitUser = user 76 } 77 78 possibles := make([]jenkinsv1.User, 0) 79 if gitUser == nil { 80 // annoyingly UserInfo swallows the error, so we recreate it! 81 log.Logger().Warnf("unable to find user with login %s from %s", user.Login, r.GitProvider.Kind()) 82 } else if user.Email != "" { 83 // Don't do this if email is empty as otherwise we risk matching any users who have empty emails! 84 for _, u := range users { 85 if u.Spec.Email == gitUser.Email { 86 possibles = append(possibles, u) 87 } 88 } 89 } 90 new := r.GitUserToUser(gitUser) 91 login := gitUser.Login 92 if login == "" { 93 login = strings.Replace(gitUser.Name, " ", "-", -1) 94 login = strings.ToLower(login) 95 } 96 id = naming.ToValidName(login) 97 // Check if the user id is available, if not append "-<n>" where <n> is some integer 98 for i := 0; true; i++ { 99 _, err := r.JXClient.JenkinsV1().Users(r.Namespace).Get(id, v1.GetOptions{}) 100 if k8serrors.IsNotFound(err) { 101 break 102 } 103 id = fmt.Sprintf("%s-%d", login, i) 104 } 105 new.Name = naming.ToValidName(id) 106 return id, possibles, new, nil 107 } 108 user.Login = naming.ToValidValue(user.Login) 109 return Resolve(user.Login, r.GitProviderKey(), r.JXClient, r.Namespace, selectUsers) 110 } 111 112 // UpdateUserFromPRAuthor will attempt to use the 113 func (r *GitUserResolver) UpdateUserFromPRAuthor(author *jenkinsv1.User, pullRequest *gits.GitPullRequest, 114 commits []*gits.GitCommit) (*jenkinsv1.User, error) { 115 116 if pullRequest != nil { 117 updated := false 118 if author != nil { 119 gitLogin := r.GitUserLogin(author) 120 if gitLogin == "" { 121 gitLogin = author.Spec.Login 122 } 123 for _, commit := range commits { 124 if commit.Author != nil && gitLogin == commit.Author.Login { 125 log.Logger().Info("Found commit author match for: " + author. 126 Spec.Login + " with email address: " + commit.Author.Email + "\n") 127 author.Spec.Email = commit.Author.Email 128 updated = true 129 break 130 } 131 } 132 } 133 if updated { 134 return r.JXClient.JenkinsV1().Users(r.Namespace).PatchUpdate(author) 135 } 136 } 137 return author, nil 138 } 139 140 // UserToGitUser performs type conversion from a Jenkins X User to a Git User 141 func (r *GitUserResolver) UserToGitUser(id string, user *jenkinsv1.User) *gits.GitUser { 142 return &gits.GitUser{ 143 Login: id, 144 Email: user.Spec.Email, 145 Name: user.Spec.Name, 146 URL: user.Spec.URL, 147 AvatarURL: user.Spec.AvatarURL, 148 } 149 } 150 151 // GitUserToUser performs type conversion from a GitUser to a Jenkins X user, 152 // attaching the Git Provider account to Accounts 153 func (r *GitUserResolver) GitUserToUser(gitUser *gits.GitUser) *jenkinsv1.User { 154 user := CreateUser(r.Namespace, gitUser.Login, gitUser.Name, gitUser.Email) 155 return AddAccountReference(user, r.GitProviderKey(), gitUser.Login) 156 } 157 158 // GitUserLogin returns the login for the git provider, or an empty string if not found 159 func (r *GitUserResolver) GitUserLogin(user *jenkinsv1.User) string { 160 for _, a := range user.Spec.Accounts { 161 if a.Provider == r.GitProviderKey() { 162 return a.ID 163 } 164 } 165 return "" 166 } 167 168 // GitProviderKey returns the provider key for this GitUserResolver 169 func (r *GitUserResolver) GitProviderKey() string { 170 if r == nil || r.GitProvider == nil { 171 return "" 172 } 173 return fmt.Sprintf("jenkins.io/git-%s-userid", r.GitProvider.Kind()) 174 } 175 176 // mergeGitUsers merges user1 into user2, replacing any that do not have empty values on user2 with those from user1