Distributing Rust binaries through NPM
![cover](https://cdn.sanity.io/images/9coakvkk/production/287aa0a521f2ffc06c94af182a8667e4a714977b-532x532.png)
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.