Skip to content

Commit 65def1c

Browse files
Micha ReiserMicha Reiser
authored andcommitted
Check os and platform even when engines is not present in package.json
1 parent 32b7bb9 commit 65def1c

File tree

5 files changed

+162
-5
lines changed

5 files changed

+162
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ Please add one entry in this file for each change in Yarn's behavior. Use the sa
3434

3535
[#6942](https://github.com/yarnpkg/yarn/pull/6942) - [**John-David Dalton**](https://twitter.com/jdalton)
3636

37+
- Check `os` and `platform` requirements from `package.json` even when the package does not specify any `engines` requirements
38+
39+
[#6976](https://github.com/yarnpkg/yarn/pull/6976) - [**Micha Reiser**](https://github.com/MichaReiser)
40+
3741
## 1.13.0
3842

3943
- Implements a new `package.json` field: `peerDependenciesMeta`

__tests__/package-compatibility.js

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* @flow */
22

3-
import {testEngine} from '../src/package-compatibility.js';
3+
import {testEngine, shouldCheck} from '../src/package-compatibility.js';
44

55
test('node semver semantics', () => {
66
expect(testEngine('node', '^5.0.0', {node: '5.1.0'}, true)).toEqual(true);
@@ -19,3 +19,67 @@ test('node semver semantics', () => {
1919
test('ignore semver prerelease semantics for yarn', () => {
2020
expect(testEngine('yarn', '^1.3.0', {yarn: '1.4.1-20180208.2355'}, true)).toEqual(true);
2121
});
22+
23+
test('shouldCheck returns true if ignorePlatform is false and the manifest specifies an os or cpu requirement', () => {
24+
expect(
25+
shouldCheck(
26+
{
27+
os: ['darwin'],
28+
},
29+
{ignorePlatform: false, ignoreEngines: false},
30+
),
31+
).toBe(true);
32+
33+
expect(
34+
shouldCheck(
35+
{
36+
cpu: ['i32'],
37+
},
38+
{ignorePlatform: false, ignoreEngines: false},
39+
),
40+
).toBe(true);
41+
42+
expect(shouldCheck({}, {ignorePlatform: false, ignoreEngines: false})).toBe(false);
43+
44+
expect(
45+
shouldCheck(
46+
{
47+
os: [],
48+
cpu: [],
49+
},
50+
{ignorePlatform: false, ignoreEngines: false},
51+
),
52+
).toBe(false);
53+
54+
expect(
55+
shouldCheck(
56+
{
57+
cpu: ['i32'],
58+
os: ['darwin'],
59+
},
60+
{ignorePlatform: true, ignoreEngines: false},
61+
),
62+
).toBe(false);
63+
});
64+
65+
test('shouldCheck returns true if ignoreEngines is false and the manifest specifies engines', () => {
66+
expect(
67+
shouldCheck(
68+
{
69+
engines: {node: '>= 10'},
70+
},
71+
{ignorePlatform: false, ignoreEngines: false},
72+
),
73+
).toBe(true);
74+
75+
expect(shouldCheck({}, {ignorePlatform: false, ignoreEngines: false})).toBe(false);
76+
77+
expect(
78+
shouldCheck(
79+
{
80+
engines: {node: '>= 10'},
81+
},
82+
{ignorePlatform: false, ignoreEngines: true},
83+
),
84+
).toBe(false);
85+
});

packages/pkg-tests/pkg-tests-specs/sources/basic.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,5 +380,69 @@ module.exports = (makeTemporaryEnv: PackageDriver) => {
380380
},
381381
),
382382
);
383+
384+
test(
385+
`it should fail if the environment does not satisfy the os platform`,
386+
makeTemporaryEnv(
387+
{
388+
os: ['unicorn'],
389+
},
390+
async ({path, run, source}) => {
391+
await expect(run(`install`)).rejects.toThrow(/The platform "\w+" is incompatible with this module\./);
392+
},
393+
),
394+
);
395+
396+
test(
397+
`it should fail if the environment does not satisfy the cpu architecture`,
398+
makeTemporaryEnv(
399+
{
400+
cpu: ['unicorn'],
401+
},
402+
async ({path, run, source}) => {
403+
await expect(run(`install`)).rejects.toThrow(/The CPU architecture "\w+" is incompatible with this module\./);
404+
},
405+
),
406+
);
407+
408+
test(
409+
`it should fail if the environment does not satisfy the engine requirements`,
410+
makeTemporaryEnv(
411+
{
412+
engines: {
413+
node: "0.18.1"
414+
}
415+
},
416+
async ({path, run, source}) => {
417+
await expect(run(`install`)).rejects.toThrow(/The engine "node" is incompatible with this module\. Expected version "0.18.1"./);
418+
},
419+
),
420+
);
421+
422+
test(
423+
`it should not fail if the environment does not satisfy the os and cpu architecture but ignore platform is true`,
424+
makeTemporaryEnv(
425+
{
426+
os: ['unicorn'],
427+
},
428+
async ({path, run, source}) => {
429+
await run(`install`, '--ignore-platform');
430+
},
431+
),
432+
);
433+
434+
test(
435+
`it should not fail if the environment does not satisfy the engine requirements but ignore engines is true`,
436+
makeTemporaryEnv(
437+
{
438+
engines: {
439+
node: "0.18.1"
440+
}
441+
},
442+
async ({path, run, source}) => {
443+
await run(`install`, '--ignore-engines');
444+
},
445+
),
446+
);
383447
});
384448
};

src/cli/commands/install.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ export class Install {
572572
this.scripts.setArtifacts(artifacts);
573573
}
574574

575-
if (!this.flags.ignoreEngines && typeof manifest.engines === 'object') {
575+
if (compatibility.shouldCheck(manifest, this.flags)) {
576576
steps.push(async (curr: number, total: number) => {
577577
this.reporter.step(curr, total, this.reporter.lang('checkingManifest'), emoji.get('mag'));
578578
await this.checkCompatibility();

src/package-compatibility.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const VERSIONS = Object.assign({}, process.versions, {
1515
yarn: yarnVersion,
1616
});
1717

18+
type PartialManifest = $Shape<Manifest>;
19+
1820
function isValid(items: Array<string>, actual: string): boolean {
1921
let isNotWhitelist = true;
2022
let isBlacklist = false;
@@ -127,19 +129,19 @@ export function checkOne(info: Manifest, config: Config, ignoreEngines: boolean)
127129
};
128130

129131
const invalidPlatform =
130-
!config.ignorePlatform && Array.isArray(info.os) && info.os.length > 0 && !isValidPlatform(info.os);
132+
shouldCheckPlatform(info, config.ignorePlatform) && info.os != null && !isValidPlatform(info.os);
131133

132134
if (invalidPlatform) {
133135
pushError(reporter.lang('incompatibleOS', process.platform));
134136
}
135137

136-
const invalidCpu = !config.ignorePlatform && Array.isArray(info.cpu) && info.cpu.length > 0 && !isValidArch(info.cpu);
138+
const invalidCpu = shouldCheckCpu(info, config.ignorePlatform) && info.cpu != null && !isValidArch(info.cpu);
137139

138140
if (invalidCpu) {
139141
pushError(reporter.lang('incompatibleCPU', process.arch));
140142
}
141143

142-
if (!ignoreEngines && typeof info.engines === 'object') {
144+
if (shouldCheckEngines(info, ignoreEngines)) {
143145
for (const entry of entries(info.engines)) {
144146
let name = entry[0];
145147
const range = entry[1];
@@ -168,3 +170,26 @@ export function check(infos: Array<Manifest>, config: Config, ignoreEngines: boo
168170
checkOne(info, config, ignoreEngines);
169171
}
170172
}
173+
174+
function shouldCheckCpu(manifest: PartialManifest, ignorePlatform: boolean): boolean {
175+
return !ignorePlatform && Array.isArray(manifest.cpu) && manifest.cpu.length > 0;
176+
}
177+
178+
function shouldCheckPlatform(manifest: PartialManifest, ignorePlatform: boolean): boolean {
179+
return !ignorePlatform && Array.isArray(manifest.os) && manifest.os.length > 0;
180+
}
181+
182+
function shouldCheckEngines(manifest: PartialManifest, ignoreEngines: boolean): boolean {
183+
return !ignoreEngines && typeof manifest.engines === 'object';
184+
}
185+
186+
export function shouldCheck(
187+
manifest: PartialManifest,
188+
options: {ignoreEngines: boolean, ignorePlatform: boolean},
189+
): boolean {
190+
return (
191+
shouldCheckCpu(manifest, options.ignorePlatform) ||
192+
shouldCheckPlatform(manifest, options.ignorePlatform) ||
193+
shouldCheckEngines(manifest, options.ignoreEngines)
194+
);
195+
}

0 commit comments

Comments
 (0)