Skip to content

Commit b438c5d

Browse files
authored
Makes the exec: protocol respect enableScripts (#7090)
## What's the problem this PR addresses? The `exec:` protocol currently ignores the `enableScripts` settings. ## How did you fix it? Makes a check before evaluating the script. ## 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.
1 parent 487d1e6 commit b438c5d

4 files changed

Lines changed: 116 additions & 0 deletions

File tree

.yarn/versions/01a2baad.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
releases:
2+
"@yarnpkg/cli": minor
3+
"@yarnpkg/plugin-exec": minor
4+
5+
declined:
6+
- "@yarnpkg/plugin-compat"
7+
- "@yarnpkg/plugin-constraints"
8+
- "@yarnpkg/plugin-dlx"
9+
- "@yarnpkg/plugin-essentials"
10+
- "@yarnpkg/plugin-init"
11+
- "@yarnpkg/plugin-interactive-tools"
12+
- "@yarnpkg/plugin-nm"
13+
- "@yarnpkg/plugin-npm-cli"
14+
- "@yarnpkg/plugin-pack"
15+
- "@yarnpkg/plugin-patch"
16+
- "@yarnpkg/plugin-pnp"
17+
- "@yarnpkg/plugin-pnpm"
18+
- "@yarnpkg/plugin-stage"
19+
- "@yarnpkg/plugin-typescript"
20+
- "@yarnpkg/plugin-version"
21+
- "@yarnpkg/plugin-workspace-tools"
22+
- "@yarnpkg/builder"
23+
- "@yarnpkg/core"
24+
- "@yarnpkg/doctor"

packages/acceptance-tests/pkg-tests-specs/sources/protocols/execs.test.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ describe(`Protocols`, () => {
88
dependencies: {
99
[`dynamic-pkg`]: `exec:./genpkg.js`,
1010
},
11+
}, {
12+
enableScripts: true,
1113
}, async ({path, run, source}) => {
1214
await xfs.writeFilePromise(`${path}/genpkg.js`, `
1315
const {buildDir} = execEnv;
@@ -21,12 +23,88 @@ describe(`Protocols`, () => {
2123
}),
2224
);
2325

26+
test(
27+
`it should respect \`enableScripts\``,
28+
makeTemporaryEnv({
29+
dependencies: {
30+
[`dynamic-pkg`]: `exec:./genpkg.js`,
31+
},
32+
}, {
33+
enableScripts: false,
34+
}, async ({path, run}) => {
35+
await xfs.writeFilePromise(`${path}/genpkg.js`, `
36+
const {buildDir} = execEnv;
37+
fs.writeFileSync(path.join(buildDir, 'index.js'), 'module.exports = 42;');
38+
fs.writeFileSync(path.join(buildDir, 'package.json'), '{}');
39+
`);
40+
41+
await expect(run(`install`)).rejects.toThrow(/all scripts have been disabled/);
42+
}),
43+
);
44+
45+
test(
46+
`it should allow \`exec:\` when explicitly enabled via \`dependenciesMeta[].built\``,
47+
makeTemporaryEnv({
48+
dependencies: {
49+
[`dynamic-pkg`]: `exec:./genpkg.js`,
50+
},
51+
dependenciesMeta: {
52+
[`dynamic-pkg`]: {
53+
built: true,
54+
},
55+
},
56+
}, {
57+
enableScripts: false,
58+
}, async ({path, run, source}) => {
59+
await xfs.writeFilePromise(`${path}/genpkg.js`, `
60+
const {buildDir} = execEnv;
61+
fs.writeFileSync(path.join(buildDir, 'index.js'), 'module.exports = 42;');
62+
fs.writeFileSync(path.join(buildDir, 'package.json'), '{}');
63+
`);
64+
65+
await run(`install`);
66+
67+
await expect(source(`require('dynamic-pkg')`)).resolves.toEqual(42);
68+
}),
69+
);
70+
71+
test(
72+
`it should prevent non-workspaces from depending on \`exec:\` packages`,
73+
makeTemporaryEnv({
74+
dependencies: {
75+
[`wrapper`]: `file:./wrapper`,
76+
},
77+
}, {
78+
enableScripts: true,
79+
}, async ({path, run}) => {
80+
await xfs.mkdirPromise(`${path}/wrapper`);
81+
82+
await xfs.writeFilePromise(`${path}/wrapper/package.json`, JSON.stringify({
83+
name: `wrapper`,
84+
version: `1.0.0`,
85+
dependencies: {
86+
[`dynamic-pkg`]: `exec:./genpkg.js`,
87+
},
88+
}));
89+
90+
await xfs.writeFilePromise(`${path}/wrapper/genpkg.js`, `
91+
const {buildDir} = execEnv;
92+
fs.writeFileSync(path.join(buildDir, 'index.js'), 'module.exports = 42;');
93+
fs.writeFileSync(path.join(buildDir, 'package.json'), '{}');
94+
`);
95+
96+
await expect(run(`install`)).rejects.toThrow(/only workspaces can depend on exec: packages/);
97+
}),
98+
);
99+
24100
test(
25101
`it should correctly inject the built-in modules as global variables`,
26102
makeTemporaryEnv({
27103
dependencies: {
28104
[`dynamic-pkg`]: `exec:./genpkg.js`,
29105
},
106+
}, {
107+
enableScripts: true,
30108
}, async ({path, run, source}) => {
31109
await xfs.writeFilePromise(`${path}/genpkg.js`, `
32110
const {buildDir} = execEnv;
@@ -50,6 +128,8 @@ describe(`Protocols`, () => {
50128
dependencies: {
51129
[`dynamic-pkg`]: `exec:./genpkg.js`,
52130
},
131+
}, {
132+
enableScripts: true,
53133
}, async ({path, run, source}) => {
54134
await xfs.writeFilePromise(`${path}/genpkg.js`, `
55135
const {buildDir} = execEnv;
@@ -73,6 +153,8 @@ describe(`Protocols`, () => {
73153
dependencies: {
74154
[`dynamic-pkg`]: `exec:./genpkg.js`,
75155
},
156+
}, {
157+
enableScripts: true,
76158
}, async ({path, run, source}) => {
77159
await xfs.writeFilePromise(`${path}/genpkg.js`, `
78160
const {buildDir} = execEnv;

packages/plugin-exec/sources/ExecFetcher.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {execUtils, scriptUtils, structUtils, tgzUtils} from '@yarnpkg/core';
22
import {Locator, formatUtils} from '@yarnpkg/core';
33
import {Fetcher, FetchOptions, MinimalFetchOptions} from '@yarnpkg/core';
4+
import {MessageName, ReportError} from '@yarnpkg/core';
45
import {PortablePath, npath, ppath, xfs, NativePath} from '@yarnpkg/fslib';
56

67
import {PROTOCOL} from './constants';
@@ -64,6 +65,11 @@ export class ExecFetcher implements Fetcher {
6465
}
6566

6667
private async fetchFromDisk(locator: Locator, opts: FetchOptions) {
68+
const dependencyMeta = opts.project.getDependencyMeta(locator, null);
69+
70+
if (!opts.project.configuration.get(`enableScripts`) && !dependencyMeta.built)
71+
throw new ReportError(MessageName.DISABLED_BUILD_SCRIPTS, `${structUtils.prettyLocator(opts.project.configuration, locator)} can't be built with the exec: protocol because all scripts have been disabled.`);
72+
6773
const generatorFile = await loadGeneratorFile(locator.reference, PROTOCOL, opts);
6874

6975
return xfs.mktempPromise(async generatorDir => {

packages/plugin-exec/sources/ExecResolver.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Resolver, ResolveOptions, MinimalResolveOptions} from '@yarnpkg/core';
22
import {Descriptor, Locator, Manifest, Package} from '@yarnpkg/core';
33
import {LinkType} from '@yarnpkg/core';
4+
import {MessageName, ReportError} from '@yarnpkg/core';
45
import {miscUtils, structUtils, hashUtils} from '@yarnpkg/core';
56

67
import {PROTOCOL} from './constants';
@@ -29,6 +30,9 @@ export class ExecResolver implements Resolver {
2930
}
3031

3132
bindDescriptor(descriptor: Descriptor, fromLocator: Locator, opts: MinimalResolveOptions) {
33+
if (opts.project.tryWorkspaceByLocator(fromLocator) === null)
34+
throw new ReportError(MessageName.INVALID_MANIFEST, `${structUtils.prettyLocator(opts.project.configuration, fromLocator)} lists ${structUtils.prettyDescriptor(opts.project.configuration, descriptor)} as dependency, but only workspaces can depend on exec: packages.`);
35+
3236
return structUtils.bindDescriptor(descriptor, {
3337
locator: structUtils.stringifyLocator(fromLocator),
3438
});

0 commit comments

Comments
 (0)