You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2019/04/24 10:48:14 UTC

[sling-org-apache-sling-committer-cli] 04/42: SLING-8311 - Investigate creating a Sling CLI tool for development task automation

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch feature/SLING-8337
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git

commit d5e841e05e0b1f3e8f1e1c224ab634f2ef254168
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Mar 19 15:16:23 2019 +0100

    SLING-8311 - Investigate creating a Sling CLI tool for development task automation
    
    Implement command for listing active releases.
---
 README.md                                          |   8 +-
 .../cli/impl/nexus/StagingRepositoryFinder.java    |  37 ++++++--
 .../apache/sling/cli/impl/release/ListCommand.java |  49 ++++++++++
 .../cli/impl/release/PrepareVoteEmailCommand.java  |  15 +--
 .../sling/cli/impl/release/ReleaseVersion.java}    |  41 +++++++--
 .../sling/cli/impl/release/TallyVotesCommand.java  |   9 +-
 .../cli/impl/release/UpdateLocalSiteCommand.java   | 102 +++++++++++++++++++++
 ...ailCommandTest.java => ReleaseVersionTest.java} |  11 ++-
 8 files changed, 234 insertions(+), 38 deletions(-)

diff --git a/README.md b/README.md
index eabf20c..45336b1 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,10 @@ This invocation produces a list of available subcommands.
 
 ## Commands
 
+Listing active releases
+
+    docker run --env-file=./docker-env apache/sling-cli release list
+
 Generating a release vote email
 
     docker run --env-file=./docker-env apache/sling-cli release prepare-email $STAGING_REPOSITORY_ID
@@ -27,7 +31,7 @@ Generating a release vote email
 Generating a release vote result email
 
     docker run --env-file=./docker-env apache/sling-cli release tally-votes $STAGING_REPOSITORY_ID
-    
+
 ## Assumptions
 
 This tool assumes that the name of the staging repository matches the one of the version in Jira. For instance, the
@@ -35,4 +39,4 @@ staging repositories are usually named _Apache Sling Foo 1.2.0_. It is then expe
 named _Foo 1.2.0_. Otherwise the link between the staging repository and the Jira release can not be found.
 
 It is allowed for staging repository names to have an _RC_ suffix, which may include a number, so that _RC_, _RC1_, _RC25_ are
-all valid suffixes.  
\ No newline at end of file
+all valid suffixes.  
diff --git a/src/main/java/org/apache/sling/cli/impl/nexus/StagingRepositoryFinder.java b/src/main/java/org/apache/sling/cli/impl/nexus/StagingRepositoryFinder.java
index 3ef7992..21b30ff 100644
--- a/src/main/java/org/apache/sling/cli/impl/nexus/StagingRepositoryFinder.java
+++ b/src/main/java/org/apache/sling/cli/impl/nexus/StagingRepositoryFinder.java
@@ -19,6 +19,9 @@ package org.apache.sling.cli.impl.nexus;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.UsernamePasswordCredentials;
@@ -43,6 +46,8 @@ import com.google.gson.Gson;
 )
 @Designate(ocd = StagingRepositoryFinder.Config.class)
 public class StagingRepositoryFinder {
+    
+    private static final String REPOSITORY_PREFIX = "orgapachesling-";
 
     @ObjectClassDefinition
     static @interface Config {
@@ -61,8 +66,30 @@ public class StagingRepositoryFinder {
         credentialsProvider.setCredentials(new AuthScope("repository.apache.org", 443), 
                 new UsernamePasswordCredentials(cfg.username(), cfg.password()));
     }
+    
+    public List<StagingRepository> list() throws IOException {
+        return this. <List<StagingRepository>> withStagingRepositories( reader -> {
+            Gson gson = new Gson();
+            return gson.fromJson(reader, StagingRepositories.class).getData().stream()
+                    .filter( r -> r.getType() == Status.closed)
+                    .filter( r -> r.getRepositoryId().startsWith(REPOSITORY_PREFIX) )
+                    .collect(Collectors.toList());            
+        });
+    }
 
     public StagingRepository find(int stagingRepositoryId) throws IOException {
+        return this.<StagingRepository> withStagingRepositories( reader -> {
+            Gson gson = new Gson();
+            return gson.fromJson(reader, StagingRepositories.class).getData().stream()
+                    .filter( r -> r.getType() == Status.closed)
+                    .filter( r -> r.getRepositoryId().startsWith(REPOSITORY_PREFIX) )
+                    .filter( r -> r.getRepositoryId().endsWith("-" + stagingRepositoryId))
+                    .findFirst()
+                    .orElseThrow(() -> new IllegalArgumentException("No repository found with id " + stagingRepositoryId));            
+        });
+    }
+    
+    private <T> T withStagingRepositories(Function<InputStreamReader, T> function) throws IOException {
         try ( CloseableHttpClient client = HttpClients.custom()
                 .setDefaultCredentialsProvider(credentialsProvider)
                 .build() ) {
@@ -73,14 +100,10 @@ public class StagingRepositoryFinder {
                         InputStreamReader reader = new InputStreamReader(content)) {
                     if ( response.getStatusLine().getStatusCode() != 200 )
                         throw new IOException("Status line : " + response.getStatusLine());
-                    Gson gson = new Gson();
-                    return gson.fromJson(reader, StagingRepositories.class).getData().stream()
-                        .filter( r -> r.getType() == Status.closed)
-                        .filter( r -> r.getRepositoryId().endsWith("-" + stagingRepositoryId))
-                        .findFirst()
-                        .orElseThrow(() -> new IllegalArgumentException("No repository found with id " + stagingRepositoryId));
+                    
+                    return function.apply(reader);
                 }
             }
-        }
+        }       
     }
 }
