• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

jreleaser / jreleaser / #556

22 Nov 2025 04:17PM UTC coverage: 46.213% (-2.0%) from 48.203%
#556

push

github

aalmiray
feat(jdks): Allow filtering by platform

Closes #2000

Co-authored-by: Ixchel Ruiz <ixchelruiz@yahoo.com>

0 of 42 new or added lines in 5 files covered. (0.0%)

1116 existing lines in 107 files now uncovered.

24939 of 53965 relevant lines covered (46.21%)

0.46 hits per line

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

51.37
/core/jreleaser-engine/src/main/java/org/jreleaser/engine/hooks/HookExecutor.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.engine.hooks;
19

20
import org.jreleaser.bundle.RB;
21
import org.jreleaser.model.JReleaserException;
22
import org.jreleaser.model.api.hooks.ExecutionEvent;
23
import org.jreleaser.model.internal.JReleaserContext;
24
import org.jreleaser.model.internal.common.Matrix;
25
import org.jreleaser.model.internal.hooks.CommandHook;
26
import org.jreleaser.model.internal.hooks.CommandHookProvider;
27
import org.jreleaser.model.internal.hooks.CommandHooks;
28
import org.jreleaser.model.internal.hooks.Hook;
29
import org.jreleaser.model.internal.hooks.Hooks;
30
import org.jreleaser.model.internal.hooks.JbangHook;
31
import org.jreleaser.model.internal.hooks.JbangHookProvider;
32
import org.jreleaser.model.internal.hooks.JbangHooks;
33
import org.jreleaser.model.internal.hooks.NamedCommandHooks;
34
import org.jreleaser.model.internal.hooks.NamedJbangHooks;
35
import org.jreleaser.model.internal.hooks.NamedScriptHooks;
36
import org.jreleaser.model.internal.hooks.ScriptHook;
37
import org.jreleaser.model.internal.hooks.ScriptHookProvider;
38
import org.jreleaser.model.internal.hooks.ScriptHooks;
39
import org.jreleaser.mustache.TemplateContext;
40
import org.jreleaser.sdk.command.Command;
41
import org.jreleaser.sdk.command.CommandException;
42
import org.jreleaser.sdk.command.CommandExecutor;
43
import org.jreleaser.sdk.tool.JBang;
44
import org.jreleaser.sdk.tool.ToolException;
45
import org.jreleaser.util.DefaultVersions;
46
import org.jreleaser.util.PlatformUtils;
47

48
import java.io.IOException;
49
import java.nio.file.Files;
50
import java.nio.file.Path;
51
import java.util.ArrayList;
52
import java.util.Collection;
53
import java.util.LinkedHashMap;
54
import java.util.List;
55
import java.util.Locale;
56
import java.util.Map;
57
import java.util.StringTokenizer;
58

59
import static java.lang.System.lineSeparator;
60
import static java.nio.charset.StandardCharsets.UTF_8;
61
import static java.nio.file.StandardOpenOption.WRITE;
62
import static org.jreleaser.model.Constants.KEY_PLATFORM;
63
import static org.jreleaser.mustache.Templates.resolveTemplate;
64
import static org.jreleaser.util.StringUtils.isFalse;
65
import static org.jreleaser.util.StringUtils.isNotBlank;
66

67
/**
68
 * @author Andres Almiray
69
 * @since 1.2.0
70
 */
