Rush and changelog generation - Part 2

kkazala

Kinga

Posted on March 2, 2022

Rush and changelog generation - Part 2

I guess I'm not alone wishing that rush uses commit messages for change log generation.
It's actually not so difficult (once it's done 😎).

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 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 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 providing Terminal and ConsoleTerminalProvider classes needed when calling projectAnalyzer.getChangedProjectsAsync.
  • 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 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:

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
Enter fullscreen mode Exit fullscreen mode

You should now have the following package.json file:

common\autoinstallers\rush-changemanager\package.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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Custom command

Add the changefiles command to the command-line.json file

common\config\rush\command-line.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"
    }
  ],
  //...
}
Enter fullscreen mode Exit fullscreen mode

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

console.log("rush-changefiles.js")
Enter fullscreen mode Exit fullscreen mode

Test

Invoke the new changefiles command:

rush changefiles
Enter fullscreen mode Exit fullscreen mode

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

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

Source Code

You may find the source code on GitHub.

💖 💪 🙅 🚩
kkazala
Kinga

Posted on March 2, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related