Skip to content

Commit dd117fe

Browse files
authored
Merge pull request #615 from Jericho/allow_milestone_without_issues
(#495) Allow milestones without issues
2 parents cfa3f16 + c4cdc00 commit dd117fe

File tree

12 files changed

+96
-29
lines changed

12 files changed

+96
-29
lines changed

docs/input/docs/configuration/default-configuration.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ create:
2121
sha-section-line-format: "- `{1}\t{0}`"
2222
allow-update-to-published: false
2323
include-contributors: false
24+
allow-milestone-without-issues: false
2425
export:
2526
include-created-date-in-title: false
2627
created-date-string-format: ''
@@ -142,6 +143,11 @@ control the look and feel of the generated release notes.
142143
in the release notes. A contributor is defined as someone who opened an issue
143144
or submitted a PR. **NOTE:** This configuration option was added in version
144145
0.19.0 of GitReleaseManager.
146+
- **allow-milestone-without-issues**
147+
- A boolean value which indicates whether an empty release will be created, when
148+
no issues are found to be associated with a milestone. The contents of the
149+
empty release can be controlled via the associated Scriban template.
150+
**NOTE:** This configuration option was added in version 0.20.0 of GitReleaseManager.
145151

146152
See the [example create configuration section](create-configuration) to see an
147153
example of how a footer can be configured.

src/GitReleaseManager.Core.Tests/VcsServiceTests.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using GitReleaseManager.Core.Model;
99
using GitReleaseManager.Core.Provider;
1010
using GitReleaseManager.Core.ReleaseNotes;
11-
using GitReleaseManager.Core.Templates;
1211
using NSubstitute;
1312
using NUnit.Framework;
1413
using Serilog;
@@ -303,7 +302,7 @@ public async Task Should_Create_Release_From_Milestone()
303302
{
304303
var release = new Release();
305304

306-
_releaseNotesBuilder.BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, ReleaseTemplates.DEFAULT_NAME)
305+
_releaseNotesBuilder.BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, null)
307306
.Returns(Task.FromResult(RELEASE_NOTES));
308307

309308
_vcsProvider.GetReleaseAsync(OWNER, REPOSITORY, MILESTONE_TITLE)
@@ -315,7 +314,7 @@ public async Task Should_Create_Release_From_Milestone()
315314
var result = await _vcsService.CreateReleaseFromMilestoneAsync(OWNER, REPOSITORY, MILESTONE_TITLE, MILESTONE_TITLE, null, null, false, null).ConfigureAwait(false);
316315
result.ShouldBeSameAs(release);
317316

318-
await _releaseNotesBuilder.Received(1).BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, ReleaseTemplates.DEFAULT_NAME).ConfigureAwait(false);
317+
await _releaseNotesBuilder.Received(1).BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, null).ConfigureAwait(false);
319318
await _vcsProvider.Received(1).GetReleaseAsync(OWNER, REPOSITORY, MILESTONE_TITLE).ConfigureAwait(false);
320319
await _vcsProvider.Received(1).CreateReleaseAsync(OWNER, REPOSITORY, Arg.Is<Release>(o =>
321320
o.Body == RELEASE_NOTES &&
@@ -333,7 +332,7 @@ public async Task Should_Create_Release_From_Milestone_With_Assets()
333332

334333
var assetsCount = _assets.Count;
335334

336-
_releaseNotesBuilder.BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, ReleaseTemplates.DEFAULT_NAME)
335+
_releaseNotesBuilder.BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, null)
337336
.Returns(Task.FromResult(RELEASE_NOTES));
338337

339338
_vcsProvider.GetReleaseAsync(OWNER, REPOSITORY, MILESTONE_TITLE)
@@ -353,7 +352,7 @@ public async Task Should_Create_Release_From_Milestone_With_Assets()
353352
null).ConfigureAwait(false);
354353
result.ShouldBeSameAs(release);
355354

356-
await _releaseNotesBuilder.Received(1).BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, ReleaseTemplates.DEFAULT_NAME).ConfigureAwait(false);
355+
await _releaseNotesBuilder.Received(1).BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, null).ConfigureAwait(false);
357356
await _vcsProvider.Received(1).GetReleaseAsync(OWNER, REPOSITORY, MILESTONE_TITLE).ConfigureAwait(false);
358357
await _vcsProvider.Received(1).CreateReleaseAsync(OWNER, REPOSITORY, Arg.Is<Release>(o =>
359358
o.Body == RELEASE_NOTES &&
@@ -430,7 +429,7 @@ public async Task Should_Update_Published_Release_On_Creating_Release_From_Miles
430429

431430
_configuration.Create.AllowUpdateToPublishedRelease = updatePublishedRelease;
432431

433-
_releaseNotesBuilder.BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, ReleaseTemplates.DEFAULT_NAME)
432+
_releaseNotesBuilder.BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, null)
434433
.Returns(Task.FromResult(RELEASE_NOTES));
435434

436435
_vcsProvider.GetReleaseAsync(OWNER, REPOSITORY, MILESTONE_TITLE)
@@ -442,7 +441,7 @@ public async Task Should_Update_Published_Release_On_Creating_Release_From_Miles
442441
var result = await _vcsService.CreateReleaseFromMilestoneAsync(OWNER, REPOSITORY, MILESTONE_TITLE, MILESTONE_TITLE, null, null, false, null).ConfigureAwait(false);
443442
result.ShouldBeSameAs(release);
444443

445-
await _releaseNotesBuilder.Received(1).BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, ReleaseTemplates.DEFAULT_NAME).ConfigureAwait(false);
444+
await _releaseNotesBuilder.Received(1).BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, null).ConfigureAwait(false);
446445
await _vcsProvider.Received(1).GetReleaseAsync(OWNER, REPOSITORY, MILESTONE_TITLE).ConfigureAwait(false);
447446
await _vcsProvider.Received(1).UpdateReleaseAsync(OWNER, REPOSITORY, release).ConfigureAwait(false);
448447

