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

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

31.55
/sdks/jreleaser-git-java-sdk/src/main/java/org/jreleaser/sdk/git/GitSdk.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.eclipse.jgit.api.Git;
21
import org.eclipse.jgit.api.ListBranchCommand;
22
import org.eclipse.jgit.api.TagCommand;
23
import org.eclipse.jgit.api.errors.EmptyCommitException;
24
import org.eclipse.jgit.api.errors.GitAPIException;
25
import org.eclipse.jgit.errors.RepositoryNotFoundException;
26
import org.eclipse.jgit.lib.Constants;
27
import org.eclipse.jgit.lib.ObjectId;
28
import org.eclipse.jgit.lib.PersonIdent;
29
import org.eclipse.jgit.lib.Ref;
30
import org.eclipse.jgit.revwalk.RevCommit;
31
import org.eclipse.jgit.revwalk.RevObject;
32
import org.eclipse.jgit.revwalk.RevWalk;
33
import org.eclipse.jgit.transport.RemoteConfig;
34
import org.eclipse.jgit.transport.URIish;
35
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
36
import org.jreleaser.bundle.RB;
37
import org.jreleaser.model.internal.JReleaserContext;
38
import org.jreleaser.model.internal.release.BaseReleaser;
39
import org.jreleaser.model.spi.release.Commit;
40
import org.jreleaser.model.spi.release.Repository;
41
import org.jreleaser.util.Env;
42
import org.jreleaser.util.StringUtils;
43

44
import java.io.File;
45
import java.io.IOException;
46
import java.nio.file.Files;
47
import java.nio.file.Path;
48
import java.time.LocalDateTime;
49
import java.time.ZoneId;
50
import java.time.ZonedDateTime;
51
import java.util.Comparator;
52
import java.util.Date;
53
import java.util.Iterator;
54
import java.util.List;
55
import java.util.TimeZone;
56

57
import static java.util.stream.Collectors.toList;
58
import static org.jreleaser.util.StringUtils.isBlank;
59

60
/**
61
 * @author Andres Almiray
62
 * @since 0.1.0
63
 */
