Skip to content
This repository was archived by the owner on Sep 20, 2022. It is now read-only.

Commit 8ffca6d

Browse files
committed
Merge pull request #10 from JakeGinnivan/MultipleReleases
Support for multiple releases (#8)
2 parents 0ec1725 + b0d51cf commit 8ffca6d

17 files changed

+373
-70
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Linq;
5+
using System.Security.Cryptography;
6+
using System.Text;
7+
using GitReleaseNotes.Git;
8+
using LibGit2Sharp;
9+
using NSubstitute;
10+
using Xunit;
11+
12+
namespace GitReleaseNotes.Tests
13+
{
14+
public class CommitGrouperTests
15+
{
16+
private readonly Dictionary<Commit, string> _tags;
17+
private readonly IRepository _repository;
18+
private readonly CommitGrouper _sut;
19+
private readonly Random _random;
20+
private DateTimeOffset _nextCommitDate;
21+
22+
public CommitGrouperTests()
23+
{
24+
_nextCommitDate = DateTimeOffset.Now;
25+
_repository = Substitute.For<IRepository>();
26+
_tags = new Dictionary<Commit, string>();
27+
var tagCollection = Substitute.For<TagCollection>();
28+
tagCollection.GetEnumerator().Returns(c => _tags.Select(p =>
29+
{
30+
var tag = Substitute.For<Tag>();
31+
tag.Target.Returns(p.Key);
32+
tag.Name.Returns(p.Value);
33+
return tag;
34+
}).GetEnumerator());
35+
_repository.Tags.Returns(tagCollection);
36+
_random = new Random();
37+
_sut = new CommitGrouper();
38+
}
39+
40+
[Fact]
41+
public void DoesNotIncludeCommitsOlderThanTag()
42+
{
43+
var commit1 = CreateCommit();
44+
var startTagCommit = CreateCommit();
45+
var commit3 = CreateCommit();
46+
SubstituteCommitLog(commit1, startTagCommit, commit3);
47+
var startTag = new TaggedCommit(startTagCommit, "1.0.0");
48+
49+
var results = _sut.GetCommitsByRelease(_repository, startTag);
50+
51+
Assert.Equal(1, results.First().Value.Count);
52+
}
53+
54+
[Fact]
55+
public void GroupsTagsByReleases()
56+
{
57+
var commit1 = CreateCommit();
58+
var commit2 = CreateCommit();
59+
var commit3 = CreateCommit();
60+
var startTagCommit = CreateCommit();
61+
SubstituteCommitLog(commit1, commit2, commit3, startTagCommit);
62+
_tags.Add(commit2, "1.1.0");
63+
var startTag = new TaggedCommit(startTagCommit, "1.0.0");
64+
65+
var results = _sut.GetCommitsByRelease(_repository, startTag);
66+
67+
Assert.Equal(2, results.Count);
68+
Assert.Equal(null, results.ElementAt(0).Key.Name);
69+
Assert.Equal(1, results.ElementAt(0).Value.Count);
70+
Assert.Equal("1.1.0", results.ElementAt(1).Key.Name);
71+
Assert.Equal(2, results.ElementAt(1).Value.Count);
72+
Assert.Equal(commit2, results.ElementAt(1).Value.ElementAt(0));
73+
}
74+
75+
private Commit CreateCommit()
76+
{
77+
var commit = Substitute.For<Commit>();
78+
commit.Author.Returns(new Signature("Some Dude", "some@dude.com", _nextCommitDate));
79+
_nextCommitDate = _nextCommitDate.AddHours(-1);
80+
var random = _random.Next().ToString(CultureInfo.InvariantCulture);
81+
var randomSha1 = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(random));
82+
commit.Id.Returns(new ObjectId(randomSha1));
83+
commit.Sha.Returns(BitConverter.ToString(randomSha1).Replace("-", string.Empty));
84+
return commit;
85+
}
86+
87+
private void SubstituteCommitLog(params Commit[] commits)
88+
{
89+
var commitLog = Substitute.For<IQueryableCommitLog>();
90+
var returnThis = commits.AsEnumerable().GetEnumerator();
91+
commitLog.GetEnumerator().Returns(returnThis);
92+
_repository.Commits.Returns(commitLog);
93+
}
94+
}
95+
}

src/GitReleaseNotes.Tests/GitReleaseNotes.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
</ItemGroup>
6363
<ItemGroup>
6464
<Compile Include="ArgumentTests.cs" />
65+
<Compile Include="CommitGrouperTests.cs" />
6566
<Compile Include="IssueTrackers\GitHub\GitHubIssueTrackerTests.cs" />
6667
<Compile Include="IssueTrackers\IssueNumberExtractor.cs" />
6768
<Compile Include="Properties\AssemblyInfo.cs" />

