Skip to content

Commit fe7c10a

Browse files
fix(fslib): handle symlinks to zip files (#6603)
This is the same as #5474 but allows edits by maintainer **What's the problem this PR addresses?** <!-- Describe the rationale of your PR. --> Yarn shouldn't care if zips in the cache are symlinks or not. The only reason it doesn't work is that finding the mount point skips zips if they're not regular files. This used to be supported until it was removed for "performance" reasons in #1474. The bulk of the logic in the linked PR involves resolving the real path of the symlink but I don't think that's needed for this? <!-- Link all issues that it closes. (Closes/Resolves #xxxx.) --> Resolves #3514 Closes #5474 (supersedes it) **How did you fix it?** I switched the check to use `stat` from `lstat`. **Checklist** <!--- Don't worry if you miss something, chores are automatically tested. --> <!--- This checklist exists to help you remember doing the chores when you submit a PR. --> <!--- Put an `x` in all the boxes that apply. --> - [X] I have read the [Contributing Guide](https://yarnpkg.com/advanced/contributing). <!-- See https://yarnpkg.com/advanced/contributing#preparing-your-pr-to-be-released for more details. --> <!-- Check with `yarn version check` and fix with `yarn version check -i` --> - [X] I have set the packages that need to be released for my changes to be effective. <!-- The "Testing chores" workflow validates that your PR follows our guidelines. --> <!-- If it doesn't pass, click on it to see details as to what your PR might be missing. --> - [x] I will check that all automated PR checks pass before the PR gets reviewed. --------- Co-authored-by: merceyz <[email protected]>
1 parent 9155359 commit fe7c10a

File tree

10 files changed

+111
-4
lines changed

10 files changed

+111
-4
lines changed

.pnp.cjs

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.yarn/versions/757578fa.yml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
"@yarnpkg/core": patch
4+
"@yarnpkg/fslib": patch
5+
"@yarnpkg/pnp": patch
6+
7+
declined:
8+
- "@yarnpkg/plugin-compat"
9+
- "@yarnpkg/plugin-constraints"
10+
- "@yarnpkg/plugin-dlx"
11+
- "@yarnpkg/plugin-essentials"
12+
- "@yarnpkg/plugin-exec"
13+
- "@yarnpkg/plugin-file"
14+
- "@yarnpkg/plugin-git"
15+
- "@yarnpkg/plugin-github"
16+
- "@yarnpkg/plugin-http"
17+
- "@yarnpkg/plugin-init"
18+
- "@yarnpkg/plugin-interactive-tools"
19+
- "@yarnpkg/plugin-link"
20+
- "@yarnpkg/plugin-nm"
21+
- "@yarnpkg/plugin-npm"
22+
- "@yarnpkg/plugin-npm-cli"
23+
- "@yarnpkg/plugin-pack"
24+
- "@yarnpkg/plugin-patch"
25+
- "@yarnpkg/plugin-pnp"
26+
- "@yarnpkg/plugin-pnpm"
27+
- "@yarnpkg/plugin-stage"
28+
- "@yarnpkg/plugin-typescript"
29+
- "@yarnpkg/plugin-version"
30+
- "@yarnpkg/plugin-workspace-tools"
31+
- vscode-zipfs
32+
- "@yarnpkg/builder"
33+
- "@yarnpkg/doctor"
34+
- "@yarnpkg/extensions"
35+
- "@yarnpkg/libzip"
36+
- "@yarnpkg/nm"
37+
- "@yarnpkg/pnpify"
38+
- "@yarnpkg/sdks"
39+
- "@yarnpkg/shell"

packages/acceptance-tests/pkg-tests-specs/sources/commands/install.test.ts

+27
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,33 @@ describe(`Commands`, () => {
109109
}),
110110
);
111111