64
public class GitSdk {
65
    public static final String REFS_TAGS = "refs/tags/";
66
    public static final String REFS_HEADS = "refs/heads/";
67
    public static final String REFS_REMOTES = "refs/remotes/";
68

69
    private final File basedir;
70
    private final boolean gitRootSearch;
71

72
    private GitSdk(File basedir, boolean gitRootSearch) {
1✔
73
        this.basedir = basedir;
1✔
74
        this.gitRootSearch = gitRootSearch;
1✔
75
    }
1✔
76

77
    public Git open() throws IOException {
78
        if (!gitRootSearch) {
1✔
79
            return Git.open(basedir);
1✔
80
        }
81

82
        File dir = basedir;
×
83

84
        while (null != dir) {
×
85
            try {
86
                return Git.open(dir);
×
87
            } catch (RepositoryNotFoundException e) {
×
88
                dir = dir.getParentFile();
×
89
            }
×
90
        }
91

92
        throw new RepositoryNotFoundException(basedir);
×
93
    }
94

95
    public Repository getRemote() throws IOException {
96
        try (Git git = open()) {
1✔
97
            String remote = resolveDefaultGitRemoteName();
1✔
98
            RemoteConfig remoteConfig = git.remoteList().call().stream()
1✔
99
                .filter(rc -> remote.equals(rc.getName()))
1✔
100
                .findFirst()
1✔
101
                .orElseThrow(() -> new IOException(RB.$("ERROR_git_repository_remote", remote)));
1✔
102

103
            List<URIish> uris = remoteConfig.getURIs();
1✔
104
            if (uris.isEmpty()) {
1✔
105
                // better be safe than sorry
106
                throw new IOException(RB.$("ERROR_git_repository_remote_missing_url", remote));
×
107
            }
108

109
            // grab the first one
110
            URIish uri = uris.get(0);
1✔
111

112
            Repository.Kind kind = Repository.Kind.OTHER;
1✔
113
            switch (uri.getHost()) {
1✔
114
                case "github.com":
115
                    kind = Repository.Kind.GITHUB;
1✔
116
                    break;
1✔
117
                case "gitlab.com":
118
                    kind = Repository.Kind.GITLAB;
×
119
                    break;
×
120
                case "codeberg.org":
121
                    kind = Repository.Kind.CODEBERG;
×
122
                    break;
×
123
                default:
124
                    // noop
125
            }
126

127
            String[] parts = uri.getPath().split("/");
1✔
128
            if (parts.length < 2) {
1✔
129
                throw new IOException(RB.$("ERROR_git_repository_remote_url_parse", uri.getPath()));
×
130
            }
131

132
            String owner = parts[parts.length - 2];
1✔
133
            String name = parts[parts.length - 1].replace(".git", "");
1✔
134

135
            return new Repository(
1✔
136
                kind,
137
                owner,
138
                name,
139
                null,
140
                uri.toString());
1✔
141
        } catch (GitAPIException e) {
×
142
            throw new IOException(RB.$("ERROR_git_repository_origin_remote"), e);
×
143
        }
144
    }
145

146
    public List<String> getLocalBranchNames() throws IOException {
147
        try (Git git = open()) {
×
148
            return git.branchList()
×
149
                .call().stream()
×
150
                .map(GitSdk::extractHeadName)
×
151
                .filter(StringUtils::isNotBlank)
×
152
                .collect(toList());
×
153
        } catch (GitAPIException e) {
×
154
            throw new IOException(RB.$("ERROR_git_repository_list_local_branch"), e);
×
155
        }
156
    }
157

158
    public List<String> getRemoteBranches() throws IOException {
159
        try (Git git = open()) {
×
160
            return git.branchList()
×
161
                .setListMode(ListBranchCommand.ListMode.REMOTE)
×
162
                .call().stream()
×
163
                .map(GitSdk::extractRemoteName)
×
164
                .filter(StringUtils::isNotBlank)
×
165
                .collect(toList());
×
166
        } catch (GitAPIException e) {
×
167
            throw new IOException(RB.$("ERROR_git_repository_list_local_branch"), e);
×
168
        }
169
    }
170

171
    public Commit head() throws IOException {
172
        return commit(Constants.HEAD);
1✔
173
    }
174

175
    public Commit commit(String input) throws IOException {
176
        try (Git git = open()) {
1✔
177
            RevWalk walk = new RevWalk(git.getRepository());
1✔
178
            ObjectId objectId = git.getRepository().resolve(input);
1✔
179
            RevCommit commit = null;
1✔
180

181
            try {
182
                commit = walk.parseCommit(objectId);
1✔
183
            } catch (NullPointerException e) {
×
184
                throw new IllegalStateException(RB.$("ERROR_commit_not_found", input));
×
185
            }
1✔
186

187
            Ref ref = git.getRepository().findRef(input);
1✔
188
            PersonIdent authorIdent = commit.getAuthorIdent();
1✔
189
            Date authorDate = authorIdent.getWhen();
1✔
190
            TimeZone authorTimeZone = authorIdent.getTimeZone();
1✔
191

192
            ZoneId zoneId = ZoneId.of(authorTimeZone.getID());
1✔
193
            LocalDateTime local = LocalDateTime.ofInstant(authorDate.toInstant(), zoneId);
1✔
194
            ZonedDateTime zoned = ZonedDateTime.of(local, zoneId);
1✔
195

196
            return new Commit(
1✔
197
                commit.getId().abbreviate(7).name(),
1✔
198
                commit.getId().name(),
1✔
199
                extractHeadName(ref),
1✔
200
                commit.getCommitTime(),
1✔
201
                zoned);
202
        }
203
    }
204

205
    public RevCommit resolveSingleCommit(Git git, Ref tag) throws GitAPIException {
206
        try {
207
            Iterable<RevCommit> commits = git.log().add(getObjectId(git, tag))
×
208
                .setMaxCount(1)
×
209
                .call();
×
210
            if (null == commits) {
×
211
                throw new EmptyCommitException(RB.$("ERROR_git_commit_not_found", tag.getName()));
×
212
            }
213
            Iterator<RevCommit> iterator = commits.iterator();
×
214
            if (iterator.hasNext()) {
×
215
                return iterator.next();
×
216
            }
217
            throw new EmptyCommitException(RB.$("ERROR_git_commit_not_found", tag.getName()));
×
218
        } catch (IOException e) {
×
219
            throw new EmptyCommitException(RB.$("ERROR_git_commit_not_found", tag.getName()), e);
×
220
        }
221
    }
222

223
    public ObjectId getObjectId(Git git, Ref ref) throws IOException {
224
        Ref peeled = git.getRepository().getRefDatabase().peel(ref);
×
225
        return null != peeled.getPeeledObjectId() ? peeled.getPeeledObjectId() : peeled.getObjectId();
×
226
    }
227

228
    public void deleteTag(String tagName) throws IOException {
229
        try (Git git = open()) {
×
230
            git.tagDelete()
×
231
                .setTags(tagName)
×
232
                .call();
×
233
        } catch (GitAPIException e) {
×
234
            throw new IOException(RB.$("ERROR_git_delete_tag", tagName), e);
×
235
        }
×
236
    }
×
237

238
    public boolean findTag(String tagName) throws IOException {
239
        try (Git git = open()) {
×
240
            return git.tagList().call().stream()
×
241
                .map(GitSdk::extractTagName)
×
242
                .anyMatch(tagName::matches);
×
243
        } catch (GitAPIException e) {
×
244
            throw new IOException(RB.$("ERROR_git_find_tag", tagName), e);
×
245
        }
246
    }
247

248
    public void checkoutBranch(String branch) throws IOException {
249
        checkoutBranch(null, branch, true, false);
×
250
    }
×
251

252
    public void checkoutBranch(BaseReleaser<?, ?> releaser, String branch, boolean checkout, boolean create) throws IOException {
253
        try (Git git = open()) {
×
254
            if (checkout) {
×
255
                git.checkout()
×
256
                    .setName(branch)
×
257
                    .setCreateBranch(create)
×
258
                    .call();
×
259
            }
260

261
            if (create) {
×
262
                UsernamePasswordCredentialsProvider credentialsProvider = new UsernamePasswordCredentialsProvider(
×
263
                    releaser.getUsername(),
×
264
                    releaser.getToken());
×
265

266
                git.push()
×
267
                    .setDryRun(false)
×
268
                    .setPushAll()
×
269
                    .setCredentialsProvider(credentialsProvider)
×
270
                    .call();
×
271
            }
272
        } catch (GitAPIException e) {
×
273
            throw new IOException(RB.$("ERROR_git_checkout_branch", branch), e);
×
274
        }
×
275
    }
×
276

277
    public boolean isShallow() {
278
        Path path = basedir.toPath();
1✔
279
        return Files.exists(path.resolve(".git/shallow"));
1✔
280
    }
281

282
    public void tag(String tagName, JReleaserContext context) throws IOException {
283
        tag(tagName, false, context);
×
284
    }
×
285

286
    public void tag(String tagName, boolean force, JReleaserContext context) throws IOException {
287
        try (Git git = open()) {
×
288
            ObjectId objectId = git.getRepository().resolve(context.getModel().getCommit().getFullHash());
×
289
            boolean signEnabled = context.getModel().getRelease().getReleaser().isSign();
×
290
            TagCommand tagCommand = git.tag()
×
291
                .setSigned(signEnabled)
×
292
                .setSigningKey("**********")
×
293
                .setGpgSigner(new JReleaserGpgSigner(context, signEnabled))
×
294
                .setName(tagName)
×
295
                .setForceUpdate(force);
×
296
            if (objectId instanceof RevObject) {
×
297
                tagCommand = tagCommand.setObjectId((RevObject) objectId);
×
298
            }
299
            tagCommand.call();
×
300
        } catch (GitAPIException e) {
×
301
            throw new IOException(RB.$("ERROR_git_create_tag", tagName), e);
×
302
        }
×
303
    }
×
304

305
    public static String resolveDefaultGitRemoteName() {
306
        String remoteName = Env.env(org.jreleaser.model.Constants.DEFAULT_GIT_REMOTE, "");
1✔
307
        if (isBlank(remoteName)) remoteName = "origin";
1✔
308
        return remoteName;
1✔
309
    }
310

311
    public static GitSdk of(JReleaserContext context) {
312
        return of(context.getBasedir().toFile(), context.isGitRootSearch());
1✔
313
    }
314

315
    public static GitSdk of(Path basedir, boolean gitRootSearch) {
UNCOV
316
        return of(basedir.toFile(), gitRootSearch);
×
317
    }
318

319
    public static GitSdk of(File basedir, boolean gitRootSearch) {
320
        return new GitSdk(basedir, gitRootSearch);
1✔
321
    }
322

323
    public static String extractTagName(Ref tag) {
324
        if (tag.getName().startsWith(REFS_TAGS)) {
×
325
            return tag.getName().substring(REFS_TAGS.length());
×
326
        }
327
        return "";
×
328
    }
329

330
    public static String extractHeadName(Ref ref) {
331
        if (ref.getTarget().getName().startsWith(REFS_HEADS)) {
1✔
332
            return ref.getTarget().getName().substring(REFS_HEADS.length());
1✔
333
        }
334
        return "";
×
335
    }
336

337
    public static String extractRemoteName(Ref ref) {
338
        if (ref.getTarget().getName().startsWith(REFS_REMOTES)) {
×
339
            String remoteAndBranch = ref.getTarget().getName().substring(REFS_REMOTES.length());
×
340
            String remoteName = resolveDefaultGitRemoteName();
×
341
            if (remoteAndBranch.startsWith(remoteName)) {
×
342
                return remoteAndBranch.substring(remoteName.length() + 1);
×
343
            }
344
        }
345
        return "";
×
346
    }
347

348
    public static class TagComparator implements Comparator<Ref> {
×
349
        @Override
350
        public int compare(Ref tag1, Ref tag2) {
351
            String tagName1 = extractTagName(tag1);
×
352
            String tagName2 = extractTagName(tag2);
×
353
            return tagName1.compareTo(tagName2);
×
354
        }
355
    }
356
}
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