background
Changesets is an atlas product of jira company, which has been transferred to the new organization of changesets for special maintenance.
- repo address: changesets/changesets
Who's using it?
Presupposition theory
Talk about workflow
workflow consistency problem
How to reach an agreed workflow?
- Within the company: pull through alignment to form a consistent resultant force
- Open source: github bot + github actions
Workflow solution of open source project
Officially recommended automated solution: Automating Changesets
github bot
- Introduction to Bot: changeset-bot
-
Standardize developer behavior
-
Automatically generate the release changelog report and control the unified release behavior (example: changesets pull) #718 )
github actions
- action address: changesets/action
Therefore, the solution process of Auto workflow of open source project is:
-
Development stage: the developer develops the code, carries out PR, and the github bot specification ensures the submission of the change set. Here is attached:
a. changelog of single change
b. Degree of impact on version -
Collection stage: the project owner collects the PR required for approve. At this time, the github bot will accumulate all changed release PR in the form of one pr. It contains:
a. Project release changelog
b. version change of each sub package
c. changelog for each sub package -
Release phase: after a period of time and collecting enough pr (change set), the project owner will merge the release PR proposed by github bot, and the release will be automatically released by github actions. Here:
a. Automatically send version to npm
b. Each sub packet receives changelog attachment and version change
Practical empowerment
Install changesets
# Install changesets pnpm add -W -D @changesets/cli # Initialize changesets folder npx changeset init
Configure changestes
to configure. changeset/config.json :
{ "$schema": "https://unpkg.com/@changesets/config@1.6.1/schema.json", // changelog generation method "changelog": "@changesets/cli/changelog", // Open source projects can use the changelog in github format with a commit link // "changelog": ["@changesets/changelog-github", { "repo": "changesets/changesets" }] // Don't let changeset do git add for us when publish ing "commit": false, // Configure which packages to share versions // Reference 1: https://github.com/changesets/changesets/blob/main/docs/config-file-options.md#linked-array-of-arrays-of-package-names // Reference 2: https://github.com/changesets/changesets/blob/main/docs/linked-packages.md#using-glob-expressions "linked": [], // Public and private security settings are available. restricted is recommended for intranet, and public is used for open source "access": "restricted", // Project main branch "baseBranch": "origin/main", // Ensure that the package that a package depends on is upgraded, and the unit of measurement (magnitude) of version upgrade should also occur for the package // https://github.com/changesets/changesets/blob/main/docs/config-file-options.md#updateinternaldependencies "updateInternalDependencies": "patch", // Packages that do not need to change version "ignore": [], // Every time the version changes, there must be no reason for patch to raise the versions of those packages that depend on him, so as to prevent major priority from not updating "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { // https://github.com/changesets/changesets/blob/c68536edf4c04e7fdf5594ec9c69471cd86fd0ce/packages/assemble-release-plan/src/determine-dependents.ts#L88 "updateInternalDependents": "always" } }
For the introduction of various options, please directly refer to the official document description. Here are two recommended solutions for different scenarios:
Business project
{ "$schema": "https://unpkg.com/@changesets/config@1.6.1/schema.json", "changelog": "@changesets/cli/changelog", "commit": false, "linked": [], "access": "restricted", "baseBranch": "origin/main", "updateInternalDependencies": "patch", "ignore": [], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { "updateInternalDependents": "always" } }
Open source project
{ "$schema": "https://unpkg.com/@changesets/config@1.6.1/schema.json", // ⬇️ This is different from the business configuration~ "changelog": ["@changesets/changelog-github", { "repo": "owner/repo" }], "commit": false, "linked": [], // ⬇️ This is different from the business configuration~ "access": "public", "baseBranch": "origin/main", "updateInternalDependencies": "patch", "ignore": [], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { "updateInternalDependents": "always" } }
Modify packages json
Modify the package. In the project root directory json :
// package.json // newly added "scripts": { // Build the product of the whole project "build": "pnpm -r --filter ./packages run build", // 1. Start filling in change sets interactively "changeset": "changeset", // 2. Used to uniformly upgrade the version number "version-packages": "changeset version", // 3. Build the post release version of the product "release": "pnpm build && pnpm release:only", "release:only": "changeset publish --registry=https://registry.npmjs.com/" } // newly added "publishConfig": { "access": "public" }
Here are also two solutions for different scenarios:
Business project
// package.json "scripts": { "build": "pnpm -r --filter ./packages run build", // ⬇️ Due to the need for frequent use, shorter commands are used locally to save costs 🥰 "change": "changeset", // ⬇️ Since there is no github bot inside, the cost can be saved locally with shorter commands 🥰 "vp": "pnpm version-packages", "version-packages": "changeset version", "release": "pnpm build && pnpm release:only", // ⬇️ Configure company source "release:only": "changeset publish --registry=https://company-registry/" }
Optional: add npmrc is limited to private sources. If the default configuration is all, this step is unnecessary
# .npmrc @scope:registry=https://company-registry/
Open source project
// package.json "scripts": { "build": "pnpm -r --filter ./packages run build", "changeset": "changeset", "version-packages": "changeset version", "release": "pnpm build && pnpm release:only", "release:only": "changeset publish --registry=https://registry.npmjs.com/" } "publishConfig": { "access": "public" }
Advanced thinking
What is the business project release flow?
- Different developers develop first and use pnpm changeset to write a change set when submitting PR
- The project owner contracts regularly, uses pnpm version packages to consume all change sets, and changesets automatically promotes the sub package version and generates changelog 😆
- Execute pnpm release to build all projects and contract out 🥰
What is the release flow of open source projects?
- With the help of github bot, each developer submits a change set before PR
- With the help of the github bot, the project owner regularly clicks the release PR proposed by the join bot to join the upgraded version in one click to generate the changelog 😆
- With the help of github actions, when the release PR is closed, it will automatically contract to npm 🥰
As you can see, what did the project owner do when publishing? A few mouse clicks 😅 , However, changelog, version upgrade and contract awarding are not few, which is really nice.
How to use changeset publish well?
In fact, changeset publish is just a pure contract issuing command. It will publish all packages once. Therefore, even if you do not upgrade the version through workflow, you can manually upgrade / modify the version and then change set publish.
For example, if you have an urgent test scenario, you can quickly and manually modify it to the version publish test with tag.
How to release with tag (like beta version)?
Method 1: manual debugging method
According to our understanding of changeset publish above, you can manually modify the version number of a package after modifying the code, and then tag and publish it:
// package.json { "name": "@scope/some-package", "version": "1.0.1-beta.1" }
# Be careful not to forget the tag option pnpm changeset publish --tag beta
Method 2: overall debugging method
Use the official prerelease mode to enter the pre mode first:
# Enter the prerelease mode with beta tag pnpm changeset pre enter beta
After that, changeset publish in this mode will default to the beta environment. Here is an example:
# 1-1 made some development # 1-2 submitting change sets pnpm changeset # 1-3 upgrade version pnpm vp # changeset version # 1-4 contract awarding pnpm release # pnpm build && pnpm changeset publish --registry=... # 1-5 to get 1.0.0-beta one # 2-1 made some development # 2-2 submitting change sets pnpm changeset # 2-3 upgrade version pnpm vp # 2-4 contract awarding pnpm release # 2-5 to get 1.0.0-beta two # ......
After complete debugging, exit the prerelease mode:
pnpm changeset pre exit
You can see that this method is more systematic. Of course, in order to facilitate local debugging and pre issuance, you can aggregate commands to run together as much as possible and shorten the command length.
In the business project monorepo, how can the mixed scenario of business applications and basic libraries be opened gracefully?
Problem scenario
Considering a business scenario, we have to put the business application in monorepo and the basic library and Toolkit in monorepo (they may not be a workspace folder, so it won't be very chaotic).
Considering the complexity and agility of the business, the advantage of this is that you can directly use the workspace:version protocol in the warehouse for rapid use without publishing, and there is no need to jump repeatedly between multiple repo s.
Problem disassembly
At this time, we will encounter a problem. Although we specify private: true for the business project, it will not be contracted, because it depends on the tool library in the warehouse, when the tool library is upgraded, the version of the business application will still be upgraded, and a changelog will be generated in the directory of the business project (even if the version field is not filled in), This was unexpected.
How to solve it?
Problem solution
In order to solve this problem, we need to add the name of the business application to the ignore field in the changeset configuration file to represent that no operation should be performed on the project.
But for a monorepo, how to solve the problem when there are more business items? Do you fill in ignore manually every time? Obviously not. We need monkey patch pnpm changeset, such as:
// package.json "scripts": { // This script will help us collect all private packages // And add their name s to the ignore list in the changeset configuration file "change": "node ./scripts/change.js", }
In this way, the problem of mixing business projects and tools can be solved automatically.
How to build the optimal pre dependency before each release of a business project?
Consider a common scenario in a business monorepo warehouse:
- We have three basic packages @ scope/a, @ scope/b and @ scope/c
- We need to build a project @ project/a, which relies on @ scope/a
If it is obviously a waste to build all the basic packages before releasing the project every time, how to solve it?
There are several solutions:
- Recursive construction using pnpm's -r native
- Use tools such as turborepo to find the best construction path
Let's compare:
Comparison item | pnpm -r | turborepo |
---|---|---|
cache | 😅 None. The secondary build needs to be packaged again | 🥰 have The local secondary construction can be skipped according to the hash. In cicd, the file cache of the last container can be used to skip the construction |
best build path | 😅 None. All -r base packages are built recursively each time | 🥰 have turbo will automatically find out which dependencies the project to be built depends on in advance. Only the required ones will be built, and the construction order is the best |
flexible exec script | 🥰 have pnpm exec can flexibly specify the command to be executed each time, and supports attached parameters | 😅 None. Only the commands contained in each package scripts can be executed |
Add another entry-level tutorial on my understanding of turborepo:
Optimal construction of complex topological relationships using turbopo
turborepo is not the only solution. There are also build system organization tools such as nx.
What does the domestic industry do?
Changesets: a popular monorepo scenario contracting tool
What's customized?
- Changeset name (default random, non customizable) generation rule
- pre release release release command combination
Comparison of similar competitive products?
lerna
- High starting cost and need to be installed in advance
- yarn does not support monorepo natively, and requires cumbersome configuration
- Implicit dependency, ghost dependency
- changelog immature
- No active maintenance
rush
The idea is similar. Developers need to provide change instructions, but the process is more cumbersome than changesets, and the documents are discouraged 🤬
The basis of effectiveness in my eyes
-
pnpm monorepo
-
changesets
-
turborepo