rushstack icon indicating copy to clipboard operation
rushstack copied to clipboard

[rush] automatically generate changelog

Open dbartholomae opened this issue 4 years ago • 25 comments

Please prefix the issue title with the project name i.e. [rush], [api-extractor] etc.

Is this a feature or a bug?

  • [X] Feature
  • [ ] Bug

Please describe the actual behavior. I use conventional commit messages to signify any changes directly in the commit message. Currently when using rush I have to manually write changelog entries, copying over what I already wrote in the commit messages.

What is the expected behavior? It would be great if rush could extract changes from commit messages and use them to create changelogs. The semantic release project is based around this idea but currently does not support monorepos. I am not sure where this kind of functionality should live and would like to use this issue to open up the discussion. I would also be willing to contribute to this effort :)

dbartholomae avatar Dec 12 '19 22:12 dbartholomae

This hasn't gone anywhere in half a year. Is this considered important or should I use semantic-release-monorepo with lerna instead?

ghost avatar Apr 23 '20 19:04 ghost

I'm not sure Git commits are the best way to author a changelog for a monorepo with lots of projects and teams.

Git commits document steps of work. For example, when I create a PR, I might break it into 5 separate commits that are easier to review one by one, rather than reading the entire PR diff. And then during the code review, people may suggest various improvements, which may lead to 10 more commits before I finally merge my PR. Some of these commits will be pure bookkeeping (ran "rush update", merged from master, etc). Git commits are written for the audience of people who work on the code. Whereas...

Change logs inform consumers what's new. Consumers need to know which bugs got fixed, which new features were added, or alert consumers about possible breaking changes. This audience often doesn't know anything about the underlying implementation, so a different style of writing is needed. In a monorepo, one Git commit might impact many different projects, and perhaps in a way that needs to be explained differently for each project. For certain important projects, the change log may serve as a public announcement to customers. The release team may have a step where they manually revise the changelog before publishing a release.

I see "semantic release" used mainly in smaller repos where this distinction between commits vs changelogs isn't too important. But it might not work so well as you scale up with multiple teams and projects coexisting.

If someone is willing to propose a design and help implement this for Rush, we would support you. But given Rush's goal of enabling solutions that can scale up, it doesn't seem like this would make sense as a default model that replaces rush change. And I'm not sure that it would be the right fit for the Rush Stack repo. (?)

octogonz avatar Apr 23 '20 23:04 octogonz

@octogonz I disagree. Semantic-release is strongly tied to commit conventions like conventional-commit which allows it to make assumptions about the reliability of your commit message.
If rush would integrate support for conventional-commit messages (which I autogenerate with commitizen), it could easily extract changelog messages from these commits. This is a common practice by now as it helps maintaining a consistent changelog on EVERY change, without a developer having to remember certain steps and adding hassle. I'm still in strong support of adding this feature and, ideally, stronger integration with such commit message standards.

ghost avatar Apr 24 '20 18:04 ghost

Just to clear two things up:

  1. With conventional-commit not all commit messages are used for change logs. E. g. refactorings and style changes are not used. Also a lot of people I know (me included) rebase and clean up commits, so I didn't have problems with conventional-commits even for larger projects.
  2. I don't think this should be forced upon everyone. It should be optional, so if someone feels their project is too big for this, they don't need to use it.

Concerning the design what I would envision based on my understanding of RushJS architecture would be:

  • Add a version policy named "semanticVersion"
  • Identify the latest commit that has been checked (based on git tag)
  • For each project, identify which commits touch files in that respective project
  • For each project parse all conventional commits into changes by mapping the type to the type and the description to the comment
  • continue as normal

dbartholomae avatar Apr 24 '20 20:04 dbartholomae

@dbartholomae Couldn't have expressed it better!
In addition to publishing the packages with optional, automatic semantic versioning at npm, it would be cool if we could publish to different distribution channels (/npm tags) like with semantic-release. I can't wait to switch to rush after this!

ghost avatar Apr 24 '20 20:04 ghost

