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 }