Distributing NPM packages is a frequent requirement for slightly larger plants, so it is not feasible to perform computationally versioning commands locally and NPM publish, metropolitan has a separate deployment system to help us do this automatically.

Today, I’ll talk about the core computing version of the deployment system and the logic and flow of the release.

Semver semantic version

Before we talk about deploying systems, let’s talk about semantic versioning, because I’ve found that a lot of people don’t know much about it.

Version of the meaning

  • < 1.0.0: beta, indicating that the library API is currently unstable
  • 1.0.0 or greater: Official
  • Versions with alpha, beta, and RC tags are collectively referred to as advanced versions in the format of X.Y.Z -[tag].
    • Alpha: internal version
    • Beta: public beta version
    • Rc: Pre-release official version

Version number format

Generally, the version numbers are in the format of X.Y.Z. The meanings are as follows:

  • X: Major, the major version number that should be changed when an incompatible API occurs
  • Y: Minor, the minor version number that should be changed when a new backward compatible feature is introduced
  • Z: Patch, which should be changed when backwards-compatible bugs need to be fixed

But the semantics are not immutable. For example, if the version number is beta (less than 1.0.0), we can change the semantics to 0.minor.patch. Since incompatible apis are common at this point, you should not change the major version number directly, but should change the minor version number, and reflect the version changes caused by new features and bug fixes on the patch.

Version Change Rule

X.Y.Z must be a positive integer and cannot be preceded by zeros.

Each time X.Y.Z changes the version number, it needs to reset the smaller version number to 0. For example, upgrade 1.0.2 to 1.1.0.

If an earlier version of the same version is released multiple times, just change the last number or meta value. For example, if 1.0.0-beta.0 is released again, the prior version should be 1.0.0-beta.1.

Beta versions generally start at 0.1.0. A formal release is usually released after a rapid iteration and when the developer considers the API to be stable.

Automatic calculation of version prerequisites

terms

NPM projects are divided into two package structures:

  • Single package, where only one NPM package exists in a project that needs to be published
  • Multiple packages. Multiple NPM packages that need to be distributed in a project are usually managed by LERNA

content

If we need to implement automatic release, we need to let the service know exactly what we need to release, otherwise it will not be possible to implement automatic release anyway.

So we need to introduce the commitizen tool.

This tool can help us to submit normalized commit information:

The format is as follows:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
Copy the code

Generally, it is not so rigorous for daily development. Type and description are mandatory, and breaking is selected when needed. Other contents are optional.

Perhaps the reader does not understand what these three represent respectively, the author will explain first

Type refers to the code changes made by this commit, which can be classified as follows:

  • Feat: A new feature that can cause the official version to change the version number
  • Fix: Fixes bugs, which can cause the official version to change the patch version
  • Refactor: Code refactoring without causing version changes
  • Docs: Documents are related and do not cause version changes
  • Style: Code format changes do not cause version changes
  • Test: indicates a test case that does not cause version change
  • Chore: Modifications related to project configurations that do not cause version changes

Of course, if the user has personalized needs, but also can add and delete this part of the content.

Description is a literal representation of local COMMIT information.

Finally, breaking, when there is an incompatible API, we need to submit this part of the content and inform the official version that the major version number needs to be changed.

PS: All the above are official releases. If the current release is beta, please refer to the version Change Rules section.

The resulting output looks something like this: Fix: do not alter Attributes.

Another point of attention is the problem of multiple packages. If you use a unified version of the distribution things are basically back to a single package structure, very simple. However, if the versions are not consistent, we need to collect which packages have changed files and only operate on the changed packages at deployment time.

There are roughly two ways to do this:

The first method is @lerna/ Changed, a tool that helps us find all the packages that have changed. Git command is the key to git command:

git diff --name-only {git tag / commit sha} --{package path}
Copy the code

Look for file changes in a package from the last Git tag or the first commit.

The second way is to modify git CZ tool and add a new function: every time you commit, you will automatically add the package that has been changed in this commit. Of course, the principle above is still used in the bottom, but we can customize more functions according to the demand, more freedom.

Formalized COMMIT information is the cornerstone of an automated deployment system, either by tool or by hand, and gives developers a clear picture of what changes have been made to the project.

How are versions calculated in the deployment system

The computational version is an interesting thing, we need semver to help us calculate, of course you can also use Lerna in multi-package scenarios, but we will just use Semver internally.

In addition to calculate the version of this matter needs to be divided into scenarios to discuss, next we look at one by one.

Before we start, of course, we need to understand the version of the common change rule, as several scenarios are based on this common rule.

Generic change logic

First of all, there are several types needed for the downscaling:

major | minor | patch | premajor | preminor | prepatch | prerelease

I talked about the first three before, and I won’t go into them here.

For example, we can change version 1.0.0 to 2.0.0-beta.0, which is essentially the same as the previous three versions, except for an additional prior release number.

The last one is also the prior version. In the beta version, for example, you can change version 1.0.0 to 1.0.1-beta.0, and you can change version 1.0.1-beta.0 to 1.0.1-beta.1.

Knowing the change version types, it’s time to figure out how to derive them. Typically, users will commit multiple commits, so we first need to find all the commit types and pick a maximum.

For example, if the user has committed three commit types (feat, doc, breakchange) since the last release, the maximum type is breakchange.

Once we have the maximum commit type, we need to calculate it for different versions. For example, the official version and the beta version of the release rules are different, see version number format, no further details.

For example, if the current version is 1.0.0, the maximum type is feat, and the prior version (beta) needs to be released. Therefore, the final change rule is preminor, and the version can be upgraded to 1.1.0-bet.0.

Analyzing Commit Information

We need to analyze the commit to get the type.

Git command:

git log -E --format=%H=%B
Copy the code

For the above commit, we can execute the command to get the following results:

In most distributions, we only need to analyze all the changes since the last release, so we need to fix the command:

git logLast commit ID... HEAD -E --format=%H=%BCopy the code

Finally, regular expressions come into play, matching whatever information you want.

Single package scenario

The single-package scenario is actually the simplest, just applying the common change logic.

More package scenario

A pure multi-package environment is also simple. One step beyond a single package is to find out which files have been changed, filter out the type of the current package in the COMMIT, and apply common change logic.

Multiple packages and interdependent scenarios

Package A, package B, package C, package A, package B, package C, package A, package B, package C, package A, package B, package C

When the general logic ends, we need to determine whether package versions need to be changed based on dependencies.

For example, local submission requires patch to change package B version, while the other two packages have no code changes. But in fact, we need to change the version of A, otherwise, the users of package A will not use the new version of package B without upgrading A.

finishing

When we have all the versions calculated, we need to write package.json and do NPM publish, and then we need to commit code and tag it.

The last

This article is about the calculation of the version and the release of this part of the content, we can exchange questions to discuss.

And I’m sure some readers will say this is too much work, but there are other tools to simplify the process. Of course, the author knows this, but the principle of their automatic modification version is the same as this article, it is not too much to know how to do things under the tool.