background

The company's internal NPM is often complained about due to some inherent bugs. Recently, I just have time to optimize, and then I try to solve a publish bug I encountered before. Below is the analysis record.

Problem phenomenon

The company's intranet NPM chose to use verdaccio for the service. At present, when a module publish includes the deprecated field, the historical version is lost, and only the version information of this publication is left.

problem causes

When NPM CLI implements deprecate, the process is like this:
https://github.com/npm/cli/blob/latest/lib/commands/deprecate.js

  1. Request the get interface to get the information of the current module
  2. Then modify the deprecated field of the corresponding version
  3. Request the put interface to update the module

Then the implementation of the new module in CLI is:
https://github.com/npm/libnpmpublish/blob/main/publish.js

  1. Read the contents of local package.json
  2. Request put interface upload module

When Verdaccio implements the server, the update module and upload module are the same service interface, and the two actions are not handled properly: https://github.com/verdaccio/verdaccio/blob/master/packages/api/src /publish.ts#L148

This part of the logic is deprecated processing logic: https://github.com/verdaccio/verdaccio/blob/master/packages/api/src/publish.ts#L306-L322

local-storage processing logic: https://github.com/verdaccio/verdaccio/blob/master/packages/store/src/local-storage.ts#L408
The invalid version information is deleted here: https://github.com/verdaccio/verdaccio/blob/master/packages/store/src/local-storage.ts#L429-L440

For example, if there is already a module with versions 1.0.0 and 1.0.1, then the data received by the service when publishing is triggered to upload version 1.0.2 is as follows:

{
  "name": "module_name",
  "version": {
    "1.0.2": {
      "deprecated": "xxx" // 如果有的话
    },
  }
}

And if you call npm deprecate module_name@1.0.0 "xxx" directly, the data received by the service is like this:

{
  "name": "module_name",
  "version": {
    "1.0.0": {
      "deprecated": "xxx" // 如果有的话
    },
    "1.0.1": {}
  }
}

The processing logic of the two on the server side is the same:

  1. storage modify the corresponding version information
  2. Filter and remove invalid version information (for example, the information of 1.0.0 and 1.0.1 will be removed here)
  3. Use the current metadata to overwrite the original package.json information

Eventually, if the package.json contains the deprecated parameter when publishing, the historical version will be lost.

Repair method

The repair method is also relatively simple, in fact, the main thing is to be able to distinguish whether the current interface trigger is caused by deprecate or publish.
Then we manually read the versions information of the current module once, and then compare the metadata received when the interface is triggered this time. If it is publish, then it will definitely not match.
Then you can add a new detection when triggering deprecated to detect whether it is published when carrying deprecated, this situation is directly ignored, and the original new module upload process is entered.

summary of a problem

In summary, the deprecated field is more like a NPM internally agreed field, rather than an explicit field that needs to be written by the user in package.json. If you need to add obsolete information to the version, please use the officially recommended solution: https ://doc.codingdict.com/npm-ref/cli/deprecate.html

And the PR has been submitted to the official, looking forward to a wave of responses: https://github.com/verdaccio/verdaccio/pull/2766


Jarvis
5.1k 声望4.7k 粉丝

GitHub: [链接]