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

jreleaser / jreleaser / #538

25 Sep 2025 05:26PM UTC coverage: 47.589% (-0.7%) from 48.299%
#538

push

github

web-flow
fix(deploy): Skip GPG key verification when signing.verify is false

Fixes #1981

1 of 25 new or added lines in 1 file covered. (4.0%)

381 existing lines in 33 files now uncovered.

25450 of 53479 relevant lines covered (47.59%)

0.48 hits per line

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

20.86
/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, 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

359
        boolean isDraftBefore = release.isDraft();
×
360
        release = api.createRelease(github.getOwner(), github.getName(), release);
×
361
        api.uploadAssets(release, assets);
×
362

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

373
        linkDiscussion(tagName, release, api);
×
374
        updateIssues(github, api);
×
375

376
        context.getLogger().debug(RB.$("git.check.release.draft", github.getOwner(), github.getName(), release.getTagName()));
×
377
        GhRelease newRelease = api.findReleaseById(github.getOwner(), github.getName(), release.getId());
×
378
        boolean isDraftAfter = newRelease.isDraft();
×
379

380
        if (!isDraftBefore && isDraftAfter) {
×
381
            GhRelease updater = new GhRelease();
×
382
            updater.setPrerelease(release.isPrerelease());
×
383
            updater.setDraft(false);
×
384
            updater.setTagName(release.getTagName());
×
385
            api.updateRelease(github.getOwner(), github.getName(), release.getId(), updater);
×
386
        }
387
    }
×
388

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

392
        List<String> issueNumbers = ChangelogProvider.getIssues(context);
1✔
393

394
        if (!issueNumbers.isEmpty()) {
1✔
395
            context.getLogger().info(RB.$("git.issue.release.mark", issueNumbers.size()));
×
396
        }
397

398
        if (context.isDryrun()) {
1✔
399
            for (String issueNumber : issueNumbers) {
1✔
400
                context.getLogger().debug(RB.$("git.issue.release", issueNumber));
×
401
            }
×
402
            return;
1✔
403
        }
404

405
        String tagName = github.getEffectiveTagName(context.getModel());
×
406
        String labelName = github.getIssues().getLabel().getName();
×
407
        String labelColor = github.getIssues().getLabel().getColor();
×
408
        TemplateContext props = github.props(context.getModel());
×
409
        github.fillProps(props, context.getModel());
×
410
        String comment = resolveTemplate(github.getIssues().getComment(), props);
×
411
        if (labelColor.startsWith("#")) {
×
412
            labelColor = labelColor.substring(1);
×
413
        }
414

415
        GhLabel ghLabel = null;
×
416

417
        try {
418
            ghLabel = api.getOrCreateLabel(
×
419
                github.getOwner(),
×
420
                github.getName(),
×
421
                labelName,
422
                labelColor,
423
                github.getIssues().getLabel().getDescription());
×
424
        } catch (RestAPIException e) {
×
425
            throw new IllegalStateException(RB.$("ERROR_git_releaser_fetch_label", tagName, labelName), e);
×
426
        }
×
427

428
        Optional<GhMilestone> milestone = Optional.empty();
×
429
        Apply applyMilestone = github.getIssues().getApplyMilestone();
×
430
        if (github.getMilestone().isClose() && !context.getModel().getProject().isSnapshot()) {
×
431
            milestone = api.findMilestoneByName(
×
432
                github.getOwner(),
×
433
                github.getName(),
×
434
                github.getMilestone().getEffectiveName());
×
435

436
            if (!milestone.isPresent()) {
×
437
                milestone = api.findClosedMilestoneByName(
×
438
                    github.getOwner(),
×
439
                    github.getName(),
×
440
                    github.getMilestone().getEffectiveName());
×
441
            }
442
        }
443

