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

jreleaser / jreleaser / #537

25 Sep 2025 02:50PM UTC coverage: 48.299% (-0.7%) from 48.959%
#537

push

github

web-flow
fix: check snapshot version for '-SNAPSHOT' tag

Fixe #1971

2 of 2 new or added lines in 1 file covered. (100.0%)

362 existing lines in 31 files now uncovered.

25821 of 53461 relevant lines covered (48.3%)

0.48 hits per line

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

50.85
/core/jreleaser-model-impl/src/main/java/org/jreleaser/model/internal/packagers/DockerPackager.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.packagers;
19

20
import com.fasterxml.jackson.annotation.JsonIgnore;
21
import org.jreleaser.model.Active;
22
import org.jreleaser.model.Stereotype;
23
import org.jreleaser.model.internal.JReleaserContext;
24
import org.jreleaser.model.internal.common.Artifact;
25
import org.jreleaser.model.internal.common.CommitAuthor;
26
import org.jreleaser.model.internal.common.CommitAuthorAware;
27
import org.jreleaser.model.internal.common.Domain;
28
import org.jreleaser.model.internal.distributions.Distribution;
29
import org.jreleaser.model.internal.project.Project;
30
import org.jreleaser.util.FileType;
31
import org.jreleaser.util.PlatformUtils;
32

33
import java.util.ArrayList;
34
import java.util.Collections;
35
import java.util.EnumSet;
36
import java.util.LinkedHashMap;
37
import java.util.List;
38
import java.util.Map;
39
import java.util.Set;
40
import java.util.stream.Collectors;
41

42
import static java.util.Collections.emptySet;
43
import static java.util.Collections.unmodifiableList;
44
import static java.util.Collections.unmodifiableMap;
45
import static java.util.Collections.unmodifiableSet;
46
import static java.util.Comparator.naturalOrder;
47
import static java.util.function.Function.identity;
48
import static java.util.stream.Collectors.toList;
49
import static java.util.stream.Collectors.toMap;
50
import static java.util.stream.Collectors.toSet;
51
import static org.jreleaser.model.Distribution.DistributionType.BINARY;
52
import static org.jreleaser.model.Distribution.DistributionType.FLAT_BINARY;
53
import static org.jreleaser.model.Distribution.DistributionType.JAVA_BINARY;
54
import static org.jreleaser.model.Distribution.DistributionType.JLINK;
55
import static org.jreleaser.model.Distribution.DistributionType.NATIVE_IMAGE;
56
import static org.jreleaser.model.Distribution.DistributionType.SINGLE_JAR;
57
import static org.jreleaser.model.api.packagers.DockerPackager.SKIP_DOCKER;
58
import static org.jreleaser.util.CollectionUtils.setOf;
59
import static org.jreleaser.util.FileType.JAR;
60
import static org.jreleaser.util.FileType.ZIP;
61
import static org.jreleaser.util.StringUtils.isBlank;
62
import static org.jreleaser.util.StringUtils.isFalse;
63

64
/**
65
 * @author Andres Almiray
66
 * @since 0.1.0
67
 */
