• 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

26.23
/sdks/jreleaser-git-java-sdk/src/main/java/org/jreleaser/sdk/git/ChangelogProvider.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.git;
19

20
import org.jreleaser.bundle.RB;
21
import org.jreleaser.model.internal.JReleaserContext;
22
import org.jreleaser.model.internal.release.BaseReleaser;
23
import org.jreleaser.model.internal.release.Changelog;
24
import org.jreleaser.util.StringUtils;
25

26
import java.io.File;
27
import java.io.IOException;
28
import java.nio.file.Files;
29
import java.nio.file.Path;
30
import java.nio.file.Paths;
31
import java.util.Collections;
32
import java.util.List;
33
import java.util.Set;
34
import java.util.TreeSet;
35
import java.util.regex.Matcher;
36
import java.util.regex.Pattern;
37

38
import static java.lang.System.lineSeparator;
39
import static java.nio.charset.StandardCharsets.UTF_8;
40
import static java.nio.file.StandardOpenOption.CREATE;
41
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
42
import static java.nio.file.StandardOpenOption.WRITE;
43
import static java.util.stream.Collectors.joining;
44
import static org.jreleaser.util.StringUtils.isNotBlank;
45

46
/**
47
 * @author Andres Almiray
48
 * @since 0.1.0
49
 */
50
public final class ChangelogProvider {
51
    public static final String ISSUES_FILE = "issues.txt";
52
    public static final String CHANGELOG_FILE = "CHANGELOG.md";
53

54
    private ChangelogProvider() {
55
        // noop
56
    }
57

58
    public static String getChangelog(JReleaserContext context) throws IOException {
59
        Changelog changelog = context.getModel().getRelease().getReleaser().getChangelog();
×
60

61
        if (!changelog.isEnabled()) {
×
62
            context.getLogger().info(RB.$("changelog.disabled"));
×
63
            return "";
×
64
        }
65

66
        String content = resolveChangelog(context);
×
67
        return storeChangelog(context, content);
×
68
    }
69

70
    public static String storeChangelog(JReleaserContext context, String content) throws IOException {
71
        Path changelogFile = getReleaseFilePath(context, CHANGELOG_FILE);
×
72

73
        context.getLogger().info(RB.$("changelog.generator.store"), context.getBasedir().relativize(changelogFile));
×
74
        context.getLogger().debug(content);
×
75

76
        Files.createDirectories(changelogFile.getParent());
×
77
        Files.write(changelogFile, content.getBytes(UTF_8), CREATE, WRITE, TRUNCATE_EXISTING);
×
78

79
        return content;
×
80
    }
81

82
    public static List<String> getIssues(JReleaserContext context) throws IOException {
83
        Path issuesFile = getReleaseFilePath(context, ISSUES_FILE);
×
84

85
        if (Files.exists(issuesFile)) {
×
86
            return Files.readAllLines(issuesFile);
×
87
        }
88

89
        return Collections.emptyList();
×
90
    }
91

92
    private static String resolveChangelog(JReleaserContext context) throws IOException {
93
        Path issuesFile = getReleaseFilePath(context, ISSUES_FILE);
×
94

95
        // clean up first
96
        if (Files.exists(issuesFile)) Files.delete(issuesFile);
×
97

98
        Changelog changelog = context.getModel().getRelease().getReleaser().getChangelog();
×
99
        String externalChangelog = changelog.getExternal();
×
100

101
        if (isNotBlank(externalChangelog)) {
×
102
            Path externalChangelogPath = context.getBasedir().resolve(Paths.get(externalChangelog));
×
103
            File externalChangelogFile = externalChangelogPath.toFile();
×
104
            if (!externalChangelogFile.exists()) {
×
105
                throw new IllegalStateException(RB.$("ERROR_changelog_not_exist", context.getBasedir().relativize(externalChangelogPath)));
×
106
            }
107

108
            context.getLogger().info(RB.$("changelog.generator.read"), context.getBasedir().relativize(externalChangelogPath));
×
109
            String content = new String(Files.readAllBytes(externalChangelogPath), UTF_8);
×
110

111
            if (context.getModel().getRelease().getReleaser().getIssues().isEnabled()) {
×
112
                context.getLogger().info(RB.$("issues.generator.extract"));
×
113
                Set<Integer> issues = extractIssues(context, content);
×
114
                storeIssues(context, issues);
×
115
            }
116

117
            return content;
×
118
        }
119

120
        context.getLogger().info(RB.$("changelog.generator.generate"));
×
121
        return ChangelogGenerator.generate(context);
×
122
    }
123

124
    public static Set<Integer> extractIssues(JReleaserContext context, String content) {
125
        BaseReleaser<?, ?> releaser = context.getModel().getRelease().getReleaser();
1✔
126
        String issueTracker = releaser.getResolvedIssueTrackerUrl(context.getModel(), true);
1✔
127

128
        String p1 = StringUtils.escapeRegexChars(issueTracker);
1✔
129
        String p2 = StringUtils.escapeRegexChars(releaser.getCanonicalRepoName());
1✔
130
        String p3 = StringUtils.escapeRegexChars(releaser.getName());
1✔
131
        String regex = "(?:" + p2 + "|(?<!/)" + p3 + ")#(?<repo>\\d+)|[^a-zA-Z0-9]#(?<hash>\\d+)";
1✔
132
        regex += isNotBlank(p1) ? "|" + p1 + "(?<tracker>\\d+)" : "";
1✔
133
        Pattern pattern = Pattern.compile(regex);
1✔
134
        Matcher matcher = pattern.matcher(content);
1✔
135
        Set<Integer> issues = new TreeSet<>();
1✔
136
        while (matcher.find()) {
1✔
137
            if (isNotBlank(matcher.group("repo"))) issues.add(Integer.valueOf(matcher.group("repo")));
1✔
138
            if (isNotBlank(matcher.group("hash"))) issues.add(Integer.valueOf(matcher.group("hash")));
1✔
139
            if (isNotBlank(p1) && isNotBlank(matcher.group("tracker")))
1✔
140
                issues.add(Integer.valueOf(matcher.group("tracker")));
1✔
141
        }
142

143
        return issues;
1✔
144
    }
145

146
    public static void storeIssues(JReleaserContext context, Set<Integer> issues) throws IOException {
147
        if (issues.isEmpty()) return;
×
148

149
        Path issuesFile = getReleaseFilePath(context, ISSUES_FILE);
×
150
        String content = issues.stream().map(String::valueOf).collect(joining(lineSeparator()));
×
151
        context.getLogger().info(RB.$("issues.generator.store"), context.getBasedir().relativize(issuesFile));
×
152
        context.getLogger().debug(content);
×
153

154
        Files.createDirectories(issuesFile.getParent());
×
155
        Files.write(issuesFile, content.getBytes(UTF_8), CREATE, WRITE, TRUNCATE_EXISTING);
×
156
    }
×
157

158
    public static Path getReleaseFilePath(JReleaserContext context, String fileName) {
159
        return context.getOutputDirectory()
×
160
            .resolve("release")
×
161
            .resolve(fileName);
×
162
    }
163
}
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