444
        for (String issueNumber : issueNumbers) {
×
445
            Optional<GhIssue> op = api.findIssue(github.getOwner(), github.getName(), Integer.parseInt(issueNumber));
×
446
            if (!op.isPresent()) continue;
×
447

448
            GhIssue ghIssue = op.get();
×
449
            if ("closed".equals(ghIssue.getState()) && ghIssue.getLabels().stream().noneMatch(l -> l.getName().equals(labelName))) {
×
450
                context.getLogger().debug(RB.$("git.issue.release", issueNumber));
×
451
                try {
452
                    api.addLabelToIssue(github.getOwner(), github.getName(), ghIssue, ghLabel);
×
453
                    api.commentOnIssue(github.getOwner(), github.getName(), ghIssue, comment);
×
454

455
                    milestone.ifPresent(ghMilestone -> applyMilestone(github, api, issueNumber, ghIssue, applyMilestone, ghMilestone));
×
456
                } catch (RestAPIException e) {
×
457
                    if (e.isForbidden()) {
×
458
                        context.getLogger().warn(e.getMessage());
×
459
                    } else {
460
                        throw e;
×
461
                    }
462
                }
×
463
            }
464
        }
×
465
    }
×
466

467
    private void applyMilestone(org.jreleaser.model.internal.release.GithubReleaser github, Github api, String issueNumber, GhIssue ghIssue, Apply applyMilestone, GhMilestone targetMilestone) {
468
        GhMilestone issueMilestone = ghIssue.getMilestone();
×
469
        String targetMilestoneTitle = targetMilestone.getTitle();
×
470

471
        if (null == issueMilestone) {
×
472
            context.getLogger().debug(RB.$("git.issue.milestone.apply", targetMilestoneTitle, issueNumber));
×
473
            api.setMilestoneOnIssue(github.getOwner(), github.getName(), ghIssue, targetMilestone);
×
474
        } else {
475
            String milestoneTitle = issueMilestone.getTitle();
×
476

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

494
    private void updateAssets(Github api, GhRelease release) throws IOException {
495
        Set<Asset> assetsToBeUpdated = new TreeSet<>();
×
496
        Set<Asset> assetsToBeUploaded = new TreeSet<>();
×
497

498
        Map<String, GhAsset> existingAssets = api.listAssets(github.getOwner(), github.getName(), release);
×
499
        Map<String, Asset> assetsToBePublished = new LinkedHashMap<>();
×
500
        assets.forEach(asset -> assetsToBePublished.put(asset.getFilename(), asset));
×
501

502
        assetsToBePublished.keySet().forEach(name -> {
×
503
            if (existingAssets.containsKey(name)) {
×
504
                assetsToBeUpdated.add(assetsToBePublished.get(name));
×
505
            } else {
506
                assetsToBeUploaded.add(assetsToBePublished.get(name));
×
507
            }
508
        });
×
509

510
        api.updateAssets(github.getOwner(), github.getName(), release, assetsToBeUpdated, existingAssets);
×
511
        api.uploadAssets(release, assetsToBeUploaded);
×
512
    }
×
513

514
    private void linkDiscussion(String tagName, GhRelease release, Github api) {
515
        String discussionCategoryName = github.getDiscussionCategoryName();
×
516
        if (context.getModel().getProject().isSnapshot() ||
×
517
            isBlank(discussionCategoryName) ||
×
518
            github.isDraft()) return;
×
519

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

522
        if (context.isDryrun()) return;
×
523

524
        try {
525
            GhRelease ghRelease = new GhRelease();
×
526
            ghRelease.setDiscussionCategoryName(discussionCategoryName);
×
527
            api.updateRelease(github.getOwner(),
×
528
                github.getName(),
×
529
                tagName,
530
                release.getId(),
×
531
                ghRelease);
532
        } catch (RestAPIException e) {
×
533
            context.getLogger().trace(e);
×
534
            context.getLogger().warn(RB.$("git.releaser.link.discussion.error"),
×
535
                tagName, discussionCategoryName);
536
        }
×
537
    }
×
538

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