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 <[email protected]> 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\" <[email protected]>\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\" <[email protected]>\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()); } }
