Photo by Nicolas Radzimski on Unsplash
Our team relies on an internal GitLab NPM registry for managing packages. This works perfectly for internal distribution, but there are instances where a package needs to be shared with external collaborators. Syncing the package from the internal registry to an external one became a new challenge.
I set out to find a solution and, as usual, encountered a series of hurdles that turned this into another chapter in my developer struggles.
Initially, I thought I could handle this with raw HTTP requests, essentially mimicking what the NPM CLI does under the hood. The idea was simple: query the source registry for package metadata, fetch the tarball, and upload it to the target registry.
• The process was too complex and fragile.
• It required manually constructing the entire package.json manifest for every version.
• Every small misstep led to broken packages.
Next, I discovered npm-registry-sync, a library designed for syncing NPM registries. This tool almost solved the problem; it could monitor changes and replicate them across registries.
• It operates in “daemon mode,” polling for updates continuously.
• In a GitLab CI pipeline, I needed a one-off execution, controlled entirely by the pipeline — no background processes allowed.
Eventually, I realized I could stick to the tried-and-true NPM CLI. The steps were straightforward:
While this worked like a charm, there were a few extra steps needed to make it CI-friendly.
Managing registry configurations dynamically in a CI pipeline was a bit tricky.
Here’s how I solved it:
Using the npm CLI, you can set parameters for each registry:
npm config set "//my.awesome.registry.com:<parameter name>=<parameter value>"
Important Gotcha:
The URL in the config must exclude the protocol (https:).
To associate a specific namespace or package with a registry:
npm config set "<your namespace>:registry" "<your registry url with https:>"
Some registries required a username/password combo, while others used tokens. Here’s what I learned:
Tokens are straightforward, but ensure you strip the protocol when configuring the auth URL:
npm config set "//my.awesome.registry.com:<parameter name>=<parameter value>"
Generating a basic auth hash (username:password) required attention to detail. In some distros, the base64 command has quirks that differ from others.
On macOS:
npm config set "<your namespace>:registry" "<your registry url with https:>"
Will give you (as expected):
npm config set "//my.registry.com:_authToken=<token>"
With docker linuxkit:
echo -n "<my username & password hash>" | base64
What is going on here? There is a line break!
On some distros, the wrap parameter has a default set to 76 chars for formatting private keys etc.
It works like this:
d2hhdCBhcmUgeW91IGRvaW5nIGhlcmU/IGdvdCB5YSEgc29tZSBtb3JlIHRleHQgdG8gbWFrZSB0aGlzIHJlYWxseSByZWFsbHkgbG9uZw==
The echo -n is very important. If omitted, echo will add a linebreak to the end of the string and this will manipulate your hash.
Putting it all together, the script could look like this:
d2hhdCBhcmUgeW91IGRvaW5nIGhlcmU/IGdvdCB5YSEgc29tZSBtb3JlIHRleHQgdG8gbWFrZSB0 aGlzIHJlYWxseSByZWFsbHkgbG9uZw==
So you could use it like this:
echo -n "<my username & password hash>" | base64 --wrap 0
Stick to Simple Tools:
The npm CLI might not be fancy for this task, but it’s reliable and gets the job done.
Mind the Details:
Configuring authentication, especially with base64, can have subtle platform-specific quirks.
Keep It CI-Friendly:
Avoid solutions like daemons or background tasks when working in CI/CD pipelines. Keep the process under pipeline control.
Syncing npm packages between registries was a frustrating but rewarding learning experience. If you’re facing a similar challenge, I hope these lessons help you navigate the struggle with a bit more ease!
The above is the detailed content of Syncing an NPM Package Between Multiple Registries. For more information, please follow other related articles on the PHP Chinese website!