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

Initial implementation of dependent pull requests #886

Closed
wants to merge 3 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
@@ -31,6 +31,7 @@ module {
opens 'org.openjdk.skara.bots.notify.json' to 'org.junit.platform.commons'
opens 'org.openjdk.skara.bots.notify.issue' to 'org.junit.platform.commons'
opens 'org.openjdk.skara.bots.notify.comment' to 'org.junit.platform.commons'
opens 'org.openjdk.skara.bots.notify.prbranch' to 'org.junit.platform.commons'
}
}

@@ -41,5 +41,6 @@
org.openjdk.skara.bots.notify.json.JsonNotifierFactory,
org.openjdk.skara.bots.notify.mailinglist.MailingListNotifierFactory,
org.openjdk.skara.bots.notify.slack.SlackNotifierFactory,
org.openjdk.skara.bots.notify.comment.CommitCommentNotifierFactory;
org.openjdk.skara.bots.notify.comment.CommitCommentNotifierFactory,
org.openjdk.skara.bots.notify.prbranch.PullRequestBranchNotifierFactory;
}
@@ -35,4 +35,8 @@ default void onNewPullRequest(PullRequest pr) {
}
default void onIntegratedPullRequest(PullRequest pr, Hash hash) {
}
default void onHeadChange(PullRequest pr, Hash oldHead) {
}
default void onStateChange(PullRequest pr, org.openjdk.skara.issuetracker.Issue.State oldState) {
}
}
@@ -23,6 +23,7 @@
package org.openjdk.skara.bots.notify;

import org.openjdk.skara.forge.PullRequest;
import org.openjdk.skara.issuetracker.Issue;
import org.openjdk.skara.vcs.Hash;

import java.util.*;
@@ -31,17 +32,23 @@ class PullRequestState {
private final String prId;
private final Set<String> issueIds;
private final Hash commitId;
private final Hash head;
private final Issue.State state;

PullRequestState(PullRequest pr, Set<String> issueIds, Hash commitId) {
PullRequestState(PullRequest pr, Set<String> issueIds, Hash commitId, Hash head, Issue.State state) {
this.prId = pr.repository().id() + ":" + pr.id();
this.issueIds = issueIds;
this.commitId = commitId;
this.head = head;
this.state = state;
}

PullRequestState(String prId, Set<String> issueIds, Hash commitId) {
PullRequestState(String prId, Set<String> issueIds, Hash commitId, Hash head, Issue.State state) {
this.prId = prId;
this.issueIds = issueIds;
this.commitId = commitId;
this.head = head;
this.state = state;
}

public String prId() {
@@ -56,12 +63,22 @@ public Optional<Hash> commitId() {
return Optional.ofNullable(commitId);
}

public Hash head() {
return head;
}

public Issue.State state() {
return state;
}

@Override
public String toString() {
return "PullRequestState{" +
"prId='" + prId + '\'' +
", issueIds=" + issueIds +
", commitId=" + commitId +
", head=" + head +
", state=" + state +
'}';
}

@@ -76,11 +93,13 @@ public boolean equals(Object o) {
var that = (PullRequestState) o;
return prId.equals(that.prId) &&
issueIds.equals(that.issueIds) &&
Objects.equals(commitId, that.commitId);
Objects.equals(commitId, that.commitId) &&
Objects.equals(head, that.head) &&
Objects.equals(state, that.state);
}

@Override
public int hashCode() {
return Objects.hash(prId, issueIds, commitId);
return Objects.hash(prId, issueIds, commitId, head);
}
}
@@ -85,11 +85,19 @@ private Set<PullRequestState> deserializePrState(String current) {
if (!obj.contains("commit")) {
obj.put("commit", Hash.zero().hex());
}
if (!obj.contains("head")) {
obj.put("head", Hash.zero().hex());
}
if (!obj.contains("state")) {
obj.put("state", JSON.of());
}

var commit = obj.get("commit").isNull() ?
null : new Hash(obj.get("commit").asString());
var state = obj.get("state").isNull() ?
null : org.openjdk.skara.issuetracker.Issue.State.valueOf(obj.get("state").asString());

return new PullRequestState(id, issues, commit);
return new PullRequestState(id, issues, commit, new Hash(obj.get("head").asString()), state);
})
.collect(Collectors.toSet());
}
@@ -119,6 +127,12 @@ private String serializePrState(Collection<PullRequestState> added, Set<PullRequ
} else {
ret.putNull("commit");
}
ret.put("head", JSON.of(pr.head().hex()));
if (pr.state() != null) {
ret.put("state", JSON.of(pr.state().toString()));
} else {
ret.putNull("state");
}
return ret;
})
.map(JSONObject::toString)
@@ -171,6 +185,14 @@ private void notifyIntegratedPr(PullRequest pr, Hash hash) {
listeners.forEach(c -> c.onIntegratedPullRequest(pr, hash));
}

private void notifyHeadChange(PullRequest pr, Hash oldHead) {
listeners.forEach(c -> c.onHeadChange(pr, oldHead));
}

private void notifyStateChange(org.openjdk.skara.issuetracker.Issue.State oldState) {
listeners.forEach(c -> c.onStateChange(pr, oldState));
}

