(#89) Add ability to populate release notes with information about contributors
Properly attribute each issue / PR which is part of a release.
Resolves #89
@gep13 please review at your convenience but do not merge yet. I just rebased your recent changes and I am now investigating how to implement my new feature in GitLat.
UPDATE: I converted to draft to make sure this PR is not merged.
I solved all the remaining issues. This logic is compatible with multiple issues/PRs being associated with a given issue/PRs and also the GetLinkedIssues method has been implemented for GitLab.
@gep13 in case I wasn't clear: this PR is ready for review.
In case anybody is interested to try this new feature, you can replace
#tool nuget:?package=GitReleaseManager&version=0.16.0
with
#tool nuget:https://f.feedz.io/jericho/jericho/nuget/?package=GitReleaseManager&version=0.17.0-collaborators0003
in your cake script and you'll be able to generate release notes similar to this: https://github.com/Jericho/ZoomNet/releases/tag/0.71.0
@Jericho please accept my apologies for the radio silence on this PR! Simply put, I haven't had any time to even think about looking at this. I am hoping to review this in the coming days, and will provide some feedback (if there is any).
Thanks again for your help with this!
No apologies necessary. I'm just glad to be contributing.
@Jericho apologies again! I haven't had a chance to look at this yet, and looks like I am going to have to do an immediate release to fix the issue with creating releases as a result of a required change upstream to Octokit.
Rebased to pull in yesterday's commits.
@Jericho is there something "special" that has to be done to get the generated release notes to contain the contributors?
The reason that I ask is that we have an upcoming release of Chocolatey CLI, and I thought I would take this for a spin to see it in action, however, I am not seeing anything other than the normal output in the generated release notes. I can see that in code, the contributors for the issues are populated in the GetContributors call, but the template changes don't seem to be taking effect.
If you are referencing my beta package #tool nuget:https://f.feedz.io/jericho/jericho/nuget/?package=GitReleaseManager&version=0.17.0-collaborators0003, nothing special that you need to do. The result will be release notes similar to the notes on this project: https://github.com/Jericho/StrongGrid/releases (please note, I started using my beta package in late December 2023 if I remember correctly, so only consider releases since January 2024).
If you are running GRM from source in Visual Studio, make sure you "rebuild" the whole project. It seems that changes to templates are not effective until you rebuild. This issue has caused me grief on several occasions where I modified the template but my changes were no reflected in the resulting release notes. My guess is that this problem is due to Visual Studio not automatically detecting changes to .sbn files. Anyway, long story short: make sure you rebuild the project and you should be good to go.
Thank you for the hint, although that doesn't seem to have helped.
I am trying this out of Visual Studio.
I went as far as closing Visual Studio, manually deleting the bin and obj folders in each project folder, then re-opening the solution, and running the GitReleaseManager.Tool project.
The contributors are still not being added into the generated release notes.
Just tried using the GitReleaseManager.Cli as the startup project, and it is having the same result.
Please set a break point on line 66 in ReleaseNotesBuilderIntegrationTests.cs and run the SingleMilestone unit test. It will generate the notes for GRM version 0.12.0 which contains a good mix of issues, PRs, excluded labels and, more importantly for the case at hand, 10 different people contributed to this release. When Visual Studio hits your break point, hover your mouse over the result variable and click on View. You should get a popup window where you'll be able to eyeball the generated release notes. I just ran this unit test and here's what I get:
Do you get the same?
No, I do not :-(
Although, I do get contributors back:
Is there a possibility that, somehow, you managed to override the built-in template with your own?
Also, can you verify that you have the expected index.sbn which should look like this:
And while we're on the topic of templates, double-check the presence of contributors.sbn and contributor-details.sbn in the src\GitReleaseManager.Core\Templates\default folder.
While I do make use of custom templates on some projects, I can confirm that none are in play when testing this PR. I can confirm this here:
Where the default file path, i.e. the embedded resource, is being used.
Yes, I can confirm that the index.sbn is the same as your screenshot.
And yes, I can also confirm that the two contributor sbn files are in that folder.
Doh! I have figured it out...
I suddenly remembered about the .tt file!
You have to right click on that, and then click Run Custom Tool to have it generate the .cs file, which is then embedded into the assembly.
I don't know exactly when this is run as part of the normal build process, but it might also explain why you found you had to clean the files in order to get things to work properly.
@AdmiringWorm might be able to provide some insight into when this part of the tooling works. Thinking about it, I "think" it runs as part of the Cake build, but I thought it ran inside Visual Studio as well.
Now that I have this working, I should be able to take this for a proper trial.
@AdmiringWorm might be able to provide some insight into when this part of the tooling works. Thinking about it, I "think" it runs as part of the Cake build, but I thought it ran inside Visual Studio as well.
It is indeed run as part of the Cake build, it is a separate task called Transform-TextTemplates. Do note that it is not the same as when running it through Visual Studio (not sure why, but the support in VS seems to become worse and worse for TT templates), but it should be close enough.
@Jericho :wave: apologies again for not coming back to you sooner about this one! I would try to give an excuse, but I really don't have one, so, moving on...
A couple of questions/observations...
- What are your thoughts about adding an
IncludeContributorsproperty to theCreateConfigclass, to allow control over whether or not to include Contributors within the release notes? As it stands, Contributors are always included, and a user would need to edit the default template if they didn't want to include that information. Having this be controllable via the configuration file would seem like a natural way to do things, and would be inline with other configuration options that already exists. - When running the
SingleMilestonetest (without making any changes to the code) I noticed something interesting. In the rendered markdown, there is this Improvement
Issue 113 was indeed raised by Geoffrey, however, there is a linked Pull Request that was raised by me. Shouldn't the description for this issue include the information about the link to the PR?
- The default template is becoming quite "busy", with lots of different options and switches that can be pulled. Do you think it would make sense to include another template by default, which would specifically be used when including Contributors? That way, the Default template can remain slightly smaller, and less involved.
-
Instantiating the GraphQLHttpClient here doesn't seem right. This client should probably be injected in the constructor or maybe, rather than mixing the REST GitHub client with the graph QL client in the same provider, there should be a separate provider specifically for graphQL operations.
I haven't really got a strong opinion either way. If we go down the route of using a Configuration value to include/exclude the Contributor information, then we could control when this is instantiated? Or, as you say, perhaps split this out into a separate provider. That might make things a little more complicated in the long run though.
- I haven't tested this yet against a GitLab repository, as I would need to set one up for testing this out. Things seem a little simpler to do via the GitLab client though, as it doesn't involve GraphQL.
Again.. I am loving this PR, and the functionality that this provides! Thank you so much for putting the work in so far to get this to where it is!
Regarding GitLab, I have just tested it on a repository that has an issue with a linked MR, but the generated release notes doesn't mention the associated MR. The LinkedIssues property is empty on the issue. Did you have to do anything "special" within GitLab to make an issue link through to an MR?
Related to the above, if I use this:
I can get the associated MR for the issue in question, so that makes me wonder if I haven't done something between the issue and the MR in GitLab to link them in the correct way.
- What are your thoughts about adding an
IncludeContributorsproperty to theCreateConfigclass, to allow control over whether or not to include Contributors within the release notes?
Sure, no problem. I can add a Boolean property to the CreateConfig class to allow developers to indicate whether they want contributors to be included in the release notes or not.
- When running the
SingleMilestonetest (without making any changes to the code) I noticed something interesting. In the rendered markdown, there is this Improvement Issue 113 was indeed raised by Geoffrey, however, there is a linked Pull Request that was raised by me. Shouldn't the description for this issue include the information about the link to the PR?
Please make yourself comfortable, answering this seemingly simple question might take a while. Here goes my attempt to explain, based on my (potentially flawed) understanding.
GitHub's API does not have a way to retrieve the list of issues/PRs linked to a given issue. The only workaround I was able to find is to use GitHub's graphql API. But even this API does not allow you to easily retrieve the linked issues/PRs. You have to retrieve so called "timeline events" of specific types (in our case we want "connected" and "disconnected"), sort them in chronological order and then we can derive which issues and PRs have been "connected" to the desired issue without having been disconnected. Here's a fictitious example: let's say you link a PR with the issue, realize you made a mistake and unlink this PR and finally you correct your mistake by linking another PR. In this fictitious scenario, the graphQL API would return the following timeline items:
- first item: issue 111 connected to PR 222
- second item: issue 111 disconnected from PR 222
- third item: issue 111 connected to PR 333
In this example, you can see that PR 222 is initially associated to the desired issue but subsequently unlinked. and finally, PR 333 is linked and it's not unlinked. Based on these timeline events we can conclude that PR 333 is the one PR associated to the desired issue. I hope you're following me so far. FYI: this logic is similar to what is described in this StackOverflow comment.
ok, so now that we have the theory out of the way, let's try to investigate specifically what is going on with issue 113 in the GitReleaseManager repo. Here's the tool I use to debug the graphql query and here's the query that you can copy and paste in this tool:
{
repository(name: "GitReleaseManager", owner: "GitTools") {
issue(number: 113) {
timelineItems(first: 50, itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT]) {
nodes {
__typename
... on ConnectedEvent {
createdAt
id
source {
__typename
... on Issue {
number
}
... on PullRequest {
number
}
}
subject {
__typename
... on Issue {
number
}
... on PullRequest {
number
}
}
}
... on DisconnectedEvent {
createdAt
id
source {
__typename
... on Issue {
number
}
... on PullRequest {
number
author {
avatarUrl
resourcePath
}
}
}
subject {
__typename
... on Issue {
number
}
... on PullRequest {
number
}
}
}
}
}
}
}
}
After you click the "Run" button, you will see this result:
As you can see in the result, there are no timeline events!!!! Not a single one!!!! Allow me to repeat what I just said, for dramatic effect: not a single one! In other words: GitHub is telling us that this issue was never connected to any other issue or PR. This does not make sense. You and I know that the issue is indeed linked to a PR, therefore we expect at the very least one "connect" timeline event. But the reality is that the API returns zero records.
The ultimate question is: why is GitHub's graphQL Api returning data that seems incorrect or incomplete. I do not know with certainty the answer to this question but I have a theory (emphasis on "theory"): if you go back the the graphQL query that I presented earlier and that you copied/pasted into the GraphQL explorer, you'll notice that I added a "filter" when querying timeline items, like so: timelineItems(first: 50, itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT]) {. The filter indicated that I am interested in "connected" and "disconnected" events because, as previously explained, these are the types of events that signify that a given issue/PR was linked/unlinked to the desired issue. My theory is that the issue and PR were associated in a way to generated a different type of event. What type of event you might ask? I have no idea. But if my theory is correct, it would explain why our graphQL query returns zero timeline event.
Anyway, I'm sure I have bored you to death with all this information. The TL;DR is that I suspect there's another type of event that in some scenarios, such as the one for issue number 113, represents the association of two records. I have no clue what if this theory is true and if it is, what would be the type of event in question. I have been using a beta copy of GRM to generate my release notes for at least 6 months and never noticed this problem. So I'm wondering if this issue you found is an edge case or if it's common.
- The default template is becoming quite "busy", with lots of different options and switches that can be pulled. Do you think it would make sense to include another template by default, which would specifically be used when including Contributors? That way, the Default template can remain slightly smaller, and less involved.
In other words, leave index.sbn alone and create a new index-with-contributors.sbn? Sure, no problem.
- Or, as you say, perhaps split this out into a separate provider. That might make things a little more complicated in the long run though.
Separating it in a distinct provider would have been my preferred method but GitLab doesn't have this concept of a GraphQL client. So I was at a loss to design a way that works for both Git environments.
- I haven't tested this yet against a GitLab repository, as I would need to set one up for testing this out. Things seem a little simpler to do via the GitLab client though, as it doesn't involve GraphQL.
Yes indeed, much much much simpler.
Related to the above, if I use this:
I can get the associated MR for the issue in question, so that makes me wonder if I haven't done something between the issue and the MR in GitLab to link them in the correct way.
My code for GitLab retrieves the issues/PRs that close a particular issue when merged. The line of code you added in your code snippet retrieves issues/PRs that are merely "related" and don't necessarily close the desired issue. That's a very subtle nuance but, as your scenario demonstrates, quite an important one. I will change my logic to match your code snippet.
@Jericho said... Sure, no problem. I can add a Boolean property to the CreateConfig class to allow developers to indicate whether they want contributors to be included in the release notes or not.
Woot! 😄
@Jericho said... In other words, leave index.sbn alone and create a new index-with-contributors.sbn? Sure, no problem.
No, this wasn't exactly what I was referring to. Rather here at this location:
https://github.com/GitTools/GitReleaseManager/tree/develop/src/GitReleaseManager.Core/Templates
We create a new folder, perhaps named contributors or something similar, and we place all the required files to generate the release notes, including the contributors in there. That way, we keep things completely separate. Then, within the code, when the new configuration value is in play, we use this named template, rather than the default one.
Does that make sense? This idea came from @AdmiringWorm, so if I haven't made this clear, perhaps he can help to elaborate.
@Jericho said... Separating it in a distinct provider would have been my preferred method but GitLab doesn't have this concept of a GraphQL client. So I was at a loss to design a way that works for both Git environments.
Hmm, that is a fair point.
What about adding an overload to the GitHubProvider, to pass in an instance of the GraphQL Client, and then in the Program.cs, based on when the contributors config value has been set, either call the new constructor with an instance of the client, or call the other one? That way, it is only created when needed. Just an idea, haven't dug into this too much, so I could be missing something here.
@Jericho said... My code for GitLab retrieves the issues/PRs that close a particular issue when merged. The line of code you added in your code snippet retrieves issues/PRs that are merely "related" and don't necessarily close the desired issue. That's a very subtle nuance but, as your scenario demonstrates, quite an important one. I will change my logic to match your code snippet.
Just to level set here... Do you have an example of a GitLab issue/PR pair that works with the current code that you have in place? How exactly did you create the association between the two? The repository I was using for testing isn't public, so I can't share, but within the issue body, there is this section:
Do you have that in the issue that you have created? Or do you have another section? If so, I would be curious to know how you have associated the two together, as I could be doing something wrong.
Changes since last review:
- Added a configuration option so users can specify whether they want contributors to be included in the release notes
- Created a separate template which is used when the config option is set to true
- Enhanced the GraphQL query to reduce the number of queries to fetch all necessary info about all the linked issues/PRs
- Injectable GraphQL client
Ready for another review
@Jericho I have just taken this for a spin, on both GitHub and GitLab, and from what I can see, this is working really well (I did have to add some additional mappings into the GitLabProfile in order to get it to function).
I am going to get this merged in, and shipped in the next release of GitReleaseManager! Apologies on it taking so long to get this in, appreciate you being patient!
@Jericho I think I would class this as a success! 😄
Glad you like it!
I can get the associated MR for the issue in question, so that makes me wonder if I haven't done something between the issue and the MR in GitLab to link them in the correct way.