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

jreleaser / jreleaser / #477

04 Apr 2025 05:53PM UTC coverage: 35.124% (-5.1%) from 40.183%
#477

push

github

aalmiray
fix(deploy): Add missing Forgejo messages

Related to #1842

18210 of 51845 relevant lines covered (35.12%)

0.35 hits per line

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

38.89
/sdks/jreleaser-tool-java-sdk/src/main/java/org/jreleaser/sdk/tool/DownloadableTool.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.sdk.tool;
19

20
import org.jreleaser.bundle.RB;
21
import org.jreleaser.logging.JReleaserLogger;
22
import org.jreleaser.mustache.TemplateContext;
23
import org.jreleaser.sdk.command.Command;
24
import org.jreleaser.sdk.command.CommandException;
25
import org.jreleaser.sdk.command.CommandExecutor;
26
import org.jreleaser.util.FileUtils;
27

28
import java.io.File;
29
import java.io.FileNotFoundException;
30
import java.io.InputStream;
31
import java.net.URI;
32
import java.nio.file.Files;
33
import java.nio.file.Path;
34
import java.nio.file.Paths;
35
import java.util.Properties;
36
import java.util.regex.Pattern;
37

38
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
39
import static org.jreleaser.model.Constants.JRELEASER_USER_HOME;
40
import static org.jreleaser.model.Constants.XDG_CACHE_HOME;
41
import static org.jreleaser.mustache.Templates.resolveTemplate;
42
import static org.jreleaser.util.StringUtils.isBlank;
43
import static org.jreleaser.util.StringUtils.isNotBlank;
44

45
/**
46
 * @author Andres Almiray
47
 * @since 1.0.0
48
 */
