• 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

20.66
/sdks/jreleaser-github-java-sdk/src/main/java/org/jreleaser/sdk/github/GithubReleaser.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.github;
19

20
import org.eclipse.jgit.api.Git;
21
import org.eclipse.jgit.api.errors.GitAPIException;
22
import org.jreleaser.bundle.RB;
23
import org.jreleaser.model.JReleaserException;
24
import org.jreleaser.model.UpdateSection;
25
import org.jreleaser.model.api.common.Apply;
26
import org.jreleaser.model.api.common.ExtraProperties;
27
import org.jreleaser.model.internal.JReleaserContext;
28
import org.jreleaser.model.internal.util.VersionUtils;
29
import org.jreleaser.model.spi.release.Asset;
30
import org.jreleaser.model.spi.release.Release;
31
import org.jreleaser.model.spi.release.ReleaseException;
32
import org.jreleaser.model.spi.release.Repository;
33
import org.jreleaser.model.spi.release.User;
34
import org.jreleaser.mustache.TemplateContext;
35
import org.jreleaser.sdk.commons.RestAPIException;
36
import org.jreleaser.sdk.git.ChangelogGenerator;
37
import org.jreleaser.sdk.git.ChangelogProvider;
38
import org.jreleaser.sdk.git.GitSdk;
39
import org.jreleaser.sdk.git.release.AbstractReleaser;
40
import org.jreleaser.sdk.github.api.GhAsset;
41
import org.jreleaser.sdk.github.api.GhIssue;
42
import org.jreleaser.sdk.github.api.GhLabel;
43
import org.jreleaser.sdk.github.api.GhMilestone;
44
import org.jreleaser.sdk.github.api.GhRelease;
45
import org.jreleaser.sdk.github.api.GhReleaseNotes;
46
import org.jreleaser.sdk.github.api.GhReleaseNotesParams;
47
import org.jreleaser.sdk.github.api.GhRepository;
48

49
import java.io.IOException;
50
import java.nio.file.Files;
51
import java.nio.file.Path;
52
import java.util.LinkedHashMap;
53
import java.util.List;
54
import java.util.Map;
55
import java.util.Optional;
56
import java.util.Set;
57
import java.util.TreeSet;
58
import java.util.regex.Pattern;
59

60
import static java.nio.charset.StandardCharsets.UTF_8;
61
import static org.jreleaser.mustache.Templates.resolveTemplate;
62
import static org.jreleaser.sdk.git.ChangelogProvider.extractIssues;
63
import static org.jreleaser.sdk.git.ChangelogProvider.storeIssues;
64
import static org.jreleaser.sdk.git.GitSdk.extractTagName;
65
import static org.jreleaser.util.StringUtils.isBlank;
66
import static org.jreleaser.util.StringUtils.uncapitalize;
67

68
/**
69
 * @author Andres Almiray
70
 * @since 0.1.0
71
 */
