yarnpkg / berry

📦🐈 Active development trunk for Yarn ⚒
https://yarnpkg.com
BSD 2-Clause "Simplified" License
7.23k stars 1.07k forks source link

[Bug?]: Yarn Cache can't be create no modify anymore #6368

Open jhudsoncedaron opened 4 days ago

jhudsoncedaron commented 4 days ago

Self-service

Describe the bug

On upgrading from yarn 1 to yarn 4, after following the upgrade instructions as much as possible, yarn install fails with:

➤ YN0001: │ Error: typescript@npm:5.4.5: EPERM: operation not permitted, chmod '\\server\yarn-offline\cache\typescript-npm-5.4.5-8568a42232-2954022ada.zip-ca9885985aca6013.tmp'

So the problem is pretty obvious; it's trying to chmod and rename files in a create-no-modify sharepoint. In order to protect against disaster, the entire tree is permission-protected so no files can be changed after creation.

To reproduce

1) Create a create-no-modify sharepoint (\server-yarn-offline) 2) Create path within: cache\v6.tmp 3) Set environment variables:

YARN_CACHE_FOLDER=\\server\yarn-offline\cache
YARN_ENABLE_GLOBAL_CACHE=false

3) Run yarn set version stable 4) Run yarn install

Get nice chmod error messages.

Merely jumping around the chmod() call won't work. The files don't have the right names yet.

The share is create-no-modify. Anything written to it can't be changed again.

Environment

➤ YN0001: │ Error: envinfo@npm:7.13.0: EPERM: operation not permitted, chmod '\\server\yarn-offline\cache\envinfo-npm-7.13.0-a98aeb2561-9c279213cb.zip-71b6913ba4ac8d20.tmp'
➤ YN0000: · Failed with errors in 0s 285ms

OS: Windows 11 x64 NPM Version: 10.5.0 node version: v20.12.1



### Additional context

Regression from Yarn 1.

Cache needs to be global so that packages exist even when they're gone.
Cache needs to be immutable to prevent some wise guy from mutating packages in the cache.

I'm not certain if I could fix this or not.
SheepReaper commented 6 hours ago

I don't understand your use case. disabling global cache is typically done to point yarn to the pre-cached dependencies inside of your project repo, typically <project-root>/.yarn/cache. Then you take the further step of specifying where the cache folder is, but in your case, it's in a non-modifiable location. Yarn can't operate like this, how is it going to download packages and then patch them? (As it does with typescript). I suppose if you have none such packages, this may work. But then how do you delete unused packages? yarn does this too when it detects unneeded dependencies.

jhudsoncedaron commented 6 hours ago

typically /.yarn/cache

The problem is the result of doing that is far larger than our git repo. The size of the resulting git repo would be unsustainable as this would add a GB to it and have to restore a GB on every build.

Yarn can't operate like this, how is it going to download packages and then patch them?

Expected behavior: download once, patch once, write once. Never touch it again. Yarn classic does this when you set yarn offline mirror.

But then how do you delete unused packages?

You don't. That's the whole point.

The entire purpose is the package exists even after it's gone, and we could ultimately call up a two decade old tree, install the old build tools, check out the stable branch and actually build it because we have all the packages going all the way back, forever.

Ultimately the cache points to a NAS drive; at $200/TB a lives forever cache is cheaper than any alternative.

SheepReaper commented 5 hours ago

hmm, that is an interesting use case. Have you tried using something like verdaccio? (proxy registry that caches packages as you pull them) then point your yarn to your private registry? Your use-case to me seems like pushing the limits of what a cache is in terms of yarn's concerns to the point of crossing over into the responsibilities of a registry....

jhudsoncedaron commented 5 hours ago

@SheepReaper : Not yet. We'd rather use a logic-free NAS drive than maintain server software if possible.

Wait? Isn't this what yarn config set yarn-offline-mirror is designed for in yarn 1, or was that intended to be something else?

SheepReaper commented 5 hours ago

that option to me is specific to yarn1. yarn2 combined the cache and the mirror. the cache is the mirror in yarn2

in yarn1, that cache contained unzipped packages (and the mirror, if configured, contained the zipped packages). in yarn2 the cache contains the zipped packages, and uses a virtual filesystem layer on top. <- the mirror became the cache, but we still call it the cache, and the "real" cache was virtualized.

SheepReaper commented 5 hours ago

ref here: https://yarnpkg.com/configuration/yarnrc#enableMirror for the only mention of mirror in the modern yarn config options.

arcanis commented 5 hours ago

You have two caches: global and local. cacheFolder is the local one, used when enableGlobalCache: false. globalFolder (to which Yarn adds a cache segment) is the global one. You can set the later to whatever path you want.

jhudsoncedaron commented 5 hours ago

@arcanis : The global cache can't be create no modify either. I checked.