Rush and changelog generation - Part 2
Tags: rush, node, monorepo
Published: 2022-03-02 10:48:23

I guess I'm not alone wishing that rush [uses commit messages](https://github.com/microsoft/rushstack/issues/1659) for change log generation. 
It's actually not so difficult (once it's done :sunglasses:).

## Requirements

- I want to use my commit messages to generate change log files. I already lint them with commitlint to ensure correct format.
- I want to follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) specification when generating the change log and bumping version:
 - **fix**: a commit of the type `fix` patches a bug in your codebase (this correlates with `PATCH` in Semantic Versioning).
 - **feat**: a commit of the type feat introduces a new feature to the codebase (this correlates with `MINOR` in Semantic Versioning).
 - **BREAKING CHANGE**: a commit that has a footer `BREAKING CHANGE:`, or appends a `!` after the type/scope, introduces a breaking API change (correlating with `MAJOR` in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type.
 - types other than `fix:` and `feat:` are allowed, for example @commitlint/config-conventional recommends `build:`, `chore:`, `ci:`, `docs:`, `style:`, `refactor:`, `perf:`, `test:`, and others.
- If my commits are not `fix:`, `feat:`, or a breaking change, I don't want to generate rush change files.
- If I'm committing with the same message, I want to skip change file generation.
- I want to make sure that `rush change --verify` will pass, before I push my code. I don't want to learn that my PR fails because I forgot to generate the, that's way too late.

### Design
#### Trigger with `post-commit` 
> This hook is invoked by `git commit`. It takes no parameters, and is invoked after a commit is made.
This hook is meant primarily for notification, and cannot affect the outcome of git commit.

Perfect. I want to take the last two commits anyway, and I want to make sure that any failures in my code will not affect commits.

#### Custom rush command
The hook will call a custom rush command, which will in turn execute my custom script. One advantage of using custom command is that I benefit from **autoinstallers** that install all required packages

#### Autoinstaller
Rush autoinstaller will take care of installing any packages I need in my script. I don't want to install them in the repo's root, this is not how rush works.

#### And finally, the script itself
The javascript script with... well, yes, the business logic. 

## My toolbox
I need couple of things here. 
- [@microsoft/rush-lib](https://rushstack.io/pages/api/rush-lib/) with: 
 - `RushConfiguration` class representing the Rush configuration for a repository, based on the "rush.json" configuration file, 
 - `ProjectChangeAnalyzer` that gets a list of projects that have changed. **Caution: ** this is still in Preview. Do not use this API in a production environment,
 - `ChangeManager` class that helps with programmatically interacting with Rush's change files.
- [@rushstack/node-core-library](https://rushstack.io/pages/api/node-core-library/) providing `Terminal` and `ConsoleTerminalProvider` classes needed when calling `projectAnalyzer.getChangedProjectsAsync`.
- [gitlog](https://www.npmjs.com/package/gitlog): a Git log parser for Node.JS because I need a solution to correctly read the commits, even if they span multiple lines.
- [recommended-bump](https://www.npmjs.com/package/recommended-bump) to calculate the recommended bump respecting Conventional Commits specification. 

## Ready? Steady? Go!

### Autoinstaller

You know the drill. Create `rush-changemanager` autoinstaller using the following commands: 
```powershell
rush init-autoinstaller --name rush-changemanager
cd common/autoinstallers/rush-changemanager

pnpm add @microsoft/rush-lib
pnpm add @rushstack/node-core-library
pnpm add gitlog
pnpm add recommended-bump
# When you are finished, run this command to ensure that the
# common/autoinstallers/rush-commitizen/ppnpm-lock.yaml file is up to date
rush update-autoinstaller --name rush-changemanager
```

You should now have the following **package.json** file:
#### common\autoinstallers\rush-changemanager\package.json
```json
{
  "name": "rush-changemanager",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "@microsoft/rush-lib": "^5.62.4",
    "@rushstack/node-core-library": "^3.45.0",
    "gitlog": "^4.0.4",
    "recommended-bump": "^1.5.2"
  }
}
```

### Custom command
Add the `changefiles` command to the **command-line.json** file 

####common\config\rush\command-line.json
```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json",
  "commands": [
    {
      "name": "changefiles",
      "commandKind": "global",
      "summary": "",
      "autoinstallerName": "rush-changemanager",
      "shellCommand": "node common/scripts/rush-changefiles.js"
    }
  ],
  //...
}
```

### The script 
Create a **rush-changefiles.js** file in the **common\scripts folder**. 
Before we dive in into the business logic implementation, let's make sure everything works as it should. I'm just going to print "rush-changefiles.js" to the console output.
#### common\scripts\rush-changefiles.js
```javascript
console.log("rush-changefiles.js")
```

### Test
Invoke the new `changefiles` command:
```powershell
rush changefiles
```
You will see that rush is executing the new command, installs all the packages needed by the autoinstaller, and prints the "rush-changefiles.js" to the console.
The autoinstaller job is only executed the first time, if you run the `rush changefiles` again, it will complete much faster:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g5rbprhbrkqlfawkcmmc.png)

Now, the only thing that is missing, is the actual business logic.

## Source Code
You may find the source code on [GitHub](https://github.com/kkazala/rush-conventionalcommits/tree/b4927ed1011cbfc599831a7b0943dbc58a5db6db).