49
public class DownloadableTool {
50
    private static final String BASE_TEMPLATE_PREFIX = "META-INF/jreleaser/tools/";
51
    private static final String K_DOWNLOAD_URL = "download.url";
52
    private static final String K_VERSION = "version";
53
    private static final String K_EXECUTABLE = ".executable";
54
    private static final String K_FILENAME = ".filename";
55
    private static final String K_COMMAND_VERSION = "command.version";
56
    private static final String K_COMMAND_VERIFY = "command.verify";
57
    private static final String K_EXECUTABLE_PATH = ".executable.path";
58
    private static final String UNPACK = "unpack";
59

60
    private final JReleaserLogger logger;
61
    private final String name;
62
    private final String version;
63
    private final String platform;
64
    private final boolean enabled;
65
    private final Properties properties;
66
    private final boolean verifyErrorOutput;
67

68
    private Path executable;
69

70
    public DownloadableTool(JReleaserLogger logger, String name, String version, String platform, boolean verifyErrorOutput) throws ToolException {
1✔
71
        this.logger = logger;
1✔
72
        this.name = name;
1✔
73
        this.version = version;
1✔
74
        this.platform = platform;
1✔
75
        this.verifyErrorOutput = verifyErrorOutput;
1✔
76

77
        String key = name + ".properties";
1✔
78

79
        try {
80
            properties = new Properties();
1✔
81
            properties.load(DownloadableTool.class.getClassLoader()
1✔
82
                .getResourceAsStream(BASE_TEMPLATE_PREFIX + key));
1✔
83
            enabled = properties.containsKey(platformKey(K_EXECUTABLE));
1✔
84
            if (enabled) {
1✔
85
                executable = Paths.get(properties.getProperty(platformKey(K_EXECUTABLE)));
1✔
86
            }
87
        } catch (Exception e) {
×
88
            throw new ToolException(RB.$("ERROR_unexpected_reading_resource_for", key, "classpath"));
×
89
        }
1✔
90
    }
1✔
91

92
    private String platformKey(String key) {
93
        String k = platform + key;
1✔
94
        if (properties.containsKey(k)) {
1✔
95
            return k;
1✔
96
        }
97

98
        return key.startsWith(".") ? key.substring(1) : key;
1✔
99
    }
100

101
    public boolean isEnabled() {
102
        return enabled;
1✔
103
    }
104

105
    public String getName() {
106
        return name;
×
107
    }
108

109
    public String getVersion() {
110
        return version;
1✔
111
    }
112

113
    public String getPlatform() {
114
        return platform;
×
115
    }
116

117
    public Path getExecutable() {
118
        return executable;
×
119
    }
120

121
    public boolean verify() {
122
        if (null != executable) {
1✔
123
            return verify(executable);
1✔
124
        }
125
        return false;
1✔
126
    }
127

128
    private boolean verify(Path executable) {
129
        Command command = new Command(executable.toString())
1✔
130
            .arg(properties.getProperty(K_COMMAND_VERSION));
1✔
131

132
        try {
133
            String verify = properties.getProperty(K_COMMAND_VERIFY).trim();
1✔
134
            TemplateContext props = props();
1✔
135
            verify = resolveTemplate(verify, props);
1✔
136

137
            Pattern pattern = Pattern.compile(verify);
1✔
138

139
            Command.Result result = executeCommand(command);
×
140
            if (result.getExitValue() != 0) {
×
141
                throw new CommandException(RB.$("ERROR_command_execution_exit_value", result.getExitValue()));
×
142
            }
143

144
            if (verifyErrorOutput) {
×
145
                return pattern.matcher(result.getOut()).find() || pattern.matcher(result.getErr()).find();
×
146
            } else {
147
                return pattern.matcher(result.getOut()).find();
×
148
            }
149
        } catch (CommandException e) {
1✔
150
            if (null != e.getCause()) {
1✔
151
                logger.debug(e.getCause().getMessage());
1✔
152
            } else {
153
                logger.debug(e.getMessage());
×
154
            }
155
        }
156
        return false;
1✔
157
    }
158

159
    public void download() throws ToolException {
160
        String filename = properties.getProperty(platformKey(K_FILENAME));
1✔
161

162
        if (isBlank(filename)) {
1✔
163
            executable = null;
1✔
164
            return;
1✔
165
        }
166

167
        Path caches = resolveJReleaserCacheDir();
×
168
        Path dest = caches.resolve(name).resolve(version);
×
169

170
        boolean unpack = Boolean.parseBoolean(properties.getProperty(UNPACK));
×
171
        String downloadUrl = properties.getProperty(K_DOWNLOAD_URL);
×
172
        String executablePath = properties.getProperty(platformKey(K_EXECUTABLE_PATH));
×
173
        String exec = properties.getProperty(platformKey(K_EXECUTABLE));
×
174

175
        TemplateContext props = props();
×
176
        filename = resolveTemplate(filename, props);
×
177
        if (isNotBlank(executablePath)) executablePath = resolveTemplate(executablePath, props);
×
178

179
        Path test = dest;
×
180
        if (unpack && isNotBlank(executablePath)) {
×
181
            test = dest.resolve(executablePath);
×
182
        }
183
        test = test.resolve(exec).toAbsolutePath();
×
184

185
        if (Files.exists(test)) {
×
186
            executable = test;
×
187
            logger.debug(RB.$("tool.cached", executable));
×
188
            return;
×
189
        }
190

191
        downloadUrl = resolveTemplate(downloadUrl, props) + filename;
×
192
        try (InputStream stream = new URI(downloadUrl).toURL().openStream()) {
×
193
            Path tmp = Files.createTempDirectory("jreleaser");
×
194
            Path destination = tmp.resolve(filename);
×
195

196
            logger.debug(RB.$("tool.located", filename));
×
197
            logger.debug(RB.$("tool.downloading", downloadUrl));
×
198
            Files.copy(stream, destination, REPLACE_EXISTING);
×
199
            logger.debug(RB.$("tool.downloaded", filename));
×
200

201
            Files.createDirectories(dest);
×
202
            if (unpack) {
×
203
                FileUtils.unpackArchive(destination, dest, false);
×
204
                logger.debug(RB.$("tool.unpacked", filename));
×
205
                if (isNotBlank(executablePath)) {
×
206
                    executable = dest.resolve(executablePath).resolve(exec).toAbsolutePath();
×
207
                } else {
208
                    executable = dest.resolve(exec).toAbsolutePath();
×
209
                }
210
            } else {
211
                Path executableFile = dest.resolve(exec);
×
212
                Files.move(destination, executableFile);
×
213
                FileUtils.grantExecutableAccess(executableFile);
×
214
                executable = executableFile.toAbsolutePath();
×
215
            }
216
            logger.debug(RB.$("tool.cached", executable));
×
217
        } catch (FileNotFoundException e) {
×
218
            logger.debug(RB.$("tool.not.found", filename));
×
219
            throw new ToolException(RB.$("tool.not.found", filename), e);
×
220
        } catch (Exception e) {
×
221
            logger.debug(RB.$("tool.download.error", filename));
×
222
            throw new ToolException(RB.$("tool.download.error", filename), e);
×
223
        }
×
224
    }
×
225

226
    public Command asCommand() {
227
        return new Command(executable.toString());
×
228
    }
229

230
    private TemplateContext props() {
231
        TemplateContext props = new TemplateContext();
1✔
232
        props.set(K_VERSION, version);
1✔
233
        return props;
1✔
234
    }
235

236
    private Command.Result executeCommand(Command command) throws CommandException {
237
        return new CommandExecutor(logger, CommandExecutor.Output.DEBUG)
1✔
238
            .executeCommand(command);
×
239
    }
240

241
    private Path resolveJReleaserCacheDir() {
242
        String home = System.getenv(XDG_CACHE_HOME);
×
243
        if (isNotBlank(home)) {
×
244
            return Paths.get(home).resolve("jreleaser");
×
245
        }
246

247
        home = System.getenv(JRELEASER_USER_HOME);
×
248
        if (isBlank(home)) {
×
249
            home = System.getProperty("user.home") + File.separator + ".jreleaser";
×
250
        }
251
        return Paths.get(home).resolve("caches");
×
252
    }
253
}
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