• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jreleaser / jreleaser / #558

08 Dec 2025 02:56PM UTC coverage: 48.239% (+0.02%) from 48.215%
#558

push

github

aalmiray
feat(core): warn when a name template cannot be resolved

Closes #1960

Closes #1961

299 of 573 new or added lines in 133 files covered. (52.18%)

4 existing lines in 4 files now uncovered.

26047 of 53996 relevant lines covered (48.24%)

0.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.0
/core/jreleaser-model-impl/src/main/java/org/jreleaser/model/internal/upload/S3Uploader.java
1
/*
2
 * SPDX-License-Identifier: Apache-2.0
3
 *
4
 * Copyright 2020-2025 The JReleaser authors.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     https://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
package org.jreleaser.model.internal.upload;
19

20
import com.fasterxml.jackson.annotation.JsonIgnore;
21
import org.jreleaser.model.Active;
22
import org.jreleaser.model.internal.JReleaserContext;
23
import org.jreleaser.model.internal.common.Artifact;
24
import org.jreleaser.mustache.TemplateContext;
25

26
import java.util.LinkedHashMap;
27
import java.util.Map;
28

29
import static java.util.Collections.unmodifiableMap;
30
import static org.jreleaser.model.Constants.HIDE;
31
import static org.jreleaser.model.Constants.UNSET;
32
import static org.jreleaser.model.api.upload.S3Uploader.TYPE;
33
import static org.jreleaser.mustache.Templates.resolveTemplate;
34
import static org.jreleaser.util.StringUtils.capitalize;
35
import static org.jreleaser.util.StringUtils.isBlank;
36
import static org.jreleaser.util.StringUtils.isNotBlank;
37

38
/**
39
 * @author Andres Almiray
40
 * @since 0.8.0
41
 */
