You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by hb...@apache.org on 2026/04/14 17:22:44 UTC

(maven-studies) branch maven-atr-plugin updated: add atr:check-composing goal

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

hboutemy pushed a commit to branch maven-atr-plugin
in repository https://gitbox.apache.org/repos/asf/maven-studies.git


The following commit(s) were added to refs/heads/maven-atr-plugin by this push:
     new 4b189edcc add atr:check-composing goal
4b189edcc is described below

commit 4b189edcc6c59b87166cef5c9c4f12424bad0725
Author: Hervé Boutemy <hb...@apache.org>
AuthorDate: Tue Apr 14 19:22:34 2026 +0200

    add atr:check-composing goal
---
 src/it/apache-release-profile/pom.xml              |  14 +-
 .../apache/maven/plugins/atr/AbstractAtrMojo.java  |  16 +-
 .../org/apache/maven/plugins/atr/AtrClient.java    | 212 +++++++++++++++++++++
 .../maven/plugins/atr/CheckComposingMojo.java      |  77 ++++++++
 .../org/apache/maven/plugins/atr/UploadMojo.java   |  14 +-
 src/site/markdown/usage.md                         |  12 +-
 6 files changed, 325 insertions(+), 20 deletions(-)

diff --git a/src/it/apache-release-profile/pom.xml b/src/it/apache-release-profile/pom.xml
index f72f722e3..a39a17f2a 100644
--- a/src/it/apache-release-profile/pom.xml
+++ b/src/it/apache-release-profile/pom.xml
@@ -59,16 +59,24 @@ under the License.
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-atr-plugin</artifactId>
+            <configuration>
+              <dryRun>true</dryRun>
+              <project>${project.artifactId}</project>
+              <version>${project.version}</version>
+            </configuration>
             <executions>
+              <execution>
+                <id>atr-check-composing</id>
+                <goals>
+                  <goal>check-composing</goal>
+                </goals>
+              </execution>
               <execution>
                 <id>upload-to-atr</id>
                 <goals>
                   <goal>upload</goal>
                 </goals>
                 <configuration>
-                  <dryRun>true</dryRun>
-                  <project>${project.artifactId}</project>
-                  <version>${project.version}</version>
                   <files>
                     <file>${project.build.directory}/${project.artifactId}-${project.version}-source-release.zip</file>
                     <file>${project.build.directory}/${project.artifactId}-${project.version}-source-release.zip.sha512</file>