src/GitReleaseNotes.Tests/IssueTrackers/GitHub/GitHubIssueTrackerTests.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ public GitHubIssueTrackerTests()
3333
public void CreatesReleaseNotesForClosedGitHubIssues()
3434
{
3535
var commit = CreateCommit("Fixes #1", DateTimeOffset.Now.AddDays(-1));
36-
var commitsToScan = new[] { commit };
36+
var commitsToScan = new List<Commit> { commit };
37+
var toScan = new Dictionary<ReleaseInfo, List<Commit>>
38+
{
39+
{new ReleaseInfo(), commitsToScan}
40+
};
3741
_issuesClient
3842
.GetForRepository("Org", "Repo", Arg.Any<RepositoryIssueRequest>())
3943
.Returns(Task.FromResult<IReadOnlyList<Issue>>(new List<Issue>
@@ -46,9 +50,9 @@ public void CreatesReleaseNotesForClosedGitHubIssues()
4650
}.AsReadOnly()));
4751
_gitHubClient.Issue.Returns(_issuesClient);
4852

49-
var releaseNotes = _sut.ScanCommitMessagesForReleaseNotes(_gitReleaseNotesArguments, commitsToScan);
53+
var releaseNotes = _sut.ScanCommitMessagesForReleaseNotes(_gitReleaseNotesArguments, toScan);
5054

51-
Assert.Equal("Issue Title", releaseNotes.ReleaseNoteItems[0].Title);
55+
Assert.Equal("Issue Title", releaseNotes.Releases[0].ReleaseNoteItems[0].Title);
5256
}
5357

5458
private static Commit CreateCommit(string message, DateTimeOffset when)

src/GitReleaseNotes.Tests/IssueTrackers/IssueNumberExtractor.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using GitReleaseNotes.IssueTrackers;
1+
using System.Collections.Generic;
2+
using GitReleaseNotes.IssueTrackers;
23
using LibGit2Sharp;
34
using NSubstitute;
45
using Xunit;
@@ -14,16 +15,21 @@ public void DiscoversIssueNumbersInCommits()
1415
commit.Message.Returns("Fixing issue #123");
1516
var commit2 = Substitute.For<Commit>();
1617
commit2.Message.Returns("Fixing issue #51401");
17-
var commits = new[]
18+
var commits = new List<Commit>
1819
{
1920
commit,
2021
commit2
2122
};
23+
var releaseInfo = new ReleaseInfo();
24+
var releases = new Dictionary<ReleaseInfo, List<Commit>>
25+
{
26+
{releaseInfo, commits}
27+
};
2228

23-
var issueNumbers = new IssueNumberExtractor().GetIssueNumbers(new GitReleaseNotesArguments(), commits, "#(?<issueNumber>\\d+)");
29+
var issueNumbers = new IssueNumberExtractor().GetIssueNumbers(new GitReleaseNotesArguments(), releases, "#(?<issueNumber>\\d+)");
2430

25-
Assert.Contains("123", issueNumbers);
26-
Assert.Contains("51401", issueNumbers);
31+
Assert.Contains("123", issueNumbers[releaseInfo]);
32+
Assert.Contains("51401", issueNumbers[releaseInfo]);
2733
}
2834
}
2935
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# vNext
2+
3+
- Issue 1 [#1](http://github.com/org/repo/issues/1) +feature
4+
5+
6+
# 1.2.0 (06 December 2013)
7+
8+
- Issue 2 [#2](http://github.com/org/repo/issues/2) +feature
9+
- Issue 3 [#3](http://github.com/org/repo/issues/3) +fix

src/GitReleaseNotes.Tests/ReleaseNotesWriterTests.cs

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ public void ApproveSimpleTests()
3131
};
3232
var releaseNotes = new SemanticReleaseNotes(new[]
3333
{
34-
35-
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new string[0])
34+
new SemanticRelease("", null, new[]
35+
{
36+
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new string[0])
37+
})
3638
});
3739

3840
_sut.WriteReleaseNotes(arguments, releaseNotes);
@@ -49,7 +51,39 @@ public void ItemIsCategorised()
4951
};
5052
var releaseNotes = new SemanticReleaseNotes(new[]
5153
{
52-
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new[]{"feature"})
54+
new SemanticRelease("", null, new[]
55+
{
56+
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"),
57+
new[] {"feature"})
58+
})
59+
});
60+
61+
_sut.WriteReleaseNotes(arguments, releaseNotes);
62+
63+
Approvals.Verify(GetContent());
64+
}
65+
66+
[Fact]
67+
public void MultipleReleases()
68+
{
69+
var arguments = new GitReleaseNotesArguments
70+
{
71+
OutputFile = "ReleaseFile.md"
72+
};
73+
var releaseNotes = new SemanticReleaseNotes(new[]
74+
{
75+
new SemanticRelease("", null, new[]
76+
{
77+
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"),
78+
new[] {"feature"})
79+
}),
80+
new SemanticRelease("1.2.0", new DateTimeOffset(2013, 12, 06, 0,0,0, new TimeSpan()), new []
81+
{
82+
new ReleaseNoteItem("Issue 2", "#2", new Uri("http://github.com/org/repo/issues/2"),
83+
new[] {"feature"}),
84+
new ReleaseNoteItem("Issue 3", "#3", new Uri("http://github.com/org/repo/issues/3"),
85+
new[] {"bug"})
86+
})
5387
});
5488

