This is an automated email from the ASF dual-hosted git repository. royteeuwen pushed a commit to branch feature/sling-cli-release-automation in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git
commit a868352f58f74d83cfdfee4a8aba3e3cf8fc7208 Author: Roy Teeuwen <[email protected]> AuthorDate: Sat May 30 08:09:57 2026 +0200 release tally-votes: PMC-aware result email The [RESULT] email's closing paragraph now depends on whether the current user is a PMC member (auto-detected). A PMC member states they will copy the release to the dist directory themselves; a non-PMC release manager promotes to Maven Central and adds a standout "ACTION NEEDED" line asking a PMC member to perform the dist upload (which is PMC-only). --- .../sling/cli/impl/release/TallyVotesCommand.java | 18 ++++++++ src/main/resources/templates/tally-votes.email | 3 +- .../cli/impl/release/TallyVotesCommandTest.java | 49 +++++++++++++++++++++- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java b/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java index 3daca17..a621168 100644 --- a/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java +++ b/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java @@ -139,6 +139,7 @@ public class TallyVotesCommand implements Command { .replace("##DATE##", dateProvider.getCurrentDateForEmailHeader()) .replace("##RELEASE_NAME##", releaseFullName) .replace("##BINDING_VOTERS##", String.join(", ", bindingVoters)) + .replace("##CLOSING_ACTION##", closingAction(releaseFullName, currentMember.isPMCMember())) .replace( "##USER_NAME##", membersFinder.getCurrentMember().getName()); @@ -193,6 +194,23 @@ public class TallyVotesCommand implements Command { return CommandLine.ExitCode.OK; } + /** + * Builds the closing paragraph of the result email. PMC members copy the release to the dist + * directory themselves; non-PMC release managers can only promote to Maven Central and must ask + * a PMC member to handle the dist upload (which is restricted to PMC members). PMC membership is + * derived from the current user, so no flag is needed. + */ + private String closingAction(String releaseFullName, boolean isPmcMember) { + if (isPmcMember) { + return "I will copy this release to the Sling dist directory and\n" + + "promote the artifacts to the central Maven repository."; + } + return "I will promote the artifacts to the central Maven repository.\n\n" + + "As I am not a PMC member, I cannot copy the release to the dist directory myself.\n\n" + + "ACTION NEEDED: can a PMC member please copy " + releaseFullName + + " to the Sling dist directory (https://dist.apache.org/repos/dist/release/sling/)?"; + } + // TODO - better detection of '+1' votes private boolean isPositiveVote(Email e) { return cleanup(e.getBody()).contains("+1"); diff --git a/src/main/resources/templates/tally-votes.email b/src/main/resources/templates/tally-votes.email index af68d64..2b08093 100644 --- a/src/main/resources/templates/tally-votes.email +++ b/src/main/resources/templates/tally-votes.email @@ -11,8 +11,7 @@ The vote has passed with the following result: +1 (binding): ##BINDING_VOTERS## +1 (non-binding): ##NON_BINDING_VOTERS## -I will copy this release to the Sling dist directory and -promote the artifacts to the central Maven repository. +##CLOSING_ACTION## Regards, ##USER_NAME## diff --git a/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java index dcd28a2..e9d1573 100644 --- a/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java +++ b/src/test/java/org/apache/sling/cli/impl/release/TallyVotesCommandTest.java @@ -110,6 +110,48 @@ public class TallyVotesCommandTest { + "John Doe\n")); } + @Test + public void testDryRunNonPmc() throws Exception { + // Daniel (a non-PMC committer) is the release manager running tally-votes; PMC status is + // auto-detected from the current user, so the result email asks a PMC member to do the dist + // upload. + Mailer mailer = mock(Mailer.class); + List<Email> thread = new ArrayList<>() { + { + add(mockEmail("[email protected]", "Daniel")); + add(mockEmail("[email protected]", "Alice")); + add(mockEmail("[email protected]", "Bob")); + add(mockEmail("[email protected]", "Charlie")); + } + }; + prepareExecution(mailer, thread, "daniel"); + Command command = createCommand(123, ExecutionMode.DRY_RUN); + assertEquals(CommandLine.ExitCode.OK, (int) command.call()); + verifyNoInteractions(mailer); + assertTrue(logCapture.containsMessage( + "From: Daniel <[email protected]>\n" + "To: \"Sling Developers List\" <[email protected]>\n" + + "Reply-To: \"Sling Developers List\" <[email protected]>\n" + + "Date: Thu, 1 Jan 1970 01:00:00 +0100\n" + + "Subject: [RESULT] [VOTE] Release Apache Sling CLI Test 1.0.0\n" + + "\n" + + "Hi,\n" + + "\n" + + "The vote has passed with the following result:\n" + + "\n" + + "+1 (binding): Alice, Bob, Charlie\n" + + "+1 (non-binding): none\n" + + "\n" + + "I will promote the artifacts to the central Maven repository.\n" + + "\n" + + "As I am not a PMC member, I cannot copy the release to the dist directory myself.\n" + + "\n" + + "ACTION NEEDED: can a PMC member please copy Apache Sling CLI Test 1.0.0 to the" + + " Sling dist directory (https://dist.apache.org/repos/dist/release/sling/)?\n" + + "\n" + + "Regards,\n" + + "Daniel\n")); + } + @Test public void testDryRunNotEnoughBindingVotes() throws Exception { Mailer mailer = mock(Mailer.class); @@ -184,8 +226,13 @@ public class TallyVotesCommandTest { } private void prepareExecution(Mailer mailer, List<Email> thread) throws IOException, IllegalAccessException { + prepareExecution(mailer, thread, "johndoe"); + } + + private void prepareExecution(Mailer mailer, List<Email> thread, String currentUsername) + throws IOException, IllegalAccessException { CredentialsService credentialsService = mock(CredentialsService.class); - when(credentialsService.getAsfCredentials()).thenReturn(new Credentials("johndoe", "secret")); + when(credentialsService.getAsfCredentials()).thenReturn(new Credentials(currentUsername, "secret")); MembersFinder membersFinder = spy(new MembersFinder()); Set<Member> members = new HashSet<>() {
