You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2019/05/05 12:01:37 UTC
[sling-org-apache-sling-committer-cli] 02/02: SLING-8391 - Add
support for execution modes
This is an automated email from the ASF dual-hosted git repository.
radu pushed a commit to branch issue/SLING-8391
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git
commit f9cfc67ab62b91ad9fe67eff9d38992b26c708b0
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Sun May 5 13:54:28 2019 +0200
SLING-8391 - Add support for execution modes
* implemented the 3 execution modes for `release prepare-email`
---
.../apache/sling/cli/impl/ExecutionContext.java | 2 +-
.../org/apache/sling/cli/impl/InputOption.java | 63 ++++++++++++++++++
.../java/org/apache/sling/cli/impl/UserInput.java | 60 +++++++++++++++++
.../org/apache/sling/cli/impl/mail/Mailer.java | 25 ++-----
.../cli/impl/release/PrepareVoteEmailCommand.java | 77 +++++++++++++++-------
.../impl/release/PrepareVoteEmailCommandTest.java | 60 +++++++++--------
src/test/resources/simplelogger.properties | 19 ++++++
7 files changed, 232 insertions(+), 74 deletions(-)
diff --git a/src/main/java/org/apache/sling/cli/impl/ExecutionContext.java b/src/main/java/org/apache/sling/cli/impl/ExecutionContext.java
index d2a433e..77eac71 100644
--- a/src/main/java/org/apache/sling/cli/impl/ExecutionContext.java
+++ b/src/main/java/org/apache/sling/cli/impl/ExecutionContext.java
@@ -64,7 +64,7 @@ public class ExecutionContext {
return mode;
}
- enum Mode {
+ public enum Mode {
DRY_RUN("--dry-run"), INTERACTIVE("--interactive"), AUTO("--auto");
private final String string;
diff --git a/src/main/java/org/apache/sling/cli/impl/InputOption.java b/src/main/java/org/apache/sling/cli/impl/InputOption.java
new file mode 100644
index 0000000..d2471d9
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/InputOption.java
@@ -0,0 +1,63 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl;
+
+public class InputOption {
+
+ private final String text;
+ private final String mnemonic;
+
+ public static final InputOption YES = new InputOption("Yes", "y");
+ public static final InputOption NO = new InputOption("No", "n");
+
+ public InputOption(String text, String mnemonic) {
+ this.text = text;
+ this.mnemonic = mnemonic;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public String getMnemonic() {
+ return mnemonic;
+ }
+
+ @Override
+ public String toString() {
+ return text + " (" + mnemonic + ")";
+ }
+
+ @Override
+ public int hashCode() {
+ return text.hashCode() + mnemonic.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof InputOption) {
+ InputOption other = (InputOption) obj;
+ return text.equals(other.text) && mnemonic.equals(other.mnemonic);
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/org/apache/sling/cli/impl/UserInput.java b/src/main/java/org/apache/sling/cli/impl/UserInput.java
new file mode 100644
index 0000000..38fb7a6
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/UserInput.java
@@ -0,0 +1,60 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl;
+
+import java.io.Console;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UserInput {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(UserInput.class);
+ private static final InputOption[] YES_NO = new InputOption[]{InputOption.YES, InputOption.NO};
+
+ public static InputOption yesNo(String question, InputOption defaultOption) {
+ LOGGER.info(question);
+ Set<InputOption> answers = new LinkedHashSet<>(Arrays.asList(YES_NO));
+ String choice =
+ answers.stream().map(InputOption::toString).collect(Collectors.joining("/")) + "? [" + defaultOption.toString() +
+ "]: ";
+ while (true) {
+ System.out.print(choice);
+ Console console = System.console();
+ if (console != null) {
+ String answerMnemonic = console.readLine();
+ if ("".equals(answerMnemonic)) {
+ return defaultOption;
+ }
+ for (InputOption o : YES_NO) {
+ if (o.getMnemonic().equals(answerMnemonic)) {
+ return o;
+ }
+ }
+ } else {
+ throw new IllegalStateException("System console unavailable.");
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/cli/impl/mail/Mailer.java b/src/main/java/org/apache/sling/cli/impl/mail/Mailer.java
index 0c0a4e4..0b5bc61 100644
--- a/src/main/java/org/apache/sling/cli/impl/mail/Mailer.java
+++ b/src/main/java/org/apache/sling/cli/impl/mail/Mailer.java
@@ -18,22 +18,17 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package org.apache.sling.cli.impl.mail;
-import java.io.UnsupportedEncodingException;
+import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
-import javax.mail.Address;
-import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
-import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.sling.cli.impl.Credentials;
import org.apache.sling.cli.impl.CredentialsService;
-import org.apache.sling.cli.impl.people.Member;
-import org.apache.sling.cli.impl.people.MembersFinder;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
@@ -57,23 +52,15 @@ public class Mailer {
@Reference
private CredentialsService credentialsService;
- @Reference
- private MembersFinder membersFinder;
-
- public void send(String to, String subject, String body) {
+ public void send(String source) {
Properties properties = new Properties(SMTP_PROPERTIES);
Session session = Session.getInstance(properties);
try {
- MimeMessage message = new MimeMessage(session);
- Member sender = membersFinder.getCurrentMember();
+ MimeMessage message = new MimeMessage(session, new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)));
Credentials credentials = credentialsService.getCredentials();
- message.setFrom(new InternetAddress(sender.getEmail(), sender.getEmail(), StandardCharsets.UTF_8.name()));
- message.setSubject(subject);
- message.setText(body, StandardCharsets.UTF_8.name());
- message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
- Transport.send(message, new Address[] {new InternetAddress(to)}, credentials.getUsername(), credentials.getPassword());
- } catch (MessagingException | UnsupportedEncodingException e) {
- LOGGER.error(String.format("Unable to send email with Subject '%s' to '%s'.", subject, to), e);
+ Transport.send(message, credentials.getUsername(), credentials.getPassword());
+ } catch (MessagingException e) {
+ LOGGER.error(String.format("Unable to send the following email:\n%s", source), e);
}
}
diff --git a/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java b/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
index 1862605..5e3f429 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommand.java
@@ -24,8 +24,11 @@ import javax.mail.internet.InternetAddress;
import org.apache.sling.cli.impl.Command;
import org.apache.sling.cli.impl.ExecutionContext;
+import org.apache.sling.cli.impl.InputOption;
+import org.apache.sling.cli.impl.UserInput;
import org.apache.sling.cli.impl.jira.Version;
import org.apache.sling.cli.impl.jira.VersionFinder;
+import org.apache.sling.cli.impl.mail.Mailer;
import org.apache.sling.cli.impl.nexus.StagingRepository;
import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
import org.apache.sling.cli.impl.people.Member;
@@ -52,6 +55,9 @@ public class PrepareVoteEmailCommand implements Command {
@Reference
private VersionFinder versionFinder;
+ @Reference
+ private Mailer mailer;
+
// TODO - replace with file template
private static final String EMAIL_TEMPLATE =
"From: ##FROM##\n" +
@@ -61,31 +67,30 @@ public class PrepareVoteEmailCommand implements Command {
"Hi,\n" +
"\n" +
"We solved ##FIXED_ISSUES_COUNT## issue(s) in ##RELEASE_OR_RELEASES##:\n" +
- "\n" +
"##RELEASE_JIRA_LINKS##\n" +
- "\n" +
- "Staging repository:\n" +
- "https://repository.apache.org/content/repositories/orgapachesling-##RELEASE_ID##/\n" +
- "\n" +
- "You can use this UNIX script to download the release and verify the signatures:\n" +
- "https://gitbox.apache.org/repos/asf?p=sling-tooling-release.git;a=blob;f=check_staged_release.sh;hb=HEAD\n" +
- "\n" +
- "Usage:\n" +
- "sh check_staged_release.sh ##RELEASE_ID## /tmp/sling-staging\n" +
- "\n" +
- "Please vote to approve this release:\n" +
- "\n" +
- " [ ] +1 Approve the release\n" +
- " [ ] 0 Don't care\n" +
- " [ ] -1 Don't release, because ...\n" +
- "\n" +
+ "\n" +
+ "Staging repository:\n" +
+ "https://repository.apache.org/content/repositories/orgapachesling-##RELEASE_ID##/\n" +
+ "\n" +
+ "You can use this UNIX script to download the release and verify the signatures:\n" +
+ "https://gitbox.apache.org/repos/asf?p=sling-tooling-release.git;a=blob;f=check_staged_release.sh;hb=HEAD\n" +
+ "\n" +
+ "Usage:\n" +
+ "sh check_staged_release.sh ##RELEASE_ID## /tmp/sling-staging\n" +
+ "\n" +
+ "Please vote to approve this release:\n" +
+ "\n" +
+ " [ ] +1 Approve the release\n" +
+ " [ ] 0 Don't care\n" +
+ " [ ] -1 Don't release, because ...\n" +
+ "\n" +
"This majority vote is open for at least 72 hours.\n" +
"\n" +
"Regards,\n" +
"##USER_NAME##\n" +
"\n";
- private static final String RELEASE_TEMPLATE =
+ private static final String RELEASE_TEMPLATE =
"https://issues.apache.org/jira/browse/SLING/fixforversion/##VERSION_ID##";
@Override
@@ -97,19 +102,19 @@ public class PrepareVoteEmailCommand implements Command {
List<Version> versions = releases.stream()
.map( r -> versionFinder.find(r.getName()))
.collect(Collectors.toList());
-
+
String releaseName = releases.stream()
.map( Release::getFullName )
.collect(Collectors.joining(", "));
-
+
int fixedIssueCounts = versions.stream().mapToInt( Version::getIssuesFixedCount).sum();
String releaseOrReleases = versions.size() > 1 ?
"these releases" : "this release";
-
+
String releaseJiraLinks = versions.stream()
.map( v -> RELEASE_TEMPLATE.replace("##VERSION_ID##", String.valueOf(v.getId())))
.collect(Collectors.joining("\n"));
-
+
Member currentMember = membersFinder.getCurrentMember();
String emailContents = EMAIL_TEMPLATE
.replace("##FROM##", new InternetAddress(currentMember.getEmail(), currentMember.getName()).toString())
@@ -119,9 +124,31 @@ public class PrepareVoteEmailCommand implements Command {
.replace("##RELEASE_JIRA_LINKS##", releaseJiraLinks)
.replace("##FIXED_ISSUES_COUNT##", String.valueOf(fixedIssueCounts))
.replace("##USER_NAME##", currentMember.getName());
-
- LOGGER.info(emailContents);
-
+ switch (context.getMode()) {
+ case DRY_RUN:
+ LOGGER.info("The following email would be sent from your @apache.org address (see the \"From:\" header):\n");
+ LOGGER.info(emailContents);
+ break;
+ case INTERACTIVE:
+ StringBuilder question = new StringBuilder("Should the following email be sent from your @apache.org address (see the" +
+ " \"From:\" header)?\n\n");
+ question.append(emailContents);
+ InputOption answer = UserInput.yesNo(question.toString(), InputOption.YES);
+ if (InputOption.YES.equals(answer)) {
+ LOGGER.info("Sending email...");
+ mailer.send(emailContents);
+ LOGGER.info("Done!");
+ } else if (InputOption.NO.equals(answer)) {
+ LOGGER.info("Aborted.");
+ }
+ break;
+ case AUTO:
+ LOGGER.info(emailContents);
+ LOGGER.info("Sending email...");
+ mailer.send(emailContents);
+ LOGGER.info("Done!");
+ break;
+ }
} catch (IOException e) {
LOGGER.warn("Failed executing command", e);
}
diff --git a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
index 24a9660..653cd47 100644
--- a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
+++ b/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
@@ -18,10 +18,13 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package org.apache.sling.cli.impl.release;
+import java.io.IOException;
+
import org.apache.sling.cli.impl.Command;
import org.apache.sling.cli.impl.ExecutionContext;
import org.apache.sling.cli.impl.jira.Version;
import org.apache.sling.cli.impl.jira.VersionFinder;
+import org.apache.sling.cli.impl.mail.Mailer;
import org.apache.sling.cli.impl.nexus.StagingRepository;
import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
import org.apache.sling.cli.impl.people.Member;
@@ -34,16 +37,13 @@ import org.osgi.framework.ServiceReference;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
@RunWith(PowerMockRunner.class)
-@PrepareForTest({PrepareVoteEmailCommand.class, LoggerFactory.class})
+@PrepareForTest({PrepareVoteEmailCommand.class})
@PowerMockIgnore({
// https://github.com/powermock/powermock/issues/864
"com.sun.org.apache.xerces.*",
@@ -57,35 +57,16 @@ public class PrepareVoteEmailCommandTest {
@Test
public void testPrepareEmailGeneration() throws Exception {
- mockStatic(LoggerFactory.class);
- Logger logger = mock(Logger.class);
- when(LoggerFactory.getLogger(PrepareVoteEmailCommand.class)).thenReturn(logger);
- MembersFinder membersFinder = mock(MembersFinder.class);
- when(membersFinder.getCurrentMember()).thenReturn(new Member("johndoe", "John Doe", true));
-
- StagingRepository stagingRepository = mock(StagingRepository.class);
- when(stagingRepository.getDescription()).thenReturn("Apache Sling CLI Test 1.0.0");
- StagingRepositoryFinder stagingRepositoryFinder = mock(StagingRepositoryFinder.class);
- when(stagingRepositoryFinder.find(123)).thenReturn(stagingRepository);
-
- VersionFinder versionFinder = mock(VersionFinder.class);
- Version version = mock(Version.class);
- when(version.getName()).thenReturn("CLI Test 1.0.0");
- when(version.getId()).thenReturn(1);
- when(version.getIssuesFixedCount()).thenReturn(42);
- when(versionFinder.find("CLI Test 1.0.0")).thenReturn(version);
-
- osgiContext.registerService(MembersFinder.class, membersFinder);
- osgiContext.registerService(StagingRepositoryFinder.class, stagingRepositoryFinder);
- osgiContext.registerService(VersionFinder.class, versionFinder);
-
+ Mailer mailer = mock(Mailer.class);
+ prepareExecution(mailer);
+ ExecutionContext context = new ExecutionContext("123", "--auto");
osgiContext.registerInjectActivateService(new PrepareVoteEmailCommand());
ServiceReference<?> reference =
osgiContext.bundleContext().getServiceReference(Command.class.getName());
Command command = (Command) osgiContext.bundleContext().getService(reference);
- command.execute(new ExecutionContext("123", null));
- verify(logger).info(
+ command.execute(context);
+ verify(mailer).send(
"From: John Doe <jo...@apache.org>\n" +
"To: \"Sling Developers List\" <de...@sling.apache.org>\n" +
"Subject: [VOTE] Release Apache Sling CLI Test 1.0.0\n" +
@@ -93,7 +74,6 @@ public class PrepareVoteEmailCommandTest {
"Hi,\n" +
"\n" +
"We solved 42 issue(s) in this release:\n" +
- "\n" +
"https://issues.apache.org/jira/browse/SLING/fixforversion/1\n" +
"\n" +
"Staging repository:\n" +
@@ -117,4 +97,26 @@ public class PrepareVoteEmailCommandTest {
"John Doe\n" +
"\n");
}
+
+ private void prepareExecution(Mailer mailer) throws IOException {
+ MembersFinder membersFinder = mock(MembersFinder.class);
+ when(membersFinder.getCurrentMember()).thenReturn(new Member("johndoe", "John Doe", true));
+
+ StagingRepository stagingRepository = mock(StagingRepository.class);
+ when(stagingRepository.getDescription()).thenReturn("Apache Sling CLI Test 1.0.0");
+ StagingRepositoryFinder stagingRepositoryFinder = mock(StagingRepositoryFinder.class);
+ when(stagingRepositoryFinder.find(123)).thenReturn(stagingRepository);
+
+ VersionFinder versionFinder = mock(VersionFinder.class);
+ Version version = mock(Version.class);
+ when(version.getName()).thenReturn("CLI Test 1.0.0");
+ when(version.getId()).thenReturn(1);
+ when(version.getIssuesFixedCount()).thenReturn(42);
+ when(versionFinder.find("CLI Test 1.0.0")).thenReturn(version);
+
+ osgiContext.registerService(MembersFinder.class, membersFinder);
+ osgiContext.registerService(StagingRepositoryFinder.class, stagingRepositoryFinder);
+ osgiContext.registerService(VersionFinder.class, versionFinder);
+ osgiContext.registerService(Mailer.class, mailer);
+ }
}
diff --git a/src/test/resources/simplelogger.properties b/src/test/resources/simplelogger.properties
new file mode 100644
index 0000000..620f7c4
--- /dev/null
+++ b/src/test/resources/simplelogger.properties
@@ -0,0 +1,19 @@
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+org.slf4j.simpleLogger.defaultLogLevel=warn