Using SNAPSHOT in private NPM like in Maven

Tristan picture Tristan · Jul 1, 2015 · Viewed 21.9k times · Source

I have a very simple working setup:

Lib A                                             Lib B
publish 1.0.0-SNAPSHOT  ->  Private Registry  ->  npm install

Keep in mind that A and B are on different machines (think jenkins cluster!).

During two releases the versions end with "-SNAPSHOT" and are updated on every build (A). Of course the depended libraries (B,...) should always use the newest version from the registry. Problem is, NPM will not refetch the same version.

Lib A                                           Lib B
small change, rebuild:
publish 1.0.0-SNAPSHOT  ->  Private Registry  !!  npm install -f
                                                  npm cache clean
                                              !!  npm install -f
                                                  rm -rf node_modules
                                              ->  npm install

Idea: Disable cache for NPM globally

That would be okay because we a local npm proxy: "~/.npmrc":

force=true
cache-min=0
cache-max=0

But this does not work! Only removing the node_modules directory works. This is not a usable solution because the developer should not need to use "rm -rf node_modules && npm install".

With maven this setup works because we use the "-U" option. This forces maven to check if an updated SNAPSHOT version is available. NPM does not understand the concept of SNAPSHOT but at least is should ask the registry every time.

npm version 2.12.0

Private Registry: Sonatype Nexus™ 2.10.0-02

Answer

dustin.schultz picture dustin.schultz · Jan 24, 2016

Idea 1

You can sort of mimic this behavior if you "abuse" the pre-release part of SemVer. I've used the following strategy successfully:

Publish your modules with -SNAPSHOT but append an incremented number each time you publish (-SNAPSHOT.# or -SNAPSHOT-#).

For instance: "x.x.x-SNAPSHOT.1" the first publish, then "x.x.x-SNAPSHOT.2" the second publish and so on.

Make sure you use a consistent pattern. So, for instance, if you used dots, stick with dots, or if you used dashes, stick with dashes.

For dependent modules, you only need to declare your version as

"^x.x.x-SNAPSHOT"

and NPM will fetch the latest version.

All of this works due to 2 reasons

  1. SemVer treats pre-releases in the following manner 1.0.0-SNAPSHOT < 1.0.0-SNAPSHOT.1 < 1.0.0-SNAPSHOT.2 < 1.0.0-SNAPSHOT.3 ...
  2. NPM will always retrieve the latest pre-release version

Caveat: this will only work for patch versions. Technically 1.2.x-SNAPSHOT is greater than 1.1.x-SNAPSHOT, however, SemVer does not consider this when using pre-releases.

From the docs:

If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will only be allowed to satisfy comparator sets if at least one comparator with the same [major, minor, patch] tuple also has a prerelease tag.

For example, the range >1.2.3-alpha.3 would be allowed to match the version 1.2.3-alpha.7, but it would not be satisfied by 3.4.5-alpha.9, even though 3.4.5-alpha.9 is technically "greater than" 1.2.3-alpha.3 according to the SemVer sort rules. The version range only accepts prerelease tags on the 1.2.3 version. The version 3.4.5 would satisfy the range, because it does not have a prerelease flag, and 3.4.5 is greater than 1.2.3-alpha.7.

Idea 2 Again, this is another "hack". If you're ok with losing the patch part of SemVer, you can publish your versions as

x.x.<unix epoch ms>.

The unix epoch is ever increasing (at least until 2038 for 32 bit) and each time you publish, you will always be publishing a greater version.