42
public final class S3Uploader extends AbstractUploader<org.jreleaser.model.api.upload.S3Uploader, S3Uploader> {
43
    private static final long serialVersionUID = -747298261100208723L;
44

45
    private final Map<String, String> headers = new LinkedHashMap<>();
1✔
46

47
    private String region;
48
    private String bucket;
49
    private String path;
50
    private String downloadUrl;
51
    private String endpoint;
52
    private String accessKeyId;
53
    private String secretKey;
54
    private String sessionToken;
55

56
    @JsonIgnore
1✔
57
    private final org.jreleaser.model.api.upload.S3Uploader immutable = new org.jreleaser.model.api.upload.S3Uploader() {
1✔
58
        private static final long serialVersionUID = -3263159021072324345L;
59

60
        @Override
61
        public String getRegion() {
62
            return region;
×
63
        }
64

65
        @Override
66
        public String getBucket() {
67
            return bucket;
×
68
        }
69

70
        @Override
71
        public String getAccessKeyId() {
72
            return accessKeyId;
×
73
        }
74

75
        @Override
76
        public String getSecretKey() {
77
            return secretKey;
×
78
        }
79

80
        @Override
81
        public String getSessionToken() {
82
            return sessionToken;
×
83
        }
84

85
        @Override
86
        public String getPath() {
87
            return path;
×
88
        }
89

90
        @Override
91
        public String getDownloadUrl() {
92
            return downloadUrl;
×
93
        }
94

95
        @Override
96
        public String getEndpoint() {
97
            return endpoint;
×
98
        }
99

100
        @Override
101
        public Map<String, String> getHeaders() {
102
            return unmodifiableMap(headers);
×
103
        }
104

105
        @Override
106
        public String getType() {
107
            return S3Uploader.this.getType();
×
108
        }
109

110
        @Override
111
        public String getName() {
112
            return S3Uploader.this.getName();
×
113
        }
114

115
        @Override
116
        public boolean isSnapshotSupported() {
117
            return S3Uploader.this.isSnapshotSupported();
×
118
        }
119

120
        @Override
121
        public boolean isArtifacts() {
122
            return S3Uploader.this.isArtifacts();
×
123
        }
124

125
        @Override
126
        public boolean isFiles() {
127
            return S3Uploader.this.isFiles();
×
128
        }
129

130
        @Override
131
        public boolean isSignatures() {
132
            return S3Uploader.this.isSignatures();
×
133
        }
134

135
        @Override
136
        public boolean isChecksums() {
137
            return S3Uploader.this.isChecksums();
×
138
        }
139

140
        @Override
141
        public boolean isCatalogs() {
142
            return S3Uploader.this.isCatalogs();
×
143
        }
144

145
        @Override
146
        public Active getActive() {
147
            return S3Uploader.this.getActive();
×
148
        }
149

150
        @Override
151
        public boolean isEnabled() {
152
            return S3Uploader.this.isEnabled();
×
153
        }
154

155
        @Override
156
        public Map<String, Object> asMap(boolean full) {
157
            return unmodifiableMap(S3Uploader.this.asMap(full));
×
158
        }
159

160
        @Override
161
        public String getPrefix() {
162
            return S3Uploader.this.prefix();
×
163
        }
164

165
        @Override
166
        public Map<String, Object> getExtraProperties() {
167
            return unmodifiableMap(S3Uploader.this.getExtraProperties());
×
168
        }
169

170
        @Override
171
        public Integer getConnectTimeout() {
172
            return S3Uploader.this.getConnectTimeout();
×
173
        }
174

175
        @Override
176
        public Integer getReadTimeout() {
177
            return S3Uploader.this.getReadTimeout();
×
178
        }
179
    };
180

181
    public S3Uploader() {
182
        super(TYPE);
1✔
183
    }
1✔
184

185
    @Override
186
    public org.jreleaser.model.api.upload.S3Uploader asImmutable() {
187
        return immutable;
1✔
188
    }
189

190
    @Override
191
    public void merge(S3Uploader source) {
192
        super.merge(source);
×
193
        this.region = merge(this.region, source.region);
×
194
        this.bucket = merge(this.bucket, source.bucket);
×
195
        this.path = merge(this.path, source.path);
×
196
        this.downloadUrl = merge(this.downloadUrl, source.downloadUrl);
×
197
        this.endpoint = merge(this.endpoint, source.endpoint);
×
198
        this.accessKeyId = merge(this.accessKeyId, source.accessKeyId);
×
199
        this.secretKey = merge(this.secretKey, source.secretKey);
×
200
        this.sessionToken = merge(this.sessionToken, source.sessionToken);
×
201
        setHeaders(merge(this.headers, source.headers));
×
202
    }
×
203

204
    @Override
205
    public String getResolvedDownloadUrl(JReleaserContext context, Artifact artifact) {
206
        return getResolvedDownloadUrl(context, context.fullProps(), artifact);
1✔
207
    }
208

209
    @Override
210
    public String getResolvedDownloadUrl(JReleaserContext context, TemplateContext props, Artifact artifact) {
211
        if (isNotBlank(getDownloadUrl())) {
1✔
212
            TemplateContext p = new TemplateContext(artifactProps(props, artifact));
×
213
            p.setAll(resolvedExtraProperties());
×
214
            p.set("bucket", bucket);
×
215
            p.set("region", region);
×
NEW
216
            return resolveTemplate(context.getLogger(), getDownloadUrl(), p);
×
217
        }
218

219
        if (isBlank(getEndpoint())) {
1✔
220
            String url = "https://{{bucket}}.s3.{{region}}.amazonaws.com/" + path;
1✔
221
            TemplateContext p = new TemplateContext(artifactProps(props, artifact));
1✔
222
            p.setAll(resolvedExtraProperties());
1✔
223
            p.set("bucket", bucket);
1✔
224
            p.set("region", region);
1✔
225
            return resolveTemplate(context.getLogger(), url, p);
1✔
226
        }
227

228
        return "";
×
229
    }
230

231
    public String getRegion() {
232
        return region;
1✔
233
    }
234

235
    public void setRegion(String region) {
236
        this.region = region;
1✔
237
    }
1✔
238

239
    public String getBucket() {
240
        return bucket;
1✔
241
    }
242

243
    public void setBucket(String bucket) {
244
        this.bucket = bucket;
1✔
245
    }
1✔
246

247
    public String getAccessKeyId() {
248
        return accessKeyId;
1✔
249
    }
250

251
    public void setAccessKeyId(String accessKeyId) {
252
        this.accessKeyId = accessKeyId;
1✔
253
    }
1✔
254

255
    public String getSecretKey() {
256
        return secretKey;
1✔
257
    }
258

259
    public void setSecretKey(String secretKey) {
260
        this.secretKey = secretKey;
1✔
261
    }
1✔
262

263
    public String getSessionToken() {
264
        return sessionToken;
1✔
265
    }
266

267
    public void setSessionToken(String sessionToken) {
268
        this.sessionToken = sessionToken;
1✔
269
    }
1✔
270

271
    public String getPath() {
272
        return path;
1✔
273
    }
274

275
    public void setPath(String path) {
276
        this.path = path;
1✔
277
    }
1✔
278

279
    public String getDownloadUrl() {
280
        return downloadUrl;
1✔
281
    }
282

283
    public void setDownloadUrl(String downloadUrl) {
284
        this.downloadUrl = downloadUrl;
1✔
285
    }
1✔
286

287
    public String getEndpoint() {
288
        return endpoint;
1✔
289
    }
290

291
    public void setEndpoint(String endpoint) {
292
        this.endpoint = endpoint;
1✔
293
    }
1✔
294

295
    public Map<String, String> getHeaders() {
296
        return headers;
1✔
297
    }
298

299
    public void setHeaders(Map<String, String> headers) {
300
        this.headers.putAll(headers);
×
301
    }
×
302

303
    @Override
304
    protected void asMap(boolean full, Map<String, Object> props) {
305
        props.put("region", region);
1✔
306
        props.put("bucket", bucket);
1✔
307
        props.put("accessKeyId", isNotBlank(accessKeyId) ? HIDE : UNSET);
1✔
308
        props.put("secretKey", isNotBlank(secretKey) ? HIDE : UNSET);
1✔
309
        props.put("sessionToken", isNotBlank(sessionToken) ? HIDE : UNSET);
1✔
310
        props.put("path", path);
1✔
311
        props.put("downloadUrl", downloadUrl);
1✔
312
        props.put("endpoint", endpoint);
1✔
313
        props.put("headers", headers);
1✔
314
    }
1✔
315

316

317
    public String getResolvedPath(JReleaserContext context, Artifact artifact) {
318
        String artifactPath = path;
1✔
319

320
        String customPathKey = "s3" + capitalize(getName()) + "Path";
1✔
321
        if (artifact.getExtraProperties().containsKey(customPathKey)) {
1✔
322
            artifactPath = artifact.getExtraProperty(customPathKey);
×
323
        }
324

325
        TemplateContext p = new TemplateContext(artifactProps(context, artifact));
1✔
326
        p.setAll(resolvedExtraProperties());
1✔
327
        return resolveTemplate(context.getLogger(), artifactPath, p);
1✔
328
    }
329
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc