diff --git a/.gitignore b/.gitignore index 734ffd405..db2efc95a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ build/ out/ bin/ +manual-test-settings.properties diff --git a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveWorkItem.java b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveWorkItem.java index 9a87fac9c..4f428830b 100644 --- a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveWorkItem.java +++ b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/ArchiveWorkItem.java @@ -61,14 +61,19 @@ public String toString() { @Override public boolean concurrentWith(WorkItem other) { - if (!(other instanceof ArchiveWorkItem)) { - return true; + if (!(other instanceof ArchiveWorkItem otherArchiveItem)) { + if (!(other instanceof LabelsUpdaterWorkItem otherLabelsUpdaterItem)) { + return true; + } + if (!bot.equals(otherLabelsUpdaterItem.bot())) { + return true; + } + return false; } - ArchiveWorkItem otherItem = (ArchiveWorkItem)other; - if (!pr.id().equals(otherItem.pr.id())) { + if (!pr.id().equals(otherArchiveItem.pr.id())) { return true; } - if (!bot.codeRepo().name().equals(otherItem.bot.codeRepo().name())) { + if (!bot.codeRepo().name().equals(otherArchiveItem.bot.codeRepo().name())) { return true; } return false; diff --git a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/LabelsUpdaterWorkItem.java b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/LabelsUpdaterWorkItem.java new file mode 100644 index 000000000..6a0f9f31c --- /dev/null +++ b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/LabelsUpdaterWorkItem.java @@ -0,0 +1,78 @@ +package org.openjdk.skara.bots.mlbridge; + +import org.openjdk.skara.bot.WorkItem; +import org.openjdk.skara.issuetracker.Label; + +import java.nio.file.Path; +import java.util.*; +import java.util.logging.Logger; + +/** + * This WorkItem runs once when the bots starts up to update the repository + * with all mailing list labels configured for it. + */ +public class LabelsUpdaterWorkItem implements WorkItem { + private static final Logger log = Logger.getLogger(LabelsUpdaterWorkItem.class.getName()); + + private final MailingListBridgeBot bot; + + public LabelsUpdaterWorkItem(MailingListBridgeBot bot) { + this.bot = bot; + } + + public MailingListBridgeBot bot() { + return bot; + } + + @Override + public boolean concurrentWith(WorkItem other) { + if (!(other instanceof LabelsUpdaterWorkItem otherItem)) { + return true; + } + if (!bot.equals(otherItem.bot)) { + return true; + } + return false; + } + + @Override + public Collection run(Path scratchPath) { + if (bot.labelsUpdated()) { + return List.of(); + } + + var existingLabelsMap = new HashMap(); + bot.codeRepo().labels().forEach(l -> existingLabelsMap.put(l.name(), l)); + + var configuredLabels = bot.lists().stream() + .flatMap(configuration -> configuration.labels().stream() + .map(labelName -> new Label(labelName, configuration.list().toString()))) + .toList(); + + for (Label configuredLabel : configuredLabels) { + var existingLabel = existingLabelsMap.get(configuredLabel.name()); + if (existingLabel == null) { + log.info("Adding label: " + configuredLabel.name() + " to repo: " + bot.codeRepo().name()); + bot.codeRepo().addLabel(configuredLabel); + } else if (!existingLabel.description().equals(configuredLabel.description())) { + log.info("Updating label: " + configuredLabel.name() + " with description: " + + configuredLabel.description() + " for repo: " + bot.codeRepo().name()); + bot.codeRepo().updateLabel(configuredLabel); + } + } + + log.fine("Done updating labels for: " + bot.codeRepo()); + bot.setLabelsUpdated(true); + return List.of(); + } + + @Override + public String botName() { + return MailingListBridgeBotFactory.NAME; + } + + @Override + public String workItemName() { + return "labels-updater"; + } +} diff --git a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBot.java b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBot.java index 3b2c54e41..7339cc8c8 100644 --- a/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBot.java +++ b/bots/mlbridge/src/main/java/org/openjdk/skara/bots/mlbridge/MailingListBridgeBot.java @@ -62,6 +62,7 @@ public class MailingListBridgeBot implements Bot { private ZonedDateTime lastPartialUpdate; private ZonedDateTime lastFullUpdate; + private volatile boolean labelsUpdated = false; MailingListBridgeBot(EmailAddress from, HostedRepository repo, HostedRepository archive, String archiveRef, HostedRepository censusRepo, String censusRef, List lists, @@ -188,9 +189,22 @@ Optional seedStorage() { return Optional.ofNullable(seedStorage); } + public boolean labelsUpdated() { + return labelsUpdated; + } + + public void setLabelsUpdated(boolean labelsUpdated) { + this.labelsUpdated = labelsUpdated; + } + @Override public List getPeriodicItems() { List ret = new LinkedList<>(); + + if (!labelsUpdated) { + ret.add(new LabelsUpdaterWorkItem(this)); + } + List prs; if (lastFullUpdate == null || lastFullUpdate.isBefore(ZonedDateTime.now().minus(Duration.ofMinutes(10)))) { diff --git a/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/LabelsUpdaterTests.java b/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/LabelsUpdaterTests.java new file mode 100644 index 000000000..4e23d7e83 --- /dev/null +++ b/bots/mlbridge/src/test/java/org/openjdk/skara/bots/mlbridge/LabelsUpdaterTests.java @@ -0,0 +1,85 @@ +package org.openjdk.skara.bots.mlbridge; + +import org.junit.jupiter.api.*; +import org.openjdk.skara.email.EmailAddress; +import org.openjdk.skara.test.*; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class LabelsUpdaterTests { + + @Test + void simple(TestInfo testInfo) throws IOException { + try (var credentials = new HostCredentials(testInfo); + var listServer = new TestMailmanServer();) { + var targetRepo = credentials.getHostedRepository(); + var listAddress = EmailAddress.parse(listServer.createList("test")); + var mlBot = MailingListBridgeBot.newBuilder() + .repo(targetRepo) + .lists(List.of(new MailingListConfiguration(listAddress, Set.of("foo", "bar")))) + .build(); + + // Check that the repo contains no labels + assertTrue(targetRepo.labels().isEmpty(), "Repo has labels from the start: " + targetRepo.labels()); + + // Run an archive pass + TestBotRunner.runPeriodicItems(mlBot); + + assertEquals(2, targetRepo.labels().size(), "Wrong number of labels"); + assertTrue(targetRepo.labels().stream() + .anyMatch(l -> l.name().equals("foo") && l.description().orElseThrow().equals(listAddress.toString())), + "No label 'foo' found"); + assertTrue(targetRepo.labels().stream() + .anyMatch(l -> l.name().equals("bar") && l.description().orElseThrow().equals(listAddress.toString())), + "No label 'bar' found"); + + // Run again and expect no change + TestBotRunner.runPeriodicItems(mlBot); + + assertEquals(2, targetRepo.labels().size(), "Wrong number of labels"); + } + } + + @Test + void update(TestInfo testInfo) throws IOException { + try (var credentials = new HostCredentials(testInfo); + var listServer = new TestMailmanServer();) { + var targetRepo = credentials.getHostedRepository(); + var listAddress = EmailAddress.parse(listServer.createList("test")); + var listAddress2 = EmailAddress.parse(listServer.createList("test2")); + var mlBot = MailingListBridgeBot.newBuilder() + .repo(targetRepo) + .lists(List.of(new MailingListConfiguration(listAddress, Set.of("foo")))) + .build(); + + // Check that the repo contains no labels + assertTrue(targetRepo.labels().isEmpty(), "Repo has labels from the start: " + targetRepo.labels()); + + // Run an archive pass + TestBotRunner.runPeriodicItems(mlBot); + + assertEquals(1, targetRepo.labels().size(), "Wrong number of labels"); + assertTrue(targetRepo.labels().stream() + .anyMatch(l -> l.name().equals("foo") && l.description().orElseThrow().equals(listAddress.toString())), + "No label 'foo' found"); + + var mlBot2 = MailingListBridgeBot.newBuilder() + .repo(targetRepo) + .lists(List.of(new MailingListConfiguration(listAddress2, Set.of("foo")))) + .build(); + + // Run second bot and expect label to have updated + TestBotRunner.runPeriodicItems(mlBot2); + + assertEquals(1, targetRepo.labels().size(), "Wrong number of labels"); + assertTrue(targetRepo.labels().stream() + .anyMatch(l -> l.name().equals("foo") && l.description().orElseThrow().equals(listAddress2.toString())), + "No label 'foo' found"); + } + } +} diff --git a/bots/tester/src/test/java/org/openjdk/skara/bots/tester/InMemoryHostedRepository.java b/bots/tester/src/test/java/org/openjdk/skara/bots/tester/InMemoryHostedRepository.java index 87e06ad65..879e7b3cb 100644 --- a/bots/tester/src/test/java/org/openjdk/skara/bots/tester/InMemoryHostedRepository.java +++ b/bots/tester/src/test/java/org/openjdk/skara/bots/tester/InMemoryHostedRepository.java @@ -223,4 +223,16 @@ public void restrictPushAccess(Branch branch, HostUser user) { public List