Changesets is a tool for the Monorepo project version and Changelog file management. At present, some popular Monorepo warehouses are using this tool for project outsourcing, such as PNPM and MOBx. The github repository is github.com/atlassian/c… , there are about 2k stars on Github.

At present, the author’s self-developed Monorepo tool also uses this scheme to replace the previous lerna package sending scheme, and forks and reconstructs this tool (the specific reasons will be explained later in the article). Finally, it is successfully implemented into the Monorepo warehouse project of most byte teams.

At the same time, this package distribution scheme has been widely applied and implemented in byte Monorepo warehouse, including the Tiktok FE team mentioned in PNPM, as well as the modern. Js warehouse currently developed by Byte open source is also using this package distribution scheme:

In this article, you will see how the Changesets tool manages the project package version in the Monorepo repository, some basic command usage and principles, as well as some bugs and current optimizations.

The Lerna packet sending scheme is defective

In previous articles, source code parsing based on lerNA packet sending scheme was introduced. At the same time, the early version of the Monorepo tool developed by the author also adopted lerna’s package sending scheme, but with its promotion and use among users, this scheme brought many problems:

  • ignoreChangesFiles cannot be completely ignored because of priority issues
  • lerna versionThe package version updated according to commit and tag is not as expected
  • The generated CHANGELOG file information is incomplete
  • lifecycle scriptsOften hit some user – defined script(e.gpublishEtc.)
  • The automated dispatch scenario in CI requires high customization costs
  • Lerna itself does not support the Workspace protocol, making some repositories developed based on PNPM unusable

Based on the above disadvantages, including the use cost of LerNA itself and redundant code design, and the current maintenance of LerNA itself, we gradually replaced the package issuing scheme in our self-developed Monorepo tool with Changesets after investigation.

Changesets workflow introduction

Earlier we talked about the role of Changesets. Changesets is mainly concerned with the update of monorepo sub-project version, changelog file generation, and package release. A Changeset is an MD file that contains information about changes made to a branch or commit. It contains information like this:

  • Packages that need to be published
  • Update level of package version (following semver specification)
  • CHANGELOG information

The Changesets workflow divides developers into two groups: project maintainers and project developers. The responsibilities of the two groups can be easily represented by the following flow chart:

According to the figure above, the changesets workflow goes like this: developers develop under Monorepo, and when they’re done, add a Changesets file to the corresponding subproject. The maintainer of the project will then consume these files through Changesets, automatically change the version of the corresponding package, generate the CHANGELOG file, and finally publish the corresponding package.

The above is a simple Changesets workflow. Of course, these workflows correspond to specific CLI commands and config configurations. Based on this workflow, I will introduce some of the most commonly used Changesets subcommands and how they work.

Subcommands and how they work

If you want to use Changesets, you need to install its CLI tool first. You can use PNPM install@changeset/CLI to install it. Once installed, you can start using the following commands.

init

Changeset init generates a. Changeset directory in the root directory of the project, which will generate a Changeset config file. Please refer to PNPM’s current root directory:

The principle of this command is relatively simple. When executing this command, you only need to use fs to write the corresponding configuration file to the directory. For details about the configuration in config, see the official documentation. Init Indicates the default configuration. Users generally do not need to modify the default configuration.

add

Add is one of the key commands in Changesets. It generates a Changeset file based on the project under Monorepo, which contains the aforementioned Changeset file information (update package name, version level, CHANGELOG information).

Using the PNPM project as an example, for example, executing changeset add in the PNPM repository causes a series of Prompt problems:

These packages are subpackages of the Monorepo project. Changesets uses getPackages() to get the information about the Monorepo subprojects. See the @manypkg/ get-Packages package under Changesets for a concrete experiment with this approach. The method essentially reads the package.json of all the sub-projects in Monorepo and builds a dependency graph. Changesets can use this result to get the projects that need to be sent out. The entire Changesets project itself is built based on this underlying approach, similar to the graph build in the Monorepo tool.

The git diff command is used to check whether the package name has been changed during the commit. However, the user can select the package name even if the package name has not been changed.

After selecting the package you want to publish, the version level of the package you want to update will be selected later. For example, I chose patch level here. According to Semver’s specification, the package selected here is @pnpm-private. A Changeset file with a random file name will be generated, as shown below:

Here the file name is generated by a library called human-id, the details can be viewed on NPM, but in fact here users can also modify the file name, there is no big relationship here, can also modify the information in the file CHANGELOG.