This sounds pretty reasonable. Is anyone available to work on it?

octogonz avatar Apr 24 '20 22:04 octogonz

I'm looking into the rush codebase - its the first time I'm working in it. Lets see if I'll be able to start anything in a pr. If anyone else has time, please go ahead :)

ghost avatar Apr 26 '20 18:04 ghost

Would love to see this feature on Rush, any progress or update on it?

anonrig avatar Jul 17 '20 15:07 anonrig

This is becoming more and more relevant for me. I might look into this soon. Most likely it would take the form of a separate tool that can create the change files that are usually created by rush change automatically based on conventional commits. @octogonz How stable is the JSON format used to save these changes? Does my idea make sense? I would also be willing to contribute the tool into this monorepo but might need some help how to best get this started.

dbartholomae avatar Aug 13 '20 22:08 dbartholomae

I'm willing to do the same @dbartholomae, happy to do this together if you need any help

anonrig avatar Aug 14 '20 06:08 anonrig

@anonrig I'm currently still on a different hobby project that will hopefully be finished in a couple days. Would love to work on this together pair programming style if you are interested and we can coordinate the time :)

dbartholomae avatar Aug 31 '20 13:08 dbartholomae

Any person working on this right now? I interesting on contribute to this story.

kamontat avatar Nov 02 '21 15:11 kamontat

Is it possible to implement this through plugin?

m1heng avatar Jan 21 '22 07:01 m1heng

In case somebody is still interested, I wrote a script creating change files based on commits, respecting conventional commits. It's described here + here. The code is on GitHub

I hope it will fit into the process described by @octogonz because I only generate change files for fix/feat/breaking changes. I would assume that developers may make more user-friendly descriptions for these cases. And there's still a possibility of either editing the change files/change log afterwards.

kkazala avatar Mar 02 '22 12:03 kkazala

FYI, conventional commits can point to an issue directly. And the issue could be closed via CI later.

For change log, is it possible to add another prompt message? like Does this change affect any open issues? Then add issue id in the json that generated by rush, which shows on changelog.md eventually? @octogonz

{
  "changes": [
    {
      "packageName": "foo",
      "comment": "some comment",
      "type": "patch"
      "issue": 56 // issue id
    }
  ],
  "packageName": "foo"
}

I'm not sure Git commits are the best way to author a changelog for a monorepo with lots of projects and teams.

Git commits document steps of work. For example, when I create a PR, I might break it into 5 separate commits that are easier to review one by one, rather than reading the entire PR diff. And then during the code review, people may suggest various improvements, which may lead to 10 more commits before I finally merge my PR. Some of these commits will be pure bookkeeping (ran "rush update", merged from master, etc). Git commits are written for the audience of people who work on the code. Whereas...

Change logs inform consumers what's new. Consumers need to know which bugs got fixed, which new features were added, or alert consumers about possible breaking changes. This audience often doesn't know anything about the underlying implementation, so a different style of writing is needed. In a monorepo, one Git commit might impact many different projects, and perhaps in a way that needs to be explained differently for each project. For certain important projects, the change log may serve as a public announcement to customers. The release team may have a step where they manually revise the changelog before publishing a release.

I see "semantic release" used mainly in smaller repos where this distinction between commits vs changelogs isn't too important. But it might not work so well as you scale up with multiple teams and projects coexisting.

If someone is willing to propose a design and help implement this for Rush, we would support you. But given Rush's goal of enabling solutions that can scale up, it doesn't seem like this would make sense as a default model that replaces rush change. And I'm not sure that it would be the right fit for the Rush Stack repo. (?)

icy0307 avatar Jun 21 '22 06:06 icy0307

For change log, is it possible to add another prompt message? like Does this change affect any open issues? Then add issue id in the json that generated by rush, which shows on changelog.md eventually? @octogonz

@icy0307 This is a good idea. Let's open a separate GitHub issue to discuss the design. It is somewhat unrelated to "conventional commits" syntax.

