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 c3ed3d4080791fdbf715c365fd584fe90bd29a91 Author: Roy Teeuwen <[email protected]> AuthorDate: Mon Jun 1 22:08:37 2026 +0200 release: allow post-vote commands to act on a release by name The release-identity commands resolved the release(s) solely from the staging repository via find(repositoryId), so they stopped working once the repository was promoted (released and dropped). Add a --release option to create-new-jira-version, release-jira-version, update-reporter and update-local-site: when given, the release is parsed directly from the name instead of being looked up from the staging repository, so these steps can be completed even after promotion. --repository is no longer required when --release is supplied; create-new-jira-version keeps --version-name as an alias. --- .../cli/impl/release/CreateJiraVersionCommand.java | 29 ++++++++++++++-------- .../impl/release/ReleaseJiraVersionCommand.java | 29 ++++++++++++++++++---- .../cli/impl/release/UpdateLocalSiteCommand.java | 28 +++++++++++++++++---- .../cli/impl/release/UpdateReporterCommand.java | 28 +++++++++++++++++---- .../release/ReleaseJiraVersionCommandTest.java | 25 +++++++++++++++++++ .../impl/release/UpdateReporterCommandTest.java | 27 ++++++++++++++++++++ 6 files changed, 140 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/apache/sling/cli/impl/release/CreateJiraVersionCommand.java b/src/main/java/org/apache/sling/cli/impl/release/CreateJiraVersionCommand.java index 226e01a..8d2083a 100644 --- a/src/main/java/org/apache/sling/cli/impl/release/CreateJiraVersionCommand.java +++ b/src/main/java/org/apache/sling/cli/impl/release/CreateJiraVersionCommand.java @@ -30,7 +30,6 @@ import org.apache.sling.cli.impl.jira.Issue; import org.apache.sling.cli.impl.jira.Version; import org.apache.sling.cli.impl.jira.VersionClient; import org.apache.sling.cli.impl.nexus.RepositoryService; -import org.apache.sling.cli.impl.nexus.StagingRepository; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; @@ -56,8 +55,7 @@ public class CreateJiraVersionCommand implements Command { @CommandLine.Option( names = {"-r", "--repository"}, - description = "Nexus repository id", - required = true) + description = "Nexus staging repository id to derive the release(s) from") private Integer repositoryId; @Reference @@ -67,9 +65,10 @@ public class CreateJiraVersionCommand implements Command { private VersionClient versionClient; @CommandLine.Option( - names = {"--version-name"}, - description = "Jira version name to use", - required = false) + names = {"--release", "--version-name"}, + description = "Release name(s) to act on, e.g. \"Apache Sling Foo 1.2.0\" (comma-separated for multiple)." + + " Use instead of --repository when the staging repository no longer exists," + + " e.g. after the release has been promoted.") private String jiraVersionName; @CommandLine.Mixin @@ -80,7 +79,12 @@ public class CreateJiraVersionCommand implements Command { @Override public Integer call() { try { - for (Release release : releases()) { + Collection<Release> releases = releases(); + if (releases == null) { + logger.error("Provide either --repository or --release."); + return CommandLine.ExitCode.USAGE; + } + for (Release release : releases) { Version version = versionClient.find(release); logger.info("Found {}.", version); Version successorVersion = versionClient.findSuccessorVersion(release); @@ -145,9 +149,12 @@ public class CreateJiraVersionCommand implements Command { } private Collection<Release> releases() throws IOException { - if (jiraVersionName != null) return Release.fromString(jiraVersionName); - - StagingRepository repo = repositoryService.find(repositoryId); - return repositoryService.getReleases(repo); + if (jiraVersionName != null && !jiraVersionName.isBlank()) { + return Release.fromString(jiraVersionName); + } + if (repositoryId == null) { + return null; + } + return repositoryService.getReleases(repositoryService.find(repositoryId)); } } diff --git a/src/main/java/org/apache/sling/cli/impl/release/ReleaseJiraVersionCommand.java b/src/main/java/org/apache/sling/cli/impl/release/ReleaseJiraVersionCommand.java index 5aa9a1c..9947e0f 100644 --- a/src/main/java/org/apache/sling/cli/impl/release/ReleaseJiraVersionCommand.java +++ b/src/main/java/org/apache/sling/cli/impl/release/ReleaseJiraVersionCommand.java @@ -18,6 +18,7 @@ */ package org.apache.sling.cli.impl.release; +import java.io.IOException; import java.util.List; import java.util.Set; @@ -28,7 +29,6 @@ import org.apache.sling.cli.impl.UserInput; import org.apache.sling.cli.impl.jira.Issue; import org.apache.sling.cli.impl.jira.VersionClient; import org.apache.sling.cli.impl.nexus.RepositoryService; -import org.apache.sling.cli.impl.nexus.StagingRepository; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; @@ -58,10 +58,16 @@ public class ReleaseJiraVersionCommand implements Command { @CommandLine.Option( names = {"-r", "--repository"}, - description = "Nexus repository id", - required = true) + description = "Nexus staging repository id to derive the release(s) from") private Integer repositoryId; + @CommandLine.Option( + names = {"--release"}, + description = "Release name(s) to act on, e.g. \"Apache Sling Foo 1.2.0\" (comma-separated for multiple)." + + " Use instead of --repository when the staging repository no longer exists," + + " e.g. after the release has been promoted.") + private String releaseName; + @Reference private RepositoryService repositoryService; @@ -71,11 +77,24 @@ public class ReleaseJiraVersionCommand implements Command { @CommandLine.Mixin private ReusableCLIOptions reusableCLIOptions; + private Set<Release> resolveReleases() throws IOException { + if (releaseName != null && !releaseName.isBlank()) { + return Set.copyOf(Release.fromString(releaseName)); + } + if (repositoryId == null) { + return null; + } + return repositoryService.getReleases(repositoryService.find(repositoryId)); + } + @Override public Integer call() { try { - StagingRepository repo = repositoryService.find(repositoryId); - Set<Release> releases = repositoryService.getReleases(repo); + Set<Release> releases = resolveReleases(); + if (releases == null) { + LOGGER.error("Provide either --repository or --release."); + return CommandLine.ExitCode.USAGE; + } ExecutionMode executionMode = reusableCLIOptions.executionMode; LOGGER.info( "The following Jira versions {} be released:{}", 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 index b9a3e6e..5e35d55 100644 --- a/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java +++ b/src/main/java/org/apache/sling/cli/impl/release/UpdateLocalSiteCommand.java @@ -28,7 +28,6 @@ import java.util.Set; import org.apache.sling.cli.impl.Command; import org.apache.sling.cli.impl.jbake.JBakeContentUpdater; import org.apache.sling.cli.impl.nexus.RepositoryService; -import org.apache.sling.cli.impl.nexus.StagingRepository; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.errors.GitAPIException; @@ -63,18 +62,37 @@ public class UpdateLocalSiteCommand implements Command { @CommandLine.Option( names = {"-r", "--repository"}, - description = "Nexus repository id", - required = true) + description = "Nexus staging repository id to derive the release(s) from") private Integer repositoryId; + @CommandLine.Option( + names = {"--release"}, + description = "Release name(s) to act on, e.g. \"Apache Sling Foo 1.2.0\" (comma-separated for multiple)." + + " Use instead of --repository when the staging repository no longer exists," + + " e.g. after the release has been promoted.") + private String releaseName; + + private Set<Release> resolveReleases() throws IOException { + if (releaseName != null && !releaseName.isBlank()) { + return Set.copyOf(Release.fromString(releaseName)); + } + if (repositoryId == null) { + return null; + } + return repositoryService.getReleases(repositoryService.find(repositoryId)); + } + @Override public Integer call() { try { ensureRepo(); try (Git git = Git.open(new File(GIT_CHECKOUT))) { - StagingRepository repository = repositoryService.find(repositoryId); - Set<Release> releases = repositoryService.getReleases(repository); + Set<Release> releases = resolveReleases(); + if (releases == null) { + logger.error("Provide either --repository or --release."); + return CommandLine.ExitCode.USAGE; + } JBakeContentUpdater updater = new JBakeContentUpdater(); diff --git a/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java b/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java index 983618c..db4bed0 100644 --- a/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java +++ b/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java @@ -37,7 +37,6 @@ import org.apache.sling.cli.impl.InputOption; import org.apache.sling.cli.impl.UserInput; import org.apache.sling.cli.impl.http.HttpClientFactory; import org.apache.sling.cli.impl.nexus.RepositoryService; -import org.apache.sling.cli.impl.nexus.StagingRepository; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; @@ -69,18 +68,37 @@ public class UpdateReporterCommand implements Command { @CommandLine.Option( names = {"-r", "--repository"}, - description = "Nexus repository id", - required = true) + description = "Nexus staging repository id to derive the release(s) from") private Integer repositoryId; + @CommandLine.Option( + names = {"--release"}, + description = "Release name(s) to act on, e.g. \"Apache Sling Foo 1.2.0\" (comma-separated for multiple)." + + " Use instead of --repository when the staging repository no longer exists," + + " e.g. after the release has been promoted.") + private String releaseName; + @CommandLine.Mixin private ReusableCLIOptions reusableCLIOptions; + private Set<Release> resolveReleases() throws IOException { + if (releaseName != null && !releaseName.isBlank()) { + return Set.copyOf(Release.fromString(releaseName)); + } + if (repositoryId == null) { + return null; + } + return repositoryService.getReleases(repositoryService.find(repositoryId)); + } + @Override public Integer call() { try { - StagingRepository repository = repositoryService.find(repositoryId); - Set<Release> releases = repositoryService.getReleases(repository); + Set<Release> releases = resolveReleases(); + if (releases == null) { + LOGGER.error("Provide either --repository or --release."); + return CommandLine.ExitCode.USAGE; + } String releaseReleases = releases.size() > 1 ? "releases" : "release"; switch (reusableCLIOptions.executionMode) { case DRY_RUN: diff --git a/src/test/java/org/apache/sling/cli/impl/release/ReleaseJiraVersionCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/ReleaseJiraVersionCommandTest.java index e80f289..d7715da 100644 --- a/src/test/java/org/apache/sling/cli/impl/release/ReleaseJiraVersionCommandTest.java +++ b/src/test/java/org/apache/sling/cli/impl/release/ReleaseJiraVersionCommandTest.java @@ -40,6 +40,7 @@ import picocli.CommandLine; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; @@ -111,6 +112,16 @@ public class ReleaseJiraVersionCommandTest { verify(versionClient, times(1)).release(any()); } + @Test + public void testReleaseByName() throws Exception { + // the release is resolved from --release, so the command works without the (now-gone) staging repo + prepare(); + Command command = createCommandByName("Apache Sling CLI Test 1.0.0", ExecutionMode.AUTO); + assertEquals(CommandLine.ExitCode.OK, (int) command.call()); + verify(versionClient, times(1)).release(any()); + verify(repositoryService, never()).find(anyInt()); + } + private Command createCommand(int repositoryId, ExecutionMode executionMode) throws Exception { ReleaseJiraVersionCommand releaseJiraVersionCommand = spy(new ReleaseJiraVersionCommand()); FieldUtils.writeField(releaseJiraVersionCommand, "repositoryId", repositoryId, true); @@ -124,4 +135,18 @@ public class ReleaseJiraVersionCommandTest { result instanceof ReleaseJiraVersionCommand); return result; } + + private Command createCommandByName(String releaseName, ExecutionMode executionMode) throws Exception { + ReleaseJiraVersionCommand releaseJiraVersionCommand = spy(new ReleaseJiraVersionCommand()); + FieldUtils.writeField(releaseJiraVersionCommand, "releaseName", releaseName, true); + ReusableCLIOptions reusableCLIOptions = mock(ReusableCLIOptions.class); + FieldUtils.writeField(reusableCLIOptions, "executionMode", executionMode, true); + FieldUtils.writeField(releaseJiraVersionCommand, "reusableCLIOptions", reusableCLIOptions, true); + osgiContext.registerInjectActivateService(releaseJiraVersionCommand); + Command result = osgiContext.getService(Command.class); + assertTrue( + "Expected to retrieve the ReleaseJiraVersionCommand from the mocked OSGi environment.", + result instanceof ReleaseJiraVersionCommand); + return result; + } } diff --git a/src/test/java/org/apache/sling/cli/impl/release/UpdateReporterCommandTest.java b/src/test/java/org/apache/sling/cli/impl/release/UpdateReporterCommandTest.java index 10f53fa..c8670c6 100644 --- a/src/test/java/org/apache/sling/cli/impl/release/UpdateReporterCommandTest.java +++ b/src/test/java/org/apache/sling/cli/impl/release/UpdateReporterCommandTest.java @@ -118,6 +118,19 @@ public class UpdateReporterCommandTest { verify(client, times(2)).execute(any()); } + @Test + public void testAutoByName() throws Exception { + // resolves the release from --release rather than a (possibly gone) staging repository + Command updateReporter = createCommandByName("Apache Sling CLI 1", ExecutionMode.AUTO); + CloseableHttpResponse response = mock(CloseableHttpResponse.class); + StatusLine statusLine = mock(StatusLine.class); + when(response.getStatusLine()).thenReturn(statusLine); + when(statusLine.getStatusCode()).thenReturn(200); + when(client.execute(any())).thenReturn(response); + assertEquals(0, (int) updateReporter.call()); + verify(client, times(1)).execute(any()); + } + private Command createCommand(int repositoryId, ExecutionMode executionMode) throws IllegalAccessException { UpdateReporterCommand updateReporterCommand = spy(new UpdateReporterCommand()); FieldUtils.writeField(updateReporterCommand, "repositoryId", repositoryId, true); @@ -131,4 +144,18 @@ public class UpdateReporterCommandTest { result instanceof UpdateReporterCommand); return result; } + + private Command createCommandByName(String releaseName, ExecutionMode executionMode) throws IllegalAccessException { + UpdateReporterCommand updateReporterCommand = spy(new UpdateReporterCommand()); + FieldUtils.writeField(updateReporterCommand, "releaseName", releaseName, true); + ReusableCLIOptions reusableCLIOptions = mock(ReusableCLIOptions.class); + FieldUtils.writeField(reusableCLIOptions, "executionMode", executionMode, true); + FieldUtils.writeField(updateReporterCommand, "reusableCLIOptions", reusableCLIOptions, true); + osgiContext.registerInjectActivateService(updateReporterCommand); + Command result = osgiContext.getService(Command.class); + assertTrue( + "Expected to retrieve the UpdateReporterCommand from the mocked OSGi environment.", + result instanceof UpdateReporterCommand); + return result; + } }