71
public final class HookExecutor {
72
    private static final String JRELEASER_OUTPUT = "JRELEASER_OUTPUT:";
73
    private final JReleaserContext context;
74

75
    public HookExecutor(JReleaserContext context) {
1✔
76
        this.context = context;
1✔
77
    }
1✔
78

79
    public void execute(String step, Runnable runnable) {
80
        executeHooks(ExecutionEvent.before(step));
1✔
81

82
        try {
83
            runnable.run();
1✔
84
        } catch (RuntimeException e) {
×
85
            executeHooks(ExecutionEvent.failure(step, e));
×
86
            throw e;
×
87
        }
1✔
88
        executeHooks(ExecutionEvent.success(step));
1✔
89
    }
1✔
90

91
    public void executeHooks(ExecutionEvent event) {
92
        Hooks hooks = context.getModel().getHooks();
1✔
93
        if (!hooks.isEnabled() || evaluateCondition(hooks.getCondition())) {
1✔
UNCOV
94
            return;
×
95
        }
96

97
        Map<String, String> rootEnv = resolveEnvironment(hooks.getEnvironment());
1✔
98
        executeScriptHooks(event, rootEnv);
1✔
99
        executeCommandHooks(event, rootEnv);
1✔
100
        executeJbangHooks(event, rootEnv);
1✔
101
    }
1✔
102

103
    private boolean evaluateCondition(String condition) {
104
        return isNotBlank(condition) && isFalse(context.eval(condition));
1✔
105
    }
106

107
    private Map<String, String> resolveEnvironment(Map<String, String> src) {
108
        return resolveEnvironment(src, null);
1✔
109
    }
110

111
    private Map<String, String> resolveEnvironment(Map<String, String> src, TemplateContext additionalContext) {
112
        Map<String, String> env = new LinkedHashMap<>();
1✔
113
        TemplateContext props = context.props().setAll(additionalContext);
1✔
114
        src.forEach((k, v) -> {
1✔
115
            String value = resolveTemplate(v, props);
1✔
116
            if (isNotBlank(value)) env.put(k, value);
1✔
117
        });
1✔
118
        return env;
1✔
119
    }
120

121
    private Map<String, String> mergeEnvironment(Map<String, String> src, Map<String, String>... others) {
122
        Map<String, String> tmp = new LinkedHashMap<>(src);
1✔
123
        if (null != others && others.length > 0) {
1✔
124
            for (Map<String, String> env : others) {
1✔
125
                tmp.putAll(env);
1✔
126
            }
127
        }
128
        return tmp;
1✔
129
    }
130

131
    private void executeScriptHooks(ExecutionEvent event, Map<String, String> rootEnv) {
132
        ScriptHooks scriptHooks = context.getModel().getHooks().getScript();
1✔
133
        if (!scriptHooks.isEnabled() || evaluateCondition(scriptHooks.getCondition())) {
1✔
134
            return;
×
135
        }
136

137
        final List<ScriptHook> hooks = collectScriptHooks(event, scriptHooks);
1✔
138
        if (!hooks.isEmpty()) {
1✔
139
            context.getLogger().info(RB.$("hooks.script.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
1✔
140
        }
141

142
        context.getLogger().setPrefix("hooks");
1✔
143
        context.getLogger().increaseIndent();
1✔
144

145
        try {
146
            for (ScriptHook hook : hooks) {
1✔
147
                String prefix = "hooks";
1✔
148
                if (isNotBlank(hook.getName())) {
1✔
149
                    prefix += "." + hook.getName();
×
150
                }
151
                context.getLogger().replacePrefix(prefix);
1✔
152

153
                if (!hook.getMatrix().isEmpty()) {
1✔
154
                    for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
1✔
155
                        if (matrixRow.containsKey(KEY_PLATFORM)) {
1✔
156
                            String srcPlatform = matrixRow.get(KEY_PLATFORM);
1✔
157
                            if (!context.isPlatformSelected(srcPlatform)) {
1✔
158
                                continue;
×
159
                            }
160
                        }
161

162
                        executeScriptHook(event, hook, mergeEnvironment(rootEnv, scriptHooks.getEnvironment()), matrixRow);
1✔
163
                    }
1✔
164
                } else {
165
                    executeScriptHook(event, hook, mergeEnvironment(rootEnv, scriptHooks.getEnvironment()), null);
1✔
166
                }
167
            }
1✔
168
        } finally {
169
            context.getLogger().decreaseIndent();
1✔
170
            context.getLogger().restorePrefix();
1✔
171
        }
172

173
        executeNamedScriptHooks(event, rootEnv, scriptHooks);
1✔
174
    }
1✔
175

176
    private void executeNamedScriptHooks(ExecutionEvent event, Map<String, String> rootEnv, ScriptHooks scriptHooks) {
177
        if (!scriptHooks.isEnabled() || evaluateCondition(scriptHooks.getCondition())) {
1✔
178
            return;
×
179
        }
180

181
        for (NamedScriptHooks group : scriptHooks.getGroups().values()) {
1✔
182
            if (!group.isEnabled() || evaluateCondition(group.getCondition())) {
×
183
                continue;
×
184
            }
185

186
            final List<ScriptHook> hooks = collectScriptHooks(event, group);
×
187
            if (!hooks.isEmpty()) {
×
188
                context.getLogger().info(RB.$("hooks.script.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
×
189
            }
190

191
            context.getLogger().setPrefix("hooks");
×
192
            context.getLogger().increaseIndent();
×
193

194
            try {
195
                for (ScriptHook hook : hooks) {
×
196
                    String prefix = "hooks";
×
197
                    if (isNotBlank(hook.getName())) {
×
198
                        prefix += "." + hook.getName();
×
199
                    }
200
                    context.getLogger().replacePrefix(prefix);
×
201

202
                    if (!hook.getMatrix().isEmpty()) {
×
203
                        for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
×
204
                            if (matrixRow.containsKey(KEY_PLATFORM)) {
×
205
                                String srcPlatform = matrixRow.get(KEY_PLATFORM);
×
206
                                if (!context.isPlatformSelected(srcPlatform)) {
×
207
                                    continue;
×
208
                                }
209
                            }
210

211
                            executeScriptHook(event, hook, mergeEnvironment(rootEnv, scriptHooks.getEnvironment(), group.getEnvironment()), matrixRow);
×
212
                        }
×
213
                    } else {
214
                        executeScriptHook(event, hook, mergeEnvironment(rootEnv, scriptHooks.getEnvironment(), group.getEnvironment()), null);
×
215
                    }
216
                }
×
217
            } finally {
218
                context.getLogger().decreaseIndent();
×
219
                context.getLogger().restorePrefix();
×
220
            }
221
        }
×
222
    }
1✔
223

224
    private List<ScriptHook> collectScriptHooks(ExecutionEvent event, ScriptHookProvider scriptHookProvider) {
225
        final List<ScriptHook> hooks = new ArrayList<>();
1✔
226

227
        switch (event.getType()) {
1✔
228
            case BEFORE:
229
                hooks.addAll((Collection<ScriptHook>) filter(scriptHookProvider.getBefore(), event));
1✔
230
                break;
1✔
231
            case SUCCESS:
232
                hooks.addAll((Collection<ScriptHook>) filter(scriptHookProvider.getSuccess(), event));
1✔
233
                break;
1✔
234
            case FAILURE:
235
                hooks.addAll((Collection<ScriptHook>) filter(scriptHookProvider.getFailure(), event));
1✔
236
                break;
237
        }
238
        return hooks;
1✔
239
    }
240

241
    private void executeScriptHook(ExecutionEvent event, ScriptHook hook, Map<String, String> env, Map<String, String> matrixRow) {
242
        TemplateContext additionalContext = Matrix.asTemplateContext(matrixRow);
1✔
243
        Map<String, String> localEnv = new LinkedHashMap<>(env);
1✔
244
        localEnv = resolveEnvironment(localEnv, additionalContext);
1✔
245
        Path scriptFile = null;
1✔
246

247
        try {
248
            scriptFile = createScriptFile(context, hook, additionalContext, event);
1✔
249
        } catch (IOException e) {
×
250
            throw new JReleaserException(RB.$("ERROR_script_hook_create_error"), e);
×
251
        }
1✔
252

253
        String resolvedCmd = hook.getShell().expression().replace("{{script}}", scriptFile.toAbsolutePath().toString());
1✔
254
        executeCommandLine(localEnv, additionalContext, hook, resolvedCmd, resolvedCmd, "ERROR_script_hook_unexpected_error");
1✔
255
    }
1✔
256

257
    private Path createScriptFile(JReleaserContext context, ScriptHook hook, TemplateContext additionalContext, ExecutionEvent event) throws IOException {
258
        String scriptContents = hook.getResolvedRun(context, additionalContext, event);
1✔
259
        Path scriptFile = Files.createTempFile("jreleaser", hook.getShell().extension());
1✔
260

261
        if (hook.getShell() == org.jreleaser.model.api.hooks.ScriptHook.Shell.PWSH ||
1✔
262
            hook.getShell() == org.jreleaser.model.api.hooks.ScriptHook.Shell.POWERSHELL) {
1✔
263
            scriptContents = "$ErrorActionPreference = 'stop'" + lineSeparator() + scriptContents;
×
264
            scriptContents += lineSeparator() + "if ((Test-Path -LiteralPath variable:\\LASTEXITCODE)) { exit $LASTEXITCODE }";
×
265
        }
266

267
        Files.write(scriptFile, scriptContents.getBytes(UTF_8), WRITE);
1✔
268
        return scriptFile;
1✔
269
    }
270

271
    private void executeCommandHooks(ExecutionEvent event, Map<String, String> rootEnv) {
272
        CommandHooks commandHooks = context.getModel().getHooks().getCommand();
1✔
273
        if (!commandHooks.isEnabled() || evaluateCondition(commandHooks.getCondition())) {
1✔
274
            return;
×
275
        }
276

277
        final List<CommandHook> hooks = collectCommandHooks(event, commandHooks);
1✔
278
        if (!hooks.isEmpty()) {
1✔
279
            context.getLogger().info(RB.$("hooks.command.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
1✔
280
        }
281

282
        context.getLogger().setPrefix("hooks");
1✔
283
        context.getLogger().increaseIndent();
1✔
284

285
        try {
286
            for (CommandHook hook : hooks) {
1✔
287
                String prefix = "hooks";
1✔
288
                if (isNotBlank(hook.getName())) {
1✔
289
                    prefix += "." + hook.getName();
×
290
                }
291
                context.getLogger().replacePrefix(prefix);
1✔
292

293
                if (!hook.getMatrix().isEmpty()) {
1✔
294
                    for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
×
295
                        if (matrixRow.containsKey(KEY_PLATFORM)) {
×
296
                            String srcPlatform = matrixRow.get(KEY_PLATFORM);
×
297
                            if (!context.isPlatformSelected(srcPlatform)) {
×
298
                                continue;
×
299
                            }
300
                        }
301

302
                        executeCommandHook(event, hook, mergeEnvironment(rootEnv, commandHooks.getEnvironment()), matrixRow);
×
303
                    }
×
304
                } else {
305
                    executeCommandHook(event, hook, mergeEnvironment(rootEnv, commandHooks.getEnvironment()), null);
1✔
306
                }
307
            }
1✔
308
        } finally {
309
            context.getLogger().decreaseIndent();
1✔
310
            context.getLogger().restorePrefix();
1✔
311
        }
312

313
        executeNamedCommandHooks(event, rootEnv, commandHooks);
1✔
314
    }
1✔
315

316
    private void executeNamedCommandHooks(ExecutionEvent event, Map<String, String> rootEnv, CommandHooks commandHooks) {
317
        if (!commandHooks.isEnabled() || evaluateCondition(commandHooks.getCondition())) {
1✔
318
            return;
×
319
        }
320

321
        for (NamedCommandHooks group : commandHooks.getGroups().values()) {
1✔
322
            if (!group.isEnabled() || evaluateCondition(group.getCondition())) {
×
323
                continue;
×
324
            }
325

326
            final List<CommandHook> hooks = collectCommandHooks(event, group);
×
327
            if (!hooks.isEmpty()) {
×
328
                context.getLogger().info(RB.$("hooks.command.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
×
329
            }
330

331
            context.getLogger().setPrefix("hooks");
×
332
            context.getLogger().increaseIndent();
×
333

334
            try {
335
                for (CommandHook hook : hooks) {
×
336
                    String prefix = "hooks";
×
337
                    if (isNotBlank(hook.getName())) {
×
338
                        prefix += "." + hook.getName();
×
339
                    }
340
                    context.getLogger().replacePrefix(prefix);
×
341

342
                    if (!hook.getMatrix().isEmpty()) {
×
343
                        for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
×
344
                            if (matrixRow.containsKey(KEY_PLATFORM)) {
×
345
                                String srcPlatform = matrixRow.get(KEY_PLATFORM);
×
346
                                if (!context.isPlatformSelected(srcPlatform)) {
×
347
                                    continue;
×
348
                                }
349
                            }
350

351
                            executeCommandHook(event, hook, mergeEnvironment(rootEnv, commandHooks.getEnvironment(), group.getEnvironment()), matrixRow);
×
352
                        }
×
353
                    } else {
354
                        executeCommandHook(event, hook, mergeEnvironment(rootEnv, commandHooks.getEnvironment(), group.getEnvironment()), null);
×
355
                    }
356
                }
×
357
            } finally {
358
                context.getLogger().decreaseIndent();
×
359
                context.getLogger().restorePrefix();
×
360
            }
361
        }
×
362
    }
1✔
363

364
    private List<CommandHook> collectCommandHooks(ExecutionEvent event, CommandHookProvider commandHookProvider) {
365
        final List<CommandHook> hooks = new ArrayList<>();
1✔
366

367
        switch (event.getType()) {
1✔
368
            case BEFORE:
369
                hooks.addAll((Collection<CommandHook>) filter(commandHookProvider.getBefore(), event));
1✔
370
                break;
1✔
371
            case SUCCESS:
372
                hooks.addAll((Collection<CommandHook>) filter(commandHookProvider.getSuccess(), event));
1✔
373
                break;
1✔
374
            case FAILURE:
375
                hooks.addAll((Collection<CommandHook>) filter(commandHookProvider.getFailure(), event));
1✔
376
                break;
377
        }
378
        return hooks;
1✔
379
    }
380

381
    private void executeCommandHook(ExecutionEvent event, CommandHook hook, Map<String, String> env, Map<String, String> matrixRow) {
382
        TemplateContext additionalContext = Matrix.asTemplateContext(matrixRow);
1✔
383
        Map<String, String> localEnv = new LinkedHashMap<>(env);
1✔
384
        localEnv = resolveEnvironment(localEnv, additionalContext);
1✔
385
        String resolvedCmd = hook.getResolvedCmd(context, additionalContext, event);
1✔
386
        executeCommandLine(localEnv, additionalContext, hook, hook.getCmd(), resolvedCmd, "ERROR_command_hook_unexpected_error");
1✔
387
    }
1✔
388

389
    private void executeCommandLine(Map<String, String> localEnv, TemplateContext additionalContext, Hook hook, String cmd, String resolvedCmd, String errorKey) {
390
        List<String> commandLine = null;
1✔
391

392
        Map<String, String> hookEnv = new LinkedHashMap<>(localEnv);
1✔
393
        hookEnv.putAll(hook.getEnvironment());
1✔
394
        hookEnv = resolveEnvironment(hookEnv, additionalContext);
1✔
395

396
        try {
397
            commandLine = parseCommand(resolvedCmd);
1✔
398
        } catch (IllegalStateException e) {
×
399
            throw new JReleaserException(RB.$("ERROR_command_hook_parser_error", cmd), e);
×
400
        }
1✔
401

402
        try {
403
            Command command = new Command(commandLine);
1✔
404
            processOutput(executeCommand(context.getBasedir(), command, hookEnv, hook.isVerbose()));
1✔
405
        } catch (CommandException e) {
1✔
406
            if (!hook.isContinueOnError()) {
1✔
407
                throw new JReleaserException(RB.$(errorKey), e);
1✔
408
            } else {
409
                if (null != e.getCause()) {
×
410
                    context.getLogger().warn(e.getCause().getMessage());
×
411
                } else {
412
                    context.getLogger().warn(e.getMessage());
×
413
                }
414
                context.getLogger().trace(RB.$(errorKey), e);
×
415
            }
416
        }
1✔
417
    }
1✔
418

419
    private void processOutput(Command.Result result) {
420
        if (!result.getOut().contains(JRELEASER_OUTPUT)) return;
1✔
421
        for (String line : result.getOut().split(lineSeparator())) {
×
422
            if (!line.startsWith(JRELEASER_OUTPUT)) continue;
×
423
            line = line.substring(JRELEASER_OUTPUT.length());
×
424
            int p = line.indexOf("=");
×
425
            String key = line.substring(0, p);
×
426
            String value = line.substring(p + 1);
×
427
            context.getModel().getEnvironment().getProperties().put(key, value);
×
428
        }
429
    }
×
430

431
    private void executeJbangHooks(ExecutionEvent event, Map<String, String> rootEnv) {
432
        JbangHooks jbangHooks = context.getModel().getHooks().getJbang();
1✔
433
        if (!jbangHooks.isEnabled() || evaluateCondition(jbangHooks.getCondition())) {
1✔
434
            return;
1✔
435
        }
436

437
        final List<JbangHook> hooks = collectJbangHooks(event, jbangHooks);
×
438
        if (!hooks.isEmpty()) {
×
439
            context.getLogger().info(RB.$("hooks.jbang.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
×
440
        }
441

442
        context.getLogger().setPrefix("hooks");
×
443
        context.getLogger().increaseIndent();
×
444

445
        try {
446
            for (JbangHook hook : hooks) {
×
447
                String prefix = "hooks";
×
448
                if (isNotBlank(hook.getName())) {
×
449
                    prefix += "." + hook.getName();
×
450
                }
451
                context.getLogger().replacePrefix(prefix);
×
452

453
                if (!hook.getMatrix().isEmpty()) {
×
454
                    for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
×
455
                        if (matrixRow.containsKey(KEY_PLATFORM)) {
×
456
                            String srcPlatform = matrixRow.get(KEY_PLATFORM);
×
457
                            if (!context.isPlatformSelected(srcPlatform)) {
×
458
                                continue;
×
459
                            }
460
                        }
461

462
                        executeJbangHook(event, hook, mergeEnvironment(rootEnv, jbangHooks.getEnvironment()), matrixRow);
×
463
                    }
×
464
                } else {
465
                    executeJbangHook(event, hook, mergeEnvironment(rootEnv, jbangHooks.getEnvironment()), null);
×
466
                }
467
            }
×
468
        } finally {
469
            context.getLogger().decreaseIndent();
×
470
            context.getLogger().restorePrefix();
×
471
        }
472

473
        executeNamedJbangHooks(event, rootEnv, jbangHooks);
×
474
    }
×
475

476
    private void executeNamedJbangHooks(ExecutionEvent event, Map<String, String> rootEnv, JbangHooks jbangHooks) {
477
        if (!jbangHooks.isEnabled() || evaluateCondition(jbangHooks.getCondition())) {
×
478
            return;
×
479
        }
480

481
        for (NamedJbangHooks group : jbangHooks.getGroups().values()) {
×
482
            if (!group.isEnabled() || evaluateCondition(group.getCondition())) {
×
483
                continue;
×
484
            }
485

486
            final List<JbangHook> hooks = collectJbangHooks(event, group);
×
487
            if (!hooks.isEmpty()) {
×
488
                context.getLogger().info(RB.$("hooks.jbang.execution"), event.getType().name().toLowerCase(Locale.ENGLISH), hooks.size());
×
489
            }
490

491
            context.getLogger().setPrefix("hooks");
×
492
            context.getLogger().increaseIndent();
×
493

494
            try {
495
                for (JbangHook hook : hooks) {
×
496
                    String prefix = "hooks";
×
497
                    if (isNotBlank(hook.getName())) {
×
498
                        prefix += "." + hook.getName();
×
499
                    }
500
                    context.getLogger().replacePrefix(prefix);
×
501

502
                    if (!hook.getMatrix().isEmpty()) {
×
503
                        for (Map<String, String> matrixRow : hook.getMatrix().resolve()) {
×
504
                            if (matrixRow.containsKey(KEY_PLATFORM)) {
×
505
                                String srcPlatform = matrixRow.get(KEY_PLATFORM);
×
506
                                if (!context.isPlatformSelected(srcPlatform)) {
×
507
                                    continue;
×
508
                                }
509
                            }
510

511
                            executeJbangHook(event, hook, mergeEnvironment(rootEnv, jbangHooks.getEnvironment(), group.getEnvironment()), matrixRow);
×
512
                        }
×
513
                    } else {
514
                        executeJbangHook(event, hook, mergeEnvironment(rootEnv, jbangHooks.getEnvironment(), group.getEnvironment()), null);
×
515
                    }
516
                }
×
517
            } finally {
518
                context.getLogger().decreaseIndent();
×
519
                context.getLogger().restorePrefix();
×
520
            }
521
        }
×
522
    }
×
523

524
    private List<JbangHook> collectJbangHooks(ExecutionEvent event, JbangHookProvider jbangHookProvider) {
525
        final List<JbangHook> hooks = new ArrayList<>();
×
526

527
        switch (event.getType()) {
×
528
            case BEFORE:
529
                hooks.addAll((Collection<JbangHook>) filter(jbangHookProvider.getBefore(), event));
×
530
                break;
×
531
            case SUCCESS:
532
                hooks.addAll((Collection<JbangHook>) filter(jbangHookProvider.getSuccess(), event));
×
533
                break;
×
534
            case FAILURE:
535
                hooks.addAll((Collection<JbangHook>) filter(jbangHookProvider.getFailure(), event));
×
536
                break;
537
        }
538
        return hooks;
×
539
    }
540

541
    private void executeJbangHook(ExecutionEvent event, JbangHook hook, Map<String, String> env, Map<String, String> matrixRow) {
542
        TemplateContext additionalContext = Matrix.asTemplateContext(matrixRow);
×
543
        Map<String, String> localEnv = new LinkedHashMap<>(env);
×
544
        localEnv = resolveEnvironment(localEnv, additionalContext);
×
545

546
        String jbangVersion = DefaultVersions.getInstance().getJbangVersion();
×
547
        if (isNotBlank(hook.getVersion())) {
×
548
            jbangVersion = hook.getVersion();
×
549
        }
550
        JBang jbang = new JBang(context.asImmutable(), jbangVersion);
×
551

552
        try {
553
            if (!jbang.setup()) {
×
554
                throw new JReleaserException(RB.$("tool_unavailable", "jbang"));
×
555
            }
556
        } catch (ToolException e) {
×
557
            throw new JReleaserException(RB.$("tool_unavailable", "jbang"), e);
×
558
        }
×
559

560
        try {
561
            List<String> args = new ArrayList<>();
×
562
            args.add("run");
×
563
            args.addAll(hook.getResolvedJbangArgs(context));
×
564
            args.add(hook.getResolvedScript(context));
×
565
            args.addAll(hook.getResolvedArgs(context));
×
566

567
            jbang.invokeVerbose(context.getBasedir(), args);
×
568
        } catch (CommandException e) {
×
569
            throw new JReleaserException(RB.$("ERROR_jbang_hook_unexpected_error"), e);
×
570
        }
×
571
    }
×
572

573
    private Collection<? extends Hook> filter(List<? extends Hook> hooks, ExecutionEvent event) {
574
        List<Hook> tmp = new ArrayList<>();
1✔
575

576
        for (Hook hook : hooks) {
1✔
577
            if (!hook.isEnabled() || evaluateCondition(hook.getCondition())) {
1✔
578
                continue;
×
579
            }
580

581
            if (!hook.getFilter().getResolvedIncludes().isEmpty()) {
1✔
582
                if (hook.getFilter().getResolvedIncludes().contains(event.getName()) && filterByPlatform(hook)) {
1✔
583
                    tmp.add(hook);
1✔
584
                }
585
            } else if (filterByPlatform(hook)) {
1✔
586
                tmp.add(hook);
1✔
587
            }
588

589
            if (hook.getFilter().getResolvedExcludes().contains(event.getName())) {
1✔
590
                tmp.remove(hook);
×
591
            }
592
        }
1✔
593

594
        return tmp;
1✔
595
    }
596

597
    private boolean filterByPlatform(Hook hook) {
598
        if (hook.getPlatforms().isEmpty()) return true;
1✔
599

600
        boolean success = true;
1✔
601
        for (String platform : hook.getPlatforms()) {
1✔
602
            boolean exclude = false;
1✔
603
            if (platform.startsWith("!")) {
1✔
604
                exclude = true;
1✔
605
                platform = platform.substring(1);
1✔
606
            }
607

608
            success &= exclude != PlatformUtils.isCompatible(PlatformUtils.getCurrentFull(), platform);
1✔
609
        }
1✔
610

611
        return success;
1✔
612
    }
613

614
    private Command.Result executeCommand(Path directory, Command command, Map<String, String> env, boolean verbose) throws CommandException {
615
        Command.Result result = new CommandExecutor(context.getLogger(), verbose ? CommandExecutor.Output.VERBOSE : CommandExecutor.Output.DEBUG)
1✔
616
            .environment(env)
1✔
617
            .executeCommand(directory, command);
1✔
618
        if (result.getExitValue() != 0) {
1✔
619
            throw new CommandException(RB.$("ERROR_command_execution_exit_value", result.getExitValue()));
1✔
620
        }
621
        return result;
1✔
622
    }
623

624
    // adjusted from org.apache.tools.ant.types.Commandline#translateCommandLine
625
    public static List<String> parseCommand(String str) {
626
        final int normal = 0;
1✔
627
        final int inQuote = 1;
1✔
628
        final int inDoubleQuote = 2;
1✔
629
        int state = normal;
1✔
630
        final StringTokenizer tok = new StringTokenizer(str, "\"' ", true);
1✔
631
        final ArrayList<String> result = new ArrayList<>();
1✔
632
        final StringBuilder current = new StringBuilder();
1✔
633
        boolean lastTokenHasBeenQuoted = false;
1✔
634

635
        while (tok.hasMoreTokens()) {
1✔
636
            String nextTok = tok.nextToken();
1✔
637
            switch (state) {
1✔
638
                case inQuote:
639
                    if ("'".equals(nextTok)) {
×
640
                        lastTokenHasBeenQuoted = true;
×
641
                        state = normal;
×
642
                    } else {
643
                        current.append(nextTok);
×
644
                    }
645
                    break;
×
646
                case inDoubleQuote:
647
                    if ("\"".equals(nextTok)) {
1✔
648
                        lastTokenHasBeenQuoted = true;
1✔
649
                        state = normal;
1✔
650
                    } else {
651
                        current.append(nextTok);
1✔
652
                    }
653
                    break;
1✔
654
                default:
655
                    if ("'".equals(nextTok)) {
1✔
656
                        state = inQuote;
×
657
                    } else if ("\"".equals(nextTok)) {
1✔
658
                        state = inDoubleQuote;
1✔
659
                    } else if (" ".equals(nextTok)) {
1✔
660
                        if (lastTokenHasBeenQuoted || current.length() > 0) {
1✔
661
                            result.add(current.toString());
1✔
662
                            current.setLength(0);
1✔
663
                        }
664
                    } else {
665
                        current.append(nextTok);
1✔
666
                    }
667
                    lastTokenHasBeenQuoted = false;
1✔
668
                    break;
669
            }
670
        }
1✔
671

672
        if (lastTokenHasBeenQuoted || current.length() > 0) {
1✔
673
            result.add(current.toString());
1✔
674
        }
675

676
        if (state == inQuote || state == inDoubleQuote) {
1✔
677
            throw new IllegalStateException(RB.$("ERROR_unbalanced_quotes", str));
×
678
        }
679

680
        return result;
1✔
681
    }
682
}
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