Skip to content

Commit 1b6f3e4

Browse files
authored
feat: support libc field checks (#54)
1 parent 1b50fa7 commit 1b6f3e4

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: 22 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,35 @@ 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+
if (target.libc) {
38+
// libc checks only work in linux, any value is a failure if we aren't
39+
if (platform !== 'linux') {
40+
libcOk = false
41+
} else {
42+
const report = process.report.getReport()
43+
if (report.header?.glibcRuntimeVersion) {
44+
libcFamily = 'glibc'
45+
} else if (Array.isArray(report.sharedObjects) && report.sharedObjects.some(isMusl)) {
46+
libcFamily = 'musl'
47+
}
48+
libcOk = libcFamily ? checkList(libcFamily, target.libc) : false
49+
}
50+
}
51+
52+
if (!osOk || !cpuOk || !libcOk) {
3453
throw Object.assign(new Error('Unsupported platform'), {
3554
pkgid: target._id,
3655
current: {
3756
os: platform,
3857
cpu: arch,
58+
libc: libcFamily,
3959
},
4060
required: {
4161
os: target.os,
4262
cpu: target.cpu,
63+
libc: target.libc,
4364
},
4465
code: 'EBADPLATFORM',
4566
})

test/check-platform.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,81 @@ 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('fails when not in linux', (t) => {
66+
PLATFORM = 'darwin'
67+
68+
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
69+
'fails for glibc when not in linux')
70+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
71+
'fails for musl when not in linux')
72+
t.end()
73+
})
74+
75+
t.test('glibc', (t) => {
76+
PLATFORM = 'linux'
77+
78+
REPORT = {}
79+
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
80+
'fails when report is missing header property')
81+
82+
REPORT = { header: {} }
83+
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
84+
'fails when header is missing glibcRuntimeVersion property')
85+
86+
REPORT = { header: { glibcRuntimeVersion: '1' } }
87+
t.doesNotThrow(() => checkPlatform({ libc: 'glibc' }), 'allows glibc on glibc')
88+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
89+
'does not allow musl on glibc')
90+
91+
t.end()
92+
})
93+
94+
t.test('musl', (t) => {
95+
PLATFORM = 'linux'
96+
97+
REPORT = {}
98+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
99+
'fails when report is missing sharedObjects property')
100+
101+
REPORT = { sharedObjects: {} }
102+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
103+
'fails when sharedObjects property is not an array')
104+
105+
REPORT = { sharedObjects: [] }
106+
t.throws(() => checkPlatform({ libc: 'musl' }), { code: 'EBADPLATFORM' },
107+
'fails when sharedObjects does not contain musl')
108+
109+
REPORT = { sharedObjects: ['ld-musl-foo'] }
110+
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as ld-musl-')
111+
112+
REPORT = { sharedObjects: ['libc.musl-'] }
113+
t.doesNotThrow(() => checkPlatform({ libc: 'musl' }), 'allows musl on musl as libc.musl-')
114+
115+
t.throws(() => checkPlatform({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
116+
'does not allow glibc on musl')
117+
118+
t.end()
119+
})
120+
121+
t.end()
122+
})

0 commit comments

Comments
 (0)