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

jreleaser / jreleaser / #508

26 Jul 2025 06:52PM UTC coverage: 49.379% (-0.002%) from 49.381%
#508

push

github

aalmiray
feat(packagers): Let docker registries be enabled/disabled for publishing

Closes #1935

7 of 48 new or added lines in 4 files covered. (14.58%)

3 existing lines in 2 files now uncovered.

25780 of 52208 relevant lines covered (49.38%)

0.49 hits per line

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

45.37
/core/jreleaser-engine/src/main/java/org/jreleaser/packagers/DockerPackagerProcessor.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.packagers;
19

20
import org.jreleaser.bundle.RB;
21
import org.jreleaser.model.internal.JReleaserContext;
22
import org.jreleaser.model.internal.common.AbstractActivatable;
23
import org.jreleaser.model.internal.common.Artifact;
24
import org.jreleaser.model.internal.distributions.Distribution;
25
import org.jreleaser.model.internal.packagers.DockerConfiguration;
26
import org.jreleaser.model.internal.packagers.DockerPackager;
27
import org.jreleaser.model.internal.packagers.DockerSpec;
28
import org.jreleaser.model.spi.packagers.PackagerProcessingException;
29
import org.jreleaser.mustache.TemplateContext;
30
import org.jreleaser.sdk.command.Command;
31
import org.jreleaser.util.FileUtils;
32
import org.jreleaser.util.PlatformUtils;
33

34
import java.io.ByteArrayInputStream;
35
import java.io.File;
36
import java.io.IOException;
37
import java.nio.file.Files;
38
import java.nio.file.Path;
39
import java.util.ArrayList;
40
import java.util.LinkedHashMap;
41
import java.util.LinkedHashSet;
42
import java.util.List;
43
import java.util.Locale;
44
import java.util.Map;
45
import java.util.Set;
46

47
import static java.nio.charset.StandardCharsets.UTF_8;
48
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
49
import static java.util.Collections.singletonList;
50
import static java.util.stream.Collectors.toList;
51
import static java.util.stream.Collectors.toSet;
52
import static org.jreleaser.model.Constants.KEY_DISTRIBUTION_JAVA_MAIN_CLASS;
53
import static org.jreleaser.model.Constants.KEY_DISTRIBUTION_JAVA_MAIN_MODULE;
54
import static org.jreleaser.model.Constants.KEY_DISTRIBUTION_PACKAGE_DIRECTORY;
55
import static org.jreleaser.model.Constants.KEY_DISTRIBUTION_PREPARE_DIRECTORY;
56
import static org.jreleaser.model.Constants.KEY_DOCKER_BASE_IMAGE;
57
import static org.jreleaser.model.Constants.KEY_DOCKER_LABELS;
58
import static org.jreleaser.model.Constants.KEY_DOCKER_POST_COMMANDS;
59
import static org.jreleaser.model.Constants.KEY_DOCKER_PRE_COMMANDS;
60
import static org.jreleaser.model.Constants.KEY_DOCKER_SPEC_NAME;
61
import static org.jreleaser.mustache.MustacheUtils.passThrough;
62
import static org.jreleaser.mustache.Templates.resolveTemplate;
63
import static org.jreleaser.templates.TemplateUtils.trimTplExtension;
64
import static org.jreleaser.util.StringUtils.isNotBlank;
65

66
/**
67
 * @author Andres Almiray
68
 * @since 0.1.0
69
 */