@@ -458,7 +457,7 @@ public async Task Should_Throw_Exception_While_Updating_Published_Release_On_Cre
458457

459458
_configuration.Create.AllowUpdateToPublishedRelease = false;
460459

461-
_releaseNotesBuilder.BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, ReleaseTemplates.DEFAULT_NAME)
460+
_releaseNotesBuilder.BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, null)
462461
.Returns(Task.FromResult(RELEASE_NOTES));
463462

464463
_vcsProvider.GetReleaseAsync(OWNER, REPOSITORY, MILESTONE_TITLE)
@@ -467,7 +466,7 @@ public async Task Should_Throw_Exception_While_Updating_Published_Release_On_Cre
467466
var ex = await Should.ThrowAsync<InvalidOperationException>(() => _vcsService.CreateReleaseFromMilestoneAsync(OWNER, REPOSITORY, MILESTONE_TITLE, MILESTONE_TITLE, null, null, false, null)).ConfigureAwait(false);
468467
ex.Message.ShouldBe($"Release with tag '{MILESTONE_TITLE}' not in draft state, so not updating");
469468

470-
await _releaseNotesBuilder.Received(1).BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, ReleaseTemplates.DEFAULT_NAME).ConfigureAwait(false);
469+
await _releaseNotesBuilder.Received(1).BuildReleaseNotesAsync(OWNER, REPOSITORY, MILESTONE_TITLE, null).ConfigureAwait(false);
471470
await _vcsProvider.Received(1).GetReleaseAsync(OWNER, REPOSITORY, MILESTONE_TITLE).ConfigureAwait(false);
472471
}
473472

src/GitReleaseManager.Core/Configuration/Config.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public Config()
2727
ShaSectionHeading = "SHA256 Hashes of the release artifacts",
2828
ShaSectionLineFormat = "- `{1}\t{0}`",
2929
AllowUpdateToPublishedRelease = false,
30+
AllowMilestonesWithoutIssues = false,
3031
IncludeContributors = false,
3132
};
3233

src/GitReleaseManager.Core/Configuration/CreateConfig.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ public class CreateConfig
3535
[YamlMember(Alias = "allow-update-to-published")]
3636
public bool AllowUpdateToPublishedRelease { get; set; }
3737

38+
[YamlMember(Alias = "allow-milestone-without-issues")]
39+
public bool AllowMilestonesWithoutIssues { get; set; }
40+
3841
[YamlMember(Alias = "include-contributors")]
3942
public bool IncludeContributors { get; set; }
4043
}

src/GitReleaseManager.Core/ReleaseNotes/ReleaseNotesBuilder.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public ReleaseNotesBuilder(IVcsProvider vcsProvider, ILogger logger, IFileSystem
3535
_templateFactory = templateFactory;
3636
}
3737

38-
public async Task<string> BuildReleaseNotesAsync(string user, string repository, string milestoneTitle, string template)
38+
public async Task<string> BuildReleaseNotesAsync(string user, string repository, string milestoneTitle, string customTemplate)
3939
{
4040
_user = user;
4141
_repository = repository;
@@ -58,12 +58,31 @@ public async Task<string> BuildReleaseNotesAsync(string user, string repository,
5858

5959
var numberOfCommits = await _vcsProvider.GetCommitsCountAsync(_user, _repository, @base, head).ConfigureAwait(false);
6060

61-
if (issues.Count == 0)
61+
if (issues.Count == 0 && !_configuration.Create.AllowMilestonesWithoutIssues)
6262
{
6363
var logMessage = string.Format(CultureInfo.CurrentCulture, "No closed issues have been found for milestone {0}, or all assigned issues are meant to be excluded from release notes, aborting release creation.", _milestoneTitle);
6464
throw new InvalidOperationException(logMessage);
6565
}
6666

67+
// By default we use the custom template, if it was provided.
68+
// Otherwise, we determine which template we should use.
69+
var template = customTemplate;
70+
if (string.IsNullOrWhiteSpace(template))
71+
{
72+
if (issues.Count == 0)
73+
{
74+
template = ReleaseTemplates.NO_ISSUES_NAME;
75+
}
76+
else if (_configuration.Create.IncludeContributors)
77+
{
78+
template = ReleaseTemplates.CONTRIBUTORS_NAME;
79+
}
80+
else
81+
{
82+
template = ReleaseTemplates.DEFAULT_NAME;
83+
}
84+
}
85+
6786
var commitsLink = _vcsProvider.GetCommitsUrl(_user, _repository, _targetMilestone?.Title, previousMilestone?.Title);
6887

6988
var issuesDict = GetIssuesDict(issues);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{{ if config.create.include_footer }}
2+
3+
### {{ config.create.footer_heading }}
4+
5+
{{ if config.create.milestone_replace_text
6+
replace_milestone_title config.create.footer_content config.create.milestone_replace_text milestone.target.title
7+
else
8+
config.create.footer_content
9+
end
10+
end }}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{{-
2+
include 'release-info'
3+
if milestone.target.description
4+
include 'milestone'
5+
end
6+
include 'issues' | string.rstrip
7+
if template_kind == "CREATE"
8+
include 'create/footer'
9+
end
10+
~}}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
This release had no issues associated with it.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
{{ milestone.target.description }}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{{
2+
if commits.count > 0
3+
}}As part of this release we had [{{ commits.count }} {{ commits.count | string.pluralize "commit" "commits" }}]({{ commits.html_url }}).
4+
{{ end -}}

0 commit comments

Comments
 (0)