5589
_sut.WriteReleaseNotes(arguments, releaseNotes);
@@ -66,7 +100,10 @@ public void LabelOfBugIsCategorisedAsFix()
66100
};
67101
var releaseNotes = new SemanticReleaseNotes(new[]
68102
{
69-
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new[]{"bug"})
103+
new SemanticRelease("", null, new[]
104+
{
105+
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new[] {"bug"})
106+
})
70107
});
71108

72109
_sut.WriteReleaseNotes(arguments, releaseNotes);
@@ -84,7 +121,11 @@ public void AdditionalCategoriesCanBeSpecifiedOnCommandLine()
84121
};
85122
var releaseNotes = new SemanticReleaseNotes(new[]
86123
{
87-
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new[]{"internal refactoring"})
124+
new SemanticRelease("", null, new[]
125+
{
126+
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"),
127+
new[] {"internal refactoring"})
128+
})
88129
});
89130

90131
_sut.WriteReleaseNotes(arguments, releaseNotes);
@@ -102,7 +143,10 @@ public void RelativePathIsWrittenToRepositoryRoot()
102143
};
103144
var releaseNotes = new SemanticReleaseNotes(new[]
104145
{
105-
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new string[0])
146+
new SemanticRelease("", null, new[]
147+
{
148+
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new string[0])
149+
})
106150
});
107151

108152
_sut.WriteReleaseNotes(arguments, releaseNotes);
@@ -121,7 +165,10 @@ public void AbsolutePathIsWrittenToRepositoryRoot()
121165
};
122166
var releaseNotes = new SemanticReleaseNotes(new[]
123167
{
124-
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new string[0])
168+
new SemanticRelease("", null, new[]
169+
{
170+
new ReleaseNoteItem("Issue 1", "#1", new Uri("http://github.com/org/repo/issues/1"), new string[0])
171+
})
125172
});
126173

127174
_sut.WriteReleaseNotes(arguments, releaseNotes);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using GitReleaseNotes.Git;
5+
using LibGit2Sharp;
6+
7+
namespace GitReleaseNotes
8+
{
9+
public class CommitGrouper
10+
{
11+
public Dictionary<ReleaseInfo, List<Commit>> GetCommitsByRelease(IRepository gitRepo, TaggedCommit tagToStartFrom)
12+
{
13+
var currentRelease = new Tuple<ReleaseInfo, List<Commit>>(new ReleaseInfo(), new List<Commit>());
14+
var releases = new Dictionary<ReleaseInfo, List<Commit>> {{currentRelease.Item1, currentRelease.Item2}};
15+
var tagLookup = gitRepo.Tags.ToDictionary(t => t.Target.Sha, t => t);
16+
foreach (var commit in gitRepo.Commits
17+
.TakeWhile(c => c != tagToStartFrom.Commit))
18+
{
19+
if (tagLookup.ContainsKey(commit.Sha))
20+
{
21+
var tag = tagLookup[commit.Sha];
22+
var releaseInfo = new ReleaseInfo(tag.Name, ((Commit) tag.Target).Author.When);
23+
var commits = new List<Commit>();
24+
currentRelease = new Tuple<ReleaseInfo, List<Commit>>(releaseInfo, commits);
25+
releases.Add(releaseInfo, commits);
26+
}
27+
currentRelease.Item2.Add(commit);
28+
}
29+
30+
return releases;
31+
}
32+
}
33+
}

src/GitReleaseNotes/GitReleaseNotes.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
<Reference Include="System.Xml" />
5555
</ItemGroup>
5656
<ItemGroup>
57+
<Compile Include="CommitGrouper.cs" />
5758
<Compile Include="FileSystem.cs" />
5859
<Compile Include="IFileSystem.cs" />
5960
<Compile Include="IssueTrackers\GitHub\GitHubIssueTracker.cs" />
@@ -71,7 +72,9 @@
7172
<Compile Include="Properties\AssemblyInfo.cs" />
7273
<Compile Include="Git\TaggedCommit.cs" />
7374
<Compile Include="Git\TaggedCommitFinder.cs" />
75+
<Compile Include="ReleaseInfo.cs" />
7476
<Compile Include="ReleaseNotesWriter.cs" />
77+
<Compile Include="SemanticRelease.cs" />
7578
<Compile Include="SemanticReleaseNotes.cs" />
7679
</ItemGroup>
7780
<ItemGroup>

0 commit comments

Comments
 (0)