diff --git a/src/main/java/org/apache/maven/plugins/atr/AbstractAtrMojo.java b/src/main/java/org/apache/maven/plugins/atr/AbstractAtrMojo.java
index d499d396c..4c08c9eba 100644
--- a/src/main/java/org/apache/maven/plugins/atr/AbstractAtrMojo.java
+++ b/src/main/java/org/apache/maven/plugins/atr/AbstractAtrMojo.java
@@ -41,6 +41,18 @@ import org.apache.maven.settings.crypto.SettingsDecryptionResult;
  */
 public abstract class AbstractAtrMojo extends AbstractMojo {
 
+    /**
+     * The project id for ATR upload.
+     */
+    @Parameter(property = "atr.project", required = true)
+    protected String project;
+
+    /**
+     * The version for ATR upload.
+     */
+    @Parameter(property = "atr.version", required = true)
+    protected String version;
+
     /**
      * The ATR server URL.
      */
@@ -77,7 +89,7 @@ public abstract class AbstractAtrMojo extends AbstractMojo {
      * Maven project.
      */
     @Parameter(defaultValue = "${project}", readonly = true, required = true)
-    protected MavenProject project;
+    protected MavenProject mavenProject;
 
     /**
      * Maven session.
@@ -148,7 +160,7 @@ public abstract class AbstractAtrMojo extends AbstractMojo {
      */
     private boolean isExecutionRoot() {
         File executionRootDirectory = new File(session.getExecutionRootDirectory());
-        File projectBaseDir = project.getBasedir();
+        File projectBaseDir = mavenProject.getBasedir();
         return executionRootDirectory.equals(projectBaseDir);
     }
 
diff --git a/src/main/java/org/apache/maven/plugins/atr/AtrClient.java b/src/main/java/org/apache/maven/plugins/atr/AtrClient.java
index 619ea365c..5c560c611 100644
--- a/src/main/java/org/apache/maven/plugins/atr/AtrClient.java
+++ b/src/main/java/org/apache/maven/plugins/atr/AtrClient.java
@@ -102,6 +102,44 @@ public class AtrClient {
         }
     }
 
+    /**
+     * Check if a version exists in ATR and get its release information.
+     *
+     * @param project the project id
+     * @param version the version
+     * @return the release information, or null if the version does not exist
+     * @throws MojoExecutionException if the check fails
+     */
+    public ReleaseInfo checkVersion(String project, String version) throws MojoExecutionException {
+        // Ensure we have a valid JWT
+        ensureJwt();
+
+        try {
+            // Create connection
+            URL checkUrl = new URL(baseUrl, "api/release/get/" + project + "/" + version);
+            HttpURLConnection conn = (HttpURLConnection) checkUrl.openConnection();
+            conn.setRequestMethod("GET");
+            conn.setRequestProperty("Authorization", "Bearer " + jwt);
+
+            // Check response
+            int responseCode = conn.getResponseCode();
+            if (responseCode == HttpURLConnection.HTTP_OK) {
+                ReleaseGetResponse response = objectMapper.readValue(conn.getInputStream(), ReleaseGetResponse.class);
+                log.debug("Version check successful: " + objectMapper.writeValueAsString(response));
+                return response.getRelease();
+            } else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
+                log.debug("Version does not exist: " + project + " " + version);
+                return null;
+            } else {
+                String errorResponse = readErrorResponse(conn.getErrorStream());
+                throw new MojoExecutionException(
+                        "Failed to check version: HTTP " + responseCode + " - " + errorResponse);
+            }
+        } catch (IOException e) {
+            throw new MojoExecutionException("Failed to check version in ATR: " + project + " " + version, e);
+        }
+    }
+
     /**
      * Upload a file to ATR.
      *
@@ -311,4 +349,178 @@ public class AtrClient {
             this.releaseName = releaseName;
         }
     }
+
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    static class ReleaseGetResponse {
+        @JsonProperty("endpoint")
+        private String endpoint;
+
+        @JsonProperty("release")
+        private ReleaseInfo release;
+
+        public String getEndpoint() {
+            return endpoint;
+        }
+
+        public void setEndpoint(String endpoint) {
+            this.endpoint = endpoint;
+        }
+
+        public ReleaseInfo getRelease() {
+            return release;
+        }
+
+        public void setRelease(ReleaseInfo release) {
+            this.release = release;
+        }
+    }
+
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class ReleaseInfo {
+        // Phase constants
+        public static final String PHASE_RELEASE_CANDIDATE_DRAFT = "release_candidate_draft";
+        public static final String PHASE_RELEASE_CANDIDATE = "release_candidate";
+        public static final String PHASE_RELEASE_PREVIEW = "release_preview";
+        public static final String PHASE_RELEASE = "release";
+
+        @JsonProperty("name")
+        private String name;
+
+        @JsonProperty("phase")
+        private String phase;
+
+        @JsonProperty("project_name")
+        private String projectName;
+
+        @JsonProperty("version")
+        private String version;
+
+        @JsonProperty("created")
+        private String created;
+
+        @JsonProperty("latest_revision_number")
+        private String latestRevisionNumber;
+
+        @JsonProperty("vote_started")
+        private String voteStarted;
+
+        @JsonProperty("vote_resolved")
+        private String voteResolved;
+
+        @JsonProperty("released")
+        private String released;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getPhase() {
+            return phase;
+        }
+
+        public void setPhase(String phase) {
+            this.phase = phase;
+        }
+
+        public String getProjectName() {
+            return projectName;
+        }
+
+        public void setProjectName(String projectName) {
+            this.projectName = projectName;
+        }
+
+        public String getVersion() {
+            return version;
+        }
+
+        public void setVersion(String version) {
+            this.version = version;
+        }
+
+        public String getCreated() {
+            return created;
+        }
+
+        public void setCreated(String created) {
+            this.created = created;
+        }
+
+        public String getLatestRevisionNumber() {
+            return latestRevisionNumber;
+        }
+
+        public void setLatestRevisionNumber(String latestRevisionNumber) {
+            this.latestRevisionNumber = latestRevisionNumber;
+        }
+
+        public String getVoteStarted() {
+            return voteStarted;
+        }
+
+        public void setVoteStarted(String voteStarted) {
+            this.voteStarted = voteStarted;
+        }
+
+        public String getVoteResolved() {
+            return voteResolved;
+        }
+
+        public void setVoteResolved(String voteResolved) {
+            this.voteResolved = voteResolved;
+        }
+
+        public String getReleased() {
+            return released;
+        }
+
+        public void setReleased(String released) {
+            this.released = released;
+        }
+
+        /**
+         * Check if the release is being composed (not yet in vote).
+         *
+         * @return true if the release is being composed
+         */
+        public boolean isComposing() {
+            return PHASE_RELEASE_CANDIDATE_DRAFT.equals(phase);
+        }
+
+        /**
+         * Check if the release has been finalized and released.
+         *
+         * @return true if the release is in the release phase
+         */
+        public boolean isReleased() {
+            return PHASE_RELEASE.equals(phase);
+        }
+
+        /**
+         * Get a human-readable description of the phase.
+         *
+         * @return phase description
+         */
+        public String getPhaseDescription() {
+            if (phase == null) {
+                return "Unknown";
+            }
+            switch (phase) {
+                case PHASE_RELEASE_CANDIDATE_DRAFT:
+                    return "Release Candidate (Draft - Being Composed)";
+                case PHASE_RELEASE_CANDIDATE:
+                    return "Release Candidate (In Voting)";
+                case PHASE_RELEASE_PREVIEW:
+                    return "Release Preview";
+                case PHASE_RELEASE:
+                    return "Released";
+                default:
+                    return phase;
+            }
+        }
+    }
 }