@Override
public Collection<WorkItem> run(Path scratchPath) {
var historyPath = scratchPath.resolve("notify").resolve("history");
@@ -181,7 +203,7 @@ public Collection<WorkItem> run(Path scratchPath) {

var issues = parseIssues();
var commit = resultingCommitHash();
var state = new PullRequestState(pr, issues, commit);
var state = new PullRequestState(pr, issues, commit, pr.headHash(), pr.state());
var stored = storage.current();
if (stored.contains(state)) {
// Already up to date
@@ -193,10 +215,20 @@ public Collection<WorkItem> run(Path scratchPath) {
.filter(ss -> ss.prId().equals(state.prId()))
.findAny();
// The stored entry could be old and be missing commit information - if so, upgrade it
if (storedState.isPresent() && storedState.get().commitId().equals(Optional.of(Hash.zero()))) {
var hash = resultingCommitHash();
storedState = Optional.of(new PullRequestState(pr, storedState.get().issueIds(), hash));
storage.put(storedState.get());
if (storedState.isPresent()) {
if (storedState.get().commitId().equals(Optional.of(Hash.zero()))) {
var hash = resultingCommitHash();
storedState = Optional.of(new PullRequestState(pr, storedState.get().issueIds(), hash, pr.headHash(), pr.state()));
storage.put(storedState.get());
}
if (storedState.get().head().equals(Hash.zero())) {
storedState = Optional.of(new PullRequestState(pr, storedState.get().issueIds(), storedState.get().commitId().orElse(null), pr.headHash(), pr.state()));
storage.put(storedState.get());
}
if (storedState.get().state() == null) {
storedState = Optional.of(new PullRequestState(pr, storedState.get().issueIds(), storedState.get().commitId().orElse(null), pr.headHash(), pr.state()));
storage.put(storedState.get());
}
}

if (storedState.isPresent()) {
@@ -208,10 +240,16 @@ public Collection<WorkItem> run(Path scratchPath) {
.filter(issue -> !storedIssues.contains(issue))
.forEach(this::notifyNewIssue);

if (!storedState.get().head().equals(state.head())) {
notifyHeadChange(pr, storedState.get().head());
}
var storedCommit = storedState.get().commitId();
if (!storedCommit.isPresent() && state.commitId().isPresent()) {
if (storedCommit.isEmpty() && state.commitId().isPresent()) {
notifyIntegratedPr(pr, state.commitId().get());
}
if (!storedState.get().state().equals(state.state())) {
notifyStateChange(storedState.get().state());
}
} else {
notifyNewPr(pr);
issues.forEach(this::notifyNewIssue);
@@ -303,6 +303,9 @@ public void onNewOpenJDKTagCommits(HostedRepository repository, Repository local
String tagBranch = null;
try {
for (var branch : repository.branches()) {
if (PreIntegrations.isPreintegrationBranch(branch.name())) {
continue;
}
var hash = localRepository.resolve(tag.tag()).orElseThrow();
if (localRepository.isAncestor(hash, branch.hash())) {
if (tagBranch == null) {
@@ -0,0 +1,87 @@
/*
* 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.notify.prbranch;

import org.openjdk.skara.bots.notify.*;
import org.openjdk.skara.forge.*;
import org.openjdk.skara.issuetracker.Issue;
import org.openjdk.skara.vcs.*;

import java.io.*;
import java.nio.file.Path;

public class PullRequestBranchNotifier implements Notifier, PullRequestListener {
private final Path seedFolder;

public PullRequestBranchNotifier(Path seedFolder) {
this.seedFolder = seedFolder;
}

@Override
public void attachTo(Emitter e) {
e.registerPullRequestListener(this);
}

private void pushBranch(PullRequest pr) {
var hostedRepositoryPool = new HostedRepositoryPool(seedFolder);
try {
var seedRepo = hostedRepositoryPool.seedRepository(pr.repository(), false);
seedRepo.push(pr.headHash(), pr.repository().url(), PreIntegrations.preIntegrateBranch(pr), true);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private void deleteBranch(PullRequest pr) {
var hostedRepositoryPool = new HostedRepositoryPool(seedFolder);
try {
var seedRepo = hostedRepositoryPool.seedRepository(pr.repository(), false);
seedRepo.prune(new Branch(PreIntegrations.preIntegrateBranch(pr)), pr.repository().url().toString());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public void onNewPullRequest(PullRequest pr) {
if (pr.state() == Issue.State.OPEN) {
pushBranch(pr);
}
}

@Override
public void onStateChange(PullRequest pr, Issue.State oldState) {
if (pr.state() == Issue.State.CLOSED) {
deleteBranch(pr);
} else {
pushBranch(pr);
}
}

@Override
public void onHeadChange(PullRequest pr, Hash oldHead) {
if (pr.state() == Issue.State.OPEN) {
pushBranch(pr);
}
}
}
@@ -0,0 +1,41 @@
/*
* 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.notify.prbranch;

import org.openjdk.skara.bot.BotConfiguration;
import org.openjdk.skara.bots.notify.*;
import org.openjdk.skara.json.JSONObject;

public class PullRequestBranchNotifierFactory implements NotifierFactory {
@Override
public String name() {
return "prbranch";
}

@Override
public Notifier create(BotConfiguration botConfiguration, JSONObject notifierConfiguration) {
var seedFolder = botConfiguration.storageFolder();
return new PullRequestBranchNotifier(seedFolder.resolve("seeds"));
}

}