@wwkit/cft 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wwkit/cft",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "author": "bluesliu <langcai163@163.com>",
5
5
  "description": "Chrome for Testing (CFT) installer — download, extract, chmod, dep-check",
6
6
  "type": "module",
@@ -11,6 +11,7 @@
11
11
  "files": [
12
12
  "bin",
13
13
  "src",
14
+ "scripts",
14
15
  "README.md",
15
16
  "LICENSE"
16
17
  ],
@@ -35,12 +36,13 @@
35
36
  },
36
37
  "dependencies": {
37
38
  "extract-zip": "^2.0.1",
38
- "@wwkit/shared": "1.0.1"
39
+ "@wwkit/shared": "1.0.2"
39
40
  },
40
41
  "devDependencies": {
41
42
  "jest": "^29.7.0"
42
43
  },
43
44
  "scripts": {
45
+ "postinstall": "node scripts/postinstall.js",
44
46
  "start": "node bin/index.js",
45
47
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --no-cache tests/unit",
46
48
  "test:all": "node --experimental-vm-modules node_modules/jest/bin/jest.js --no-cache tests/unit",
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process'
4
+ import path from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
8
+ const bin = path.join(__dirname, '..', 'bin', 'index.js')
9
+
10
+ const result = spawnSync('node', [bin, 'install'], { stdio: 'inherit' })
11
+ if (result.status !== 0) {
12
+ console.error('cft: postinstall (cft install) failed')
13
+ process.exit(1)
14
+ }
package/src/depchecker.js CHANGED
@@ -17,13 +17,13 @@ const APT_MAP = {
17
17
  'libasound.so.2': 'libasound2t64',
18
18
  'libpango-1.0.so.0': 'libpango-1.0-0',
19
19
  'libcairo.so.2': 'libcairo2',
20
- 'libpangoft2-1.0.so.0': 'libpango1.0-0',
20
+ 'libpangoft2-1.0.so.0': 'libpangoft2-1.0-0',
21
21
  'libharfbuzz.so.0': 'libharfbuzz0b',
22
- 'libxss.so.1': 'libxss1',
22
+ 'libXss.so.1': 'libxss1',
23
23
  'libatspi.so.0': 'libatspi2.0-0t64',
24
24
  'libxcb.so.1': 'libxcb1',
25
- 'libx11.so.6': 'libx11-6',
26
- 'libxext.so.6': 'libxext6',
25
+ 'libX11.so.6': 'libx11-6',
26
+ 'libXext.so.6': 'libxext6',
27
27
  'libnssutil3.so': 'libnss3',
28
28
  'libsmime3.so': 'libnss3',
29
29
  'libnspr4.so': 'libnspr4',
@@ -32,7 +32,6 @@ const APT_MAP = {
32
32
  'libxrender.so.1': 'libxrender1',
33
33
  'libglib-2.0.so.0': 'libglib2.0-0',
34
34
  'libgobject-2.0.so.0': 'libglib2.0-0',
35
- 'libpango-1.0-0.so.0': 'libpango-1.0-0',
36
35
  }
37
36
 
38
37
  const DNF_MAP = {
@@ -52,11 +51,11 @@ const DNF_MAP = {
52
51
  'libcairo.so.2': 'cairo',
53
52
  'libpangoft2-1.0.so.0': 'pango',
54
53
  'libharfbuzz.so.0': 'harfbuzz',
55
- 'libxss.so.1': 'libXScrnSaver',
54
+ 'libXss.so.1': 'libXScrnSaver',
56
55
  'libatspi.so.0': 'at-spi2-core',
57
56
  'libxcb.so.1': 'libxcb',
58
- 'libx11.so.6': 'libX11',
59
- 'libxext.so.6': 'libXext',
57
+ 'libX11.so.6': 'libX11',
58
+ 'libXext.so.6': 'libXext',
60
59
  'libnssutil3.so': 'nss',
61
60
  'libsmime3.so': 'nss',
62
61
  'libnspr4.so': 'nspr',
@@ -71,6 +70,8 @@ export function isSupported() {
71
70
  return process.platform === 'linux'
72
71
  }
73
72
 
73
+ export { APT_MAP, DNF_MAP }
74
+
74
75
  export function check(binaryPath) {
75
76
  if (!isSupported()) return []
76
77
  if (!binaryPath || !fs.existsSync(binaryPath)) return []
@@ -81,7 +82,7 @@ export function check(binaryPath) {
81
82
  }
82
83
  let result
83
84
  try {
84
- result = execSync(`ldd "${binaryPath}"`, { captureOutput: true, timeout: 20000 })
85
+ result = execSync(`ldd "${binaryPath}"`, { stdio: 'pipe', timeout: 20000 })
85
86
  } catch {
86
87
  return []
87
88
  }
@@ -95,6 +96,9 @@ export function check(binaryPath) {
95
96
  }
96
97
 
97
98
  export function mapToPackages(libs, packageManager) {
99
+ if (packageManager !== 'apt' && packageManager !== 'dnf') {
100
+ return { packages: [], unknown: [...libs] }
101
+ }
98
102
  const table = packageManager === 'apt' ? APT_MAP : DNF_MAP
99
103
  const packages = []
100
104
  const unknown = []
@@ -111,23 +115,36 @@ export function mapToPackages(libs, packageManager) {
111
115
  return { packages, unknown }
112
116
  }
113
117
 
114
- export function install(packages, packageManager) {
115
- if (!packages.length) return true
116
- const cmd = packageManager === 'apt'
117
- ? ['apt-get', 'install', '-y', ...packages]
118
- : ['dnf', 'install', '-y', ...packages]
118
+ export function fallbackPackages(packages, packageManager) {
119
+ if (packageManager !== 'apt') return []
120
+ const fb = packages.map((p) => p.replace(/t64$/, ''))
121
+ return fb.some((p, i) => p !== packages[i]) ? fb : []
122
+ }
123
+
124
+ function runInstall(packages, packageManager) {
125
+ const tool = packageManager === 'apt' ? 'apt-get' : 'dnf'
126
+ const args = [tool, 'install', '-y', ...packages]
127
+ const cmd = process.getuid && process.getuid() === 0 ? args : ['sudo', ...args]
119
128
  try {
120
- const result = execSync(['sudo', ...cmd].join(' '), { timeout: 600000 })
129
+ execSync(cmd.join(' '), { stdio: 'pipe', timeout: 600000 })
121
130
  return true
122
131
  } catch {
123
132
  return false
124
133
  }
125
134
  }
126
135
 
136
+ export function install(packages, packageManager) {
137
+ if (!packages.length) return true
138
+ if (packageManager !== 'apt' && packageManager !== 'dnf') return false
139
+ if (runInstall(packages, packageManager)) return true
140
+ const fb = fallbackPackages(packages, packageManager)
141
+ return fb.length ? runInstall(fb, packageManager) : false
142
+ }
143
+
127
144
  function detectPackageManager() {
128
145
  try { execSync('which apt-get', { stdio: 'pipe' }); return 'apt' } catch {}
129
146
  try { execSync('which dnf', { stdio: 'pipe' }); return 'dnf' } catch {}
130
- return 'apt'
147
+ return null
131
148
  }
132
149
 
133
150
  export { detectPackageManager }
package/src/installer.js CHANGED
@@ -146,6 +146,10 @@ function checkSystemDeps(chromeExe, driverExe) {
146
146
  if (!exePath || !fs.existsSync(exePath)) continue
147
147
  const missing = checkDeps(exePath)
148
148
  if (!missing.length) continue
149
+ if (!packageManager) {
150
+ log(`${label}: 缺少系统依赖 ${missing.join(', ')},未检测到 apt-get/dnf,请手动安装`)
151
+ continue
152
+ }
149
153
  const { packages, unknown } = mapToPackages(missing, packageManager)
150
154
  if (packages.length) {
151
155
  log(`${label}: 自动安装系统依赖 ${packages.join(', ')}`)
@@ -168,6 +172,10 @@ export async function install() {
168
172
  throw new Error(`Unsupported OS: ${osType}`)
169
173
  }
170
174
 
175
+ if (process.platform === 'linux' && process.arch !== 'x64') {
176
+ throw new Error(`Chrome for Testing 仅提供 linux64 (x86_64) 构建,不支持 linux-${process.arch}`)
177
+ }
178
+
171
179
  const { installed, info } = checkInstalled(version, location, osType)
172
180
  if (installed) {
173
181
  log(`CFT ${version} 已安装,跳过`)