niels segers

Distributing Rust binaries through NPM

cover

Sep 21, 2023

A few months ago, I made the decision to rewrite supdock once again, this time using Rust. However, I needed to find a solution that wouldn't force users to change how they installed the package if they were accustomed to using npm for installing updates.

After a bit of research I figured the key to properly handling this is by utilizing npm's preinstall script. As you can guess from the name, yes it runs before the actual install. So... theoretically if the binary is in downloaded or in place before npm install runs, the symbolic link should be able to be created.

There are a few ways I thought to approach this.

1. Downloading the binaries

Initially, I experimented with this option but felt uneasy about potentially violating user privacy by downloading files without their knowledge. This also kind of serves as a reminder of the risks associated with pre and post install scripts. Always be cautious.

The idea is that a preinstall script determines the current architecture where the package is installed on. Based on that architecture it downloads the binary from the corresponding GitHub release and moves that to the bin directory, allowing a symbolic link to be created when the actual npm install hits.

2. Bundling all binaries in the published package

Apart from the added package size and disk space, this appears to be a reasonable and less intrusive approach. However, it does introduce an additional point of potential failure since all logic would have to go through a wrapper script that still needs to determine the current architecture to determine the right binary to run. This or there is a preinstall script which renames the binary to something that the package.json bin points to.

3. Bundling the binaries, but cleaning up like a good boy

Ultimately, I settled on this option. Bundle all the binaries in the published package, but have a preinstall script that removes all the binaries of the other architectures.

Although it still feels slightly weird to remove files on the filesystem, I am less concerned because it only involves managing bundled binaries from the package itself. The initial download may be larger due to the bundled binaries, but the final installation size will only be that of a single binary.

I am still not entirely sure if this option is what I'll eventually stick with. Option #1 ultimately feels like the most straight forward one, but I can't get over the idea of a bad actor adding a preinstall script to open source code that downloads something that is harmful to the filesystem. It just feels wrong.