octogonz avatar Jun 25 '22 00:06 octogonz

In case somebody is still interested, I wrote a script creating change files based on commits, respecting conventional commits. It's described here + here. The code is on GitHub

I hope it will fit into the process described by @octogonz because I only generate change files for fix/feat/breaking changes. I would assume that developers may make more user-friendly descriptions for these cases. And there's still a possibility of either editing the change files/change log afterwards.

@kkazala Interesting! How do you handle this type of scenario:

  1. I make a PR whose branch has 20 different commits. These consist of initial work, followed by incremental fixes based on feedback from coworkers during the code review discussions.
  2. The individual commits are meaningful to GitHub (or whatever system we use for reviewing code), so I don't want to overwrite them by force-pushing the branch. (However, I'm fine with the commits getting squashed when my PR is finally merged.)
  3. This PR makes changes to 5 different Rush projects. These projects have different change logs. For example, my change log for example-app may say Added ability to search by title, whereas my change log for example-lib may say Fixed bug with searchInsensitive() API.
  4. The changes in step 3 are introduced by a single git commit -- it does not makes sense to split the example-app and example-lib fixes into two commits, because if we separate them, the branch doesn't compile.
  5. After my PR is merged, the owners of the example-lib project need to revise the change log prior to release, because their library is an SDK for customers. For example, a later PR renamed the API to searchCaseInsensitive() and so they want to rename the change log to be consistent. (The owners of example-app project don't care about this sort of cleanups, because their change log is for an internal audience only.)

The big challenge for "conventional commits" is: How to handle these sorts of requirements using git commit strings? All these problems seem to require a JSON file.

octogonz avatar Jun 25 '22 00:06 octogonz

@octogonz Thank you for taking time considering this idea.

I totally see the difference between commits and change files, your comment very clearly explains the characteristics of each of them. And I admit that my "workaround" is just that- a workaround. The generated change log has to be updated and yes, it would very quickly become a mess.

On the other hand, I very often find myself scratching my head, when it comes to generating change files. I think it would be useful to know which commits triggered the requirement for a change file. I imagine I'm not alone when I say- work can get crazy sometimes. It may be difficult and time consuming to recall the commits and to decide what should be included in the change file. And obviously I can check my commits history but… from which point of time? And as you say- what if one commit impacts multiple projects? All this takes valuable time.

And of top of that, if I'm using conventional commits, the information on change type is already "there". So a suggestion, based on the commits, would go a long way.

What do you think about the following approach - The general idea of creating change files, and change logs stays the same. - When running rush change I can use additional parameter, let's say --show-commits that based on mergeCommit returned by Git.getMergeBase would execute git log to show the commits. - And if --conventional-commits parameter is used, the commits are parsed and the change type is suggested using, for example recommended-bump that I'm using myself, or conventional-recommended-bump or sth else :)

This would be a massive help, and I think wouldn't impact the flow currently defined by rush. What's your opinion?

kkazala avatar Jul 04 '22 10:07 kkazala

This is the best idea I've heard yet for adapting the spirit of conventional commits to the rush change workflow. 👌 Could you give a little more detail about the design? Like examples of the console outputs/prompts and how the parsing might work? It does not sound like a lot of work to implement.

octogonz avatar Jul 04 '22 23:07 octogonz

@octogonz I was thinking about sth like that

We add two parameters: --show-commits and --recommend-changetype

In _askQuestions we follow the usual sequence until the user confirms they wish to create a change file. image

In such case we call _parseCommits method

