Skip to content

Commit 623dace

Browse files
committed
feat: support libc field checks
1 parent 1b50fa7 commit 623dace

File tree

3 files changed

+104
-4
lines changed

3 files changed

+104
-4
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ Errors have a `required` and `current` fields.
1111

1212
### .checkEngine(pkg, npmVer, nodeVer, force = false)
1313

14-
Check if node/npm version is supported by the package. If it isn't
15-
supported, an error is thrown.
14+
Check if a package's `engines.node` and `engines.npm` match the running system.
1615

1716
`force` argument will override the node version check, but not the npm
1817
version check, as this typically would indicate that the current version of
@@ -22,6 +21,8 @@ Error code: 'EBADENGINE'
2221

2322
### .checkPlatform(pkg, force)
2423

25-
Check if OS/Arch is supported by the package.
24+
Check if a package's `os`, `cpu` and `libc` match the running system.
25+
26+
`force` argument skips all checks.
2627

2728
Error code: 'EBADPLATFORM'

lib/index.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const checkEngine = (target, npmVer, nodeVer, force = false) => {
2020
}
2121
}
2222

23+
const isMusl = (file) => file.includes('libc.musl-') || file.includes('ld-musl-')
24+
2325
const checkPlatform = (target, force = false) => {
2426
if (force) {
2527
return
@@ -30,16 +32,31 @@ const checkPlatform = (target, force = false) => {
3032
const osOk = target.os ? checkList(platform, target.os) : true
3133
const cpuOk = target.cpu ? checkList(arch, target.cpu) : true
3234

33-
if (!osOk || !cpuOk) {
35+
let libcOk = true
36+
let libcFamily = null
37+
// libc checks only work in linux
38+
if (platform === 'linux' && target.libc) {
39+
const report = process.report.getReport()
40+
if (report.header?.glibcRuntimeVersion) {
41+
libcFamily = 'glibc'
42+
} else if (Array.isArray(report.sharedObjects) && report.sharedObjects.some(isMusl)) {
43+
libcFamily = 'musl'
44+
}
45+
libcOk = libcFamily ? checkList(libcFamily, target.libc) : false
46+
}
47+
48+
if (!osOk || !cpuOk || !libcOk) {
3449
throw Object.assign(new Error('Unsupported platform'), {
3550
pkgid: target._id,
3651
current: {
3752
os: platform,
3853
cpu: arch,
54+
libc: libcFamily,
3955
},
4056
required: {
4157
os: target.os,
4258
cpu: target.cpu,
59+
libc: target.libc,
4360
},
4461
code: 'EBADPLATFORM',
4562
})

test/check-platform.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,85 @@ t.test('os wrong (negation)', async t =>
4242

4343
t.test('nothing wrong (negation)', async t =>
4444
checkPlatform({ cpu: '!enten-cpu', os: '!enten-os' }))
45+
46+
t.test('libc', (t) => {
47+
let PLATFORM = ''
48+
49+
const _processPlatform = Object.getOwnPropertyDescriptor(process, 'platform')
50+
Object.defineProperty(process, 'platform', {
51+
enumerable: true,
52+
configurable: true,
53+
get: () => PLATFORM,
54+
})
55+
56+
let REPORT = {}
57+
const _processReport = process.report.getReport
58+
process.report.getReport = () => REPORT
59+
60+
t.teardown(() => {
61+
Object.defineProperty(process, 'platform', _processPlatform)
62+
process.report.getReport = _processReport
63+
})
64+
65+
t.test('ignored when not in linux', (t) => {
66+
PLATFORM = 'darwin'
67+
68+
t.doesNotThrow(() => checkPlatform({ libc: 'glibc' }), 'allows glibc')
69+
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl')
70+
t.end()
71+
})
72+
73+
t.test('report missing info', (t) => {
74+
PLATFORM = 'linux'
75+
76+
t.end()
77+
})
78+
79+
t.test('glibc', (t) => {
80+
PLATFORM = 'linux'
81+
82+
REPORT = {}
83+
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
84+
'fails when report is missing header property')
85+
86+
REPORT = { header: {} }
87+
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
88+
'fails when header is missing glibcRuntimeVersion property')
89+
90+
REPORT = { header: { glibcRuntimeVersion: '1' } }
91+
t.doesNotThrow(() => checkPlatform({ libc: 'glibc' }), 'allows glibc on glibc')
92+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
93+
'does not allow musl on glibc')
94+
95+
t.end()
96+
})
97+
98+
t.test('musl', (t) => {
99+
PLATFORM = 'linux'
100+
101+
REPORT = {}
102+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
103+
'fails when report is missing sharedObjects property')
104+
105+
REPORT = { sharedObjects: {} }
106+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
107+
'fails when sharedObjects property is not an array')
108+
109+
REPORT = { sharedObjects: [] }
110+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
111+
'fails when sharedObjects does not contain musl')
112+
113+
REPORT = { sharedObjects: ['ld-musl-foo'] }
114+
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as ld-musl-')
115+
116+
REPORT = { sharedObjects: ['libc.musl-'] }
117+
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as libc.musl-')
118+
119+
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
120+
'does not allow glibc on musl')
121+
122+
t.end()
123+
})
124+
125+
t.end()
126+
})

0 commit comments

Comments
 (0)