All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jenkins.updatebot.github.Issues Maven / Gradle / Ivy

There is a newer version: 1.1.7
Show newest version
/*
 * Copyright 2016 Red Hat, Inc.
 *
 * Red Hat licenses this file to you under the Apache License, version
 * 2.0 (the "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package io.jenkins.updatebot.github;

import io.jenkins.updatebot.Configuration;
import io.jenkins.updatebot.commands.CommandContext;
import io.jenkins.updatebot.kind.DependenciesCheck;
import io.jenkins.updatebot.kind.Kind;
import io.jenkins.updatebot.kind.KindDependenciesCheck;
import io.jenkins.updatebot.kind.npm.dependency.DependencyCheck;
import io.jenkins.updatebot.model.DependencyVersionChange;
import io.jenkins.updatebot.support.Markdown;
import io.jenkins.updatebot.support.Strings;
import io.fabric8.utils.Objects;
import org.kohsuke.github.GHIssue;
import org.kohsuke.github.GHIssueComment;
import org.kohsuke.github.GHIssueState;
import org.kohsuke.github.GHLabel;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static io.jenkins.updatebot.github.GitHubHelpers.retryGithub;

/**
 */
public class Issues {
    public static final String BODY = Markdown.UPDATEBOT_ICON + " cannot update some dependency versions until other projects are released to fix dependency conflicts.\n\n" +
            "This issue is used to coordinate version changes on this repository coming from other repositories and will be closed once all the version conflicts are resolved.";
    public static final String CLOSE_MESSAGE = Markdown.UPDATEBOT_ICON + " closing as no more dependency conflicts while ";
    public static final String PENDING_CHANGE_COMMENT_PREFIX = Markdown.UPDATEBOT_ICON + " detected conflicts while ";
    public static final String HEADER_KIND = "## ";
    public static final String CONFLICTS_HEADER = "### Conflicts";
    public static final String CONFLICT_PREFIX = "* ";
    public static final String PENDING_COMMAND_PREFIX = "    ";
    private static final transient Logger LOG = LoggerFactory.getLogger(Issues.class);

    public static List getOpenIssues(GHRepository ghRepository, Configuration configuration) throws IOException {
        String label = configuration.getGithubPullRequestLabel();
        return getOpenIssues(ghRepository, label);
    }

    public static List getOpenIssues(GHRepository ghRepository, String label) throws IOException {
        List issues = retryGithub(() -> ghRepository.getIssues(GHIssueState.OPEN));
        List answer = new ArrayList<>();
        for (GHIssue issue : issues) {
            if (GitHubHelpers.hasLabel(getLabels(issue), label) && !issue.isPullRequest()) {
                answer.add(issue);
            }
        }
        return answer;
    }


    public static List loadPendingChangesFromIssue(CommandContext context, GHIssue issue) throws IOException {
        List comments = issue.getComments();
        String lastCommand = null;
        for (GHIssueComment comment : comments) {
            String command = updateBotIssuePendingChangesComment(context, comment);
            if (command != null) {
                lastCommand = command;
            }
        }
        if (lastCommand == null) {
            LOG.warn("No UpdateBot comment found on issue " + issue.getHtmlUrl());
            return new ArrayList<>();
        }
        return parseUpdateBotIssuePendingChangesComment(lastCommand);
    }

    public static List parseUpdateBotIssuePendingChangesComment(String command) {
        String[] lines = command.split("\n");
        List answer = new ArrayList<>();
        Kind kind = null;
        for (String line : lines) {
            boolean commandLine = line.startsWith(PENDING_COMMAND_PREFIX);
            String text = line.trim();
            if (Strings.notEmpty(text)) {
                if (text.startsWith("#")) {
                    String header = Strings.trimAllPrefix(text, "#").trim();
                    Kind k = Kind.fromName(header);
                    if (k != null) {
                        kind = k;
                    }
                } else if (commandLine) {
                    if (kind != null) {
                        addChangeFromCommentLine(answer, kind, text);
                    }
                }
            }
        }
        return answer;
    }