_parseCommits()
      if (showCommits) {
        this._git.getShortLog(mergeCommitHash, projectInfo.projectRelativeFolder);
      }
      if (recommendChangeType) {
        const ccHelper: ConventionalCommits = new ConventionalCommits(this.rushConfiguration);
        const changeType: string = ccHelper.getRecommendedChangeType(
          mergeCommitHash,
          projectInfo.projectRelativeFolder
        );
        console.log(
          `Based on conventional commits convention, we recommend the following change type: ` +
            colors.green(changeType)
        );

If --show-commits, we display commits history, I'm currently using git shortlog but if there are many commits, this will have to be approached differently. It's not use-friendly to display it in the console. So I was thinking.... separate file, that gets deleted after change file is created? image

If --recommend-changetype, we filter commits and suggest the change type image

To check if there are any commits requiring major/minor/patch, I'm calling git rev-list --extended-regexp --grep

kkazala avatar Jul 05 '22 12:07 kkazala

@octogonz Did you have a chance to consider my proposal? In the meantime, I had an idea that maybe this new capabilities could be more tightly connected to the rush change and rush change -v. It would basically display the same info, along what rush change / rush change -v already do.

Also.... I looked into the ChangeAction._verifyAsync() and from what I understand, if I:

  • make changes and commit the code
  • generate change files
  • make some more changes and commits the rush change -v will be satisfied with the existing change file and will NOT list the project.

Is that correct? Maybe it would be helpful to detect if there are any new commits after the newest change file has been created?

kkazala avatar Jul 20 '22 16:07 kkazala

If --show-commits, we display commits history, I'm currently using git shortlog but if there are many commits, this will have to be approached differently. It's not use-friendly to display it in the console. So I was thinking.... separate file, that gets deleted after change file is created?

Maybe this is not necessary. If there are too many commits, we could simply say so, and the person can just as easily use git log to inspect them.

octogonz avatar Jul 25 '22 19:07 octogonz

Maybe it would be helpful to detect if there are any new commits after the newest change file has been created?

Our typical workflow goes like this:

  1. You make 10 commits to implement a new feature
  2. You create a PR
  3. The PR build failed because you didn't run rush change. Go write change logs for your new feature, now the build succeeds. 👍
  4. Other people review your PR. This produces another 10 commits to address all their feedback.
  5. Now we're ready to merge. A few tests are failing, we need to merge master a couple times to fix merge conflicts.
  6. Finally it's green and gets merged. 🎉

In my experience additional rush change prompting/breaks would be a big nuisance during steps 4-6 (unless it's about a new project that I didn't already write a change log for).

octogonz avatar Jul 25 '22 19:07 octogonz

@octogonz

What I had in mind by "more tightly connected to the rush change" is that this new logic would not be executed by default, but instead:

  • rush change: no change to the flow
  • rush change -v: no change to the flow
  • rush change --recommend-changetype: does the usual work, and before asking for the change description it first displays the recommendation. Initially I though it should ONLY recommend the change type, but the user would need to run rush change separately to create the change files. I think we can do both steps at once? Maybe this was obvious for you from the start =) and my thinking is influenced by writing custom commands that always run separately from rush commands

"if there are too many commits, we could simply say so," The big question when running the git log is - what is the mergebase? It is not difficult to find it but it takes time, and also some understanding of what rush is actually doing. When asking user to run use git log I would at least display the command to invoke. Alternatively, the --show-commits could accept format: --show-commits shortlog --show-commits full. Something like this

kkazala avatar Jul 26 '22 06:07 kkazala

@octogonz

I made a small improvement in the meantime, to retrieve the creation date&time of the newest change file, and only parse/return commits after this date.

So for example, after running rush change --recommend-changetype --show-commits long: image If the change file will be generated/changes appended to the existing one, I'm checking for any new commits AFTER the creation date/time of the newest change file. If there we re no commits, appropriate information is displayed.

On the other hand, if commits are found, they are either displayed in the terminal (--show-commits short) or saved to a file (--show-commits long). The change type recommendation is displayed, and also, if user choses to describe the changes, the recommended change type is preselected in the "select the type of change" options: image

The gitlog folder is then deleted, along with all its contents

I would be happy to contribute a PR, but at the same time I don't want to force it on the rush team. Could you have a look at the logic and the code and let me know if it makes sense? I imagine I should also write tests, but I don't want to start if the idea dies ;)

kkazala avatar Aug 16 '22 12:08 kkazala