72
public class GithubReleaser extends AbstractReleaser<org.jreleaser.model.api.release.GithubReleaser> {
73
    private static final long serialVersionUID = -825713164586669508L;
74

75
    private static final String NOREPLY_GITHUB_COM_EMAIL = "noreply@github.com";
76

77
    private final org.jreleaser.model.internal.release.GithubReleaser github;
78

79
    public GithubReleaser(JReleaserContext context, Set<Asset> assets) {
80
        super(context, assets);
1✔
81
        github = context.getModel().getRelease().getGithub();
1✔
82
    }
1✔
83

84
    @Override
85
    public org.jreleaser.model.api.release.GithubReleaser getReleaser() {
86
        return github.asImmutable();
×
87
    }
88

89
    @Override
90
    public String generateReleaseNotes() throws IOException {
91
        if (github.getReleaseNotes().isEnabled()) {
1✔
92
            String content = generateReleaseNotesByAPI();
×
93

94
            if (github.getIssues().isEnabled()) {
×
95
                context.getLogger().info(RB.$("issues.generator.extract"));
×
96
                Set<Integer> issues = extractIssues(context, content);
×
97
                storeIssues(context, issues);
×
98
            }
99

100
            return ChangelogProvider.storeChangelog(context, content);
×
101
        }
102

103
        try {
104
            return ChangelogProvider.getChangelog(context).trim();
1✔
105
        } catch (IOException e) {
×
106
            throw new JReleaserException(RB.$("ERROR_unexpected_error_changelog"), e);
×
107
        }
108
    }
109

110
    private String generateReleaseNotesByAPI() throws JReleaserException {
111
        org.jreleaser.model.internal.release.GithubReleaser github = context.getModel().getRelease().getGithub();
×
112
        String tagName = github.getEffectiveTagName(context.getModel());
×
113

114
        try {
115
            Git git = GitSdk.of(context).open();
×
116
            ChangelogGenerator.Tags tags = new ChangelogGenerator().resolveTags(git, context);
×
117
            GhReleaseNotesParams params = new GhReleaseNotesParams();
×
118
            params.setTagName(tagName);
×
119
            if (!isTagInRemote(context, tagName)) {
×
120
                params.setTagName("HEAD");
×
121
            }
122
            if (tags.getPrevious().isPresent()) {
×
123
                params.setPreviousTagName(extractTagName(tags.getPrevious().get()));
×
124
            }
125
            params.setTargetCommitish(github.getBranch());
×
126
            GhReleaseNotes releaseNotes = new Github(context.asImmutable(),
×
127
                github.getApiEndpoint(),
×
128
                github.getToken(),
×
129
                github.getConnectTimeout(),
×
130
                github.getReadTimeout())
×
131
                .generateReleaseNotes(github.getOwner(), github.getName(), params);
×
132
            return releaseNotes.getBody().replace("...HEAD", "..." + tagName);
×
133
        } catch (IOException | GitAPIException e) {
×
134
            throw new JReleaserException(RB.$("ERROR_unexpected_error_changelog"), e);
×
135
        }
136
    }
137

138
    protected boolean isTagInRemote(JReleaserContext context, String tagName) {
139
        org.jreleaser.model.internal.release.GithubReleaser github = context.getModel().getRelease().getGithub();
×
140

141
        Github api = new Github(context.asImmutable(),
×
142
            github.getApiEndpoint(),
×
143
            github.getToken(),
×
144
            github.getConnectTimeout(),
×
145
            github.getReadTimeout());
×
146
        GhRepository repository = api.findRepository(github.getOwner(), github.getName());
×
147
        if (null == repository) {
×
148
            // remote does not exist!
149
            throw new IllegalStateException(RB.$("ERROR_git_repository_not_exists", github.getCanonicalRepoName()));
×
150
        }
151

152
        return api.listTags(github.getOwner(), github.getName()).stream()
×
153
            .anyMatch(tag -> tag.getName().equals(tagName));
×
154
    }
155

156
    @Override
157
    protected void createRelease() throws ReleaseException {
158
        String pullBranch = github.getBranch();
1✔
159
        String pushBranch = github.getResolvedBranchPush(context.getModel());
1✔
160
        boolean mustCheckoutBranch = !pushBranch.equals(pullBranch);
1✔
161

162
        context.getLogger().info(RB.$("git.releaser.releasing"), github.getResolvedRepoUrl(context.getModel()), pushBranch);
1✔
163
        String tagName = github.getEffectiveTagName(context.getModel());
1✔
164

165
        try {
166
            Github api = new Github(context.asImmutable(),
1✔
167
                github.getApiEndpoint(),
1✔
168
                github.getToken(),
1✔
169
                github.getConnectTimeout(),
1✔
170
                github.getReadTimeout());
1✔
171

172
            if (!context.isDryrun()) {
1✔
173
                List<String> branchNames = api.listBranches(github.getOwner(), github.getName());
×
174
                GitSdk.of(context).checkoutBranch(github, pushBranch, mustCheckoutBranch, !branchNames.contains(pushBranch));
×
175
            }
176

177
            String changelog = normalizeChangelog(context.getChangelog().getResolvedChangelog());
1✔
178

179
            context.getLogger().debug(RB.$("git.releaser.release.lookup"), tagName, github.getCanonicalRepoName());
1✔
180
            GhRelease release = findReleaseByTag(api, tagName);
1✔
181
            boolean snapshot = context.getModel().getProject().isSnapshot();
1✔
182
            if (null != release) {
1✔
183
                context.getLogger().debug(RB.$("git.releaser.release.exists"), tagName);
×
184
                if (github.isOverwrite() || snapshot) {
×
185
                    context.getLogger().debug(RB.$("git.releaser.release.delete"), tagName);
×
186
                    if (!context.isDryrun()) {
×
187
                        api.deleteRelease(github.getOwner(), github.getName(), tagName, release.getId());
×
188
                    }
189
                    context.getLogger().debug(RB.$("git.releaser.release.create"), tagName);
×
190
                    createRelease(api, tagName, changelog, snapshot && github.isMatch());
×
191
                } else if (github.getUpdate().isEnabled()) {
×
192
                    context.getLogger().debug(RB.$("git.releaser.release.update"), tagName);
×
193
                    if (!context.isDryrun()) {
×
194
                        GhRelease updater = new GhRelease();
×
195
                        if (github.getPrerelease().isEnabledSet()) {
×
196
                            updater.setPrerelease(github.getPrerelease().isEnabled());
×
197
                        }
198
                        if (github.isDraftSet()) {
×
199
                            updater.setDraft(github.isDraft());
×
200
                        }
201
                        if (github.getUpdate().getSections().contains(UpdateSection.TITLE)) {
×
202
                            context.getLogger().info(RB.$("git.releaser.release.update.title"), github.getEffectiveReleaseName());
×
203
                            updater.setName(github.getEffectiveReleaseName());
×
204
                        }
205
                        if (github.getUpdate().getSections().contains(UpdateSection.BODY)) {
×
206
                            context.getLogger().info(RB.$("git.releaser.release.update.body"));
×
207
                            updater.setBody(changelog);
×
208
                        }
209
                        api.updateRelease(github.getOwner(), github.getName(), release.getId(), updater);
×
210

211
                        if (github.getUpdate().getSections().contains(UpdateSection.ASSETS)) {
×
212
                            updateAssets(api, release);
×
213
                        }
214
                        linkDiscussion(tagName, release, api);
×
215
                        updateIssues(github, api);
×
216
                    }
×
217
                } else {
218
                    if (context.isDryrun()) {
×
219
                        context.getLogger().debug(RB.$("git.releaser.release.create"), tagName);
×
220
                        createRelease(api, tagName, changelog, false);
×
221
                        return;
×
222
                    }
223

224
                    throw new IllegalStateException(RB.$("ERROR_git_releaser_cannot_release",
×
225
                        "GitHub", tagName));
226
                }
227
            } else {
228
                context.getLogger().debug(RB.$("git.releaser.release.not.found"), tagName);
1✔
229
                context.getLogger().debug(RB.$("git.releaser.release.create"), tagName);
1✔
230
                createRelease(api, tagName, changelog, snapshot && github.isMatch());
1✔
231
            }
232
        } catch (RestAPIException | IOException | IllegalStateException e) {
×
233
            context.getLogger().trace(e);
×
234
            throw new ReleaseException(e);
×
235
        }
1✔
236
    }
1✔
237

238
    private String normalizeChangelog(String changelog) throws ReleaseException {
239
        if (changelog.length() > 10_000) {
1✔
240
            try {
241
                Path tmp = Files.createTempDirectory("jreleaser");
×
242
                Path releaseMd = tmp.resolve("RELEASE.md");
×
243
                Files.write(releaseMd, changelog.getBytes(UTF_8));
×
244
                assets.add(Asset.file(releaseMd));
×
245
            } catch (IOException e) {
×
246
                throw new ReleaseException(e);
×
247
            }
×
248
            context.getLogger().warn(RB.$("github.release.changelog.trimmed"));
×
249
            return changelog.substring(0, 9995) + " ...";
×
250
        }
251
        return changelog;
1✔
252
    }
253

254
    private GhRelease findReleaseByTag(Github api, String tagName) {
255
        if (context.isDryrun()) return null;
1✔
256
        return api.findReleaseByTag(github.getOwner(), github.getName(), tagName);
×
257
    }
258

259
    @Override
260
    public Repository maybeCreateRepository(String owner, String repo, String password, ExtraProperties extraProperties) throws IOException {
261
        context.getLogger().debug(RB.$("git.repository.lookup"), owner, repo);
1✔
262

263
        Github api = new Github(context.asImmutable(),
1✔
264
            github.getApiEndpoint(),
1✔
265
            password,
266
            github.getConnectTimeout(),
1✔
267
            github.getReadTimeout());
1✔
268
        GhRepository repository = api.findRepository(owner, repo);
1✔
269
        if (null == repository) {
1✔
270
            repository = api.createRepository(owner, repo);
×
271
        }
272

273
        return new Repository(
1✔
274
            Repository.Kind.GITHUB,
275
            owner,
276
            repo,
277
            repository.getHtmlUrl(),
1✔
278
            repository.getCloneUrl());
1✔
279
    }
280

281
    @Override
282
    public Optional<User> findUser(String email, String name) {
UNCOV
283
        if (NOREPLY_GITHUB_COM_EMAIL.equals(email)) return Optional.empty();
×
284

285
        try {
UNCOV
286
            return new Github(context.asImmutable(),
×
UNCOV
287
                github.getApiEndpoint(),
×
UNCOV
288
                github.getToken(),
×
UNCOV
289
                github.getConnectTimeout(),
×
UNCOV
290
                github.getReadTimeout())
×
UNCOV
291
                .findUser(email, name);
×
292
        } catch (RestAPIException e) {
×
293
            context.getLogger().trace(e);
×
294
            context.getLogger().debug(RB.$("git.releaser.user.not.found"), email);
×
295
        }
296

297
        return Optional.empty();
×
298
    }
299

300
    @Override
301
    public List<Release> listReleases(String owner, String repo) throws IOException {
302
        Github api = new Github(context.asImmutable(),
1✔
303
            github.getApiEndpoint(),
1✔
304
            github.getToken(),
1✔
305
            github.getConnectTimeout(),
1✔
306
            github.getReadTimeout());
1✔
307

308
        List<Release> releases = api.listReleases(owner, repo);
1✔
309

310
        VersionUtils.clearUnparseableTags();
1✔
311
        Pattern versionPattern = VersionUtils.resolveVersionPattern(context);
1✔
312
        for (Release release : releases) {
1✔
313
            release.setVersion(VersionUtils.version(context, release.getTagName(), versionPattern));
×
314
        }
×
315

316
        releases.sort((r1, r2) -> r2.getVersion().compareTo(r1.getVersion()));
1✔
317

318
        return releases;
1✔
319
    }
320

321
    private void createRelease(Github api, String tagName, String changelog, boolean deleteTags) throws IOException {
322
        if (context.isDryrun()) {
1✔
323
            for (Asset asset : assets) {
1✔
324
                if (0 == Files.size(asset.getPath()) || !Files.exists(asset.getPath())) {
1✔
325
                    // do not upload empty or non existent files
326
                    continue;
×
327
                }
328

329
                context.getLogger().info(" " + RB.$("git.upload.asset"), asset.getFilename());
1✔
330
            }
1✔
331
            updateIssues(github, api);
1✔
332
            return;
1✔
333
        }
334

335
        if (deleteTags) {
×
336
            deleteTags(api, github.getOwner(), github.getName(), tagName);
×
337
        }
338

339
        // local tag
340
        if (deleteTags || !github.isSkipTag()) {
×
341
            context.getLogger().debug(RB.$("git.releaser.repository.tag"), tagName, context.getModel().getCommit().getShortHash());
×
342
            GitSdk.of(context).tag(tagName, true, context);
×
343
        }
344

345
        // remote tag/release
346
        GhRelease release = new GhRelease();
×
347
        release.setName(github.getEffectiveReleaseName());
×
348
        release.setTagName(tagName);
×
349
        release.setTargetCommitish(github.getResolvedBranchPush(context.getModel()));
×
350
        release.setBody(changelog);
×
351
        release.setMakeLatest(github.getMakeLatest().formatted());
×
352
        if (github.getPrerelease().isEnabledSet()) {
×
353
            release.setPrerelease(github.getPrerelease().isEnabled());
×
354
        }
355
        if (github.isDraftSet()) {
×
356
            release.setDraft(github.isDraft());
×
357
        }
358
        if (github.isImmutableReleaseSet()) {
×
359
            // If immutable, create as draft first, upload assets, then publish
360
            release.setDraft(github.isDraft() || github.isImmutableRelease());
×
361
        }
362

363
        boolean isDraftBefore = release.isDraft();
×
364
        release = api.createRelease(github.getOwner(), github.getName(), release);
×
365
        api.uploadAssets(release, assets);
×
366

367
        if (github.getMilestone().isClose() && !context.getModel().getProject().isSnapshot()) {
×
368
            Optional<GhMilestone> milestone = api.findMilestoneByName(
×
369
                github.getOwner(),
×
370
                github.getName(),
×
371
                github.getMilestone().getEffectiveName());
×
372
            milestone.ifPresent(gtMilestone -> api.closeMilestone(github.getOwner(),
×
373
                github.getName(),
×
374
                gtMilestone));
375
        }
376

377
        linkDiscussion(tagName, release, api);
×
378
        updateIssues(github, api);
×
379

380
        context.getLogger().debug(RB.$("git.check.release.draft", github.getOwner(), github.getName(), release.getTagName()));
×
381
        GhRelease newRelease = api.findReleaseById(github.getOwner(), github.getName(), release.getId());
×
382
        boolean isDraftAfter = newRelease.isDraft();
×
383

384
        boolean shouldUndraftImmutableRelease = github.isImmutableReleaseSet() && github.isImmutableRelease() && !(github.isDraftSet() && github.isDraft());
×
385

386
        if (!isDraftBefore && isDraftAfter || shouldUndraftImmutableRelease) {
×
387
            GhRelease updater = new GhRelease();
×
388
            updater.setPrerelease(release.isPrerelease());
×
389
            updater.setDraft(false);
×
390
            updater.setTagName(release.getTagName());
×
391
            api.updateRelease(github.getOwner(), github.getName(), release.getId(), updater);
×
392
        }
393
    }
×
394

395
    private void updateIssues(org.jreleaser.model.internal.release.GithubReleaser github, Github api) throws IOException {
396
        if (!github.getIssues().isEnabled()) return;
1✔
397

398
        List<String> issueNumbers = ChangelogProvider.getIssues(context);
1✔
399

400
        if (!issueNumbers.isEmpty()) {
1✔
401
            context.getLogger().info(RB.$("git.issue.release.mark", issueNumbers.size()));
×
402
        }
403

404
        if (context.isDryrun()) {
1✔
405
            for (String issueNumber : issueNumbers) {
1✔
406
                context.getLogger().debug(RB.$("git.issue.release", issueNumber));
×
407
            }
×
408
            return;
1✔
409
        }
410

411
        String tagName = github.getEffectiveTagName(context.getModel());
×
412
        String labelName = github.getIssues().getLabel().getName();
×
413
        String labelColor = github.getIssues().getLabel().getColor();
×
414
        TemplateContext props = github.props(context.getModel());
×
415
        github.fillProps(props, context.getModel());
×
416
        String comment = resolveTemplate(github.getIssues().getComment(), props);
×
417
        if (labelColor.startsWith("#")) {
×
418
            labelColor = labelColor.substring(1);
×
419
        }
420

421
        GhLabel ghLabel = null;
×
422

423
        try {
424
            ghLabel = api.getOrCreateLabel(
×
425
                github.getOwner(),
×
426
                github.getName(),
×
427
                labelName,
428
                labelColor,
429
                github.getIssues().getLabel().getDescription());
×
430
        } catch (RestAPIException e) {
×
431
            throw new IllegalStateException(RB.$("ERROR_git_releaser_fetch_label", tagName, labelName), e);
×
432
        }
×
433

434
        Optional<GhMilestone> milestone = Optional.empty();
×
435
        Apply applyMilestone = github.getIssues().getApplyMilestone();
×
436
        if (github.getMilestone().isClose() && !context.getModel().getProject().isSnapshot()) {
×
437
            milestone = api.findMilestoneByName(
×
438
                github.getOwner(),
×
439
                github.getName(),
×
440
                github.getMilestone().getEffectiveName());
×
441

442
            if (!milestone.isPresent()) {
×
443
                milestone = api.findClosedMilestoneByName(
×
444
                    github.getOwner(),
×
445
                    github.getName(),
×
446
                    github.getMilestone().getEffectiveName());
×
447
            }
448
        }
449

450
        for (String issueNumber : issueNumbers) {
×
451
            Optional<GhIssue> op = api.findIssue(github.getOwner(), github.getName(), Integer.parseInt(issueNumber));
×
452
            if (!op.isPresent()) continue;
×
453

454
            GhIssue ghIssue = op.get();
×
455
            if ("closed".equals(ghIssue.getState()) && ghIssue.getLabels().stream().noneMatch(l -> l.getName().equals(labelName))) {
×
456
                context.getLogger().debug(RB.$("git.issue.release", issueNumber));
×
457
                try {
458
                    api.addLabelToIssue(github.getOwner(), github.getName(), ghIssue, ghLabel);
×
459
                    api.commentOnIssue(github.getOwner(), github.getName(), ghIssue, comment);
×
460

461
                    milestone.ifPresent(ghMilestone -> applyMilestone(github, api, issueNumber, ghIssue, applyMilestone, ghMilestone));
×
462
                } catch (RestAPIException e) {
×
463
                    if (e.isForbidden()) {
×
464
                        context.getLogger().warn(e.getMessage());
×
465
                    } else {
466
                        throw e;
×
467
                    }
468
                }
×
469
            }
470
        }
×
471
    }
×
472

473
    private void applyMilestone(org.jreleaser.model.internal.release.GithubReleaser github, Github api, String issueNumber, GhIssue ghIssue, Apply applyMilestone, GhMilestone targetMilestone) {
474
        GhMilestone issueMilestone = ghIssue.getMilestone();
×
475
        String targetMilestoneTitle = targetMilestone.getTitle();
×
476

477
        if (null == issueMilestone) {
×
478
            context.getLogger().debug(RB.$("git.issue.milestone.apply", targetMilestoneTitle, issueNumber));
×
479
            api.setMilestoneOnIssue(github.getOwner(), github.getName(), ghIssue, targetMilestone);
×
480
        } else {
481
            String milestoneTitle = issueMilestone.getTitle();
×
482

483
            if (applyMilestone == Apply.ALWAYS) {
×
484
                context.getLogger().debug(uncapitalize(RB.$("git.issue.milestone.warn", issueNumber, milestoneTitle)));
×
485
            } else if (applyMilestone == Apply.WARN) {
×
486
                if (!milestoneTitle.equals(targetMilestoneTitle)) {
×
487
                    context.getLogger().warn(RB.$("git.issue.milestone.warn", issueNumber, milestoneTitle));
×
488
                }
489
            } else if (applyMilestone == Apply.FORCE) {
×
490
                if (!milestoneTitle.equals(targetMilestoneTitle)) {
×
491
                    context.getLogger().warn(RB.$("git.issue.milestone.force", targetMilestoneTitle, issueNumber, milestoneTitle));
×
492
                    api.setMilestoneOnIssue(github.getOwner(), github.getName(), ghIssue, targetMilestone);
×
493
                } else {
494
                    context.getLogger().debug(uncapitalize(RB.$("git.issue.milestone.warn", issueNumber, milestoneTitle)));
×
495
                }
496
            }
497
        }
498
    }
×
499

500
    private void updateAssets(Github api, GhRelease release) throws IOException {
501
        Set<Asset> assetsToBeUpdated = new TreeSet<>();
×
502
        Set<Asset> assetsToBeUploaded = new TreeSet<>();
×
503

504
        Map<String, GhAsset> existingAssets = api.listAssets(github.getOwner(), github.getName(), release);
×
505
        Map<String, Asset> assetsToBePublished = new LinkedHashMap<>();
×
506
        assets.forEach(asset -> assetsToBePublished.put(asset.getFilename(), asset));
×
507

508
        assetsToBePublished.keySet().forEach(name -> {
×
509
            if (existingAssets.containsKey(name)) {
×
510
                assetsToBeUpdated.add(assetsToBePublished.get(name));
×
511
            } else {
512
                assetsToBeUploaded.add(assetsToBePublished.get(name));
×
513
            }
514
        });
×
515

516
        api.updateAssets(github.getOwner(), github.getName(), release, assetsToBeUpdated, existingAssets);
×
517
        api.uploadAssets(release, assetsToBeUploaded);
×
518
    }
×
519

520
    private void linkDiscussion(String tagName, GhRelease release, Github api) {
521
        String discussionCategoryName = github.getDiscussionCategoryName();
×
522
        if (context.getModel().getProject().isSnapshot() ||
×
523
            isBlank(discussionCategoryName) ||
×
524
            github.isDraft()) return;
×
525

526
        context.getLogger().debug(RB.$("git.releaser.link.discussion"), tagName, discussionCategoryName);
×
527

528
        if (context.isDryrun()) return;
×
529

530
        try {
531
            GhRelease ghRelease = new GhRelease();
×
532
            ghRelease.setDiscussionCategoryName(discussionCategoryName);
×
533
            api.updateRelease(github.getOwner(),
×
534
                github.getName(),
×
535
                tagName,
536
                release.getId(),
×
537
                ghRelease);
538
        } catch (RestAPIException e) {
×
539
            context.getLogger().trace(e);
×
540
            context.getLogger().warn(RB.$("git.releaser.link.discussion.error"),
×
541
                tagName, discussionCategoryName);
542
        }
×
543
    }
×
544

545
    private void deleteTags(Github api, String owner, String repo, String tagName) {
546
        // delete remote tag
547
        try {
548
            api.deleteTag(owner, repo, tagName);
×
549
        } catch (RestAPIException e) {
×
550
            if (e.isUnprocessableEntity()) {
×
551
                // OK, tag does not exist on remote
552
                return;
×
553
            }
554
            throw e;
×
555
        }
×
556
    }
×
557
}
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