    protected static void addChangeFromCommentLine(List answer, Kind kind, String text) {
        String[] words = text.split("\\s+");
        if (words.length < 2) {
            LOG.warn("Ignoring command: Not enough arguments: " + text);
            return;
        }
        DependencyVersionChange change;
        String dependency = words[0];
        String version = words[1];
        String scope = null;
        if (words.length > 2) {
            scope = words[2];
        }
        if (scope == null) {
            change = new DependencyVersionChange(kind, dependency, version);
        } else {
            change = new DependencyVersionChange(kind, dependency, version, scope);
        }
        answer.add(change);
    }

    protected static String createPendingVersionChangeCommands(List changes) {
        StringBuilder builder = new StringBuilder();
        for (DependencyVersionChange change : changes) {
            builder.append("\n    ");
            String scope = change.getScope();
            if (scope == null) {
                scope = "";
            }
            builder.append(String.join(" ", change.getDependency(), change.getVersion(), scope));
        }
        return builder.toString();
    }


    public static void addConflictsComment(GHIssue issue, List pendingChanges, String operationDescription, DependenciesCheck check) throws IOException {
        String prefix = PENDING_CHANGE_COMMENT_PREFIX + operationDescription + "\n";
        String issueComment = prefix + conflictChangesComment(pendingChanges, check);
        issue.comment(issueComment);
    }

    public static String conflictChangesComment(List pendingChanges, DependenciesCheck check) {
        StringBuilder builder = new StringBuilder();
        Map failures = check.getFailures();
        for (Map.Entry entry : failures.entrySet()) {
            Kind kind = entry.getKey();
            KindDependenciesCheck kindCheck = entry.getValue();
            List kindChanges = DependencyVersionChange.forKind(kind, pendingChanges);
            List kindConflicts = kindCheck.getFailedChecksFor(kindChanges);

            boolean hasConflicts = kindConflicts.size() > 0;
            boolean hasChanges = kindChanges.size() > 0;
            if (hasConflicts && hasChanges) {
                builder.append("\n\n");
                builder.append(HEADER_KIND);
                builder.append(kind.getName());
                if (hasChanges) {
                    builder.append("\n");
                    builder.append(createPendingVersionChangeCommands(kindChanges));
                }
                if (hasConflicts) {
                    builder.append("\n\n");
                    builder.append(CONFLICTS_HEADER);
                    builder.append("\n\n");
                    builder.append(createConflictComments(kindConflicts));
                }
            }
        }
        return builder.toString();
    }

    private static String createConflictComments(List conflicts) {
        StringBuilder builder = new StringBuilder();
        for (DependencyCheck conflict : conflicts) {
            if (conflict.isValid()) {
                continue;
            }
            String message = conflict.getMessage();
            String dependency = conflict.getDependency();
            builder.append(CONFLICT_PREFIX + "`" + dependency + "` " + message + "\n");
        }
        return builder.toString();
    }

    protected static String createConfictIssueComment(DependenciesCheck check) {
        return null;
    }


    public static String updateBotIssuePendingChangesComment(CommandContext context, GHIssueComment comment) throws IOException {
        GHUser user = comment.getUser();
        if (user != null) {
            if (Objects.equal(context.getConfiguration().getGithubUsername(), user.getLogin())) {
                String body = comment.getBody();
                if (body != null) {
                    body = body.trim();
                    if (body.startsWith(PENDING_CHANGE_COMMENT_PREFIX)) {
                        return body;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Lets try find the issue
     */
    public static GHIssue findIssue(CommandContext context, List issues) {
        String prefix = context.createIssueTitlePrefix();
        if (issues != null) {
            for (GHIssue issue : issues) {
                String title = issue.getTitle();
                if (title != null && title.startsWith(prefix)) {
                    return issue;
                }
            }
        }
        return null;
    }

    public static GHIssue createIssue(CommandContext context, GHRepository repository) throws IOException {
        return repository.createIssue(context.createIssueTitlePrefix()).
                body(BODY).
                label(context.getConfiguration().getGithubPullRequestLabel()).
                create();
    }

    public static void logOpen(List issues) {
        for (GHIssue issue : issues) {
            LOG.info("Open issue " + issue.getHtmlUrl());
        }
    }

    /**
     * Lets return the labels on an issue with retries
     */
    public static Collection getLabels(GHIssue issue) throws IOException {
        return retryGithub(() -> issue.getLabels());
    }

    public static boolean isOpen(GHIssue issue) {
        GHIssueState state = issue.getState();
        if (state == null) {
            return true;
        }
        return state == GHIssueState.OPEN;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy