View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.resolver.internal.ant.tasks;
20  
21  import org.apache.maven.resolver.internal.ant.AntRepoSys;
22  import org.apache.maven.resolver.internal.ant.types.RemoteRepository;
23  import org.apache.tools.ant.BuildException;
24  import org.apache.tools.ant.types.Reference;
25  
26  /**
27   * Ant task to deploy artifacts to a remote Maven repository.
28   * <p>
29   * This task uploads artifacts, POM files, and optionally metadata such as licenses or dependencies
30   * to a remote repository using Maven Resolver. It mimics the behavior of {@code mvn deploy}
31   * but is integrated into Ant build scripts.
32   * </p>
33   *
34   * <h2>Usage Example:</h2>
35   * <pre>{@code
36   * <repo:deploy>
37   *   <repo:artifact file='build/libs/my-lib.jar'
38   *             groupId='com.example'
39   *             artifactId='my-lib'
40   *             version='1.0.0'
41   *             packaging='jar'/>
42   *   <repo:repository id="snapshots" url='https://my.repo.com/snapshots'>
43   *     <repo:authentication username='user' password='pass'/>
44   *   </repo:repository>
45   * </repo:deploy>
46   * }</pre>
47   * * If you have used the pom task to register an existing POM, or you used the
48   * createPom task to generate and register a POM, you can instead do this:
49   * <pre>{@code
50   * <repo:artifacts id='remoteArtifacts'>
51   *       <repo:artifact refid='jar'/>
52   *       <repo:artifact refid='sourceJar'/>
53   *       <repo:artifact refid='javadocJar'/>
54   * </repo:artifacts>
55   * <repo:deploy artifactsref='remoteArtifacts'>
56   *   <repo:remoteRepo id='localNexus', url='http://localhost:8081/repository/repo/'/>
57   * </repo:deploy>
58   * }</pre>
59   * <h2>Attributes:</h2>
60   * <ul>
61   *   <li><strong>failOnMissingPom</strong> — whether to fail if no POM information is provided (default: true)</li>
62   * </ul>
63   *
64   * <h2>Nested Elements:</h2>
65   * <ul>
66   *   <li>{@code <artifact>} — specifies the artifact file and its coordinates to be deployed</li>
67   *   <li>{@code <pom>} — (optional) provides the POM file to upload, or one will be generated</li>
68   *   <li>{@code <repository>} — defines the target repository for deployment</li>
69   * </ul>
70   *
71   * <h2>Behavior:</h2>
72   * <ul>
73   *   <li>If no POM is provided, a basic one will be generated using the artifact's metadata</li>
74   *   <li>Uploads artifacts using Maven's supported protocols (e.g., HTTP/S with authentication)</li>
75   *   <li>Supports deployment to both snapshot and release repositories</li>
76   * </ul>
77   *
78   * <p>
79   * This task is typically used in CI/CD pipelines or custom release scripts that need to publish artifacts from Ant.
80   * </p>
81   *
82   * @see org.apache.maven.resolver.internal.ant.tasks.CreatePom
83   * @see org.apache.maven.resolver.internal.ant.types.Artifact
84   * @see org.apache.maven.resolver.internal.ant.types.Pom
85   * @see org.apache.maven.resolver.internal.ant.types.RemoteRepository
86   */
87  public class Deploy extends AbstractDistTask {
88  
89      private RemoteRepository repository;
90  
91      private RemoteRepository snapshotRepository;
92  
93      /**
94       * Default constructor for the Deploy task.
95       * <p>
96       * Initializes a new instance of the Deploy task.
97       * </p>
98       */
99      public Deploy() {}
100 
101     @Override
102     protected void validate() {
103         super.validate();
104 
105         if (repository == null) {
106             throw new BuildException("You must specify the <remoteRepo id=\"...\" url=\"...\"> element"
107                     + " to denote the target repository for the deployment");
108         } else {
109             repository.validate(this);
110         }
111         if (snapshotRepository != null) {
112             snapshotRepository.validate(this);
113         }
114     }
115 
116     /**
117      * Allows ant to add a remote repository which artifacts will be deployed to.
118      * <p>
119      * This method is invoked by Ant when a {@code <repository>} nested element is defined
120      * inside the {@code <deploy>} task. Each repository must specify a unique {@code id}
121      * and a deployment {@code url}, and may optionally include layout and authentication information.
122      * </p>
123      *
124      * <p>
125      * Multiple repositories can be specified, though typically only one is used for deployment.
126      * </p>
127      *
128      * <b>Example:</b>
129      * <pre>{@code
130      * <deploy>
131      *   <artifact ... />
132      *   <repository id="release"
133      *               url="https://repo.example.com/releases"
134      *               layout="default">
135      *     <authentication username="user" password="pass"/>
136      *   </repository>
137      * </deploy>
138      * }</pre>
139      *
140      * @param repository the remote repository configuration to add
141      *
142      * @see org.apache.maven.resolver.internal.ant.types.RemoteRepository
143      * @see #execute()
144      */
145     public void addRemoteRepo(RemoteRepository repository) {
146         if (this.repository != null) {
147             throw new BuildException("You must not specify multiple <remoteRepo> elements");
148         }
149         this.repository = repository;
150     }
151 
152     /**
153      * Sets a reference to a predefined {@code <repository>} element to be used as the deployment target.
154      * <p>
155      * This allows the {@code <deploy>} task to reuse a {@link org.apache.maven.resolver.internal.ant.types.RemoteRepository}
156      * definition declared elsewhere in the build script using an {@code id} and {@code refid}.
157      * </p>
158      *
159      * <p>
160      * This is functionally equivalent to defining a {@code <repository>} nested element inline, but enables
161      * reuse across multiple deployment tasks.
162      * </p>
163      *
164      * <b>Example:</b>
165      * <pre>{@code
166      * <repository id="release.repo" url="https://repo.example.com/releases" layout="default">
167      *   <authentication username="deploy" password="secret"/>
168      * </repository>
169      *
170      * <deploy>
171      *   <artifact ... />
172      *   <remoteRepo refid="release.repo"/>
173      * </deploy>
174      * }</pre>
175      *
176      * @param ref the Ant reference to a {@code <repository>} definition
177      *
178      * @see org.apache.maven.resolver.internal.ant.types.RemoteRepository
179      * @see #addRemoteRepo(RemoteRepository)
180      */
181     public void setRemoteRepoRef(Reference ref) {
182         if (repository == null) {
183             repository = new RemoteRepository();
184             repository.setProject(getProject());
185         }
186         repository.setRefid(ref);
187     }
188 
189     /**
190      * Adds a snapshot repository to which snapshot artifacts will be deployed.
191      * <p>
192      * This method is invoked by Ant when a {@code <snapshotRepository>} nested element is defined
193      * within the {@code <deploy>} task. It provides a separate deployment target specifically
194      * for artifacts whose version ends with {@code -SNAPSHOT}.
195      * </p>
196      *
197      * <p>
198      * If no snapshot repository is provided, and a regular {@code <repository>} is defined,
199      * all artifacts (including snapshots) will be deployed to that single repository.
200      * </p>
201      *
202      * <b>Example:</b>
203      * <pre>{@code
204      * <deploy>
205      *   <artifact ... />
206      *
207      *   <repository id="release.repo" url="https://repo.example.com/releases"/>
208      *
209      *   <snapshotRepository id="snapshot.repo" url="https://repo.example.com/snapshots">
210      *     <authentication username="deploy" password="secret"/>
211      *   </snapshotRepository>
212      * </deploy>
213      * }</pre>
214      *
215      * @param snapshotRepository the snapshot repository configuration to use for {@code -SNAPSHOT} artifacts
216      *
217      * @see org.apache.maven.resolver.internal.ant.types.RemoteRepository
218      * @see #addRemoteRepo(RemoteRepository)
219      */
220     public void addSnapshotRepo(RemoteRepository snapshotRepository) {
221         if (this.snapshotRepository != null) {
222             throw new BuildException("You must not specify multiple <snapshotRepo> elements");
223         }
224         this.snapshotRepository = snapshotRepository;
225     }
226 
227     /**
228      * Sets a reference to a predefined {@code <snapshotRepository>} element.
229      * <p>
230      * This allows the {@code <deploy>} task to reuse an existing
231      * {@link org.apache.maven.resolver.internal.ant.types.RemoteRepository} configuration
232      * for deploying snapshot artifacts (i.e., versions ending in {@code -SNAPSHOT}).
233      * </p>
234      *
235      * <p>
236      * This is equivalent to defining a {@code <snapshotRepository>} nested element,
237      * but enables reuse across multiple tasks or modules by referencing a shared definition
238      * declared elsewhere in the build file.
239      * </p>
240      *
241      * <b>Example:</b>
242      * <pre>{@code
243      * <repository id="snap.repo" url="https://repo.example.com/snapshots" layout="default">
244      *   <authentication username="deploy" password="secret"/>
245      * </repository>
246      *
247      * <deploy>
248      *   <artifact ... />
249      *   <snapshotRepository refid="snap.repo"/>
250      * </deploy>
251      * }</pre>
252      *
253      * @param ref an Ant {@link org.apache.tools.ant.types.Reference} to a snapshot repository definition
254      *
255      * @see org.apache.maven.resolver.internal.ant.types.RemoteRepository
256      * @see #addSnapshotRepo(RemoteRepository)
257      */
258     public void setSnapshotRepoRef(Reference ref) {
259         if (snapshotRepository == null) {
260             snapshotRepository = new RemoteRepository();
261             snapshotRepository.setProject(getProject());
262         }
263         snapshotRepository.setRefid(ref);
264     }
265 
266     @Override
267     public void execute() throws BuildException {
268         validate();
269 
270         AntRepoSys.getInstance(getProject()).deploy(this, getPom(), getArtifacts(), repository, snapshotRepository);
271     }
272 }