diff --git a/src/main/java/org/apache/sling/cli/impl/release/ListCommand.java b/src/main/java/org/apache/sling/cli/impl/release/ListCommand.java
new file mode 100644
index 0000000..1d35e29
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/release/ListCommand.java
@@ -0,0 +1,49 @@
+/*
+ * 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.release;
+
+import java.io.IOException;
+
+import org.apache.sling.cli.impl.Command;
+import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(service = Command.class, property = {
+        Command.PROPERTY_NAME_COMMAND + "=release",
+        Command.PROPERTY_NAME_SUBCOMMAND + "=list",
+        Command.PROPERTY_NAME_SUMMARY + "=Lists all open releases" })
+public class ListCommand implements Command {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Reference
+    private StagingRepositoryFinder repoFinder;
+
+    @Override
+    public void execute(String target) {
+        try {
+            repoFinder.list().stream()
+                .forEach( r -> logger.info("{}\t{}", r.getRepositoryId(), r.getDescription()));
+        } catch (IOException e) {
+            logger.warn("Failed executing command", 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 eff3a3f..5b8df75 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
@@ -36,7 +36,7 @@ public class PrepareVoteEmailCommand implements Command {
 
     // TODO - replace with file template
     private static final String EMAIL_TEMPLATE ="To: \"Sling Developers List\" <de...@sling.apache.org>\n" + 
-            "Subject: [VOTE] Release Apache Sling ##RELEASE_NAME##\n" + 
+            "Subject: [VOTE] Release ##RELEASE_NAME##\n" + 
             "\n" + 
             "Hi,\n" + 
             "\n" + 
@@ -73,11 +73,11 @@ public class PrepareVoteEmailCommand implements Command {
         try {
             int repoId = Integer.parseInt(target);
             StagingRepository repo = repoFinder.find(repoId);
-            String cleanVersion = getCleanVersion(repo.getDescription());
-            Version version = versionFinder.find(cleanVersion);
+            ReleaseVersion releaseVersion = ReleaseVersion.fromRepositoryDescription(repo.getDescription());
+            Version version = versionFinder.find(releaseVersion.getName());
             
             String emailContents = EMAIL_TEMPLATE
-                    .replace("##RELEASE_NAME##", cleanVersion)
+                    .replace("##RELEASE_NAME##", releaseVersion.getFullName())
                     .replace("##RELEASE_ID##", String.valueOf(repoId))
                     .replace("##VERSION_ID##", String.valueOf(version.getId()))
                     .replace("##FIXED_ISSUES_COUNT##", String.valueOf(version.getIssuesFixedCount()));
@@ -88,11 +88,4 @@ public class PrepareVoteEmailCommand implements Command {
             logger.warn("Failed executing command", e);
         }
     }
-
-    static String getCleanVersion(String repoDescription) {
-        return repoDescription
-                .replace("Apache Sling ", "") // Apache Sling prefix
-                .replaceAll(" RC[0-9]*$", ""); // 'release candidate' suffix 
-    }
-
 }
diff --git a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java b/src/main/java/org/apache/sling/cli/impl/release/ReleaseVersion.java
similarity index 50%
copy from src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
copy to src/main/java/org/apache/sling/cli/impl/release/ReleaseVersion.java
index 8dd81aa..b629e19 100644
--- a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/ReleaseVersion.java
@@ -16,16 +16,39 @@
  */
 package org.apache.sling.cli.impl.release;
 
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-public class PrepareVoteEmailCommandTest {
+public final class ReleaseVersion {
+    
+    public static ReleaseVersion fromRepositoryDescription(String repositoryDescription) {
+        
+        ReleaseVersion rel = new ReleaseVersion();
+        
+        rel.fullName = repositoryDescription
+            .replaceAll(" RC[0-9]*$", ""); // 'release candidate' suffix
+        rel.name = rel.fullName
+            .replace("Apache Sling ", ""); // Apache Sling prefix
+        rel.version = rel.fullName.substring(rel.fullName.lastIndexOf(' ') + 1);
+        
+        return rel;
+    }
+    
+    private String fullName;
+    private String name;
+    private String version;
 
-    @Test
-    public void cleanVersion() {
+    private ReleaseVersion() {
         
-        assertEquals("Resource Merger 1.3.10", 
-                PrepareVoteEmailCommand.getCleanVersion("Apache Sling Resource Merger 1.3.10 RC1"));
     }
+    
+    public String getFullName() {
+        return fullName;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    
+    public String getVersion() {
+        return version;
+    }
+    
 }
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 8e34d87..f15f60b 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
@@ -42,7 +42,7 @@ public class TallyVotesCommand implements Command {
     private static final String EMAIL_TEMPLATE ="\n" + 
             "\n" + 
             "To: \"Sling Developers List\" <de...@sling.apache.org>\n" + 
-            "Subject: [RESULT] [VOTE] Release Apache Sling ##RELEASE_NAME##\n" + 
+            "Subject: [RESULT] [VOTE] Release ##RELEASE_NAME##\n" + 
             "\n" + 
             "Hi,\n" + 
             "\n" + 
@@ -65,9 +65,8 @@ public class TallyVotesCommand implements Command {
         try {
             
             StagingRepository repository = repoFinder.find(Integer.parseInt(target));
-            // TODO - release name cleanup does not belong here
-            String releaseName = repository.getDescription().replaceFirst(" RC[0-9]+", "");
-            EmailThread voteThread = voteThreadFinder.findVoteThread(releaseName);
+            ReleaseVersion releaseVersion = ReleaseVersion.fromRepositoryDescription(repository.getDescription()); 
+            EmailThread voteThread = voteThreadFinder.findVoteThread(releaseVersion.getFullName());
 
             // TODO - validate which voters are binding and list them separately in the email
             String bindingVoters = voteThread.getEmails().stream()
@@ -76,7 +75,7 @@ public class TallyVotesCommand implements Command {
                 .collect(Collectors.joining(", "));
             
             String email = EMAIL_TEMPLATE
-                .replace("##RELEASE_NAME##", releaseName)
+                .replace("##RELEASE_NAME##", releaseVersion.getFullName())
                 .replace("##BINDING_VOTERS##", bindingVoters);
             
             logger.info(email);
diff --git a/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java b/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java
new file mode 100644
index 0000000..483613e
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java
@@ -0,0 +1,102 @@
+/*
+ * 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.release;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import org.apache.sling.cli.impl.Command;
+import org.apache.sling.cli.impl.jbake.JBakeContentUpdater;
+import org.apache.sling.cli.impl.nexus.StagingRepository;
+import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(service = Command.class, property = {
+    Command.PROPERTY_NAME_COMMAND+"=release",
+    Command.PROPERTY_NAME_SUBCOMMAND+"=update-local-site",
+    Command.PROPERTY_NAME_SUMMARY+"=Updates the Sling website with the new release information, based on a local checkout"
+})
+public class UpdateLocalSiteCommand implements Command {
+    
+    public static void main(String[] args) {
+        System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("MMMM uuuu", Locale.ENGLISH)));
+    }
+
+    private static final String GIT_CHECKOUT = "/tmp/sling-site";
+
+    @Reference
+    private StagingRepositoryFinder repoFinder;
+    
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    
+    @Override
+    public void execute(String target) {
+        
+        
+        try {
+            ensureRepo();
+            try ( Git git = Git.open(new File(GIT_CHECKOUT)) ) {
+                
+                StagingRepository repository = repoFinder.find(Integer.parseInt(target));
+                ReleaseVersion releaseVersion = ReleaseVersion.fromRepositoryDescription(repository.getDescription());
+                
+                JBakeContentUpdater updater = new JBakeContentUpdater();
+        
+                Path templatePath = Paths.get(GIT_CHECKOUT, "src", "main", "jbake", "templates", "downloads.tpl");
+                Path releasesPath = Paths.get(GIT_CHECKOUT, "src", "main", "jbake", "content", "releases.md");
+                updater.updateDownloads(templatePath, releaseVersion.getName(), releaseVersion.getVersion());
+                updater.updateReleases(releasesPath, releaseVersion.getName(), releaseVersion.getVersion(), LocalDateTime.now());
+        
+                git.diff()
+                    .setOutputStream(System.out)
+                    .call();
+            }
+        } catch (GitAPIException | IOException e) {
+            logger.warn("Failed executing command", e);
+        }
+            
+    }
+
+    private void ensureRepo() throws GitAPIException, IOException {
+        
+        if ( !Paths.get(GIT_CHECKOUT).toFile().exists() ) {
+            Git.cloneRepository()
+            .setURI("https://github.com/apache/sling-site.git")
+            .setProgressMonitor(new TextProgressMonitor())
+            .setDirectory(new File(GIT_CHECKOUT))
+            .call();
+        } else {
+            try ( Git git = Git.open(new File(GIT_CHECKOUT)) )  {
+                git.reset()
+                    .setMode(ResetType.HARD)
+                    .call();
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/ReleaseVersionTest.java
similarity index 70%
rename from src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
rename to src/test/java/org/apache/sling/cli/impl/release/ReleaseVersionTest.java
index 8dd81aa..90ed3e5 100644
--- a/src/test/java/org/apache/sling/cli/impl/release/PrepareVoteEmailCommandTest.java
+++ b/src/test/java/org/apache/sling/cli/impl/release/ReleaseVersionTest.java
@@ -20,12 +20,15 @@ import static org.junit.Assert.assertEquals;
 
 import org.junit.Test;
 
-public class PrepareVoteEmailCommandTest {
+public class ReleaseVersionTest {
 
     @Test
-    public void cleanVersion() {
+    public void fromRepositoryDescription() {
         
-        assertEquals("Resource Merger 1.3.10", 
-                PrepareVoteEmailCommand.getCleanVersion("Apache Sling Resource Merger 1.3.10 RC1"));
+        ReleaseVersion rel = ReleaseVersion.fromRepositoryDescription("Apache Sling Resource Merger 1.3.10 RC1");
+        
+        assertEquals("Resource Merger 1.3.10", rel.getName());
+        assertEquals("Apache Sling Resource Merger 1.3.10", rel.getFullName());
+        assertEquals("1.3.10", rel.getVersion());
     }
 }