The file is essentially a pre-store of information that can be customized before the file is consumed. Changeset files can be accumulated in a cycle as different developers iterate. PNPM, for example, now has some changeset files:

If CHANGELOG has the same information but different Changeset files, these files will be merged when consumed. That is, the version of the corresponding package will not be upgraded multiple times.

version

The version command can be understood here as bump version, and essentially what it does is consume the Changeset file and modify the corresponding package version and the package version that depends on that package, At the same time, the system generates the corresponding CHANGELOG information based on the information in the previous Changeset file. The source code flow of version is as follows:

The core of this step relies on two libraries under the Changesets project, @Changesets /assemble-release-plan and @Changesets /apply-release-plan. AssembleReleasePlan reads the generated Changesets file, analyzes the package version that needs to be updated and its dependencies, and then sends the read result to applyReleasePlan. In applyReleasePlan, the swapped version is modified, the Changeset file is consumed, and the CHANGELOG file is updated (if not, a new one is generated) based on the information.

For example, if you now execute changeset Version once in the root directory of the PNPM repository, you will get this result according to the flow above:

The corresponding Changeset file is consumed, and the CHANGELOG and version of the corresponding subproject are changed. Of course, unsatisfied users can manually modify the CHANGELOG. The changelog information is automatically modified as follows:

Other commands

Changesets also provides a few other commands that I won’t go through here, but they are relatively easy to understand and not particularly difficult to understand in their implementation. For example, if a user wants to send a prelease version of the package (such as beta, alpha), he can use the Changeset pre command and then use the version command to bump the version.

If you want to see the current Changesets file consumption status, you can use the Changeset status command.

Changeset publish essentially encapsulates NPM publish, and at the same time checks whether there is a version of the corresponding package in registry. If it already exists, the package will not be issued again. If not, an NPM publish is performed for the corresponding package version.

Changesets is currently defective

In fact, I mentioned earlier that the team did not directly access changesets when developing the Monorepo tool, but directly fork the repository to modify it, mainly because there are many problems with the Monorepo tool in some usage scenarios.

Changeset file name is random

As mentioned earlier, the changeset file name generated by the add command is random (generated by the human-id library), so in a rapid iterative Monorepo development scenario. For example, the author’s Monorepo project generates about 20+ Changeset files every week, and the names of these files are random, which is very difficult for users to manage and identify.

Therefore, after forking the project, I modified the @Changesets/Write code so that the generated Changeset files can be displayed in the form of branch name + user name + ID, so that different developers can filter their Own Changeset files.

None of the commands support item filtering

For example, the add command cannot specify specific packages. Instead, the previous getPackages() method gets all the subproject names to select from. If there are dozens of subprojects under a project, finding specific projects can be costly.

However, the add command will at least screen out the modified subpackage names through git diff, which to some extent reduces the cost for users to find projects. However, the version command does not provide the corresponding filtering function, resulting in some scenarios, Users who just want to consume a specific Changeset file to update a specific package can’t do it.

So I fork the project and link it with PNPM’s filter mechanism (reference: PNPM.io/Filtering)…

The Prelease package publishing process is cumbersome

If you want to send some test version packages using Changesets, you need to repeatedly execute changeset Pre Enter, Changeset Pre Exit and Changeset version commands. The whole process is very complicated. In fact, in the process of self-maintenance, these trivial processes can be assembled into a command to complete, without consuming so much cost.

Lack of project maintenance

This point is actually an important reason to support the author to fork the source code for a new set of it. At present, the project is in a state of no PR merge for a long time. In the past six months, the MERGED PR are some simple document modifications without substantial functional progress:

At the same time, the documentation of Changesets itself is still insufficient. For example, some common FAQ documents are still in the TODO state.

However, the good news is that the author has become active recently and responded to a large number of issues. I hope the whole project can be put into operation again in the near future.

conclusion

The current Changesets scheme is generally applicable to the Monorepo project, and there are no major technical difficulties in the overall architecture. The main difficulty lies in version Bump.

The author thinks that the biggest advantage of this scheme is that it provides users with great autonomy and can make some appropriate adjustments in complex business scenarios. For example, users can modify the Changeset file, Changelog file, or even the unsatisfactory version after bump version.

Compared with the idealized scheme provided by Lerna, Changeset itself is a set of highly versatile schemes, and more suitable for some operation modes under the current Monorepo workflow scenario, although it still has many shortcomings 😅.

Expect changesets to become more and more popular in the future as it is currently used by many Monorepo projects