royteeuwen commented on code in PR #56:
URL: 
https://github.com/apache/sling-org-apache-sling-committer-cli/pull/56#discussion_r3890396825


##########
src/main/java/org/apache/sling/cli/impl/release/UpdateDistCommand.java:
##########
@@ -169,22 +189,50 @@ record DistReleasePlan(
      * Shared by this command and {@link FinalizeCommand} so the flow is not 
duplicated. When the version is
      * already present in {@code dist/release} the returned plan is marked 
{@link DistReleasePlan#alreadyPublished()}.
      */
-    static DistReleasePlan planDistRelease(
+    static List<DistReleasePlan> planDistRelease(
             RepositoryService repositoryService, StagingRepository repository, 
String previousVersion)
             throws IOException {
         LocalRepository localRepository = 
repositoryService.download(repository);
-        Artifact primary = localRepository.getArtifacts().stream()
+        List<Artifact> artifacts = localRepository.getArtifacts().stream()
                 .filter(a -> "pom".equals(a.getType()))
-                .findFirst()
-                .orElseThrow(() -> new IllegalStateException("No POM artifact 
found in staging repository"));
-        String artifactId = primary.getArtifactId();
-        String newVersion = primary.getVersion();
-        if (DistRepository.isVersionPublished(artifactId, newVersion)) {
-            return new DistReleasePlan(artifactId, newVersion, List.of(), 
List.of(), true);
+                .toList();
+
+        if (artifacts.isEmpty()) {
+            throw new IllegalStateException("No POM artifact found in staging 
repository");
+        }
+
+        List<DistReleasePlan> plans = artifacts.stream()
+                .filter(a -> {
+                    try {
+                        return 
DistRepository.isVersionPublished(a.getArtifactId(), a.getVersion());
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                })
+                .map(a -> new DistReleasePlan(a.getArtifactId(), 
a.getVersion(), List.of(), List.of(), true))
+                .toList();
+
+        if (!plans.isEmpty()) {

Review Comment:
   **Blocking: partial publishes become permanently unrecoverable.**
   
   This filter keeps only the artifacts that are *already* published, and then 
`if (!plans.isEmpty()) return plans;` throws away every artifact that still 
needs publishing. So if even one artifact of a multi-artifact release is 
already in `dist/release`, the others are silently dropped from the plan — and 
`doUpdateDist`'s `plans.stream().allMatch(DistReleasePlan::alreadyPublished)` 
is then trivially true, so it logs "nothing to do" and exits `OK`.
   
   This is exactly the state you land in after a partially-failed run (see my 
comment on the per-plan `DistRepository.publish` call): artifact A committed, 
artifact B failed → re-run `update-dist` → "nothing to do", forever, with no 
way to recover other than doing it by hand.
   
   The whole point of `isVersionPublished` was to make re-runs safe and this 
inverts it. I'd suggest a single pass that builds one plan per artifact with 
its own `alreadyPublished` flag, and then has `doUpdateDist` skip the published 
ones and publish the rest.



##########
src/main/java/org/apache/sling/cli/impl/release/UpdateDistCommand.java:
##########
@@ -169,22 +189,50 @@ record DistReleasePlan(
      * Shared by this command and {@link FinalizeCommand} so the flow is not 
duplicated. When the version is
      * already present in {@code dist/release} the returned plan is marked 
{@link DistReleasePlan#alreadyPublished()}.
      */
-    static DistReleasePlan planDistRelease(
+    static List<DistReleasePlan> planDistRelease(
             RepositoryService repositoryService, StagingRepository repository, 
String previousVersion)
             throws IOException {
         LocalRepository localRepository = 
repositoryService.download(repository);
-        Artifact primary = localRepository.getArtifacts().stream()
+        List<Artifact> artifacts = localRepository.getArtifacts().stream()
                 .filter(a -> "pom".equals(a.getType()))
-                .findFirst()
-                .orElseThrow(() -> new IllegalStateException("No POM artifact 
found in staging repository"));
-        String artifactId = primary.getArtifactId();
-        String newVersion = primary.getVersion();
-        if (DistRepository.isVersionPublished(artifactId, newVersion)) {
-            return new DistReleasePlan(artifactId, newVersion, List.of(), 
List.of(), true);
+                .toList();
+
+        if (artifacts.isEmpty()) {
+            throw new IllegalStateException("No POM artifact found in staging 
repository");
+        }
+
+        List<DistReleasePlan> plans = artifacts.stream()
+                .filter(a -> {
+                    try {
+                        return 
DistRepository.isVersionPublished(a.getArtifactId(), a.getVersion());
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                })
+                .map(a -> new DistReleasePlan(a.getArtifactId(), 
a.getVersion(), List.of(), List.of(), true))
+                .toList();
+
+        if (!plans.isEmpty()) {
+            return plans;
+        } else {
+            return artifacts.stream()
+                    .map(a -> {
+                        try {
+                            String artifactId = a.getArtifactId();
+                            String newVersion = a.getVersion();
+                            List<Path> newFiles = 
collectDownloadedFiles(localRepository.getRootFolder()).stream()
+                                    .filter(path ->
+                                            
path.getFileName().toString().startsWith(artifactId + "-" + newVersion))

Review Comment:
   **Blocking: `startsWith` reintroduces the version-prefix bug.**
   
   Publishing `1.0.14` will pick up `1.0.140`'s files here. 
`DistRepository.belongsToVersion` already handles exactly this (there's even a 
test guarding it — 
`testAutoDeduceDoesNotConfuseVersionPrefixesAndKeepsNewerVersions`); it's just 
`private`. Could you widen it to package-visible and reuse it instead?
   
   Separately, a behaviour change worth confirming is intentional: previously 
*every* downloaded file was published, now only files matching some pom's 
`artifactId-version` prefix are, and anything that doesn't match is silently 
dropped.



##########
src/main/java/org/apache/sling/cli/impl/release/FinalizeCommand.java:
##########
@@ -305,39 +304,8 @@ private void stepPromoteStage(StagingRepository 
repository, ExecutionMode mode)
         }
     }
 
-    private void stepUpdateDist(StagingRepository repository, ExecutionMode 
mode) throws IOException {
-        // Delegate the download/collect/publish flow to UpdateDistCommand so 
it is not duplicated here.
-        UpdateDistCommand.DistReleasePlan plan = 
UpdateDistCommand.planDistRelease(repositoryService, repository, null);
-
-        if (plan.alreadyPublished()) {
-            LOGGER.info("dist/release already contains {} {}; skipping.", 
plan.artifactId(), plan.newVersion());
-            return;
-        }
-        if (plan.newFiles().isEmpty()) {
-            LOGGER.warn(
-                    "No artifacts were downloaded for {} {}; skipping dist 
update.",
-                    plan.artifactId(),
-                    plan.newVersion());
-            return;
-        }
-
-        if (mode == ExecutionMode.DRY_RUN) {
-            LOGGER.info(
-                    "Would publish {} file(s) to dist/release for {} {}",
-                    plan.newFiles().size(),
-                    plan.artifactId(),
-                    plan.newVersion());
-            LOGGER.info(
-                    "Would remove {} old file(s) from dist/release",
-                    plan.oldFiles().size());
-        } else {
-            DistRepository.publish(
-                    plan.artifactId(),
-                    plan.newVersion(),
-                    plan.newFiles(),
-                    plan.oldFiles(),
-                    credentialsService.getAsfCredentials());
-        }
+    private void stepUpdateDist(ExecutionMode mode) throws IOException {

Review Comment:
   **Blocking: the exit code is discarded.**
   
   `doUpdateDist` swallows `IOException`, logs a warning and returns 
`ExitCode.SOFTWARE` — which is thrown away here, so finalize continues straight 
on to **Step 2: Promote to Maven Central** as though dist succeeded. Previously 
the `IOException` from `DistRepository.publish` propagated out of 
`stepUpdateDist` and aborted finalize *before* the irreversible promote, which 
is the one ordering that really matters here.
   
   Also: `repository` is already resolved and passed into 
`stepUpdateDistStage`, but passing `repositoryId` here makes `doUpdateDist` 
call `repositoryService.find()` and download the whole staging repository a 
second time.



##########
src/test/java/org/apache/sling/cli/impl/release/UpdateDistCommandTest.java:
##########
@@ -539,4 +582,15 @@ private Command createCommand(ExecutionMode executionMode, 
String previousVersio
                 result instanceof UpdateDistCommand);
         return result;
     }
+
+    private record ArtifactUpdate(String artifactId, String newVersion, String 
oldVersion) {
+        String oldArtifact() {
+            return artifactId() + "-" + oldVersion();
+        }
+
+        String newArtifact() {
+            return artifactId() + "-" + newVersion();
+        }
+    }
+    ;

Review Comment:
   Stray `;` after the record declaration. Also `newArtifact()` above is unused.



##########
src/main/java/org/apache/sling/cli/impl/release/UpdateDistCommand.java:
##########
@@ -90,70 +93,87 @@ public class UpdateDistCommand implements Command {
 
     @Override
     public Integer call() {
+        Integer ok = doUpdateDist(
+                repositoryService, repositoryId, previousVersion, 
reusableCLIOptions.executionMode, credentialsService);
+        if (ok != null) return ok;
+        return CommandLine.ExitCode.OK;
+    }
+
+    public static @Nullable Integer doUpdateDist(

Review Comment:
   The `@Nullable Integer` contract where `null` means success is hard to read 
— `call()` ends up doing `if (ok != null) return ok; return 
CommandLine.ExitCode.OK;`, and both `null` and `ExitCode.OK` mean OK. Could 
this just return the exit code directly?
   
   Also, `planDistRelease` still declares `throws IOException` but now leaks 
`UncheckedIOException` from the stream lambdas instead (hence the `catch 
(UncheckedIOException | IOException)` here). Plain `for` loops in 
`planDistRelease` would be shorter and keep the checked exception honest.



##########
src/main/java/org/apache/sling/cli/impl/release/UpdateDistCommand.java:
##########
@@ -90,70 +93,87 @@ public class UpdateDistCommand implements Command {
 
     @Override
     public Integer call() {
+        Integer ok = doUpdateDist(
+                repositoryService, repositoryId, previousVersion, 
reusableCLIOptions.executionMode, credentialsService);
+        if (ok != null) return ok;
+        return CommandLine.ExitCode.OK;
+    }
+
+    public static @Nullable Integer doUpdateDist(
+            RepositoryService repositoryService,
+            Integer repositoryId,
+            String previousVersion,
+            ExecutionMode executionMode,
+            CredentialsService credentialsService) {
         try {
-            DistReleasePlan plan =
+            List<DistReleasePlan> plans =
                     planDistRelease(repositoryService, 
repositoryService.find(repositoryId), previousVersion);
 
-            if (plan.alreadyPublished()) {
-                LOGGER.info(
-                        "dist/release already contains {} {}; nothing to do.", 
plan.artifactId(), plan.newVersion());
+            if (plans.stream().allMatch(DistReleasePlan::alreadyPublished)) {
+                for (DistReleasePlan plan : plans) {
+                    LOGGER.info(
+                            "dist/release already contains {} {}; nothing to 
do.",
+                            plan.artifactId(),
+                            plan.newVersion());
+                }
                 return CommandLine.ExitCode.OK;
             }
-            if (plan.newFiles().isEmpty()) {
+
+            boolean noArtifacts = plans.stream()
+                    .flatMap(plan -> plan.newFiles().stream())
+                    .findFirst()
+                    .isEmpty();
+            if (noArtifacts) {

Review Comment:
   **Blocking: this can delete an artifact's previous release without 
publishing the new one.**
   
   `noArtifacts` is now true only if *every* plan has empty `newFiles`. If 
artifact A has staged files and B doesn't, B's plan still reaches 
`DistRepository.publish(id, ver, List.of(), oldFiles, ...)` below — and 
`commitFiles` has no guard for an empty `newFiles`, so it happily deletes 
`oldFiles` and adds nothing. B's previous release is then gone from 
`dist/release`.
   
   The `newFiles().isEmpty()` check needs to stay per-plan (skip that plan, or 
fail the whole command).



##########
src/main/java/org/apache/sling/cli/impl/release/UpdateDistCommand.java:
##########
@@ -90,70 +93,87 @@ public class UpdateDistCommand implements Command {
 
     @Override
     public Integer call() {
+        Integer ok = doUpdateDist(
+                repositoryService, repositoryId, previousVersion, 
reusableCLIOptions.executionMode, credentialsService);
+        if (ok != null) return ok;
+        return CommandLine.ExitCode.OK;
+    }
+
+    public static @Nullable Integer doUpdateDist(
+            RepositoryService repositoryService,
+            Integer repositoryId,
+            String previousVersion,
+            ExecutionMode executionMode,
+            CredentialsService credentialsService) {
         try {
-            DistReleasePlan plan =
+            List<DistReleasePlan> plans =
                     planDistRelease(repositoryService, 
repositoryService.find(repositoryId), previousVersion);
 
-            if (plan.alreadyPublished()) {
-                LOGGER.info(
-                        "dist/release already contains {} {}; nothing to do.", 
plan.artifactId(), plan.newVersion());
+            if (plans.stream().allMatch(DistReleasePlan::alreadyPublished)) {
+                for (DistReleasePlan plan : plans) {
+                    LOGGER.info(
+                            "dist/release already contains {} {}; nothing to 
do.",
+                            plan.artifactId(),
+                            plan.newVersion());
+                }
                 return CommandLine.ExitCode.OK;
             }
-            if (plan.newFiles().isEmpty()) {
+
+            boolean noArtifacts = plans.stream()
+                    .flatMap(plan -> plan.newFiles().stream())
+                    .findFirst()
+                    .isEmpty();
+            if (noArtifacts) {
                 LOGGER.warn("No artifacts were downloaded for staging 
repository {}.", repositoryId);
                 return CommandLine.ExitCode.USAGE;
             }
 
-            switch (reusableCLIOptions.executionMode) {
-                case DRY_RUN:
-                    LOGGER.info(
-                            "Would publish {} file(s) to dist/release for {} 
{}:",
-                            plan.newFiles().size(),
-                            plan.artifactId(),
-                            plan.newVersion());
-                    plan.newFiles()
-                            .forEach(f -> LOGGER.info(
-                                    "  put {} -> {}{}", f, 
DistRepository.DIST_RELEASE_URL, f.getFileName()));
-                    if (!plan.oldFiles().isEmpty()) {
-                        LOGGER.info(
-                                "Would remove {} old file(s) from 
dist/release:",
-                                plan.oldFiles().size());
-                        plan.oldFiles().forEach(f -> LOGGER.info("  rm {}", 
DistRepository.DIST_RELEASE_URL + f));
-                    }
-                    break;
-                case INTERACTIVE:
+            for (DistReleasePlan plan : plans) {
+                boolean doPerformPublish = executionMode == ExecutionMode.AUTO;
+                if (executionMode == ExecutionMode.INTERACTIVE) {
                     String question = String.format(
                             "Publish %d file(s) for %s %s to dist/release and 
remove %d older file(s) for %s?",
                             plan.newFiles().size(),
                             plan.artifactId(),
                             plan.newVersion(),
                             plan.oldFiles().size(),
                             plan.artifactId());
-                    if (InputOption.YES.equals(UserInput.yesNo(question, 
InputOption.YES))) {
-                        DistRepository.publish(
-                                plan.artifactId(),
-                                plan.newVersion(),
-                                plan.newFiles(),
-                                plan.oldFiles(),
-                                credentialsService.getAsfCredentials());
-                    } else {
+                    doPerformPublish = 
InputOption.YES.equals(UserInput.yesNo(question, InputOption.YES));
+                    if (!doPerformPublish) {
                         LOGGER.info("Aborted.");
                     }
-                    break;
-                case AUTO:
+                }
+
+                if (doPerformPublish) {

Review Comment:
   **Blocking (see summary item 3):** calling `publish` once per plan turns a 
multi-artifact release into N separate SVN commits on dist.apache.org. 
`DistRepository.publish`'s javadoc promises "a single atomic revision", and 
`commitFiles` aborts the edit on failure specifically so a partial commit is 
never left behind — that guarantee no longer holds for the release as a whole.
   
   Collecting all plans' `newFiles`/`oldFiles` into one `publish` call would 
keep it atomic (the commit message would need to name the set rather than a 
single `artifactId version`).



##########
src/test/java/org/apache/sling/cli/impl/release/FinalizeCommandTest.java:
##########
@@ -159,12 +159,12 @@ public void testDryRunPmc() throws Exception {
         try (MockedStatic<UpdateDistCommand> dist = 
mockStatic(UpdateDistCommand.class);
                 MockedStatic<DistRepository> distRepo = 
mockStatic(DistRepository.class)) {
             dist.when(() -> UpdateDistCommand.planDistRelease(any(), any(), 
any()))
-                    .thenReturn(new UpdateDistCommand.DistReleasePlan(
+                    .thenReturn(List.of(new UpdateDistCommand.DistReleasePlan(

Review Comment:
   `testDryRunPmc` is vacuous now: with `mockStatic(UpdateDistCommand.class)` 
and no `thenCallRealMethod()` for `doUpdateDist`, the entire dist step is a 
no-op — this `planDistRelease` stub is never reached and the 
`verify(DistRepository.publish, never())` below passes trivially. `testAutoPmc` 
got the `thenCallRealMethod()`; this test needs it too.



##########
src/main/java/org/apache/sling/cli/impl/release/UpdateDistCommand.java:
##########
@@ -90,70 +93,87 @@ public class UpdateDistCommand implements Command {
 
     @Override
     public Integer call() {
+        Integer ok = doUpdateDist(
+                repositoryService, repositoryId, previousVersion, 
reusableCLIOptions.executionMode, credentialsService);
+        if (ok != null) return ok;
+        return CommandLine.ExitCode.OK;
+    }
+
+    public static @Nullable Integer doUpdateDist(
+            RepositoryService repositoryService,
+            Integer repositoryId,
+            String previousVersion,
+            ExecutionMode executionMode,
+            CredentialsService credentialsService) {
         try {
-            DistReleasePlan plan =
+            List<DistReleasePlan> plans =
                     planDistRelease(repositoryService, 
repositoryService.find(repositoryId), previousVersion);
 
-            if (plan.alreadyPublished()) {
-                LOGGER.info(
-                        "dist/release already contains {} {}; nothing to do.", 
plan.artifactId(), plan.newVersion());
+            if (plans.stream().allMatch(DistReleasePlan::alreadyPublished)) {
+                for (DistReleasePlan plan : plans) {
+                    LOGGER.info(
+                            "dist/release already contains {} {}; nothing to 
do.",
+                            plan.artifactId(),
+                            plan.newVersion());
+                }
                 return CommandLine.ExitCode.OK;
             }
-            if (plan.newFiles().isEmpty()) {
+
+            boolean noArtifacts = plans.stream()
+                    .flatMap(plan -> plan.newFiles().stream())
+                    .findFirst()
+                    .isEmpty();
+            if (noArtifacts) {
                 LOGGER.warn("No artifacts were downloaded for staging 
repository {}.", repositoryId);
                 return CommandLine.ExitCode.USAGE;
             }
 
-            switch (reusableCLIOptions.executionMode) {
-                case DRY_RUN:
-                    LOGGER.info(
-                            "Would publish {} file(s) to dist/release for {} 
{}:",
-                            plan.newFiles().size(),
-                            plan.artifactId(),
-                            plan.newVersion());
-                    plan.newFiles()
-                            .forEach(f -> LOGGER.info(
-                                    "  put {} -> {}{}", f, 
DistRepository.DIST_RELEASE_URL, f.getFileName()));
-                    if (!plan.oldFiles().isEmpty()) {
-                        LOGGER.info(
-                                "Would remove {} old file(s) from 
dist/release:",
-                                plan.oldFiles().size());
-                        plan.oldFiles().forEach(f -> LOGGER.info("  rm {}", 
DistRepository.DIST_RELEASE_URL + f));
-                    }
-                    break;
-                case INTERACTIVE:
+            for (DistReleasePlan plan : plans) {
+                boolean doPerformPublish = executionMode == ExecutionMode.AUTO;
+                if (executionMode == ExecutionMode.INTERACTIVE) {
                     String question = String.format(
                             "Publish %d file(s) for %s %s to dist/release and 
remove %d older file(s) for %s?",
                             plan.newFiles().size(),
                             plan.artifactId(),
                             plan.newVersion(),
                             plan.oldFiles().size(),
                             plan.artifactId());
-                    if (InputOption.YES.equals(UserInput.yesNo(question, 
InputOption.YES))) {
-                        DistRepository.publish(
-                                plan.artifactId(),
-                                plan.newVersion(),
-                                plan.newFiles(),
-                                plan.oldFiles(),
-                                credentialsService.getAsfCredentials());
-                    } else {
+                    doPerformPublish = 
InputOption.YES.equals(UserInput.yesNo(question, InputOption.YES));
+                    if (!doPerformPublish) {

Review Comment:
   Declining an interactive prompt now falls into the `else` branch below, so 
the user sees `Aborted.` immediately followed by `Would publish 5 file(s) to 
dist/release for ...` — the dry-run text. DRY_RUN and "user said no" probably 
shouldn't share a branch.
   
   Also worth considering: declining one artifact silently continues prompting 
for the next, and there's no summary at the end of what was skipped.



##########
src/main/java/org/apache/sling/cli/impl/release/UpdateDistCommand.java:
##########
@@ -169,22 +189,50 @@ record DistReleasePlan(
      * Shared by this command and {@link FinalizeCommand} so the flow is not 
duplicated. When the version is
      * already present in {@code dist/release} the returned plan is marked 
{@link DistReleasePlan#alreadyPublished()}.
      */
-    static DistReleasePlan planDistRelease(
+    static List<DistReleasePlan> planDistRelease(
             RepositoryService repositoryService, StagingRepository repository, 
String previousVersion)
             throws IOException {
         LocalRepository localRepository = 
repositoryService.download(repository);
-        Artifact primary = localRepository.getArtifacts().stream()
+        List<Artifact> artifacts = localRepository.getArtifacts().stream()
                 .filter(a -> "pom".equals(a.getType()))
-                .findFirst()
-                .orElseThrow(() -> new IllegalStateException("No POM artifact 
found in staging repository"));
-        String artifactId = primary.getArtifactId();
-        String newVersion = primary.getVersion();
-        if (DistRepository.isVersionPublished(artifactId, newVersion)) {
-            return new DistReleasePlan(artifactId, newVersion, List.of(), 
List.of(), true);
+                .toList();
+
+        if (artifacts.isEmpty()) {
+            throw new IllegalStateException("No POM artifact found in staging 
repository");
+        }
+
+        List<DistReleasePlan> plans = artifacts.stream()
+                .filter(a -> {
+                    try {
+                        return 
DistRepository.isVersionPublished(a.getArtifactId(), a.getVersion());
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                })
+                .map(a -> new DistReleasePlan(a.getArtifactId(), 
a.getVersion(), List.of(), List.of(), true))
+                .toList();
+
+        if (!plans.isEmpty()) {
+            return plans;
+        } else {
+            return artifacts.stream()
+                    .map(a -> {
+                        try {
+                            String artifactId = a.getArtifactId();
+                            String newVersion = a.getVersion();
+                            List<Path> newFiles = 
collectDownloadedFiles(localRepository.getRootFolder()).stream()

Review Comment:
   Minor: `collectDownloadedFiles` walks the entire download tree once per 
artifact. Worth hoisting the call out of the `map` and filtering the single 
result per artifact.



##########
src/main/java/org/apache/sling/cli/impl/release/FinalizeCommand.java:
##########
@@ -288,7 +287,7 @@ private void stepUpdateDistStage(StagingRepository 
repository, ExecutionMode mod
             LOGGER.info("SKIPPED (staging repository already promoted; if dist 
still needs updating a PMC"
                     + " member must run update-dist separately)");
         } else {
-            stepUpdateDist(repository, mode);
+            stepUpdateDist(reusableCLIOptions.executionMode);

Review Comment:
   Nit: `stepUpdateDistStage` receives `mode` and it's now unused — this 
reaches for `reusableCLIOptions.executionMode` instead. Same value today, but 
inconsistent with the rest of the class (`stepPromoteStage`, `stepUpdateSite`, 
… all use the parameter).
   
   Worth calling out too: the old `stepUpdateDist` had no INTERACTIVE branch 
and published directly, so finalize will now prompt per artifact mid-run. 
Probably an improvement, but it's an unannounced behaviour change.



##########
src/test/java/org/apache/sling/cli/impl/release/UpdateDistCommandTest.java:
##########
@@ -91,7 +92,7 @@ public void 
testAutoDeducePreviousFilesExcludesNewVersionAndSiblings() throws Ex
                 ARTIFACT + "-1.3.4.pom.asc",
                 ARTIFACT + "-1.3.4-source-release.zip",
                 ARTIFACT + "-1.3.4-source-release.zip.asc",
-                ARTIFACT + "-1.3.6.pom", // the new version - must be kept 
(not removed)
+                ARTIFACT + "-1.3.6.pom", // the new newVersion - must be kept 
(not removed)

Review Comment:
   This looks like an accidental rename-in-comments: "the new **newVersion**". 
It recurs about ten times through this file (lines ~95, 108, 118, 124, 127, 
206, 261, 269-270, 273, 386) and makes the comments read wrong. Could you 
revert the comment/assertion-message churn so the diff stays focused?



##########
src/test/java/org/apache/sling/cli/impl/release/UpdateDistCommandTest.java:
##########
@@ -202,7 +203,49 @@ public void 
testAutoDeduceKeepsOlderMajorWhenNoSameMajorPredecessorExists() thro
 
             List<String> old = 
DistRepository.listPreviousReleaseFiles(ARTIFACT, "2.0.0", null);
 
-            assertTrue("a different major version must never be removed", 
old.isEmpty());
+            assertTrue("a different major newVersion must never be removed", 
old.isEmpty());
+        }
+    }
+
+    @Test
+    public void testAutoDeduceIdentifiesMultiArtifactReleases() throws 
Exception {

Review Comment:
   This test doesn't actually exercise the new code — it only calls 
`DistRepository.listPreviousReleaseFiles`, which this PR doesn't touch. It 
never goes through `planDistRelease` with multiple artifacts, nor 
`doUpdateDist` over multiple plans. Both `ArtifactUpdate` entries also share 
the same `artifactId` with different versions, so it isn't really a 
multi-*artifact* release either.
   
   The cases I'd most like to see covered, since they're the ones that bite in 
production:
   - multiple pom artifacts in the staging repo → one plan each, correct 
`newFiles` split between them
   - one artifact already published + one not → the unpublished one still gets 
published (currently broken)
   - one artifact with no staged files → its `oldFiles` are *not* deleted 
(currently broken)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to