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

Bridgekeeper bot #245

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
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2019, 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.
*/

module {
name = 'org.openjdk.skara.bots.bridgekeeper'
test {
requires 'org.junit.jupiter.api'
requires 'org.openjdk.skara.vcs'
requires 'org.openjdk.skara.test'
opens 'org.openjdk.skara.bots.bridgekeeper' to 'org.junit.platform.commons'
}
}

dependencies {
implementation project(':host')
implementation project(':forge')
implementation project(':issuetracker')
implementation project(':bot')
implementation project(':census')
implementation project(':json')
implementation project(':vcs')

testImplementation project(':test')
}
@@ -0,0 +1,30 @@
import org.openjdk.skara.bots.bridgekeeper.BridgekeeperBotFactory;

/*
* Copyright (c) 2019, 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.
*/
module org.openjdk.skara.bots.bridgekeeper {
requires org.openjdk.skara.bot;
requires java.logging;

provides org.openjdk.skara.bot.BotFactory with BridgekeeperBotFactory;
}
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2019, 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.bridgekeeper;

import org.openjdk.skara.bot.*;
import org.openjdk.skara.forge.*;

import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Logger;

class BridgekeeperWorkItem implements WorkItem {
private final Logger log = Logger.getLogger("org.openjdk.skara.bots");;
private final HostedRepository repository;
private final PullRequest pr;
private final Consumer<RuntimeException> errorHandler;

BridgekeeperWorkItem(HostedRepository repository, PullRequest pr, Consumer<RuntimeException> errorHandler) {
this.pr = pr;
this.repository = repository;
this.errorHandler = errorHandler;
}

private final String welcomeMarker = "<!-- BridgeKeeperBot welcome message -->";

private void checkWelcomeMessage() {
log.info("Checking welcome message of " + pr);

var comments = pr.comments();
var welcomePosted = comments.stream()
.anyMatch(comment -> comment.body().contains(welcomeMarker));

if (!welcomePosted) {
var message = "Welcome to the OpenJDK organization on GitHub!\n\n" +
"This repository is currently a read-only git mirror of the official Mercurial " +
"repository (located at https://hg.openjdk.java.net/). As such, we are not " +
"currently accepting pull requests here. If you would like to contribute to " +
"the OpenJDK project, please see http://openjdk.java.net/contribute/ on how " +
"to proceed.\n\n" +
"This pull request will be automatically closed.";

log.fine("Posting welcome message");
pr.addComment(welcomeMarker + "\n\n" + message);
}
pr.setState(PullRequest.State.CLOSED);
}


@Override
public boolean concurrentWith(WorkItem other) {
if (!(other instanceof BridgekeeperWorkItem)) {
return true;
}
BridgekeeperWorkItem otherItem = (BridgekeeperWorkItem)other;
if (!pr.id().equals(otherItem.pr.id())) {
return true;
}
if (!repository.name().equals(otherItem.repository.name())) {
return true;
}
return false;
}

@Override
public void run(Path scratchPath) {
checkWelcomeMessage();
}

@Override
public void handleRuntimeException(RuntimeException e) {
errorHandler.accept(e);
}
}

public class BridgekeeperBot implements Bot {
private final HostedRepository remoteRepo;
private final PullRequestUpdateCache updateCache;

BridgekeeperBot(HostedRepository repo) {
this.remoteRepo = repo;
this.updateCache = new PullRequestUpdateCache();
}

@Override
public List<WorkItem> getPeriodicItems() {
List<WorkItem> ret = new LinkedList<>();

for (var pr : remoteRepo.pullRequests()) {
if (updateCache.needsUpdate(pr)) {
var item = new BridgekeeperWorkItem(remoteRepo, pr, e -> updateCache.invalidate(pr));
ret.add(item);
}
}

return ret;
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2019, 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.bridgekeeper;

import org.openjdk.skara.bot.*;

import java.util.*;

public class BridgekeeperBotFactory implements BotFactory {
@Override
public String name() {
return "bridgekeeper";
}

@Override
public List<Bot> create(BotConfiguration configuration) {
var ret = new ArrayList<Bot>();
var specific = configuration.specific();

for (var repo : specific.get("repositories").asArray()) {
var bot = new BridgekeeperBot(configuration.repository(repo.asString()));
ret.add(bot);
}

return ret;
}
}
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2019, 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.bridgekeeper;

import org.openjdk.skara.issuetracker.Issue;
import org.openjdk.skara.test.*;

import org.junit.jupiter.api.*;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;

class BridgekeeperBotTests {
@Test
void simple(TestInfo testInfo) throws IOException {
try (var credentials = new HostCredentials(testInfo);
var tempFolder = new TemporaryDirectory()) {
var author = credentials.getHostedRepository();
var bot = new BridgekeeperBot(author);

// Populate the projects repository
var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType());
var masterHash = localRepo.resolve("master").orElseThrow();
localRepo.push(masterHash, author.url(), "master", true);

// Make a change with a corresponding PR
var editHash = CheckableRepository.appendAndCommit(localRepo);
localRepo.push(editHash, author.url(), "edit", true);
var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request");

// Let the bot see it
TestBotRunner.runPeriodicItems(bot);

// There should now be no open PRs
var prs = author.pullRequests();
assertEquals(0, prs.size());
}
}

@Test
void keepClosing(TestInfo testInfo) throws IOException {
try (var credentials = new HostCredentials(testInfo);
var tempFolder = new TemporaryDirectory()) {
var author = credentials.getHostedRepository();
var bot = new BridgekeeperBot(author);

// Populate the projects repository
var localRepo = CheckableRepository.init(tempFolder.path(), author.repositoryType());
var masterHash = localRepo.resolve("master").orElseThrow();
localRepo.push(masterHash, author.url(), "master", true);

// Make a change with a corresponding PR
var editHash = CheckableRepository.appendAndCommit(localRepo);
localRepo.push(editHash, author.url(), "edit", true);
var pr = credentials.createPullRequest(author, "master", "edit", "This is a pull request");

// Let the bot see it
TestBotRunner.runPeriodicItems(bot);

// There should now be no open PRs
var prs = author.pullRequests();
assertEquals(0, prs.size());

// The author is persistent
pr.setState(Issue.State.OPEN);
prs = author.pullRequests();
assertEquals(1, prs.size());

// But so is the bot
TestBotRunner.runPeriodicItems(bot);
prs = author.pullRequests();
assertEquals(0, prs.size());

// There should still only be one welcome comment
assertEquals(1, pr.comments().size());
}
}
}
@@ -44,6 +44,7 @@ include 'network'
include 'forge'
include 'issuetracker'

include 'bots:bridgekeeper'
include 'bots:cli'
include 'bots:forward'
include 'bots:hgbridge'