112+
tests.testIf(
113+
() => process.platform !== `win32`,
114+
`it should install from zips that are symlinks`,
115+
makeTemporaryEnv({
116+
dependencies: {
117+
[`no-deps`]: `1.0.0`,
118+
},
119+
}, async ({path, run, source}) => {
120+
await run(`install`);
121+
122+
const allFiles = await xfs.readdirPromise(ppath.join(path, `.yarn/cache`));
123+
const zipFiles = allFiles.filter(file => file.endsWith(`.zip`));
124+
125+
await xfs.mkdirPromise(ppath.join(path, `store`));
126+
for (const filename of zipFiles) {
127+
const zipFile = ppath.join(path, `.yarn/cache`, filename);
128+
const storePath = ppath.join(path, `store`, filename);
129+
await xfs.movePromise(zipFile, storePath);
130+
await xfs.symlinkPromise(storePath, zipFile);
131+
}
132+
133+
await xfs.removePromise(ppath.join(path, Filename.pnpCjs));
134+
135+
await run(`install`, `--immutable`);
136+
}),
137+
);
138+
112139
test(
113140
`it should refuse to create a lockfile when using --immutable`,
114141
makeTemporaryEnv({

packages/acceptance-tests/pkg-tests-specs/sources/pnp.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -2253,4 +2253,31 @@ describe(`Plug'n'Play`, () => {
22532253
});
22542254
}),
22552255
);
2256+
2257+
testIf(
2258+
() => process.platform !== `win32`,
2259+
`it can resolve files from zips that are symlinks`,
2260+
makeTemporaryEnv({
2261+
dependencies: {
2262+
[`no-deps`]: `1.0.0`,
2263+
},
2264+
}, async ({path, run, source}) => {
2265+
await run(`install`);
2266+
2267+
const allFiles = await xfs.readdirPromise(ppath.join(path, `.yarn/cache`));
2268+
const zipFiles = allFiles.filter(file => file.endsWith(`.zip`));
2269+
2270+
await xfs.mkdirPromise(ppath.join(path, `store`));
2271+
for (const filename of zipFiles) {
2272+
const zipFile = ppath.join(path, `.yarn/cache`, filename);
2273+
const storePath = ppath.join(path, `store`, filename);
2274+
await xfs.movePromise(zipFile, storePath);
2275+
await xfs.symlinkPromise(storePath, zipFile);
2276+
}
2277+
2278+
await expect(
2279+
source(`require('no-deps')`),
2280+
).resolves.toEqual({name: `no-deps`, version: `1.0.0`});
2281+
}),
2282+
);
22562283
});

packages/yarnpkg-core/sources/worker-zip/index.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/yarnpkg-fslib/sources/MountFS.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ export class MountFS<MountedFS extends MountableFS> extends BasePortableFakeFS {
10131013
continue;
10141014

10151015
try {
1016-
if (this.typeCheck !== null && (this.baseFs.lstatSync(filePath).mode & constants.S_IFMT) !== this.typeCheck) {
1016+
if (this.typeCheck !== null && (this.baseFs.statSync(filePath).mode & constants.S_IFMT) !== this.typeCheck) {
10171017
this.notMount.add(filePath);
10181018
continue;
10191019
}

packages/yarnpkg-libzip/tests/ZipOpenFS.test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ export const ZIP_DIR3 = ppath.join(
1313
npath.toPortablePath(__dirname),
1414
`fixtures/foo.hiddenzip` as Filename,
1515
);
16+
export const ZIP_DIR4 = ppath.join(
17+
npath.toPortablePath(__dirname),
18+
`fixtures/symlink.zip` as Filename,
19+
);
1620

1721
export const ZIP_FILE1 = ppath.join(ZIP_DIR1, `foo.txt`);
1822
export const ZIP_FILE2 = ppath.join(ZIP_DIR2, `foo.txt`);
1923
export const ZIP_FILE3 = ppath.join(ZIP_DIR3, `foo.txt`);
24+
export const ZIP_FILE4 = ppath.join(ZIP_DIR4, `foo.txt`);
2025

2126
afterEach(() => {
2227
jest.useRealTimers();
@@ -81,6 +86,14 @@ describe(`ZipOpenFS`, () => {
8186
fs.discardAndClose();
8287
});
8388

89+
it(`can read from a zip file that's a symlink`, () => {
90+
const fs = new ZipOpenFS();
91+
92+
expect(fs.readFileSync(ZIP_FILE4, `utf8`)).toEqual(`foo\n`);
93+
94+
fs.discardAndClose();
95+
});
96+
8497
it(`doesn't close a ZipFS instance with open handles`, () => {
8598
const fs = new ZipOpenFS({maxOpenFiles: 1});
8699

Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
symlink-source.zip

packages/yarnpkg-pnp/sources/hook.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)