diff --git a/src/main/java/org/apache/maven/plugins/atr/CheckComposingMojo.java b/src/main/java/org/apache/maven/plugins/atr/CheckComposingMojo.java
new file mode 100644
index 000000000..a7e043f9b
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/atr/CheckComposingMojo.java
@@ -0,0 +1,77 @@
+/*
+ * 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.maven.plugins.atr;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * Check if a version exists in ATR and is being composed (in draft phase).
+ *
+ * @author Maven Team
+ */
+@Mojo(name = "check-composing", defaultPhase = LifecyclePhase.INITIALIZE, threadSafe = true)
+public class CheckComposingMojo extends AbstractAtrMojo {
+
+    @Override
+    protected void atrExecute() throws MojoExecutionException, MojoFailureException {
+        if (dryRun) {
+            getLog().info("DRY RUN: Simulating ATR version check (no actual API call will be made)");
+        }
+
+        getLog().info("Checking project release in ATR: " + project + " version " + version);
+
+        if (dryRun) {
+            getLog().info("DRY RUN: Would check version at " + url + "api/release/get/" + project + "/" + version);
+            return;
+        }
+
+        AtrClient client = new AtrClient(url, getServer(), getLog());
+        AtrClient.ReleaseInfo releaseInfo = client.checkVersion(project, version);
+
+        if (releaseInfo == null) {
+            getLog().info("Version does not exist in ATR: " + project + " " + version + System.lineSeparator()
+                    + "       See " + url + "projects/" + project + " for more information");
+            throw new MojoFailureException("Version does not exist in ATR: " + project + " " + version);
+        }
+
+        getLog().info("Version exists in ATR:" + System.lineSeparator()
+                + "         Phase: " + releaseInfo.getPhaseDescription() + System.lineSeparator()
+                + "         Created: " + releaseInfo.getCreated() + System.lineSeparator()
+                + "         Latest Revision: " + releaseInfo.getLatestRevisionNumber());
+
+        // Check fail conditions
+        if (releaseInfo.isComposing()) {
+            getLog().info("Version is being composed in ATR: " + project + " " + version + System.lineSeparator()
+                    + "       See " + url + "compose/" + project + "/" + version + " for more information");
+        } else {
+            if (releaseInfo.isReleased()) {
+                getLog().error("Version is already released: " + project + " " + version + System.lineSeparator()
+                        + "       See " + url + "file/" + project + "/" + version + " for more information");
+            } else {
+                getLog().error("Version is " + releaseInfo.getPhaseDescription() + ": " + project + " " + version
+                        + System.lineSeparator() + "       See " + url + "compose/" + project + "/" + version
+                        + " for more information");
+            }
+            throw new MojoFailureException("Version is not being composed: " + project + " " + version);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/maven/plugins/atr/UploadMojo.java b/src/main/java/org/apache/maven/plugins/atr/UploadMojo.java
index 3d29fa262..b2e82a36f 100644
--- a/src/main/java/org/apache/maven/plugins/atr/UploadMojo.java
+++ b/src/main/java/org/apache/maven/plugins/atr/UploadMojo.java
@@ -31,21 +31,9 @@ import org.apache.maven.plugins.annotations.Parameter;
  *
  * @author Maven Team
  */
-@Mojo(name = "upload", requiresProject = true, defaultPhase = LifecyclePhase.DEPLOY, threadSafe = true)
+@Mojo(name = "upload", defaultPhase = LifecyclePhase.DEPLOY, threadSafe = true)
 public class UploadMojo extends AbstractAtrMojo {
 
-    /**
-     * The project id for ATR upload.
-     */
-    @Parameter(property = "atr.project", required = true)
-    private String project;
-
-    /**
-     * The version for ATR upload.
-     */
-    @Parameter(property = "atr.version", required = true)
-    private String version;
-
     /**
      * The files to upload to ATR.
      */
diff --git a/src/site/markdown/usage.md b/src/site/markdown/usage.md
index f40f548fa..4fbd2bf07 100644
--- a/src/site/markdown/usage.md
+++ b/src/site/markdown/usage.md
@@ -75,15 +75,23 @@ The typical use case is to integrate the ATR plugin into your `apache-release` p
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-atr-plugin</artifactId>
+            <configuration>
+              <project>${project.artifactId}</project>
+              <version>${project.version}</version>
+            </configuration>
             <executions>
+              <execution>
+                <id>atr-check-composing</id>
+                <goals>
+                  <goal>check-composing</goal>
+                </goals>
+              </execution>
               <execution>
                 <id>upload-to-atr</id>
                 <goals>
                   <goal>upload</goal>
                 </goals>
                 <configuration>
-                  <project>${project.artifactId}</project>
-                  <version>${project.version}</version>
                   <files>
                     <file>${project.build.directory}/${project.artifactId}-${project.version}-source-release.zip</file>
                     <file>${project.build.directory}/${project.artifactId}-${project.version}-source-release.zip.sha512</file>