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