Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node executable and libs seem to be duplicated #809

Open
unphased opened this issue Aug 1, 2024 · 10 comments
Open

node executable and libs seem to be duplicated #809

unphased opened this issue Aug 1, 2024 · 10 comments

Comments

@unphased
Copy link

unphased commented Aug 1, 2024

Bug Report

Summary and Repro

I set $N_PREFIX to ~/.n from my shell.

n seems to install:

  • ~/.n/n/versions/node/22.5.1/ (around 183MB)

  • ~/.n/bin, ~/.n/lib, ~/.n/include, ~/.n/share:

    ❯ du -hs ~/.n/*
    109M    /home/slu/.n/bin
    56M     /home/slu/.n/include
    18M     /home/slu/.n/lib
    183M    /home/slu/.n/n
    72K     /home/slu/.n/share
    

Expected Behaviour

Some sort of symlink from ~/.n/bin/node to ~/.n/n/versions/node/22.5.1/bin/node and so on.
If I install one node version it will consume 180-whatever MB disk.
If I install two node versions it will consume 360-whatever MB disk.

Actual Behaviour

If I install one node version it will consume 360-whatever MB disk.
If I install two node versions it will consume 550-whatever MB disk.

Other Information

I fully expect that there are various issues that have come up in the past that express some desire for $N_PREFIX/bin/node and friends NOT to be symlinks. However, I am not sure I buy that, because $N_PREFIX/bin/npm is itself a symlink, to a "duplicated" $N_PREFIX/bin/../lib/node_modules/npm/bin/npm-cli.js:

❯ ll ~/.n/bin/npm
lrwxrwxrwx 1 slu slu 38 Aug  1 07:38 /home/slu/.n/bin/npm -> ../lib/node_modules/npm/bin/npm-cli.js

Even if the 100+MB node executable has to get duplicated on disk why must we also do so for include/ and lib/?

@unphased
Copy link
Author

unphased commented Aug 1, 2024

I guess one "workaround" is if we want to save disk with things as they are, we can use n to install some version of node and then uninstall it with n. This should remove it then from $N_PREFIX/n/versions while leaving the whole installed copy under $N_PREFIX/bin etc. By this perspective $N_PREFIX/n/versions/* is a simple download cache.

HOWEVER. the behavior of n prune Remove all downloaded versions except the installed version would contradict this. In the situation where one version of node has been installed via n, running prune will not remove that one version, but it should if the above were true.

@shadowspawn
Copy link
Collaborator

shadowspawn commented Aug 1, 2024

As you worked out, the downloaded versions are cached in $N_PREFIX/n/versions and also installed (copied) to $N_PREFIX/bin.

So when you install 10 different versions of Node.js you end up with 11 copies. The active version and the 10 versions in the cache.

HOWEVER. the behavior of n prune Remove all downloaded versions except the installed version would contradict this.

This is how prune was originally implemented (#407). The installed (active) version of node is not deleted from the cache by prune. So only keeping the most relevant version. This lets you try another version of node and get back to where you were.

Following on from my original example, when I get annoyed I have 11 copies of node because I installed 10 different versions, I can run n prune which will drop me back down to 2 copies. One active, and one in the cache.

It is ok to install a version of node and then immediately delete it from the cache, like:

n 18.15.0
n rm 18.15.0

@unphased
Copy link
Author

unphased commented Aug 1, 2024

Yeah I see that to get full storage efficiency we can n rm the installed version. It's just definitely annoying that prune will take your 11 copies across 10 versions down to 2 identical copies of the installed version when the spirit of prune would here clearly call for bringing it down to just leaving installed intact.

@unphased
Copy link
Author

unphased commented Aug 1, 2024

What's even more confusing is this:

  • n latest -- installs 22.5.1
  • n 20 -- installs 20
  • n rm 20
  • n rm latest

Installed node is v20

  • n ls
    shows empty list! At minimum here it should be telling you that v20 is currently installed.

Clearly it's an uncommon use case as n is intended for rapid switching. But even the copying step if you switch between two cached versions takes nontrivial time because the executables and files are being actually copied around... somebody has to have an explanation for why we're doing all of these shenanigans when they could be done via changing symlinks.

@unphased
Copy link
Author

unphased commented Aug 1, 2024

While I'm complaining about that. If you just

  • n and pick 20
  • n and pick 20
  • same if you run n 20

In all these scenarios 20 is already installed but it repeats all the work of copying it in anyway.

@shadowspawn
Copy link
Collaborator

shadowspawn commented Aug 1, 2024

In all these scenarios 20 is already installed but it repeats all the work of copying it in anyway.

I understand your desire to skip the install. I used to think that was a good idea too.

The reinstall was a deliberate change in v5.0.0. The goal is a simple predictable behaviour. When you run n install 20 (or pick 20 again) it actually installs 20.

The previous behaviour was it would not install if the active version matches. Great when things are working properly. The problem is if your active version is broken. I found myself suggesting people with problems do things like:

n 8.0.0 # any version other than the one you actually want
n 20.0.0 # the one you actually want, and because it is changing versions it will actually reinstall

@shadowspawn
Copy link
Collaborator

n ls
shows empty list! At minimum here it should be telling you that v20 is currently installed.

n ls shows the local versions available in the cache.

@stasadev
Copy link

stasadev commented Aug 1, 2024

@unphased, thank you for opening this issue.

I'm working on adding node from n in Docker and before using N_PREFIX I tried to install one node globally in /usr/local, but it was taking up suspiciously much space.

@shadowspawn thank you for mentioning n rm, it solved the problem.

ENV NODE_VERSION=20
RUN curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n | bash -s "${NODE_VERSION}" && \
    npm install -g n && \
    n rm "${NODE_VERSION}" && \
    ln -sf "$(which node)" "$(which node)js"

@shadowspawn
Copy link
Collaborator

n is a single bash file, so you could just save the curl download instead of piping it to bash. Something like:

curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n -o /usr/local/bin/n
chmod a+x /usr/local/bin/n
n install "${NODE_VERSION}"
n rm "${NODE_VERSION}"

The n README suggests installing using npm so that you can manage n along with any other global npm packages, like npm list -g and npm outdated -g. This may not be useful in a docker container.

@shadowspawn
Copy link
Collaborator

I would like to make it easier to install node with a single command without leaving behind a cache copy. Trying out some option names:

n install --rm auto
n install --cleanup auto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants