Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

182: Implement a cooldown period when bridging pull request comments to mailing lists #386

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -15,6 +15,7 @@
class ArchiveItem {
private final String id;
private final ZonedDateTime created;
private final ZonedDateTime updated;
private final HostUser author;
private final Map<String, String> extraHeaders;
private final ArchiveItem parent;
@@ -23,9 +24,10 @@ class ArchiveItem {
private final Supplier<String> body;
private final Supplier<String> footer;

private ArchiveItem(ArchiveItem parent, String id, ZonedDateTime created, HostUser author, Map<String, String> extraHeaders, Supplier<String> subject, Supplier<String> header, Supplier<String> body, Supplier<String> footer) {
private ArchiveItem(ArchiveItem parent, String id, ZonedDateTime created, ZonedDateTime updated, HostUser author, Map<String, String> extraHeaders, Supplier<String> subject, Supplier<String> header, Supplier<String> body, Supplier<String> footer) {
this.id = id;
this.created = created;
this.updated = updated;
this.author = author;
this.extraHeaders = extraHeaders;
this.parent = parent;
@@ -36,7 +38,7 @@ private ArchiveItem(ArchiveItem parent, String id, ZonedDateTime created, HostUs
}

static ArchiveItem from(PullRequest pr, Repository localRepo, URI issueTracker, String issuePrefix, WebrevStorage.WebrevGenerator webrevGenerator, WebrevNotification webrevNotification, Hash base, Hash head) {
return new ArchiveItem(null, "fc", pr.createdAt(), pr.author(), Map.of("PR-Head-Hash", head.hex(), "PR-Base-Hash", base.hex()),
return new ArchiveItem(null, "fc", pr.createdAt(), pr.updatedAt(), pr.author(), Map.of("PR-Head-Hash", head.hex(), "PR-Base-Hash", base.hex()),
() -> "RFR: " + pr.title(),
() -> "",
() -> ArchiveMessages.composeConversation(pr, base, head),
@@ -48,7 +50,7 @@ static ArchiveItem from(PullRequest pr, Repository localRepo, URI issueTracker,
}

static ArchiveItem from(PullRequest pr, Repository localRepo, WebrevStorage.WebrevGenerator webrevGenerator, WebrevNotification webrevNotification, Hash lastBase, Hash lastHead, Hash base, Hash head, int index, ArchiveItem parent) {
return new ArchiveItem(parent,"ha" + head.hex(), ZonedDateTime.now(), pr.author(), Map.of("PR-Head-Hash", head.hex(), "PR-Base-Hash", base.hex()),
return new ArchiveItem(parent,"ha" + head.hex(), ZonedDateTime.now(), ZonedDateTime.now(), pr.author(), Map.of("PR-Head-Hash", head.hex(), "PR-Base-Hash", base.hex()),
() -> String.format("Re: [Rev %02d] RFR: %s", index, pr.title()),
() -> "",
() -> ArchiveMessages.composeRevision(pr, localRepo, base, head, lastBase, lastHead),
@@ -77,23 +79,23 @@ static ArchiveItem from(PullRequest pr, Repository localRepo, WebrevStorage.Webr
}

static ArchiveItem from(PullRequest pr, Comment comment, HostUserToEmailAuthor hostUserToEmailAuthor, ArchiveItem parent) {
return new ArchiveItem(parent, "pc" + comment.id(), comment.createdAt(), comment.author(), Map.of(),
return new ArchiveItem(parent, "pc" + comment.id(), comment.createdAt(), comment.updatedAt(), comment.author(), Map.of(),
() -> ArchiveMessages.composeReplySubject(parent.subject()),
() -> ArchiveMessages.composeReplyHeader(parent.createdAt(), hostUserToEmailAuthor.author(parent.author)),
() -> ArchiveMessages.composeComment(comment),
() -> ArchiveMessages.composeReplyFooter(pr));
}

static ArchiveItem from(PullRequest pr, Review review, HostUserToEmailAuthor hostUserToEmailAuthor, HostUserToUserName hostUserToUserName, HostUserToRole hostUserToRole, ArchiveItem parent) {
return new ArchiveItem(parent, "rv" + review.id(), review.createdAt(), review.reviewer(), Map.of(),
return new ArchiveItem(parent, "rv" + review.id(), review.createdAt(), review.createdAt(), review.reviewer(), Map.of(),
() -> ArchiveMessages.composeReplySubject(parent.subject()),
() -> ArchiveMessages.composeReplyHeader(parent.createdAt(), hostUserToEmailAuthor.author(parent.author())),
() -> ArchiveMessages.composeReview(pr, review, hostUserToUserName, hostUserToRole),
() -> ArchiveMessages.composeReviewFooter(pr, review, hostUserToUserName, hostUserToRole));
}

static ArchiveItem from(PullRequest pr, ReviewComment reviewComment, HostUserToEmailAuthor hostUserToEmailAuthor, ArchiveItem parent) {
return new ArchiveItem(parent, "rc" + reviewComment.id(), reviewComment.createdAt(), reviewComment.author(), Map.of(),
return new ArchiveItem(parent, "rc" + reviewComment.id(), reviewComment.createdAt(), reviewComment.updatedAt(), reviewComment.author(), Map.of(),
() -> ArchiveMessages.composeReplySubject(parent.subject()),
() -> ArchiveMessages.composeReplyHeader(parent.createdAt(), hostUserToEmailAuthor.author(parent.author())),
() -> ArchiveMessages.composeReviewComment(pr, reviewComment) ,
@@ -170,6 +172,10 @@ ZonedDateTime createdAt() {
return created;
}

ZonedDateTime updatedAt() {
return updated;
}

HostUser author() {
return author;
}
@@ -24,7 +24,7 @@

import org.openjdk.skara.bot.WorkItem;
import org.openjdk.skara.email.*;
import org.openjdk.skara.forge.PullRequest;
import org.openjdk.skara.forge.*;
import org.openjdk.skara.host.HostUser;
import org.openjdk.skara.issuetracker.Comment;
import org.openjdk.skara.mailinglist.*;
@@ -33,12 +33,12 @@
import java.io.*;
import java.net.URI;
import java.nio.file.Path;
import java.time.Duration;
import java.time.*;
import java.util.*;
import java.util.function.*;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.*;

class ArchiveWorkItem implements WorkItem {
private final PullRequest pr;
@@ -82,34 +82,6 @@ private void pushMbox(Repository localRepo, String message) {
}
}

private static final Pattern replyToPattern = Pattern.compile("^\\s*@([-A-Za-z0-9]+)");

private Optional<Comment> getParentPost(Comment post, List<Comment> all) {
var matcher = replyToPattern.matcher(post.body());
if (matcher.find()) {
var replyToName = matcher.group(1);
var replyToNamePattern = Pattern.compile("^" + replyToName + "$");

var postIterator = all.listIterator();
while (postIterator.hasNext()) {
var cur = postIterator.next();
if (cur == post) {
break;
}
}

while (postIterator.hasPrevious()) {
var cur = postIterator.previous();
var userMatcher = replyToNamePattern.matcher(cur.author().userName());
if (userMatcher.matches()) {
return Optional.of(cur);
}
}
}

return Optional.empty();
}

private Repository materializeArchive(Path scratchPath) {
try {
return Repository.materialize(scratchPath, bot.archiveRepo().url(),
@@ -312,7 +284,7 @@ public void run(Path scratchPath) {
}

var webrevGenerator = bot.webrevStorage().generator(pr, localRepo, webrevPath);
var newMails = archiver.generateNewEmails(sentMails, localRepo, bot.issueTracker(), jbs.toUpperCase(), webrevGenerator,
var newMails = archiver.generateNewEmails(sentMails, bot.cooldown(), localRepo, bot.issueTracker(), jbs.toUpperCase(), webrevGenerator,
(index, full, inc) -> updateWebrevComment(comments, index, full, inc),
user -> getAuthorAddress(census, user),
user -> getAuthorUserName(census, user),
@@ -51,14 +51,15 @@ public class MailingListBridgeBot implements Bot {
private final URI issueTracker;
private final PullRequestUpdateCache updateCache;
private final Duration sendInterval;
private final Duration cooldown;

MailingListBridgeBot(EmailAddress from, HostedRepository repo, HostedRepository archive, String archiveRef,
HostedRepository censusRepo, String censusRef, EmailAddress list,
Set<String> ignoredUsers, Set<Pattern> ignoredComments, URI listArchive, String smtpServer,
HostedRepository webrevStorageRepository, String webrevStorageRef,
Path webrevStorageBase, URI webrevStorageBaseUri, Set<String> readyLabels,
Map<String, Pattern> readyComments, URI issueTracker, Map<String, String> headers,
Duration sendInterval) {
Duration sendInterval, Duration cooldown) {
emailAddress = from;
codeRepo = repo;
archiveRepo = archive;
@@ -75,12 +76,17 @@ public class MailingListBridgeBot implements Bot {
this.headers = headers;
this.issueTracker = issueTracker;
this.sendInterval = sendInterval;
this.cooldown = cooldown;

this.webrevStorage = new WebrevStorage(webrevStorageRepository, webrevStorageRef, webrevStorageBase,
webrevStorageBaseUri, from);
this.updateCache = new PullRequestUpdateCache();
}

static MailingListBridgeBotBuilder newBuilder() {
return new MailingListBridgeBotBuilder();
}

HostedRepository codeRepo() {
return codeRepo;
}
@@ -113,6 +119,10 @@ Duration sendInterval() {
return sendInterval;
}

Duration cooldown() {
return cooldown;
}

Set<String> ignoredUsers() {
return ignoredUsers;
}
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.skara.bots.mlbridge;

import org.openjdk.skara.email.EmailAddress;
import org.openjdk.skara.forge.HostedRepository;

import java.net.URI;
import java.nio.file.Path;
import java.time.Duration;
import java.util.*;
import java.util.regex.Pattern;

public class MailingListBridgeBotBuilder {
private EmailAddress from;
private HostedRepository repo;
private HostedRepository archive;
private String archiveRef = "master";
private HostedRepository censusRepo;
private String censusRef = "master";
private EmailAddress list;
private Set<String> ignoredUsers = Set.of();
private Set<Pattern> ignoredComments = Set.of();
private URI listArchive;
private String smtpServer;
private HostedRepository webrevStorageRepository;
private String webrevStorageRef;
private Path webrevStorageBase;
private URI webrevStorageBaseUri;
private Set<String> readyLabels = Set.of();
private Map<String, Pattern> readyComments = Map.of();
private URI issueTracker;
private Map<String, String> headers = Map.of();
private Duration sendInterval = Duration.ZERO;
private Duration cooldown = Duration.ZERO;

MailingListBridgeBotBuilder() {
}

public MailingListBridgeBotBuilder from(EmailAddress from) {
this.from = from;
return this;
}

public MailingListBridgeBotBuilder repo(HostedRepository repo) {
this.repo = repo;
return this;
}

public MailingListBridgeBotBuilder archive(HostedRepository archive) {
this.archive = archive;
return this;
}

public MailingListBridgeBotBuilder archiveRef(String archiveRef) {
this.archiveRef = archiveRef;
return this;
}

public MailingListBridgeBotBuilder censusRepo(HostedRepository censusRepo) {
this.censusRepo = censusRepo;
return this;
}

public MailingListBridgeBotBuilder censusRef(String censusRef) {
this.censusRef = censusRef;
return this;
}

public MailingListBridgeBotBuilder list(EmailAddress list) {
this.list = list;
return this;
}

public MailingListBridgeBotBuilder ignoredUsers(Set<String> ignoredUsers) {
this.ignoredUsers = ignoredUsers;
return this;
}

public MailingListBridgeBotBuilder ignoredComments(Set<Pattern> ignoredComments) {
this.ignoredComments = ignoredComments;
return this;
}

public MailingListBridgeBotBuilder listArchive(URI listArchive) {
this.listArchive = listArchive;
return this;
}

public MailingListBridgeBotBuilder smtpServer(String smtpServer) {
this.smtpServer = smtpServer;
return this;
}

public MailingListBridgeBotBuilder webrevStorageRepository(HostedRepository webrevStorageRepository) {
this.webrevStorageRepository = webrevStorageRepository;
return this;
}

public MailingListBridgeBotBuilder webrevStorageRef(String webrevStorageRef) {
this.webrevStorageRef = webrevStorageRef;
return this;
}

public MailingListBridgeBotBuilder webrevStorageBase(Path webrevStorageBase) {
this.webrevStorageBase = webrevStorageBase;
return this;
}

public MailingListBridgeBotBuilder webrevStorageBaseUri(URI webrevStorageBaseUri) {
this.webrevStorageBaseUri = webrevStorageBaseUri;
return this;
}

public MailingListBridgeBotBuilder readyLabels(Set<String> readyLabels) {
this.readyLabels = readyLabels;
return this;
}

public MailingListBridgeBotBuilder readyComments(Map<String, Pattern> readyComments) {
this.readyComments = readyComments;
return this;
}

public MailingListBridgeBotBuilder issueTracker(URI issueTracker) {
this.issueTracker = issueTracker;
return this;
}

public MailingListBridgeBotBuilder headers(Map<String, String> headers) {
this.headers = headers;
return this;
}

public MailingListBridgeBotBuilder sendInterval(Duration sendInterval) {
this.sendInterval = sendInterval;
return this;
}

public MailingListBridgeBotBuilder cooldown(Duration cooldown) {
this.cooldown = cooldown;
return this;
}

public MailingListBridgeBot build() {
return new MailingListBridgeBot(from, repo, archive, archiveRef, censusRepo, censusRef, list,
ignoredUsers, ignoredComments, listArchive, smtpServer,
webrevStorageRepository, webrevStorageRef, webrevStorageBase, webrevStorageBaseUri,
readyLabels, readyComments, issueTracker, headers, sendInterval, cooldown);
}
}