68
public final class DockerPackager extends AbstractDockerConfiguration<DockerPackager> implements RepositoryPackager<org.jreleaser.model.api.packagers.DockerPackager>, CommitAuthorAware {
1✔
69
    private static final Map<org.jreleaser.model.Distribution.DistributionType, Set<String>> SUPPORTED = new LinkedHashMap<>();
1✔
70
    //private static final long serialVersionUID = -8293471753814007950L;
71

72
    static {
73
        Set<String> extensions = setOf(ZIP.extension());
1✔
74
        SUPPORTED.put(NATIVE_IMAGE, extensions);
1✔
75
        SUPPORTED.put(BINARY, extensions);
1✔
76
        SUPPORTED.put(JAVA_BINARY, extensions);
1✔
77
        SUPPORTED.put(JLINK, extensions);
1✔
78
        SUPPORTED.put(SINGLE_JAR, setOf(JAR.extension()));
1✔
79
        SUPPORTED.put(FLAT_BINARY, emptySet());
1✔
80
    }
1✔
81

82
    private final Map<String, DockerSpec> specs = new LinkedHashMap<>();
1✔
83
    private final CommitAuthor commitAuthor = new CommitAuthor();
1✔
84
    private final DockerRepository repository = new DockerRepository();
1✔
85

86
    private Boolean continueOnError;
87
    private Boolean skipPublishing;
88
    private String downloadUrl;
89

90
    @JsonIgnore
1✔
91
    private final org.jreleaser.model.api.packagers.DockerPackager immutable = new org.jreleaser.model.api.packagers.DockerPackager() {
1✔
92
        private static final long serialVersionUID = -4589365903340650890L;
93

94
        private Set<? extends org.jreleaser.model.api.packagers.DockerConfiguration.Registry> registries;
95
        private Map<String, ? extends org.jreleaser.model.api.packagers.DockerSpec> specs;
96

97
        @Override
98
        public Map<String, ? extends org.jreleaser.model.api.packagers.DockerSpec> getSpecs() {
99
            if (null == specs) {
×
100
                specs = DockerPackager.this.specs.values().stream()
×
101
                    .map(DockerSpec::asImmutable)
×
102
                    .collect(toMap(org.jreleaser.model.api.packagers.DockerSpec::getName, identity()));
×
103
            }
104
            return specs;
×
105
        }
106

107
        @Override
108
        public DockerRepository getRepository() {
109
            return repository.asImmutable();
×
110
        }
111

112
        @Override
113
        public DockerCommand getCommand() {
114
            return DockerPackager.this.getCommand();
×
115
        }
116

117
        @Override
118
        public org.jreleaser.model.api.common.CommitAuthor getCommitAuthor() {
119
            return commitAuthor.asImmutable();
×
120
        }
121

122
        @Override
123
        public String getTemplateDirectory() {
124
            return DockerPackager.this.getTemplateDirectory();
×
125
        }
126

127
        @Override
128
        public List<String> getSkipTemplates() {
129
            return unmodifiableList(DockerPackager.this.getSkipTemplates());
×
130
        }
131

132
        @Override
133
        public String getBaseImage() {
134
            return DockerPackager.this.getBaseImage();
×
135
        }
136

137
        @Override
138
        public String getEntrypoint() {
139
            return DockerPackager.this.getEntrypoint();
×
140
        }
141

142
        @Override
143
        public String getCmd() {
144
            return DockerPackager.this.getCmd();
×
145
        }
146

147
        @Override
148
        public Map<String, String> getLabels() {
149
            return unmodifiableMap(DockerPackager.this.getLabels());
×
150
        }
151

152
        @Override
153
        public Set<String> getImageNames() {
154
            return unmodifiableSet(DockerPackager.this.getImageNames());
×
155
        }
156

157
        @Override
158
        public List<String> getBuildArgs() {
159
            return unmodifiableList(DockerPackager.this.getBuildArgs());
×
160
        }
161

162
        @Override
163
        public List<String> getPreCommands() {
164
            return unmodifiableList(DockerPackager.this.getPreCommands());
×
165
        }
166

167
        @Override
168
        public List<String> getPostCommands() {
169
            return unmodifiableList(DockerPackager.this.getPostCommands());
×
170
        }
171

172
        @Override
173
        public Set<? extends org.jreleaser.model.api.packagers.DockerConfiguration.Registry> getRegistries() {
174
            if (null == registries) {
×
175
                registries = DockerPackager.this.getRegistries().stream()
×
176
                    .map(DockerConfiguration.Registry::asImmutable)
×
177
                    .collect(toSet());
×
178
            }
179
            return registries;
×
180
        }
181

182
        @Override
183
        public boolean isUseLocalArtifact() {
184
            return DockerPackager.this.isUseLocalArtifact();
×
185
        }
186

187
        @Override
188
        public org.jreleaser.model.api.packagers.PackagerRepository getPackagerRepository() {
189
            return getRepository();
×
190
        }
191

192
        @Override
193
        public String getType() {
194
            return TYPE;
×
195
        }
196

197
        @Override
198
        public String getDownloadUrl() {
199
            return downloadUrl;
×
200
        }
201

202
        @Override
203
        public boolean supportsPlatform(String platform) {
204
            return DockerPackager.this.supportsPlatform(platform);
×
205
        }
206

207
        @Override
208
        public boolean supportsDistribution(org.jreleaser.model.Distribution.DistributionType distributionType) {
209
            return DockerPackager.this.supportsDistribution(distributionType);
×
210
        }
211

212
        @Override
213
        public Set<String> getSupportedFileExtensions(org.jreleaser.model.Distribution.DistributionType distributionType) {
214
            return DockerPackager.this.getSupportedFileExtensions(distributionType);
×
215
        }
216

217
        @Override
218
        public Set<Stereotype> getSupportedStereotypes() {
219
            return DockerPackager.this.getSupportedStereotypes();
×
220
        }
221

222
        @Override
223
        public boolean isSnapshotSupported() {
224
            return DockerPackager.this.isSnapshotSupported();
×
225
        }
226

227
        @Override
228
        public boolean isContinueOnError() {
229
            return DockerPackager.this.isContinueOnError();
×
230
        }
231

232
        @Override
233
        public boolean isSkipPublishing() {
234
            return DockerPackager.this.isSkipPublishing();
×
235
        }
236

237
        @Override
238
        public Active getActive() {
239
            return DockerPackager.this.getActive();
×
240
        }
241

242
        @Override
243
        public boolean isEnabled() {
244
            return DockerPackager.this.isEnabled();
×
245
        }
246

247
        @Override
248
        public Map<String, Object> asMap(boolean full) {
249
            return unmodifiableMap(DockerPackager.this.asMap(full));
×
250
        }
251

252
        @Override
253
        public String getPrefix() {
254
            return DockerPackager.this.prefix();
×
255
        }
256

257
        @Override
258
        public Buildx getBuildx() {
259
            return DockerPackager.this.getBuildx().asImmutable();
×
260
        }
261

262
        @Override
263
        public Map<String, Object> getExtraProperties() {
264
            return unmodifiableMap(DockerPackager.this.getExtraProperties());
×
265
        }
266
    };
267

268
    @JsonIgnore
269
    private boolean failed;
270

271
    @Override
272
    public org.jreleaser.model.api.packagers.DockerPackager asImmutable() {
UNCOV
273
        return immutable;
×
274
    }
275

276
    @Override
277
    public void merge(DockerPackager source) {
278
        super.merge(source);
1✔
279
        this.continueOnError = merge(this.continueOnError, source.continueOnError);
1✔
280
        this.skipPublishing = merge(this.skipPublishing, source.skipPublishing);
1✔
281
        this.downloadUrl = merge(this.downloadUrl, source.downloadUrl);
1✔
282
        this.failed = source.failed;
1✔
283
        setSpecs(mergeModel(this.specs, source.specs));
1✔
284
        setCommitAuthor(source.commitAuthor);
1✔
285
        setRepository(source.repository);
1✔
286
    }
1✔
287

288
    @Override
289
    public boolean resolveEnabled(Project project, Distribution distribution) {
290
        resolveEnabled(project);
×
291
        if (!supportsDistribution(distribution.getType())) {
×
292
            disable();
×
293
        }
294
        return isEnabled();
×
295
    }
296

297
    @Override
298
    public void fail() {
299
        this.failed = true;
×
300
    }
×
301

302
    @Override
303
    public boolean isFailed() {
UNCOV
304
        return failed;
×
305
    }
306

307
    @Override
308
    public boolean isContinueOnError() {
309
        return null != continueOnError && continueOnError;
1✔
310
    }
311

312
    @Override
313
    public void setContinueOnError(Boolean continueOnError) {
314
        this.continueOnError = continueOnError;
1✔
315
    }
1✔
316

317
    @Override
318
    public boolean isContinueOnErrorSet() {
319
        return null != continueOnError;
1✔
320
    }
321

322
    @Override
323
    public boolean isSkipPublishing() {
324
        return null != skipPublishing && skipPublishing;
1✔
325
    }
326

327
    @Override
328
    public void setSkipPublishing(Boolean skipPublishing) {
329
        this.skipPublishing = skipPublishing;
1✔
330
    }
1✔
331

332
    @Override
333
    public boolean isSkipPublishingSet() {
334
        return null != skipPublishing;
1✔
335
    }
336

337
    @Override
338
    public String getDownloadUrl() {
339
        return downloadUrl;
1✔
340
    }
341

342
    @Override
343
    public void setDownloadUrl(String downloadUrl) {
344
        this.downloadUrl = downloadUrl;
1✔
345
    }
1✔
346

347
    @Override
348
    public boolean supportsPlatform(String platform) {
349
        return isBlank(platform) || PlatformUtils.isUnix(platform);
1✔
350
    }
351

352
    @Override
353
    public boolean supportsDistribution(org.jreleaser.model.Distribution.DistributionType distributionType) {
354
        return SUPPORTED.containsKey(distributionType);
×
355
    }
356

357
    @Override
358
    public Set<String> getSupportedFileExtensions(org.jreleaser.model.Distribution.DistributionType distributionType) {
359
        return unmodifiableSet(SUPPORTED.getOrDefault(distributionType, emptySet()));
1✔
360
    }
361

362
    @Override
363
    public Set<Stereotype> getSupportedStereotypes() {
364
        return EnumSet.allOf(Stereotype.class);
×
365
    }
366

367
    @Override
368
    public List<Artifact> resolveCandidateArtifacts(JReleaserContext context, Distribution distribution) {
369
        if (distribution.getType() == FLAT_BINARY && supportsDistribution(distribution.getType())) {
1✔
370
            return distribution.getArtifacts().stream()
×
371
                .filter(Artifact::isActiveAndSelected)
×
372
                .filter(artifact -> supportsPlatform(artifact.getPlatform()))
×
373
                .filter(this::isNotSkipped)
×
374
                .sorted(Artifact.comparatorByPlatform())
×
375
                .collect(toList());
×
376
        }
377

378
        List<String> fileExtensions = new ArrayList<>(getSupportedFileExtensions(distribution.getType()));
1✔
379
        fileExtensions.sort(naturalOrder());
1✔
380

381
        return distribution.getArtifacts().stream()
1✔
382
            .filter(Artifact::isActiveAndSelected)
1✔
383
            .filter(artifact -> fileExtensions.stream().anyMatch(ext -> artifact.getResolvedPath(context, distribution).toString().endsWith(ext)))
1✔
384
            .filter(artifact -> supportsPlatform(artifact.getPlatform()))
1✔
385
            .filter(this::isNotSkipped)
1✔
386
            .sorted(Artifact.comparatorByPlatform().thenComparingInt(artifact -> {
1✔
387
                String ext = FileType.getExtension(artifact.getResolvedPath(context, distribution));
×
388
                return fileExtensions.indexOf(ext);
×
389
            }))
390
            .collect(toList());
1✔
391
    }
392

393
    @Override
394
    public List<Artifact> resolveArtifacts(JReleaserContext context, Distribution distribution) {
395
        return resolveCandidateArtifacts(context, distribution).stream()
×
396
            .filter(Artifact::resolvedPathExists)
×
397
            .collect(toList());
×
398
    }
399

400
    private boolean isNotSkipped(Artifact artifact) {
401
        return isFalse(artifact.getExtraProperties().get(SKIP_DOCKER));
1✔
402
    }
403

404
    @Override
405
    public boolean isSnapshotSupported() {
406
        return true;
×
407
    }
408

409
    @Override
410
    public String getType() {
411
        return TYPE;
1✔
412
    }
413

414
    @Override
415
    public CommitAuthor getCommitAuthor() {
416
        return commitAuthor;
1✔
417
    }
418

419
    @Override
420
    public void setCommitAuthor(CommitAuthor commitAuthor) {
421
        this.commitAuthor.merge(commitAuthor);
1✔
422
    }
1✔
423

424
    public List<DockerSpec> getActiveSpecs() {
UNCOV
425
        return specs.values().stream()
×
UNCOV
426
            .filter(DockerSpec::isEnabled)
×
UNCOV
427
            .collect(Collectors.toList());
×
428
    }
429

430
    public Map<String, DockerSpec> getSpecs() {
431
        return specs;
1✔
432
    }
433

434
    public void setSpecs(Map<String, DockerSpec> specs) {
435
        this.specs.clear();
1✔
436
        this.specs.putAll(specs);
1✔
437
    }
1✔
438

439
    public void addSpecs(Map<String, DockerSpec> specs) {
440
        this.specs.putAll(specs);
×
441
    }
×
442

443
    public void addSpec(DockerSpec spec) {
444
        this.specs.put(spec.getName(), spec);
×
445
    }
×
446

447
    @Override
448
    public Map<String, Object> asMap(boolean full) {
449
        if (!full && !isEnabled()) return Collections.emptyMap();
1✔
450

451
        Map<String, Object> map = new LinkedHashMap<>();
1✔
452
        map.put(getType(), super.asMap(full));
1✔
453
        return map;
1✔
454
    }
455

456
    @Override
457
    protected void asMap(boolean full, Map<String, Object> props) {
458
        props.put("commitAuthor", commitAuthor.asMap(full));
1✔
459
        props.put("repository", repository.asMap(full));
1✔
460
        props.put("downloadUrl", downloadUrl);
1✔
461
        props.put("continueOnError", isContinueOnError());
1✔
462
        props.put("skipPublishing", isSkipPublishing());
1✔
463
        List<Map<String, Object>> specs = this.specs.values()
1✔
464
            .stream()
1✔
465
            .filter(d -> full || d.isEnabled())
1✔
466
            .map(d -> d.asMap(full))
1✔
467
            .collect(Collectors.toList());
1✔
468
        if (!specs.isEmpty()) props.put("specs", specs);
1✔
469
    }
1✔
470

471
    public void setRepository(DockerRepository repository) {
472
        this.repository.merge(repository);
1✔
473
    }
1✔
474

475
    @Override
476
    public RepositoryTap getRepositoryTap() {
477
        return getPackagerRepository();
1✔
478
    }
479

480
    public DockerRepository getPackagerRepository() {
481
        return repository;
1✔
482
    }
483

484
    public static final class DockerRepository extends AbstractRepositoryTap<DockerRepository> implements Domain {
485
        private static final long serialVersionUID = 6677470182457638257L;
486

487
        private Boolean versionedSubfolders;
488

489
        @JsonIgnore
1✔
490
        private final org.jreleaser.model.api.packagers.DockerPackager.DockerRepository immutable = new org.jreleaser.model.api.packagers.DockerPackager.DockerRepository() {
1✔
491
            private static final long serialVersionUID = 1452104357672519L;
492

493
            @Override
494
            public boolean isVersionedSubfolders() {
495
                return DockerRepository.this.isVersionedSubfolders();
×
496
            }
497

498
            @Override
499
            public String getBasename() {
500
                return DockerRepository.this.getBasename();
×
501
            }
502

503
            @Override
504
            public String getCanonicalRepoName() {
505
                return DockerRepository.this.getCanonicalRepoName();
×
506
            }
507

508
            @Override
509
            public String getName() {
510
                return DockerRepository.this.getName();
×
511
            }
512

513
            @Override
514
            public String getTagName() {
515
                return DockerRepository.this.getTagName();
×
516
            }
517

518
            @Override
519
            public String getBranch() {
520
                return DockerRepository.this.getBranch();
×
521
            }
522

523
            @Override
524
            public String getBranchPush() {
525
                return DockerRepository.this.getBranchPush();
×
526
            }
527

528
            @Override
529
            public String getUsername() {
530
                return DockerRepository.this.getUsername();
×
531
            }
532

533
            @Override
534
            public String getToken() {
535
                return DockerRepository.this.getToken();
×
536
            }
537

538
            @Override
539
            public String getCommitMessage() {
540
                return DockerRepository.this.getCommitMessage();
×
541
            }
542

543
            @Override
544
            public Active getActive() {
545
                return DockerRepository.this.getActive();
×
546
            }
547

548
            @Override
549
            public boolean isEnabled() {
550
                return DockerRepository.this.isEnabled();
×
551
            }
552

553
            @Override
554
            public Map<String, Object> asMap(boolean full) {
555
                return unmodifiableMap(DockerRepository.this.asMap(full));
×
556
            }
557

558
            @Override
559
            public String getOwner() {
560
                return DockerRepository.this.getOwner();
×
561
            }
562

563
            @Override
564
            public String getPrefix() {
565
                return DockerRepository.this.prefix();
×
566
            }
567

568
            @Override
569
            public Map<String, Object> getExtraProperties() {
570
                return unmodifiableMap(DockerRepository.this.getExtraProperties());
×
571
            }
572
        };
573

574
        public DockerRepository() {
575
            super("docker", "docker");
1✔
576
        }
1✔
577

578
        public org.jreleaser.model.api.packagers.DockerPackager.DockerRepository asImmutable() {
579
            return immutable;
×
580
        }
581

582
        @Override
583
        public void merge(DockerRepository source) {
584
            super.merge(source);
1✔
585
            this.versionedSubfolders = this.merge(this.versionedSubfolders, source.versionedSubfolders);
1✔
586
        }
1✔
587

588
        @Override
589
        public String prefix() {
590
            return "repository";
×
591
        }
592

593
        public boolean isVersionedSubfolders() {
594
            return null != versionedSubfolders && versionedSubfolders;
1✔
595
        }
596

597
        public void setVersionedSubfolders(Boolean versionedSubfolders) {
598
            this.versionedSubfolders = versionedSubfolders;
1✔
599
        }
1✔
600

601
        public boolean isVersionedSubfoldersSet() {
602
            return null != versionedSubfolders;
1✔
603
        }
604

605
        @Override
606
        public Map<String, Object> asMap(boolean full) {
607
            Map<String, Object> map = super.asMap(full);
1✔
608
            map.put("versionedSubfolders", isVersionedSubfolders());
1✔
609
            return map;
1✔
610
        }
611
    }
612
}
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