70
public class DockerPackagerProcessor extends AbstractRepositoryPackagerProcessor<DockerPackager> {
71
    private static final String ROOT = "ROOT";
72

73
    public DockerPackagerProcessor(JReleaserContext context) {
74
        super(context);
1✔
75
    }
1✔
76

77
    @Override
78
    protected void doPrepareDistribution(Distribution distribution,
79
                                         TemplateContext props,
80
                                         String distributionName,
81
                                         Path prepareDirectory,
82
                                         String templateDirectory,
83
                                         String packagerName,
84
                                         boolean copyLicense) throws IOException, PackagerProcessingException {
85
        if (packager.getActiveSpecs().isEmpty()) {
1✔
86
            super.doPrepareDistribution(distribution, props, distributionName,
×
87
                prepareDirectory, templateDirectory, packagerName, true);
88

89
            if (!packager.isUseLocalArtifact()) {
×
90
                Files.move(prepareDirectory.resolve("Dockerfile-remote"),
×
91
                    prepareDirectory.resolve("Dockerfile"),
×
92
                    REPLACE_EXISTING);
93
            } else {
94
                Files.deleteIfExists(prepareDirectory.resolve("Dockerfile-remote"));
×
95
            }
96

97
            return;
×
98
        }
99

100
        // copy root files
101
        String rootTemplateDirectory = getPackager().getTemplateDirectory() + File.separator + ROOT;
1✔
102
        super.doPrepareDistribution(distribution, props, distributionName,
1✔
103
            prepareDirectory.resolve(ROOT),
1✔
104
            rootTemplateDirectory,
105
            packager.getType(),
1✔
106
            false);
107
        Files.deleteIfExists(prepareDirectory.resolve(ROOT).resolve("Dockerfile"));
1✔
108
        Files.deleteIfExists(prepareDirectory.resolve(ROOT).resolve("Dockerfile-remote"));
1✔
109

110
        for (DockerSpec spec : packager.getActiveSpecs()) {
1✔
111
            prepareSpec(distribution, props, distributionName, prepareDirectory, spec);
1✔
112
        }
1✔
113
    }
1✔
114

115
    private void prepareSpec(Distribution distribution,
116
                             TemplateContext props,
117
                             String distributionName,
118
                             Path prepareDirectory,
119
                             DockerSpec spec) throws IOException, PackagerProcessingException {
120
        TemplateContext newProps = fillSpecProps(distribution, props, spec);
1✔
121
        context.getLogger().debug(RB.$("distributions.action.preparing") + " {} spec", spec.getName());
1✔
122
        super.doPrepareDistribution(distribution, newProps, distributionName,
1✔
123
            prepareDirectory.resolve(spec.getName()),
1✔
124
            spec.getTemplateDirectory(),
1✔
125
            spec.getName() + "/" + packager.getType(),
1✔
126
            false);
127

128
        if (!spec.isUseLocalArtifact()) {
1✔
129
            Files.move(prepareDirectory.resolve(spec.getName()).resolve("Dockerfile-remote"),
×
130
                prepareDirectory.resolve(spec.getName()).resolve("Dockerfile"),
×
131
                REPLACE_EXISTING);
132
        } else {
133
            Files.deleteIfExists(prepareDirectory.resolve(spec.getName()).resolve("Dockerfile-remote"));
1✔
134
        }
135
    }
1✔
136

137
    private TemplateContext fillSpecProps(Distribution distribution, TemplateContext props, DockerSpec spec) {
138
        List<Artifact> artifacts = singletonList(spec.getArtifact());
1✔
139
        TemplateContext newProps = fillProps(distribution, props);
1✔
140
        newProps.set(KEY_DOCKER_SPEC_NAME, spec.getName());
1✔
141
        fillDockerProperties(newProps, spec);
1✔
142
        verifyAndAddArtifacts(newProps, distribution, artifacts);
1✔
143
        Path prepareDirectory = newProps.get(KEY_DISTRIBUTION_PREPARE_DIRECTORY);
1✔
144
        newProps.set(KEY_DISTRIBUTION_PREPARE_DIRECTORY, prepareDirectory.resolve(spec.getName()));
1✔
145
        Path packageDirectory = newProps.get(KEY_DISTRIBUTION_PACKAGE_DIRECTORY);
1✔
146
        newProps.set(KEY_DISTRIBUTION_PACKAGE_DIRECTORY, packageDirectory.resolve(spec.getName()));
1✔
147
        return newProps;
1✔
148
    }
149

150
    @Override
151
    protected boolean verifyAndAddArtifacts(TemplateContext props, Distribution distribution) {
152
        if (packager.getActiveSpecs().isEmpty()) {
1✔
153
            return super.verifyAndAddArtifacts(props, distribution);
×
154
        }
155
        return true;
1✔
156
    }
157

158
    @Override
159
    protected void doPackageDistribution(Distribution distribution,
160
                                         TemplateContext props,
161
                                         Path packageDirectory) throws PackagerProcessingException {
162
        if (packager.getActiveSpecs().isEmpty()) {
1✔
163
            List<Artifact> artifacts = packager.resolveArtifacts(context, distribution);
×
164
            packageDocker(distribution, props, packageDirectory, getPackager(), artifacts);
×
165
            return;
×
166
        }
167

168
        Path rootPrepareDirectory = getPrepareDirectory(props).resolve(ROOT);
1✔
169
        Path rootPackageDirectory = getPackageDirectory(props).resolve(ROOT);
1✔
170
        copyFiles(rootPrepareDirectory, rootPackageDirectory);
1✔
171

172
        for (DockerSpec spec : packager.getActiveSpecs()) {
1✔
173
            context.getLogger().debug(RB.$("distributions.action.packaging") + " {} spec", spec.getName());
1✔
174
            TemplateContext newProps = fillSpecProps(distribution, props, spec);
1✔
175
            packageDocker(distribution, newProps, packageDirectory.resolve(spec.getName()),
1✔
176
                spec, singletonList(spec.getArtifact()));
1✔
177
        }
1✔
178
    }
1✔
179

180
    protected void packageDocker(Distribution distribution,
181
                                 TemplateContext props,
182
                                 Path packageDirectory,
183
                                 DockerConfiguration docker,
184
                                 List<Artifact> artifacts) throws PackagerProcessingException {
185
        super.doPackageDistribution(distribution, props, packageDirectory);
1✔
186

187
        try {
188
            // copy files
189
            Path workingDirectory = prepareAssembly(distribution, props, packageDirectory, artifacts);
1✔
190

191
            Map<String, List<String>> tagNames = resolveTagNames(docker, props);
1✔
192
            List<String> tags = tagNames.values().stream()
1✔
193
                .flatMap(List::stream)
1✔
194
                .collect(toList());
1✔
195

196
            tags.forEach(tag -> context.getLogger().info(" - {}", tag));
1✔
197

198
            if (docker.getBuildx().isEnabled()) {
1✔
199
                // create builder if needed
200
                createBuildxBuilder(props, docker);
×
201
                configureAndExecuteBuildCommand(buildxBuildCommand(props, docker), workingDirectory, tags);
×
202
            } else {
203
                configureAndExecuteBuildCommand(buildCommand(props, docker), workingDirectory, tags);
1✔
204
            }
205
        } catch (IOException e) {
×
206
            throw new PackagerProcessingException(e);
×
207
        }
1✔
208
    }
1✔
209

210
    private void configureAndExecuteBuildCommand(Command cmd, Path workingDirectory, List<String> tags) throws PackagerProcessingException {
211
        if (!cmd.hasArg("-q") && !cmd.hasArg("--quiet")) {
1✔
212
            cmd.arg("--quiet");
1✔
213
        }
214
        cmd.arg("--file");
1✔
215
        cmd.arg(workingDirectory.resolve("Dockerfile").toAbsolutePath().toString());
1✔
216
        for (String tag : tags) {
1✔
217
            cmd.arg("--tag");
1✔
218
            cmd.arg(tag);
1✔
219
        }
1✔
220
        cmd.arg(workingDirectory.toAbsolutePath().toString());
1✔
221
        context.getLogger().debug(String.join(" ", cmd.getArgs()));
1✔
222

223
        // execute
224
        executeCommand(cmd);
1✔
225
    }
1✔
226

227
    private void createBuildxBuilder(TemplateContext props, DockerConfiguration docker) throws PackagerProcessingException {
228
        if (!docker.getBuildx().isCreateBuilder()) return;
×
229

230
        Command cmd = new Command("docker" + (PlatformUtils.isWindows() ? ".exe" : ""))
×
231
            .arg("buildx")
×
232
            .arg("ls");
×
233
        Command.Result result = executeCommand(cmd);
×
234
        if (result.getOut().contains("jreleaser")) return;
×
235

236
        cmd = buildxCreateCommand(props, docker);
×
237
        context.getLogger().debug(String.join(" ", cmd.getArgs()));
×
238
        executeCommand(cmd);
×
239
    }
×
240

241
    private Path prepareAssembly(Distribution distribution,
242
                                 TemplateContext props,
243
                                 Path packageDirectory,
244
                                 List<Artifact> artifacts) throws IOException, PackagerProcessingException {
245
        copyPreparedFiles(props);
1✔
246
        Path assemblyDirectory = packageDirectory.resolve("assembly");
1✔
247

248
        Files.createDirectories(assemblyDirectory);
1✔
249

250
        for (Artifact artifact : artifacts) {
1✔
251
            Path artifactPath = artifact.getEffectivePath(context, distribution);
1✔
252
            if (distribution.getType() == org.jreleaser.model.Distribution.DistributionType.BINARY) {
1✔
253
                if (artifactPath.toString().endsWith(".zip")) {
×
254
                    FileUtils.unpackArchive(artifactPath, assemblyDirectory);
×
255
                } else {
256
                    Files.copy(artifactPath, assemblyDirectory.resolve(artifactPath.getFileName()), REPLACE_EXISTING);
×
257
                }
258
            } else {
259
                Files.copy(artifactPath, assemblyDirectory.resolve(artifactPath.getFileName()), REPLACE_EXISTING);
1✔
260
            }
261
        }
1✔
262

263
        return packageDirectory;
1✔
264
    }
265

266
    private Command buildCommand(TemplateContext props, DockerConfiguration docker) {
267
        Command cmd = createCommand(docker, "build");
1✔
268
        for (int i = 0; i < docker.getBuildArgs().size(); i++) {
1✔
269
            String arg = docker.getBuildArgs().get(i);
×
270
            if (arg.contains("{{")) {
×
271
                cmd.arg(resolveTemplate(arg, props).trim());
×
272
            } else {
273
                cmd.arg(arg.trim());
×
274
            }
275
        }
276
        return cmd;
1✔
277
    }
278

279
    private Command buildxBuildCommand(TemplateContext props, DockerConfiguration docker) {
280
        Command cmd = createCommand(docker, "buildx");
×
281
        cmd.arg("build");
×
282

283
        List<String> platforms = new ArrayList<>();
×
284
        for (int i = 0; i < docker.getBuildx().getPlatforms().size(); i++) {
×
285
            String arg = docker.getBuildx().getPlatforms().get(i);
×
286
            if (arg.contains("{{")) {
×
287
                platforms.add(resolveTemplate(arg, props).trim());
×
288
            } else {
289
                platforms.add(arg.trim());
×
290
            }
291
        }
292
        cmd.arg("--platform")
×
293
            .arg(String.join(",", platforms));
×
294

295
        for (int i = 0; i < docker.getBuildArgs().size(); i++) {
×
296
            String arg = docker.getBuildArgs().get(i);
×
297
            if (arg.contains("{{")) {
×
298
                cmd.arg(resolveTemplate(arg, props).trim());
×
299
            } else {
300
                cmd.arg(arg.trim());
×
301
            }
302
        }
303
        return cmd;
×
304
    }
305

306
    private Command buildxCreateCommand(TemplateContext props, DockerConfiguration docker) {
307
        Command cmd = createCommand(docker, "buildx");
×
308
        cmd.arg("create");
×
309
        for (int i = 0; i < docker.getBuildx().getCreateBuilderFlags().size(); i++) {
×
310
            String arg = docker.getBuildx().getCreateBuilderFlags().get(i);
×
311
            if (arg.contains("{{")) {
×
312
                cmd.arg(resolveTemplate(arg, props).trim());
×
313
            } else {
314
                cmd.arg(arg.trim());
×
315
            }
316
        }
317
        return cmd;
×
318
    }
319

320
    private Command createCommand(DockerConfiguration docker, String name) {
321
        return new Command(docker.getCommand().formatted() + (PlatformUtils.isWindows() ? ".exe" : ""))
1✔
322
            .arg("--log-level")
1✔
323
            .arg("error")
1✔
324
            .arg(name);
1✔
325
    }
326

327
    @Override
328
    public void publishDistribution(Distribution distribution, TemplateContext props) throws PackagerProcessingException {
329
        if (packager.getActiveSpecs().isEmpty()) {
1✔
330
            publishToRepository(distribution, props);
×
331
            super.publishDistribution(distribution, props);
×
332
            cleanupBuilder(props, getPackager());
×
333
            return;
×
334
        }
335

336
        publishToRepository(distribution, props);
1✔
337
        for (DockerSpec spec : packager.getActiveSpecs()) {
1✔
338
            context.getLogger().debug(RB.$("distributions.action.publishing") + " {} spec", spec.getName());
1✔
339
            TemplateContext newProps = fillSpecProps(distribution, props, spec);
1✔
340
            publishDocker(newProps, spec);
1✔
341
        }
1✔
342
        cleanupBuilder(props, packager.getActiveSpecs());
1✔
343
    }
1✔
344

345
    private void publishToRepository(Distribution distribution, TemplateContext props) throws PackagerProcessingException {
346
        super.doPublishDistribution(distribution, fillProps(distribution, props));
1✔
347
    }
1✔
348

349
    @Override
350
    protected void doPublishDistribution(Distribution distribution, TemplateContext props) throws PackagerProcessingException {
351
        publishDocker(props, getPackager());
×
352
    }
×
353

354
    protected void publishDocker(TemplateContext props, DockerConfiguration docker) throws PackagerProcessingException {
355
        Map<String, List<String>> tagNames = resolveTagNames(docker, props);
1✔
356

357
        if (context.isDryrun()) {
1✔
358
            for (Map.Entry<String, List<String>> e : tagNames.entrySet()) {
1✔
359
                Set<String> uniqueImageNames = e.getValue().stream()
1✔
360
                    .map(tag -> tag.split(":")[0])
1✔
361
                    .collect(toSet());
1✔
362
                for (String imageName : uniqueImageNames) {
1✔
363
                    context.getLogger().info(" - {}", imageName);
1✔
364
                }
1✔
365
            }
1✔
366
            return;
1✔
367
        }
368

369
        for (DockerConfiguration.Registry registry : docker.getRegistries()) {
×
NEW
370
            if (registry.isEnabled()) {
×
NEW
371
                login(docker, registry);
×
372
            } else {
NEW
373
                context.getLogger().info(RB.$("docker.publish.disabled"), registry.getServerName(), registry.getRepositoryName());
×
374
            }
UNCOV
375
        }
×
376

NEW
377
        boolean activeRegistries = docker.getRegistries().stream().anyMatch(AbstractActivatable::isEnabled);
×
378

NEW
379
        if (activeRegistries) {
×
NEW
380
            if (docker.getBuildx().isEnabled()) {
×
NEW
381
                Path workingDirectory = props.get(KEY_DISTRIBUTION_PACKAGE_DIRECTORY);
×
NEW
382
                List<String> tags = tagNames.values().stream()
×
NEW
383
                    .flatMap(List::stream)
×
NEW
384
                    .collect(toList());
×
385

386
                // command line
NEW
387
                Command cmd = buildxBuildCommand(props, docker);
×
NEW
388
                if (!cmd.hasArg("-q") && !cmd.hasArg("--quiet")) {
×
NEW
389
                    cmd.arg("--quiet");
×
390
                }
NEW
391
                cmd.arg("--file");
×
NEW
392
                cmd.arg(workingDirectory.resolve("Dockerfile").toAbsolutePath().toString());
×
NEW
393
                for (String tag : tags) {
×
NEW
394
                    cmd.arg("--tag");
×
NEW
395
                    cmd.arg(tag);
×
NEW
396
                }
×
NEW
397
                cmd.arg("--push");
×
NEW
398
                cmd.arg(workingDirectory.toAbsolutePath().toString());
×
NEW
399
                context.getLogger().debug(String.join(" ", cmd.getArgs()));
×
400

401
                // execute
NEW
402
                executeCommand(cmd);
×
NEW
403
            } else {
×
NEW
404
                for (Map.Entry<String, List<String>> e : tagNames.entrySet()) {
×
NEW
405
                    Set<String> uniqueImageNames = e.getValue().stream()
×
NEW
406
                        .map(tag -> tag.split(":")[0])
×
NEW
407
                        .collect(toSet());
×
NEW
408
                    for (String imageName : uniqueImageNames) {
×
NEW
409
                        push(docker, e.getKey(), imageName);
×
NEW
410
                    }
×
411
                }
×
412
            }
413
        }
414

415
        for (DockerConfiguration.Registry registry : docker.getRegistries()) {
×
NEW
416
            if (registry.isEnabled()) logout(docker, registry);
×
417
        }
×
418
    }
×
419

420
    private void cleanupBuilder(TemplateContext props, DockerConfiguration docker) throws PackagerProcessingException {
421
        if (docker.getBuildx().isEnabled() && docker.getBuildx().isCreateBuilder()) {
×
422
            int i = docker.getBuildx().getCreateBuilderFlags().indexOf("--name");
×
423
            String builderName = docker.getBuildx().getCreateBuilderFlags().get(i + 1);
×
424
            Command cmd = createCommand(docker, "buildx")
×
425
                .arg("rm")
×
426
                .arg(resolveTemplate(builderName, props).trim());
×
427

428
            executeCommand(cmd);
×
429
        }
430
    }
×
431

432
    private void cleanupBuilder(TemplateContext props, List<DockerSpec> specs) throws PackagerProcessingException {
433
        Set<String> builderNames = new LinkedHashSet<>();
1✔
434
        for (DockerSpec spec : specs) {
1✔
435
            if (spec.getBuildx().isEnabled() && spec.getBuildx().isCreateBuilder()) {
1✔
436
                int i = spec.getBuildx().getCreateBuilderFlags().indexOf("--name");
×
437
                builderNames.add(spec.getBuildx().getCreateBuilderFlags().get(i + 1));
×
438
            }
439
        }
1✔
440

441
        for (String builderName : builderNames) {
1✔
442
            Command cmd = createCommand(packager, "buildx")
×
443
                .arg("rm")
×
444
                .arg(resolveTemplate(builderName, props).trim());
×
445

446
            executeCommand(cmd);
×
447
        }
×
448
    }
1✔
449

450
    private void login(DockerConfiguration docker, DockerConfiguration.Registry registry) throws PackagerProcessingException {
451
        if (registry.isExternalLogin()) return;
×
452

453
        Command cmd = createCommand(docker, "login");
×
454
        if (isNotBlank(registry.getServer())) {
×
455
            cmd.arg(registry.getServer());
×
456
        }
457
        cmd.arg("-u");
×
458
        cmd.arg(registry.getUsername());
×
459
        cmd.arg("-p");
×
460
        cmd.arg(registry.getPassword());
×
461

462
        ByteArrayInputStream in = new ByteArrayInputStream((registry.getPassword() + System.lineSeparator()).getBytes(UTF_8));
×
463

464
        context.getLogger().debug(RB.$("docker.login"),
×
465
            registry.getServerName(),
×
466
            isNotBlank(registry.getServer()) ? " (" + registry.getServer() + ")" : "");
×
467
        if (!context.isDryrun()) executeCommand(cmd, in);
×
468
    }
×
469

470
    private Map<String, List<String>> resolveTagNames(DockerConfiguration docker, TemplateContext props) {
471
        Map<String, List<String>> tags = new LinkedHashMap<>();
1✔
472

473
        for (DockerConfiguration.Registry registry : docker.getRegistries()) {
1✔
474
            for (String imageName : docker.getImageNames()) {
1✔
475
                imageName = resolveTemplate(imageName, props).toLowerCase(Locale.ENGLISH);
1✔
476

477
                String tag = imageName;
1✔
478
                String serverName = registry.getServerName();
1✔
479
                String server = registry.getServer();
1✔
480
                String repositoryName = registry.getRepositoryName();
1✔
481

482
                // if serverName == DEFAULT
483
                //   tag: docker.io/repositoryName/imageName
484
                // else
485
                //   tag: server/repositoryName/imageName
486

487
                if (DockerConfiguration.Registry.DEFAULT_NAME.equals(serverName)) {
1✔
488
                    if (!tag.startsWith(repositoryName)) {
1✔
489
                        int pos = tag.indexOf("/");
1✔
490
                        if (pos < 0) {
1✔
491
                            tag = server + "/" + repositoryName + "/" + tag;
×
492
                        } else {
493
                            tag = server + "/" + repositoryName + tag.substring(pos);
1✔
494
                        }
495
                    }
1✔
496
                } else {
497
                    if (!tag.startsWith(server)) {
×
498
                        int pos = tag.indexOf("/");
×
499
                        if (pos < 0) {
×
500
                            tag = server + "/" + repositoryName + "/" + tag;
×
501
                        } else {
502
                            tag = server + "/" + repositoryName + tag.substring(pos);
×
503
                        }
504
                    }
505
                }
506

507
                tags.computeIfAbsent(server, k -> new ArrayList<>()).add(tag);
1✔
508
            }
1✔
509
        }
1✔
510

511
        return tags;
1✔
512
    }
513

514
    private void push(DockerConfiguration docker, String server, String imageName) throws PackagerProcessingException {
515
        Command cmd = createCommand(docker, "push")
×
516
            .arg("--quiet")
×
517
            .arg("--all-tags")
×
518
            .arg(imageName);
×
519

520
        context.getLogger().info(" - {}", imageName);
×
521
        context.getLogger().debug(RB.$("docker.push", imageName, server));
×
522
        context.getLogger().debug(String.join(" ", cmd.getArgs()));
×
523
        if (!context.isDryrun()) executeCommand(cmd);
×
524
    }
×
525

526
    private void logout(DockerConfiguration docker, DockerConfiguration.Registry registry) throws PackagerProcessingException {
527
        if (registry.isExternalLogin()) return;
×
528

529
        Command cmd = createCommand(docker, "logout");
×
530
        if (isNotBlank(registry.getServer())) {
×
531
            cmd.arg(registry.getServerName());
×
532
        }
533

534
        context.getLogger().debug(RB.$("docker.logout"),
×
535
            registry.getServerName(),
×
536
            isNotBlank(registry.getServer()) ? " (" + registry.getServer() + ")" : "");
×
537
        if (!context.isDryrun()) executeCommand(cmd);
×
538
    }
×
539

540
    @Override
541
    protected void fillPackagerProperties(TemplateContext props, Distribution distribution) {
542
        props.set(KEY_DISTRIBUTION_JAVA_MAIN_CLASS, distribution.getJava().getMainClass());
1✔
543
        props.set(KEY_DISTRIBUTION_JAVA_MAIN_MODULE, distribution.getJava().getMainModule());
1✔
544
        fillDockerProperties(props, getPackager());
1✔
545
    }
1✔
546

547
    protected void fillDockerProperties(TemplateContext props, DockerConfiguration docker) {
548
        props.set(KEY_DOCKER_BASE_IMAGE,
1✔
549
            resolveTemplate(docker.getBaseImage(), props));
1✔
550

551
        List<String> labels = new ArrayList<>();
1✔
552
        docker.getLabels().forEach((label, value) -> labels.add(passThrough("\"" + label + "\"=\"" +
1✔
553
            resolveTemplate(value, props) + "\"")));
1✔
554
        props.set(KEY_DOCKER_LABELS, labels);
1✔
555
        props.set(KEY_DOCKER_PRE_COMMANDS, docker.getPreCommands().stream()
1✔
556
            .map(c -> passThrough(resolveTemplate(c, props)))
1✔
557
            .collect(toList()));
1✔
558
        props.set(KEY_DOCKER_POST_COMMANDS, docker.getPostCommands().stream()
1✔
559
            .map(c -> passThrough(resolveTemplate(c, props)))
1✔
560
            .collect(toList()));
1✔
561
    }
1✔
562

563
    @Override
564
    protected void writeFile(Distribution distribution,
565
                             String content,
566
                             TemplateContext props,
567
                             Path outputDirectory,
568
                             String fileName) throws PackagerProcessingException {
569
        fileName = trimTplExtension(fileName);
1✔
570

571
        Path outputFile = "executable".equals(fileName) ?
1✔
572
            outputDirectory.resolve("assembly").resolve(distribution.getExecutable().getName()) :
×
573
            outputDirectory.resolve(fileName);
1✔
574

575
        writeFile(content, outputFile);
1✔
576
    }
1✔
577

578
    @Override
579
    protected void prepareWorkingCopy(TemplateContext props, Path directory, Distribution distribution) throws IOException {
580
        Path packageDirectory = props.get(KEY_DISTRIBUTION_PACKAGE_DIRECTORY);
×
581

582
        List<DockerSpec> activeSpecs = packager.getActiveSpecs();
×
583

584
        if (activeSpecs.isEmpty()) {
×
585
            for (String imageName : packager.getImageNames()) {
×
586
                copyDockerfiles(packageDirectory, resolveTemplate(imageName, props), directory, packager, false);
×
587
            }
×
588
        } else {
589
            // copy files that do not belong to specs
590
            prepareWorkingCopy(packageDirectory.resolve(ROOT), directory);
×
591

592
            for (DockerSpec spec : activeSpecs) {
×
593
                TemplateContext newProps = fillSpecProps(distribution, props, spec);
×
594
                for (String imageName : spec.getImageNames()) {
×
595
                    copyDockerfiles(packageDirectory.resolve(spec.getName()), resolveTemplate(imageName, newProps), directory, spec, true);
×
596
                }
×
597
            }
×
598
        }
599
    }
×
600

601
    private void copyDockerfiles(Path source, String imageName, Path directory, DockerConfiguration docker, boolean isSpec) throws IOException {
602
        Path destination = directory;
×
603

604
        String[] parts = imageName.split("/");
×
605
        parts = parts[parts.length - 1].split(":");
×
606
        if (isSpec) {
×
607
            destination = directory.resolve(parts[0]);
×
608
        }
609

610
        if (packager.getPackagerRepository().isVersionedSubfolders()) {
×
611
            destination = directory.resolve(parts[1]);
×
612
        }
613

614
        Path assembly = destination.resolve("assembly");
×
615
        FileUtils.deleteFiles(assembly);
×
616

617
        Files.createDirectories(destination);
×
618
        prepareWorkingCopy(source, destination,
×
619
            path -> !docker.isUseLocalArtifact() &&
×
620
                "assembly".equals(path.getFileName().toString()));
×
621
    }
×
622
}
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