Package org.jenkinsci.plugins.ghprb

Source Code of org.jenkinsci.plugins.ghprb.GhprbPullRequest

package org.jenkinsci.plugins.ghprb;

import com.google.common.base.Joiner;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.github.GHCommitState;
import org.kohsuke.github.GHIssue;
import org.kohsuke.github.GHIssueComment;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHPullRequestCommitDetail;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitUser;

import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/**
* Maintains state about a Pull Request for a particular Jenkins job.  This is what understands the current state
* of a PR for a particular job.
*
* @author Honza Brázdil <jbrazdil@redhat.com>
*/
public class GhprbPullRequest {

    private static final Logger logger = Logger.getLogger(GhprbPullRequest.class.getName());

    private final int id;
    private final GHUser author;
    private final GHPullRequest pr;
    private String title;
    private Date updated;
    private String head;
    private boolean mergeable;
    private String reponame;
    private String target;
    private String source;
    private String authorEmail;
    private URL url;
   
    private GHUser triggerSender;
    private GitUser commitAuthor;

    private boolean shouldRun = false;
    private boolean accepted = false;
    private boolean triggered = false;

    private transient Ghprb helper;
    private transient GhprbRepository repo;

  private String commentBody;

    GhprbPullRequest(GHPullRequest pr, Ghprb helper, GhprbRepository repo) {
        id = pr.getNumber();
        updated = pr.getUpdatedAt();
        head = pr.getHead().getSha();
        title = pr.getTitle();
        author = pr.getUser();
        reponame = repo.getName();
        target = pr.getBase().getRef();
        source = pr.getHead().getRef();
        url = pr.getUrl();
        this.pr = pr;
        obtainAuthorEmail(pr);

        this.helper = helper;
        this.repo = repo;

        if (helper.isWhitelisted(author)) {
            accepted = true;
            shouldRun = true;
        } else {
            logger.log(Level.INFO, "Author of #{0} {1} on {2} not in whitelist!", new Object[]{id, author.getLogin(), reponame});
            repo.addComment(id, GhprbTrigger.getDscp().getRequestForTestingPhrase());
        }

        logger.log(Level.INFO, "Created Pull Request #{0} on {1} by {2} ({3}) updated at: {4} SHA: {5}", new Object[]{id, reponame, author.getLogin(), authorEmail, updated, head});
    }

    public void init(Ghprb helper, GhprbRepository repo) {
        this.helper = helper;
        this.repo = repo;
        if (reponame == null) {
            reponame = repo.getName(); // If this instance was created before v1.8, it can be null.
        }
    }
   
    /**
     * Returns skip build phrases from Jenkins global configuration
     * @return
     */
    public Set<String> getSkipBuildPhrases() {
    return new HashSet<String>(Arrays.asList(GhprbTrigger.getDscp().getSkipBuildPhrase().split("[\\r\\n]+")));
  }
 
    /**
     * Checks for skip build phrase in pull request comment. If present it updates shouldRun as false.
     * @param issue
     */
  private void checkSkipBuild(GHIssue issue) {
    // check for skip build phrase.
    String pullRequestBody = issue.getBody();
    if(StringUtils.isNotBlank(pullRequestBody)) {
      pullRequestBody = pullRequestBody.trim();
      Set<String> skipBuildPhrases = getSkipBuildPhrases();
      skipBuildPhrases.remove("");
     
      for (String skipBuildPhrase : skipBuildPhrases) {
        skipBuildPhrase = skipBuildPhrase.trim();
        Pattern skipBuildPhrasePattern = Pattern.compile(skipBuildPhrase, Pattern.CASE_INSENSITIVE);
        if(skipBuildPhrasePattern.matcher(pullRequestBody).matches()) {
          logger.log(Level.INFO, "Pull request commented with {0} skipBuildPhrase. Hence skipping the build.", skipBuildPhrase);
          shouldRun = false;
          break;
        }
      }
    }
  }

