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