    /**
     * Checks this Pull Request representation against a GitHub version of the Pull Request, and triggers
     * a build if necessary.
     *
     * @param pr
     */
    public void check(GHPullRequest pr) {
        if (target == null) {
            target = pr.getBase().getRef(); // If this instance was created before target was introduced (before v1.8), it can be null.
        }
        if (source == null) {
            source = pr.getHead().getRef(); // If this instance was created before target was introduced (before v1.8), it can be null.
        }

        if (isUpdated(pr)) {
            logger.log(Level.INFO, "Pull request #{0} was updated on {1} at {2} by {3}", new Object[]{id, reponame, updated, author});

            // the title could have been updated since the original PR was opened
            title = pr.getTitle();
            int commentsChecked = checkComments(pr);
            boolean newCommit = checkCommit(pr.getHead().getSha());

            if (!newCommit && commentsChecked == 0) {
                logger.log(Level.INFO, "Pull request #{0} was updated on repo {1} but there aren''t any new comments nor commits; that may mean that commit status was updated.", new Object[] {id, reponame});
            }
            updated = pr.getUpdatedAt();
        }
        checkSkipBuild(pr);
        tryBuild(pr);
    }

    public void check(GHIssueComment comment) {
        try {
            checkComment(comment);
            updated = comment.getUpdatedAt();
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Couldn't check comment #" + comment.getId(), ex);
            return;
        }

        GHPullRequest pr = null;
        try {
            pr = repo.getPullRequest(id);
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Couldn't get GHPullRequest for checking mergeable state");
        }
        checkSkipBuild(comment.getParent());
        tryBuild(pr);
    }

    public boolean isWhiteListedTargetBranch() {
        List<GhprbBranch> branches = helper.getWhiteListTargetBranches();
        if (branches.isEmpty() || (branches.size() == 1 && branches.get(0).getBranch().equals(""))) {
            // no branches in white list means we should test all
            return true;
        }
        for (GhprbBranch b : branches) {
            if (b.matches(target)) {
                // the target branch is in the whitelist!
                return true;
            }
        }
        logger.log(Level.FINEST, "PR #{0} target branch: {1} isn''t in our whitelist of target branches: {2}", new Object[]{id, target, Joiner.on(',').skipNulls().join(branches)});
        return false;
    }

    private boolean isUpdated(GHPullRequest pr) {
        boolean ret = updated.compareTo(pr.getUpdatedAt()) < 0;
        ret = ret || !pr.getHead().getSha().equals(head);
        return ret;
    }

    private void tryBuild(GHPullRequest pr) {
        if (helper.ifOnlyTriggerPhrase() && !triggered) {
            logger.log(Level.FINEST, "Trigger only phrase but we are not triggered");
            shouldRun = false;
        }
        if (!isWhiteListedTargetBranch()) {
            return;
        }
        if (shouldRun) {
            logger.log(Level.FINEST, "Running the build");

            if (authorEmail == null) {
                // If this instance was create before authorEmail was introduced (before v1.10), it can be null.
                obtainAuthorEmail(pr);
                logger.log(Level.FINEST, "Author email was not set, trying to set it to {0}", authorEmail);
            }

            if (pr != null) {
                logger.log(Level.FINEST, "PR is not null, checking if mergable");
                checkMergeable(pr);
                try {
                  for (GHPullRequestCommitDetail commitDetails : pr.listCommits()) {
                if (commitDetails.getSha().equals(getHead())) {
                  commitAuthor = commitDetails.getCommit().getCommitter();
                  break;
                }
              }
                } catch (Exception ex) {
                  logger.log(Level.INFO, "Unable to get PR commits: ", ex);
                }
               
            }

            logger.log(Level.FINEST, "Running build...");
            build();

            shouldRun = false;
            triggered = false;
        }
    }

  private void build() {
        String message = helper.getBuilds().build(this, triggerSender, commentBody);
    repo.createCommitStatus(head, GHCommitState.PENDING, null, message,id);
        logger.log(Level.INFO, message);
    }

    // returns false if no new commit
    private boolean checkCommit(String sha) {
        if (head.equals(sha)) {
            return false;
        }
        logger.log(Level.FINE, "New commit. Sha: {0} => {1}", new Object[]{head, sha});
        head = sha;
        if (accepted) {
            shouldRun = true;
        }
        return true;
    }

    private void checkComment(GHIssueComment comment) throws IOException {
        GHUser sender = comment.getUser();
        String body = comment.getBody();

        // ignore comments from bot user, this fixes an issue where the bot would auto-whitelist
        // a user or trigger a build when the 'request for testing' phrase contains the
        // whitelist/trigger phrase and the bot is a member of a whitelisted organisation
        if (helper.isBotUser(sender)) {
            logger.log(Level.FINEST, "Comment from bot user {0} ignored.", sender);
            return;
        }

        if (helper.isWhitelistPhrase(body) && helper.isAdmin(sender)) {       // add to whitelist
            if (!helper.isWhitelisted(author)) {
                logger.log(Level.FINEST, "Author {0} not whitelisted, adding to whitelist.", author);
                helper.addWhitelist(author.getLogin());
            }
            accepted = true;
            shouldRun = true;
        } else if (helper.isOktotestPhrase(body) && helper.isAdmin(sender)) {       // ok to test
            logger.log(Level.FINEST, "Admin {0} gave OK to test", sender);
            accepted = true;
            shouldRun = true;
        } else if (helper.isRetestPhrase(body)) {        // test this please
            logger.log(Level.FINEST, "Retest phrase");
            if (helper.isAdmin(sender)) {
                logger.log(Level.FINEST, "Admin {0} gave retest phrase", sender);
                shouldRun = true;
            } else if (accepted && helper.isWhitelisted(sender)) {
                logger.log(Level.FINEST, "Retest accepted and user {0} is whitelisted", sender);
                shouldRun = true;
                triggered = true;
            }
        } else if (helper.isTriggerPhrase(body)) {      // trigger phrase
            logger.log(Level.FINEST, "Trigger phrase");
            if (helper.isAdmin(sender)) {
                logger.log(Level.FINEST, "Admin {0} ran trigger phrase", sender);
                shouldRun = true;
                triggered = true;
            } else if (accepted && helper.isWhitelisted(sender)) {
                logger.log(Level.FINEST, "Trigger accepted and user {0} is whitelisted", sender);
                shouldRun = true;
                triggered = true;
            }
        }
       
        if (shouldRun) {
          triggerSender = sender;
          commentBody = body;
        }
    }

    private int checkComments(GHPullRequest pr) {
        int count = 0;
        try {
            for (GHIssueComment comment : pr.getComments()) {
                if (updated.compareTo(comment.getUpdatedAt()) < 0) {
                    count++;
                    try {
                        checkComment(comment);
                    } catch (IOException ex) {
                        logger.log(Level.SEVERE, "Couldn't check comment #" + comment.getId(), ex);
                    }
                }
            }
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Couldn't obtain comments.", e);
        }
        return count;
    }

    private void checkMergeable(GHPullRequest pr) {
        try {
            int r = 5;
            Boolean isMergeable = pr.getMergeable();
            while (isMergeable == null && r-- > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    break;
                }
                isMergeable = pr.getMergeable();
                pr = repo.getPullRequest(id);
            }
            mergeable = isMergeable != null && isMergeable;
        } catch (IOException e) {
            mergeable = false;
            logger.log(Level.SEVERE, "Couldn't obtain mergeable status.", e);
        }
    }

    private void obtainAuthorEmail(GHPullRequest pr) {
        try {
            authorEmail = pr.getUser().getEmail();
        } catch (Exception e) {
            logger.log(Level.WARNING, "Couldn't obtain author email.", e);
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof GhprbPullRequest)) {
            return false;
        }
        GhprbPullRequest o = (GhprbPullRequest) obj;
        return o.id == id;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + this.id;
        return hash;
    }

    public int getId() {
        return id;
    }

    public String getHead() {
        return head;
    }

    public boolean isMergeable() {
        return mergeable;
    }

    public String getTarget() {
        return target;
    }

    public String getSource() {
        return source;
    }

    public String getAuthorEmail() {
        return authorEmail;
    }

    public String getTitle() {
        return title;
    }

    /**
     * Returns the URL to the Github Pull Request.
     *
     * @return the Github Pull Request URL
     */
    public URL getUrl() {
        return url;
    }

  public GitUser getCommitAuthor() {
    return commitAuthor;
  }
 
  public GHPullRequest getPullRequest() {
    return pr;
  }
}
TOP

Related Classes of org.jenkinsci.plugins.ghprb.GhprbPullRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.